1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-08-18 15:08:22 +00:00
seahub/media/aloha-0.22.7/lib/css.js
llj 720ac28c22 [aloha] upgraded to 0.22.7 and added textcolor plugin
* textcolor: fixed plugin bugs, added translation for zh
* image: fixed default.jpg src bug
* added 'ru' support for seaf edit
* rm aloha-0.22.3 and ununsed files in aloha-0.22.7
2013-01-15 14:48:04 +08:00

456 lines
14 KiB
JavaScript

/** MIT License (c) copyright B Cavalier & J Hann */
/**
* curl css! plugin
*
* Licensed under the MIT License at:
* http://www.opensource.org/licenses/mit-license.php
*
*/
(function (global) {
"use strict";
/*
* AMD css! plugin
* This plugin will load and wait for css files. This could be handy when
* loading css files as part of a layer or as a way to apply a run-time theme.
* Most browsers do not support the load event handler of the link element.
* Therefore, we have to use other means to detect when a css file loads.
* (The HTML5 spec states that the LINK element should have a load event, but
* not even Chrome 8 or FF4b7 have it, yet.
* http://www.w3.org/TR/html5/semantics.html#the-link-element)
*
* This plugin tries to use the load event and a universal work-around when
* it is invoked the first time. If the load event works, it is used on
* every successive load. Therefore, browsers that support the load event will
* just work (i.e. no need for hacks!). FYI, Feature-detecting the load
* event is tricky since most browsers have a non-functional onload property.
*
* The universal work-around watches a stylesheet until its rules are
* available (not null or undefined). There are nuances, of course, between
* the various browsers. The isLinkReady function accounts for these.
*
* Note: it appears that all browsers load @import'ed stylesheets before
* fully processing the rest of the importing stylesheet. Therefore, we
* don't need to find and wait for any @import rules explicitly.
*
* Note #2: for Opera compatibility, stylesheets must have at least one rule.
* AFAIK, there's no way to tell the difference between an empty sheet and
* one that isn't finished loading in Opera (XD or same-domain).
*
* Options:
* !nowait - does not wait for the stylesheet to be parsed, just loads it
*
* Global configuration options:
*
* cssDeferLoad: Boolean. You can also instruct this plugin to not wait
* for css resources. They'll get loaded asap, but other code won't wait
* for them. This is just like using the !nowait option on every css file.
*
* cssWatchPeriod: if direct load-detection techniques fail, this option
* determines the msec to wait between brute-force checks for rules. The
* default is 50 msec.
*
* You may specify an alternate file extension:
* require('css!myproj/component.less') // --> myproj/component.less
* require('css!myproj/component.scss') // --> myproj/component.scss
*
* When using alternative file extensions, be sure to serve the files from
* the server with the correct mime type (text/css) or some browsers won't
* parse them, causing an error in the plugin.
*
* usage:
* require(['css!myproj/comp']); // load and wait for myproj/comp.css
* define(['css!some/folder/file'], {}); // wait for some/folder/file.css
* require(['css!myWidget!nowait']);
*
* Tested in:
* Firefox 1.5, 2.0, 3.0, 3.5, 3.6, and 4.0b6
* Safari 3.0.4, 3.2.1, 5.0
* Chrome 7 (8+ is partly b0rked)
* Opera 9.52, 10.63, and Opera 11.00
* IE 6, 7, and 8
* Netscape 7.2 (WTF? SRSLY!)
* Does not work in Safari 2.x :(
* In Chrome 8+, there's no way to wait for cross-domain (XD) stylesheets.
* See comments in the code below.
* TODO: figure out how to be forward-compatible when browsers support HTML5's
* load handler without breaking IE and Opera
*/
var
// compressibility shortcuts
onreadystatechange = 'onreadystatechange',
onload = 'onload',
createElement = 'createElement',
// failed is true if RequireJS threw an exception
failed = false,
undef,
insertedSheets = {},
features = {
// true if the onload event handler works
// "event-link-onload" : false
},
// this actually tests for absolute urls and root-relative urls
// they're both non-relative
nonRelUrlRe = /^\/|^[^:]*:\/\//,
// Note: this will fail if there are parentheses in the url
findUrlRx = /url\s*\(['"]?([^'"\)]*)['"]?\)/g,
// doc will be undefined during a build
doc = global.document,
// find the head element and set it to it's standard property if nec.
head,
// collection of modules that have been written to the built file
built = {};
if (doc) {
head = doc.head || (doc.head = doc.getElementsByTagName('head')[0]);
}
function has (feature) {
return features[feature];
}
/***** load-detection functions *****/
function loadHandler (params, cb) {
// We're using 'readystatechange' because IE and Opera happily support both
var link = params.link;
link[onreadystatechange] = link[onload] = function () {
if (!link.readyState || link.readyState == 'complete') {
features["event-link-onload"] = true;
cleanup(params);
cb();
}
};
}
function nameWithExt (name, defaultExt) {
return name.lastIndexOf('.') <= name.lastIndexOf('/') ?
name + '.' + defaultExt : name;
}
function parseSuffixes (name) {
// creates a dual-structure: both an array and a hashmap
// suffixes[0] is the actual name
var parts = name.split('!'),
suf, i = 1, pair;
while ((suf = parts[i++])) { // double-parens to avoid jslint griping
pair = suf.split('=', 2);
parts[pair[0]] = pair.length == 2 ? pair[1] : true;
}
return parts;
}
var collectorSheet;
function createLink (doc, optHref) {
// detect if we need to avoid 31-sheet limit in IE (how to detect this for realz?)
if (document.createStyleSheet) {
if (!collectorSheet) {
collectorSheet = document.createStyleSheet();
}
if (document.styleSheets.length >= 30) {
moveLinksToCollector();
}
}
var link = doc[createElement]('link');
link.rel = "stylesheet";
link.type = "text/css";
link.setAttribute('_curl_movable', true);
if (optHref) {
link.href = optHref;
}
return link;
}
var testEl;
function styleIsApplied () {
// Chrome 8 hax0rs!
// This is an ugly hack needed by Chrome 8+ which no longer waits for rules
// to be applied to the document before exposing them to javascript.
// Unfortunately, this routine will never fire for XD stylesheets since
// Chrome will also throw an exception if attempting to access the rules
// of an XD stylesheet. Therefore, there's no way to detect the load
// event of XD stylesheets until Google fixes this, preferably with a
// functional load event! As a work-around, use domReady() before
// rendering widgets / components that need the css to be ready.
if (!testEl) {
testEl = doc[createElement]('div');
testEl.id = '_cssx_load_test';
head.appendChild(testEl);
}
return doc.defaultView.getComputedStyle(testEl, null).marginTop == '-5px';
}
function isLinkReady (link) {
// This routine is a bit fragile: browser vendors seem oblivious to
// the need to know precisely when stylesheets load. Therefore, we need
// to continually test beta browsers until they all support the LINK load
// event like IE and Opera.
var sheet, rules, ready = false;
try {
// webkit's and IE's sheet is null until the sheet is loaded
sheet = link.sheet || link.styleSheet;
// mozilla's sheet throws an exception if trying to access xd rules
rules = sheet.cssRules || sheet.rules;
// webkit's xd sheet returns rules == null
// opera's sheet always returns rules, but length is zero until loaded
// friggin IE doesn't count @import rules as rules, but IE should
// never hit this routine anyways.
ready = rules ?
rules.length > 0 : // || (sheet.imports && sheet.imports.length > 0) :
rules !== undef;
// thanks, Chrome 8+, for this lovely hack. TODO: find a better way
if (ready && {}.toString.call(window.chrome) == '[object Chrome]') {
// fwiw, we'll never get this far if this is an XD stylesheet
sheet.insertRule('#_cssx_load_test{margin-top:-5px;}', 0);
ready = styleIsApplied();
sheet.deleteRule(0);
}
}
catch (ex) {
// 1000 means FF loaded an xd stylesheet
// other browsers just throw a security error here (IE uses the phrase 'Access is denied')
ready = (ex.code == 1000) || (ex.message.match(/security|denied/i));
}
return ready;
}
function ssWatcher (params, cb) {
// watches a stylesheet for loading signs.
if (isLinkReady(params.link)) {
cleanup(params);
cb();
}
else if (!failed) {
setTimeout(function () { ssWatcher(params, cb); }, params.wait);
}
}
function loadDetector (params, cb) {
// It would be nice to use onload everywhere, but the onload handler
// only works in IE and Opera.
// Detecting it cross-browser is completely impossible, too, since
// THE BROWSERS ARE LIARS! DON'T TELL ME YOU HAVE AN ONLOAD PROPERTY
// IF IT DOESN'T DO ANYTHING!
var loaded;
function cbOnce () {
if (!loaded) {
loaded = true;
cb();
}
}
loadHandler(params, cbOnce);
if (!has("event-link-onload")) {
ssWatcher(params, cbOnce);
}
}
function cleanup (params) {
var link = params.link;
link[onreadystatechange] = link[onload] = null;
}
function moveLinksToCollector () {
// IE 6-8 fails when over 31 sheets, so we collect them.
// Note: this hack relies on proper cache headers.
var link, links, collector, pos = 0;
collector = collectorSheet;
collectorSheet = null; // so a new one will be created
links = document.getElementsByTagName('link');
while ((link = links[pos])) {
if (link.getAttribute('_curl_movable')) {
// move to the collectorSheet (note: bad cache directive will cause a re-download)
collector.addImport(link.href);
// remove from document
link.parentNode && link.parentNode.removeChild(link);
}
else {
// skip this sheet
pos++;
}
}
}
/***** style element functions *****/
var currentStyle;
function translateUrls (cssText, baseUrl) {
return cssText.replace(findUrlRx, function (all, url) {
return 'url("' + translateUrl(url, baseUrl) + '")';
});
}
function translateUrl (url, parentPath) {
// if this is a relative url
if (!nonRelUrlRe.test(url)) {
// append path onto it
url = parentPath + url;
}
return url;
}
function createStyle (cssText) {
clearTimeout(createStyle.debouncer);
if (createStyle.accum) {
createStyle.accum.push(cssText);
}
else {
createStyle.accum = [cssText];
currentStyle = doc.createStyleSheet ? doc.createStyleSheet() :
head.appendChild(doc.createElement('style'));
}
createStyle.debouncer = setTimeout(function () {
// Note: IE 6-8 won't accept the W3C method for inserting css text
var style, allCssText;
style = currentStyle;
currentStyle = undef;
allCssText = createStyle.accum.join('\n');
createStyle.accum = undef;
// for safari which chokes on @charset "UTF-8";
allCssText = allCssText.replace(/.+charset[^;]+;/g, '');
// TODO: hoist all @imports to the top of the file to follow w3c spec
'cssText' in style ? style.cssText = allCssText :
style.appendChild(doc.createTextNode(allCssText));
}, 0);
return currentStyle;
}
function createSheetProxy (sheet) {
return {
cssRules: function () {
return sheet.cssRules || sheet.rules;
},
insertRule: sheet.insertRule || function (text, index) {
var parts = text.split(/\{|\}/g);
sheet.addRule(parts[0], parts[1], index);
return index;
},
deleteRule: sheet.deleteRule || function (index) {
sheet.removeRule(index);
return index;
},
sheet: function () {
return sheet;
}
};
}
/***** finally! the actual plugin *****/
define(/*=='css',==*/ {
'normalize': function (resourceId, normalize) {
var resources, normalized;
if (!resourceId) return resourceId;
resources = resourceId.split(",");
normalized = [];
for (var i = 0, len = resources.length; i < len; i++) {
normalized.push(normalize(resources[i]));
}
return normalized.join(',');
},
'load': function (resourceId, require, callback, config) {
var resources = (resourceId || '').split(","),
loadingCount = resources.length;
// all detector functions must ensure that this function only gets
// called once per stylesheet!
function loaded () {
// load/error handler may have executed before stylesheet is
// fully parsed / processed in Opera, so use setTimeout.
// Opera will process before the it next enters the event loop
// (so 0 msec is enough time).
if(--loadingCount == 0){
// TODO: move this setTimeout to loadHandler
setTimeout(function () { callback(createSheetProxy(link.sheet || link.styleSheet)); }, 0);
}
}
if (!resourceId) {
// return the run-time API
callback({
'translateUrls': function (cssText, baseId) {
var baseUrl;
baseUrl = require['toUrl'](baseId);
baseUrl = baseUrl.substr(0, baseUrl.lastIndexOf('/') + 1);
return translateUrls(cssText, baseUrl);
},
'injectStyle': function (cssText) {
return createStyle(cssText);
},
'proxySheet': function (sheet) {
// for W3C, `sheet` is a reference to a <style> node so we need to
// return the sheet property.
if (sheet.sheet /* instanceof CSSStyleSheet */) sheet = sheet.sheet;
return createSheetProxy(sheet);
}
});
}
else {
// `after` will become truthy once the loop executes a second time
for (var i = resources.length - 1, after; i >= 0; i--, after = true) {
resourceId = resources[i];
var
// TODO: this is a bit weird: find a better way to extract name?
opts = parseSuffixes(resourceId),
name = opts.shift(),
url = require['toUrl'](nameWithExt(name, 'css')),
link = createLink(doc),
nowait = 'nowait' in opts ? opts['nowait'] != 'false' : !!config['cssDeferLoad'],
params = {
link: link,
url: url,
wait: config['cssWatchPeriod'] || 50
};
if (nowait) {
callback(createSheetProxy(link.sheet || link.styleSheet));
}
else {
// hook up load detector(s)
loadDetector(params, loaded);
}
// go!
link.href = url;
if (after) {
head.insertBefore(link, insertedSheets[after].previousSibling);
}
else {
head.appendChild(link);
}
insertedSheets[url] = link;
}
}
},
'plugin-builder': './builder/css'
});
})(this);