(function() {
if (this.__polyfills_originalGlobals == null) {
this.__polyfills_originalGlobals = {
define: this.define,
underscore: this._,
less: this.less,
eventemitter2: this.EventEmitter2,
SelectorSet: this.SelectorSet
};
}
this.define = void 0;
}).call(this);
// Underscore.js 1.5.2
// http://underscorejs.org
// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Underscore may be freely distributed under the MIT license.
(function() {
// Baseline setup
// --------------
// Establish the root object, `window` in the browser, or `exports` on the server.
var root = this;
// Save the previous value of the `_` variable.
var previousUnderscore = root._;
// Establish the object that gets returned to break out of a loop iteration.
var breaker = {};
// Save bytes in the minified (but not gzipped) version:
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
// Create quick reference variables for speed access to core prototypes.
var
push = ArrayProto.push,
slice = ArrayProto.slice,
concat = ArrayProto.concat,
toString = ObjProto.toString,
hasOwnProperty = ObjProto.hasOwnProperty;
// All **ECMAScript 5** native function implementations that we hope to use
// are declared here.
var
nativeForEach = ArrayProto.forEach,
nativeMap = ArrayProto.map,
nativeReduce = ArrayProto.reduce,
nativeReduceRight = ArrayProto.reduceRight,
nativeFilter = ArrayProto.filter,
nativeEvery = ArrayProto.every,
nativeSome = ArrayProto.some,
nativeIndexOf = ArrayProto.indexOf,
nativeLastIndexOf = ArrayProto.lastIndexOf,
nativeIsArray = Array.isArray,
nativeKeys = Object.keys,
nativeBind = FuncProto.bind;
// Create a safe reference to the Underscore object for use below.
var _ = function(obj) {
if (obj instanceof _) return obj;
if (!(this instanceof _)) return new _(obj);
this._wrapped = obj;
};
// Export the Underscore object for **Node.js**, with
// backwards-compatibility for the old `require()` API. If we're in
// the browser, add `_` as a global object via a string identifier,
// for Closure Compiler "advanced" mode.
if (typeof exports !== 'undefined') {
if (typeof module !== 'undefined' && module.exports) {
exports = module.exports = _;
}
exports._ = _;
} else {
root._ = _;
}
// Current version.
_.VERSION = '1.5.2';
// Collection Functions
// --------------------
// The cornerstone, an `each` implementation, aka `forEach`.
// Handles objects with the built-in `forEach`, arrays, and raw objects.
// Delegates to **ECMAScript 5**'s native `forEach` if available.
var each = _.each = _.forEach = function(obj, iterator, context) {
if (obj == null) return;
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, length = obj.length; i < length; i++) {
if (iterator.call(context, obj[i], i, obj) === breaker) return;
}
} else {
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
}
}
};
// Return the results of applying the iterator to each element.
// Delegates to **ECMAScript 5**'s native `map` if available.
_.map = _.collect = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
};
var reduceError = 'Reduce of empty array with no initial value';
// **Reduce** builds up a single result from a list of values, aka `inject`,
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduce && obj.reduce === nativeReduce) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
}
each(obj, function(value, index, list) {
if (!initial) {
memo = value;
initial = true;
} else {
memo = iterator.call(context, memo, value, index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// The right-associative version of reduce, also known as `foldr`.
// Delegates to **ECMAScript 5**'s native `reduceRight` if available.
_.reduceRight = _.foldr = function(obj, iterator, memo, context) {
var initial = arguments.length > 2;
if (obj == null) obj = [];
if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
if (context) iterator = _.bind(iterator, context);
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
}
var length = obj.length;
if (length !== +length) {
var keys = _.keys(obj);
length = keys.length;
}
each(obj, function(value, index, list) {
index = keys ? keys[--length] : --length;
if (!initial) {
memo = obj[index];
initial = true;
} else {
memo = iterator.call(context, memo, obj[index], index, list);
}
});
if (!initial) throw new TypeError(reduceError);
return memo;
};
// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, iterator, context) {
var result;
any(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
return true;
}
});
return result;
};
// Return all the elements that pass a truth test.
// Delegates to **ECMAScript 5**'s native `filter` if available.
// Aliased as `select`.
_.filter = _.select = function(obj, iterator, context) {
var results = [];
if (obj == null) return results;
if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
return _.filter(obj, function(value, index, list) {
return !iterator.call(context, value, index, list);
}, context);
};
// Determine whether all of the elements match a truth test.
// Delegates to **ECMAScript 5**'s native `every` if available.
// Aliased as `all`.
_.every = _.all = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = true;
if (obj == null) return result;
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if at least one element in the object matches a truth test.
// Delegates to **ECMAScript 5**'s native `some` if available.
// Aliased as `any`.
var any = _.some = _.any = function(obj, iterator, context) {
iterator || (iterator = _.identity);
var result = false;
if (obj == null) return result;
if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
each(obj, function(value, index, list) {
if (result || (result = iterator.call(context, value, index, list))) return breaker;
});
return !!result;
};
// Determine if the array or object contains a given value (using `===`).
// Aliased as `include`.
_.contains = _.include = function(obj, target) {
if (obj == null) return false;
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
return any(obj, function(value) {
return value === target;
});
};
// Invoke a method (with arguments) on every item in a collection.
_.invoke = function(obj, method) {
var args = slice.call(arguments, 2);
var isFunc = _.isFunction(method);
return _.map(obj, function(value) {
return (isFunc ? method : value[method]).apply(value, args);
});
};
// Convenience version of a common use case of `map`: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, function(value){ return value[key]; });
};
// Convenience version of a common use case of `filter`: selecting only objects
// containing specific `key:value` pairs.
_.where = function(obj, attrs, first) {
if (_.isEmpty(attrs)) return first ? void 0 : [];
return _[first ? 'find' : 'filter'](obj, function(value) {
for (var key in attrs) {
if (attrs[key] !== value[key]) return false;
}
return true;
});
};
// Convenience version of a common use case of `find`: getting the first object
// containing specific `key:value` pairs.
_.findWhere = function(obj, attrs) {
return _.where(obj, attrs, true);
};
// Return the maximum element or (element-based computation).
// Can't optimize arrays of integers longer than 65,535 elements.
// See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.max.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return -Infinity;
var result = {computed : -Infinity, value: -Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed > result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
return Math.min.apply(Math, obj);
}
if (!iterator && _.isEmpty(obj)) return Infinity;
var result = {computed : Infinity, value: Infinity};
each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Shuffle an array, using the modern version of the
// [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
_.shuffle = function(obj) {
var rand;
var index = 0;
var shuffled = [];
each(obj, function(value) {
rand = _.random(index++);
shuffled[index - 1] = shuffled[rand];
shuffled[rand] = value;
});
return shuffled;
};
// Sample **n** random values from an array.
// If **n** is not specified, returns a single random element from the array.
// The internal `guard` argument allows it to work with `map`.
_.sample = function(obj, n, guard) {
if (arguments.length < 2 || guard) {
return obj[_.random(obj.length - 1)];
}
return _.shuffle(obj).slice(0, Math.max(0, n));
};
// An internal function to generate lookup iterators.
var lookupIterator = function(value) {
return _.isFunction(value) ? value : function(obj){ return obj[value]; };
};
// Sort the object's values by a criterion produced by an iterator.
_.sortBy = function(obj, value, context) {
var iterator = lookupIterator(value);
return _.pluck(_.map(obj, function(value, index, list) {
return {
value: value,
index: index,
criteria: iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria;
var b = right.criteria;
if (a !== b) {
if (a > b || a === void 0) return 1;
if (a < b || b === void 0) return -1;
}
return left.index - right.index;
}), 'value');
};
// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
return function(obj, value, context) {
var result = {};
var iterator = value == null ? _.identity : lookupIterator(value);
each(obj, function(value, index) {
var key = iterator.call(context, value, index, obj);
behavior(result, key, value);
});
return result;
};
};
// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = group(function(result, key, value) {
(_.has(result, key) ? result[key] : (result[key] = [])).push(value);
});
// Indexes the object's values by a criterion, similar to `groupBy`, but for
// when you know that your index values will be unique.
_.indexBy = group(function(result, key, value) {
result[key] = value;
});
// Counts instances of an object that group by a certain criterion. Pass
// either a string attribute to count by, or a function that returns the
// criterion.
_.countBy = group(function(result, key) {
_.has(result, key) ? result[key]++ : result[key] = 1;
});
// Use a comparator function to figure out the smallest index at which
// an object should be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator, context) {
iterator = iterator == null ? _.identity : lookupIterator(iterator);
var value = iterator.call(context, obj);
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >>> 1;
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
}
return low;
};
// Safely create a real, live array from anything iterable.
_.toArray = function(obj) {
if (!obj) return [];
if (_.isArray(obj)) return slice.call(obj);
if (obj.length === +obj.length) return _.map(obj, _.identity);
return _.values(obj);
};
// Return the number of elements in an object.
_.size = function(obj) {
if (obj == null) return 0;
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
};
// Array Functions
// ---------------
// Get the first element of an array. Passing **n** will return the first N
// values in the array. Aliased as `head` and `take`. The **guard** check
// allows it to work with `_.map`.
_.first = _.head = _.take = function(array, n, guard) {
if (array == null) return void 0;
return (n == null) || guard ? array[0] : slice.call(array, 0, n);
};
// Returns everything but the last entry of the array. Especially useful on
// the arguments object. Passing **n** will return all the values in
// the array, excluding the last N. The **guard** check allows it to work with
// `_.map`.
_.initial = function(array, n, guard) {
return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
};
// Get the last element of an array. Passing **n** will return the last N
// values in the array. The **guard** check allows it to work with `_.map`.
_.last = function(array, n, guard) {
if (array == null) return void 0;
if ((n == null) || guard) {
return array[array.length - 1];
} else {
return slice.call(array, Math.max(array.length - n, 0));
}
};
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
// Especially useful on the arguments object. Passing an **n** will return
// the rest N values in the array. The **guard**
// check allows it to work with `_.map`.
_.rest = _.tail = _.drop = function(array, n, guard) {
return slice.call(array, (n == null) || guard ? 1 : n);
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.filter(array, _.identity);
};
// Internal implementation of a recursive `flatten` function.
var flatten = function(input, shallow, output) {
if (shallow && _.every(input, _.isArray)) {
return concat.apply(output, input);
}
each(input, function(value) {
if (_.isArray(value) || _.isArguments(value)) {
shallow ? push.apply(output, value) : flatten(value, shallow, output);
} else {
output.push(value);
}
});
return output;
};
// Flatten out an array, either recursively (by default), or just one level.
_.flatten = function(array, shallow) {
return flatten(array, shallow, []);
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
return _.difference(array, slice.call(arguments, 1));
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function(array, isSorted, iterator, context) {
if (_.isFunction(isSorted)) {
context = iterator;
iterator = isSorted;
isSorted = false;
}
var initial = iterator ? _.map(array, iterator, context) : array;
var results = [];
var seen = [];
each(initial, function(value, index) {
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
seen.push(value);
results.push(array[index]);
}
});
return results;
};
// Produce an array that contains the union: each distinct element from all of
// the passed-in arrays.
_.union = function() {
return _.uniq(_.flatten(arguments, true));
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersection = function(array) {
var rest = slice.call(arguments, 1);
return _.filter(_.uniq(array), function(item) {
return _.every(rest, function(other) {
return _.indexOf(other, item) >= 0;
});
});
};
// Take the difference between one array and a number of other arrays.
// Only the elements present in just the first array will remain.
_.difference = function(array) {
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
return _.filter(array, function(value){ return !_.contains(rest, value); });
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var length = _.max(_.pluck(arguments, "length").concat(0));
var results = new Array(length);
for (var i = 0; i < length; i++) {
results[i] = _.pluck(arguments, '' + i);
}
return results;
};
// Converts lists into objects. Pass either a single array of `[key, value]`
// pairs, or two parallel arrays of the same length -- one of keys, and one of
// the corresponding values.
_.object = function(list, values) {
if (list == null) return {};
var result = {};
for (var i = 0, length = list.length; i < length; i++) {
if (values) {
result[list[i]] = values[i];
} else {
result[list[i][0]] = list[i][1];
}
}
return result;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
// we need this function. Return the position of the first occurrence of an
// item in an array, or -1 if the item is not included in the array.
// Delegates to **ECMAScript 5**'s native `indexOf` if available.
// If the array is large and already in sort order, pass `true`
// for **isSorted** to use binary search.
_.indexOf = function(array, item, isSorted) {
if (array == null) return -1;
var i = 0, length = array.length;
if (isSorted) {
if (typeof isSorted == 'number') {
i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
} else {
i = _.sortedIndex(array, item);
return array[i] === item ? i : -1;
}
}
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
for (; i < length; i++) if (array[i] === item) return i;
return -1;
};
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
_.lastIndexOf = function(array, item, from) {
if (array == null) return -1;
var hasIndex = from != null;
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
}
var i = (hasIndex ? from : array.length);
while (i--) if (array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python `range()` function. See
// [the Python documentation](http://docs.python.org/library/functions.html#range).
_.range = function(start, stop, step) {
if (arguments.length <= 1) {
stop = start || 0;
start = 0;
}
step = arguments[2] || 1;
var length = Math.max(Math.ceil((stop - start) / step), 0);
var idx = 0;
var range = new Array(length);
while(idx < length) {
range[idx++] = start;
start += step;
}
return range;
};
// Function (ahem) Functions
// ------------------
// Reusable constructor function for prototype setting.
var ctor = function(){};
// Create a function bound to a given object (assigning `this`, and arguments,
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
// available.
_.bind = function(func, context) {
var args, bound;
if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
if (!_.isFunction(func)) throw new TypeError;
args = slice.call(arguments, 2);
return bound = function() {
if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
ctor.prototype = func.prototype;
var self = new ctor;
ctor.prototype = null;
var result = func.apply(self, args.concat(slice.call(arguments)));
if (Object(result) === result) return result;
return self;
};
};
// Partially apply a function by creating a version that has had some of its
// arguments pre-filled, without changing its dynamic `this` context.
_.partial = function(func) {
var args = slice.call(arguments, 1);
return function() {
return func.apply(this, args.concat(slice.call(arguments)));
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = slice.call(arguments, 1);
if (funcs.length === 0) throw new Error("bindAll must be passed function names");
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Memoize an expensive function by storing its results.
_.memoize = function(func, hasher) {
var memo = {};
hasher || (hasher = _.identity);
return function() {
var key = hasher.apply(this, arguments);
return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
};
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = slice.call(arguments, 2);
return setTimeout(function(){ return func.apply(null, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
};
// Returns a function, that, when invoked, will only be triggered at most once
// during a given window of time. Normally, the throttled function will run
// as much as it can, without ever going more than once per `wait` duration;
// but if you'd like to disable the execution on the leading edge, pass
// `{leading: false}`. To disable execution on the trailing edge, ditto.
_.throttle = function(func, wait, options) {
var context, args, result;
var timeout = null;
var previous = 0;
options || (options = {});
var later = function() {
previous = options.leading === false ? 0 : new Date;
timeout = null;
result = func.apply(context, args);
};
return function() {
var now = new Date;
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
context = this;
args = arguments;
if (remaining <= 0) {
clearTimeout(timeout);
timeout = null;
previous = now;
result = func.apply(context, args);
} else if (!timeout && options.trailing !== false) {
timeout = setTimeout(later, remaining);
}
return result;
};
};
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
_.debounce = function(func, wait, immediate) {
var timeout, args, context, timestamp, result;
return function() {
context = this;
args = arguments;
timestamp = new Date();
var later = function() {
var last = (new Date()) - timestamp;
if (last < wait) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
if (!immediate) result = func.apply(context, args);
}
};
var callNow = immediate && !timeout;
if (!timeout) {
timeout = setTimeout(later, wait);
}
if (callNow) result = func.apply(context, args);
return result;
};
};
// Returns a function that will be executed at most one time, no matter how
// often you call it. Useful for lazy initialization.
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return function() {
var args = [func];
push.apply(args, arguments);
return wrapper.apply(this, args);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = arguments;
return function() {
var args = arguments;
for (var i = funcs.length - 1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
// Returns a function that will only be executed after being called N times.
_.after = function(times, func) {
return function() {
if (--times < 1) {
return func.apply(this, arguments);
}
};
};
// Object Functions
// ----------------
// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
if (obj !== Object(obj)) throw new TypeError('Invalid object');
var keys = [];
for (var key in obj) if (_.has(obj, key)) keys.push(key);
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var values = new Array(length);
for (var i = 0; i < length; i++) {
values[i] = obj[keys[i]];
}
return values;
};
// Convert an object into a list of `[key, value]` pairs.
_.pairs = function(obj) {
var keys = _.keys(obj);
var length = keys.length;
var pairs = new Array(length);
for (var i = 0; i < length; i++) {
pairs[i] = [keys[i], obj[keys[i]]];
}
return pairs;
};
// Invert the keys and values of an object. The values must be serializable.
_.invert = function(obj) {
var result = {};
var keys = _.keys(obj);
for (var i = 0, length = keys.length; i < length; i++) {
result[obj[keys[i]]] = keys[i];
}
return result;
};
// Return a sorted list of the function names available on the object.
// Aliased as `methods`
_.functions = _.methods = function(obj) {
var names = [];
for (var key in obj) {
if (_.isFunction(obj[key])) names.push(key);
}
return names.sort();
};
// Extend a given object with all the properties in passed-in object(s).
_.extend = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
obj[prop] = source[prop];
}
}
});
return obj;
};
// Return a copy of the object only containing the whitelisted properties.
_.pick = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
each(keys, function(key) {
if (key in obj) copy[key] = obj[key];
});
return copy;
};
// Return a copy of the object without the blacklisted properties.
_.omit = function(obj) {
var copy = {};
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
for (var key in obj) {
if (!_.contains(keys, key)) copy[key] = obj[key];
}
return copy;
};
// Fill in a given object with default properties.
_.defaults = function(obj) {
each(slice.call(arguments, 1), function(source) {
if (source) {
for (var prop in source) {
if (obj[prop] === void 0) obj[prop] = source[prop];
}
}
});
return obj;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (!_.isObject(obj)) return obj;
return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
};
// Invokes interceptor with the obj, and then returns obj.
// The primary purpose of this method is to "tap into" a method chain, in
// order to perform operations on intermediate results within the chain.
_.tap = function(obj, interceptor) {
interceptor(obj);
return obj;
};
// Internal recursive comparison function for `isEqual`.
var eq = function(a, b, aStack, bStack) {
// Identical objects are equal. `0 === -0`, but they aren't identical.
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
if (a === b) return a !== 0 || 1 / a == 1 / b;
// A strict comparison is necessary because `null == undefined`.
if (a == null || b == null) return a === b;
// Unwrap any wrapped objects.
if (a instanceof _) a = a._wrapped;
if (b instanceof _) b = b._wrapped;
// Compare `[[Class]]` names.
var className = toString.call(a);
if (className != toString.call(b)) return false;
switch (className) {
// Strings, numbers, dates, and booleans are compared by value.
case '[object String]':
// Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
// equivalent to `new String("5")`.
return a == String(b);
case '[object Number]':
// `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
// other numeric values.
return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
case '[object Date]':
case '[object Boolean]':
// Coerce dates and booleans to numeric primitive values. Dates are compared by their
// millisecond representations. Note that invalid dates with millisecond representations
// of `NaN` are not equivalent.
return +a == +b;
// RegExps are compared by their source patterns and flags.
case '[object RegExp]':
return a.source == b.source &&
a.global == b.global &&
a.multiline == b.multiline &&
a.ignoreCase == b.ignoreCase;
}
if (typeof a != 'object' || typeof b != 'object') return false;
// Assume equality for cyclic structures. The algorithm for detecting cyclic
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
var length = aStack.length;
while (length--) {
// Linear search. Performance is inversely proportional to the number of
// unique nested structures.
if (aStack[length] == a) return bStack[length] == b;
}
// Objects with different constructors are not equivalent, but `Object`s
// from different frames are.
var aCtor = a.constructor, bCtor = b.constructor;
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
_.isFunction(bCtor) && (bCtor instanceof bCtor))) {
return false;
}
// Add the first object to the stack of traversed objects.
aStack.push(a);
bStack.push(b);
var size = 0, result = true;
// Recursively compare objects and arrays.
if (className == '[object Array]') {
// Compare array lengths to determine if a deep comparison is necessary.
size = a.length;
result = size == b.length;
if (result) {
// Deep compare the contents, ignoring non-numeric properties.
while (size--) {
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
}
}
} else {
// Deep compare objects.
for (var key in a) {
if (_.has(a, key)) {
// Count the expected number of properties.
size++;
// Deep compare each member.
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
}
}
// Ensure that both objects contain the same number of properties.
if (result) {
for (key in b) {
if (_.has(b, key) && !(size--)) break;
}
result = !size;
}
}
// Remove the first object from the stack of traversed objects.
aStack.pop();
bStack.pop();
return result;
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
return eq(a, b, [], []);
};
// Is a given array, string, or object empty?
// An "empty" object has no enumerable own-properties.
_.isEmpty = function(obj) {
if (obj == null) return true;
if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
for (var key in obj) if (_.has(obj, key)) return false;
return true;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType === 1);
};
// Is a given value an array?
// Delegates to ECMA5's native Array.isArray
_.isArray = nativeIsArray || function(obj) {
return toString.call(obj) == '[object Array]';
};
// Is a given variable an object?
_.isObject = function(obj) {
return obj === Object(obj);
};
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
_['is' + name] = function(obj) {
return toString.call(obj) == '[object ' + name + ']';
};
});
// Define a fallback version of the method in browsers (ahem, IE), where
// there isn't any inspectable "Arguments" type.
if (!_.isArguments(arguments)) {
_.isArguments = function(obj) {
return !!(obj && _.has(obj, 'callee'));
};
}
// Optimize `isFunction` if appropriate.
if (typeof (/./) !== 'function') {
_.isFunction = function(obj) {
return typeof obj === 'function';
};
}
// Is a given object a finite number?
_.isFinite = function(obj) {
return isFinite(obj) && !isNaN(parseFloat(obj));
};
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
_.isNaN = function(obj) {
return _.isNumber(obj) && obj != +obj;
};
// Is a given value a boolean?
_.isBoolean = function(obj) {
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return obj === void 0;
};
// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
return hasOwnProperty.call(obj, key);
};
// Utility Functions
// -----------------
// Run Underscore.js in *noConflict* mode, returning the `_` variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
// Run a function **n** times.
_.times = function(n, iterator, context) {
var accum = Array(Math.max(0, n));
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
return accum;
};
// Return a random integer between min and max (inclusive).
_.random = function(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
};
// List of HTML entities for escaping.
var entityMap = {
escape: {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
}
};
entityMap.unescape = _.invert(entityMap.escape);
// Regexes containing the keys and values listed immediately above.
var entityRegexes = {
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
};
// Functions for escaping and unescaping strings to/from HTML interpolation.
_.each(['escape', 'unescape'], function(method) {
_[method] = function(string) {
if (string == null) return '';
return ('' + string).replace(entityRegexes[method], function(match) {
return entityMap[method][match];
});
};
});
// If the value of the named `property` is a function then invoke it with the
// `object` as context; otherwise, return it.
_.result = function(object, property) {
if (object == null) return void 0;
var value = object[property];
return _.isFunction(value) ? value.call(object) : value;
};
// Add your own custom functions to the Underscore object.
_.mixin = function(obj) {
each(_.functions(obj), function(name) {
var func = _[name] = obj[name];
_.prototype[name] = function() {
var args = [this._wrapped];
push.apply(args, arguments);
return result.call(this, func.apply(_, args));
};
});
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = ++idCounter + '';
return prefix ? prefix + id : id;
};
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
// Add a "chain" function, which will delegate to the wrapper.
_.chain = function(obj) {
return _(obj).chain();
};
// OOP
// ---------------
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
// Helper function to continue chaining intermediate results.
var result = function(obj) {
return this._chain ? _(obj).chain() : obj;
};
// Add all of the Underscore functions to the wrapper object.
_.mixin(_);
// Add all mutator Array functions to the wrapper.
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
var obj = this._wrapped;
method.apply(obj, arguments);
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
return result.call(this, obj);
};
});
// Add all accessor Array functions to the wrapper.
each(['concat', 'join', 'slice'], function(name) {
var method = ArrayProto[name];
_.prototype[name] = function() {
return result.call(this, method.apply(this._wrapped, arguments));
};
});
_.extend(_.prototype, {
// Start chaining a wrapped Underscore object.
chain: function() {
this._chain = true;
return this;
},
// Extracts the result from a wrapped and chained object.
value: function() {
return this._wrapped;
}
});
}).call(this);
/*!
* Sizzle CSS Selector Engine v1.10.19
* http://sizzlejs.com/
*
* Copyright 2013 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2014-04-18
*/
(function( window ) {
var i,
support,
Expr,
getText,
isXML,
tokenize,
compile,
select,
outermostContext,
sortInput,
hasDuplicate,
// Local document vars
setDocument,
document,
docElem,
documentIsHTML,
rbuggyQSA,
rbuggyMatches,
matches,
contains,
// Instance-specific data
expando = "sizzle" + -(new Date()),
preferredDoc = window.document,
dirruns = 0,
done = 0,
classCache = createCache(),
tokenCache = createCache(),
compilerCache = createCache(),
sortOrder = function( a, b ) {
if ( a === b ) {
hasDuplicate = true;
}
return 0;
},
// General-purpose constants
strundefined = typeof undefined,
MAX_NEGATIVE = 1 << 31,
// Instance methods
hasOwn = ({}).hasOwnProperty,
arr = [],
pop = arr.pop,
push_native = arr.push,
push = arr.push,
slice = arr.slice,
// Use a stripped-down indexOf if we can't use a native one
indexOf = arr.indexOf || function( elem ) {
var i = 0,
len = this.length;
for ( ; i < len; i++ ) {
if ( this[i] === elem ) {
return i;
}
}
return -1;
},
booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
// Regular expressions
// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
whitespace = "[\\x20\\t\\r\\n\\f]",
// http://www.w3.org/TR/css3-syntax/#characters
characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
// Loosely modeled on CSS identifier characters
// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
identifier = characterEncoding.replace( "w", "w#" ),
// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
// Operator (capture 2)
"*([*^$|!~]?=)" + whitespace +
// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
"*\\]",
pseudos = ":(" + characterEncoding + ")(?:\\((" +
// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
// 1. quoted (capture 3; capture 4 or capture 5)
"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
// 2. simple (capture 6)
"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
// 3. anything else (capture 2)
".*" +
")\\)|)",
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
rpseudo = new RegExp( pseudos ),
ridentifier = new RegExp( "^" + identifier + "$" ),
matchExpr = {
"ID": new RegExp( "^#(" + characterEncoding + ")" ),
"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
"ATTR": new RegExp( "^" + attributes ),
"PSEUDO": new RegExp( "^" + pseudos ),
"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
// For use in libraries implementing .is()
// We use this for POS matching in `select`
"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
},
rinputs = /^(?:input|select|textarea|button)$/i,
rheader = /^h\d$/i,
rnative = /^[^{]+\{\s*\[native \w/,
// Easily-parseable/retrievable ID or TAG or CLASS selectors
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
rsibling = /[+~]/,
rescape = /'|\\/g,
// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
funescape = function( _, escaped, escapedWhitespace ) {
var high = "0x" + escaped - 0x10000;
// NaN means non-codepoint
// Support: Firefox<24
// Workaround erroneous numeric interpretation of +"0x"
return high !== high || escapedWhitespace ?
escaped :
high < 0 ?
// BMP codepoint
String.fromCharCode( high + 0x10000 ) :
// Supplemental Plane codepoint (surrogate pair)
String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
};
// Optimize for push.apply( _, NodeList )
try {
push.apply(
(arr = slice.call( preferredDoc.childNodes )),
preferredDoc.childNodes
);
// Support: Android<4.0
// Detect silently failing push.apply
arr[ preferredDoc.childNodes.length ].nodeType;
} catch ( e ) {
push = { apply: arr.length ?
// Leverage slice if possible
function( target, els ) {
push_native.apply( target, slice.call(els) );
} :
// Support: IE<9
// Otherwise append directly
function( target, els ) {
var j = target.length,
i = 0;
// Can't trust NodeList.length
while ( (target[j++] = els[i++]) ) {}
target.length = j - 1;
}
};
}
function Sizzle( selector, context, results, seed ) {
var match, elem, m, nodeType,
// QSA vars
i, groups, old, nid, newContext, newSelector;
if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
setDocument( context );
}
context = context || document;
results = results || [];
if ( !selector || typeof selector !== "string" ) {
return results;
}
if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
return [];
}
if ( documentIsHTML && !seed ) {
// Shortcuts
if ( (match = rquickExpr.exec( selector )) ) {
// Speed-up: Sizzle("#ID")
if ( (m = match[1]) ) {
if ( nodeType === 9 ) {
elem = context.getElementById( m );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document (jQuery #6963)
if ( elem && elem.parentNode ) {
// Handle the case where IE, Opera, and Webkit return items
// by name instead of ID
if ( elem.id === m ) {
results.push( elem );
return results;
}
} else {
return results;
}
} else {
// Context is not a document
if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
contains( context, elem ) && elem.id === m ) {
results.push( elem );
return results;
}
}
// Speed-up: Sizzle("TAG")
} else if ( match[2] ) {
push.apply( results, context.getElementsByTagName( selector ) );
return results;
// Speed-up: Sizzle(".CLASS")
} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
push.apply( results, context.getElementsByClassName( m ) );
return results;
}
}
// QSA path
if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
nid = old = expando;
newContext = context;
newSelector = nodeType === 9 && selector;
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
groups = tokenize( selector );
if ( (old = context.getAttribute("id")) ) {
nid = old.replace( rescape, "\\$&" );
} else {
context.setAttribute( "id", nid );
}
nid = "[id='" + nid + "'] ";
i = groups.length;
while ( i-- ) {
groups[i] = nid + toSelector( groups[i] );
}
newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
newSelector = groups.join(",");
}
if ( newSelector ) {
try {
push.apply( results,
newContext.querySelectorAll( newSelector )
);
return results;
} catch(qsaError) {
} finally {
if ( !old ) {
context.removeAttribute("id");
}
}
}
}
}
// All others
return select( selector.replace( rtrim, "$1" ), context, results, seed );
}
/**
* Create key-value caches of limited size
* @returns {Function(string, Object)} Returns the Object data after storing it on itself with
* property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
* deleting the oldest entry
*/
function createCache() {
var keys = [];
function cache( key, value ) {
// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
if ( keys.push( key + " " ) > Expr.cacheLength ) {
// Only keep the most recent entries
delete cache[ keys.shift() ];
}
return (cache[ key + " " ] = value);
}
return cache;
}
/**
* Mark a function for special use by Sizzle
* @param {Function} fn The function to mark
*/
function markFunction( fn ) {
fn[ expando ] = true;
return fn;
}
/**
* Support testing using an element
* @param {Function} fn Passed the created div and expects a boolean result
*/
function assert( fn ) {
var div = document.createElement("div");
try {
return !!fn( div );
} catch (e) {
return false;
} finally {
// Remove from its parent by default
if ( div.parentNode ) {
div.parentNode.removeChild( div );
}
// release memory in IE
div = null;
}
}
/**
* Adds the same handler for all of the specified attrs
* @param {String} attrs Pipe-separated list of attributes
* @param {Function} handler The method that will be applied
*/
function addHandle( attrs, handler ) {
var arr = attrs.split("|"),
i = attrs.length;
while ( i-- ) {
Expr.attrHandle[ arr[i] ] = handler;
}
}
/**
* Checks document order of two siblings
* @param {Element} a
* @param {Element} b
* @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
*/
function siblingCheck( a, b ) {
var cur = b && a,
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
( ~b.sourceIndex || MAX_NEGATIVE ) -
( ~a.sourceIndex || MAX_NEGATIVE );
// Use IE sourceIndex if available on both nodes
if ( diff ) {
return diff;
}
// Check if b follows a
if ( cur ) {
while ( (cur = cur.nextSibling) ) {
if ( cur === b ) {
return -1;
}
}
}
return a ? 1 : -1;
}
/**
* Returns a function to use in pseudos for input types
* @param {String} type
*/
function createInputPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === type;
};
}
/**
* Returns a function to use in pseudos for buttons
* @param {String} type
*/
function createButtonPseudo( type ) {
return function( elem ) {
var name = elem.nodeName.toLowerCase();
return (name === "input" || name === "button") && elem.type === type;
};
}
/**
* Returns a function to use in pseudos for positionals
* @param {Function} fn
*/
function createPositionalPseudo( fn ) {
return markFunction(function( argument ) {
argument = +argument;
return markFunction(function( seed, matches ) {
var j,
matchIndexes = fn( [], seed.length, argument ),
i = matchIndexes.length;
// Match elements found at the specified indexes
while ( i-- ) {
if ( seed[ (j = matchIndexes[i]) ] ) {
seed[j] = !(matches[j] = seed[j]);
}
}
});
});
}
/**
* Checks a node for validity as a Sizzle context
* @param {Element|Object=} context
* @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
*/
function testContext( context ) {
return context && typeof context.getElementsByTagName !== strundefined && context;
}
// Expose support vars for convenience
support = Sizzle.support = {};
/**
* Detects XML nodes
* @param {Element|Object} elem An element or a document
* @returns {Boolean} True iff elem is a non-HTML XML node
*/
isXML = Sizzle.isXML = function( elem ) {
// documentElement is verified for cases where it doesn't yet exist
// (such as loading iframes in IE - #4833)
var documentElement = elem && (elem.ownerDocument || elem).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
/**
* Sets document-related variables once based on the current document
* @param {Element|Object} [doc] An element or document object to use to set the document
* @returns {Object} Returns the current document
*/
setDocument = Sizzle.setDocument = function( node ) {
var hasCompare,
doc = node ? node.ownerDocument || node : preferredDoc,
parent = doc.defaultView;
// If no document and documentElement is available, return
if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
return document;
}
// Set our document
document = doc;
docElem = doc.documentElement;
// Support tests
documentIsHTML = !isXML( doc );
// Support: IE>8
// If iframe document is assigned to "document" variable and if iframe has been reloaded,
// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
// IE6-8 do not support the defaultView property so parent will be undefined
if ( parent && parent !== parent.top ) {
// IE11 does not have attachEvent, so all must suffer
if ( parent.addEventListener ) {
parent.addEventListener( "unload", function() {
setDocument();
}, false );
} else if ( parent.attachEvent ) {
parent.attachEvent( "onunload", function() {
setDocument();
});
}
}
/* Attributes
---------------------------------------------------------------------- */
// Support: IE<8
// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
support.attributes = assert(function( div ) {
div.className = "i";
return !div.getAttribute("className");
});
/* getElement(s)By*
---------------------------------------------------------------------- */
// Check if getElementsByTagName("*") returns only elements
support.getElementsByTagName = assert(function( div ) {
div.appendChild( doc.createComment("") );
return !div.getElementsByTagName("*").length;
});
// Check if getElementsByClassName can be trusted
support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
div.innerHTML = "
";
// Support: Safari<4
// Catch class over-caching
div.firstChild.className = "i";
// Support: Opera<10
// Catch gEBCN failure to find non-leading classes
return div.getElementsByClassName("i").length === 2;
});
// Support: IE<10
// Check if getElementById returns elements by name
// The broken getElementById methods don't pick up programatically-set names,
// so use a roundabout getElementsByName test
support.getById = assert(function( div ) {
docElem.appendChild( div ).id = expando;
return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
});
// ID find and filter
if ( support.getById ) {
Expr.find["ID"] = function( id, context ) {
if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
var m = context.getElementById( id );
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
return m && m.parentNode ? [ m ] : [];
}
};
Expr.filter["ID"] = function( id ) {
var attrId = id.replace( runescape, funescape );
return function( elem ) {
return elem.getAttribute("id") === attrId;
};
};
} else {
// Support: IE6/7
// getElementById is not reliable as a find shortcut
delete Expr.find["ID"];
Expr.filter["ID"] = function( id ) {
var attrId = id.replace( runescape, funescape );
return function( elem ) {
var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
return node && node.value === attrId;
};
};
}
// Tag
Expr.find["TAG"] = support.getElementsByTagName ?
function( tag, context ) {
if ( typeof context.getElementsByTagName !== strundefined ) {
return context.getElementsByTagName( tag );
}
} :
function( tag, context ) {
var elem,
tmp = [],
i = 0,
results = context.getElementsByTagName( tag );
// Filter out possible comments
if ( tag === "*" ) {
while ( (elem = results[i++]) ) {
if ( elem.nodeType === 1 ) {
tmp.push( elem );
}
}
return tmp;
}
return results;
};
// Class
Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
return context.getElementsByClassName( className );
}
};
/* QSA/matchesSelector
---------------------------------------------------------------------- */
// QSA and matchesSelector support
// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
rbuggyMatches = [];
// qSa(:focus) reports false when true (Chrome 21)
// We allow this because of a bug in IE8/9 that throws an error
// whenever `document.activeElement` is accessed on an iframe
// So, we allow :focus to pass through QSA all the time to avoid the IE error
// See http://bugs.jquery.com/ticket/13378
rbuggyQSA = [];
if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
// Build QSA regex
// Regex strategy adopted from Diego Perini
assert(function( div ) {
// Select is set to empty string on purpose
// This is to test IE's treatment of not explicitly
// setting a boolean content attribute,
// since its presence should be enough
// http://bugs.jquery.com/ticket/12359
div.innerHTML = "";
// Support: IE8, Opera 11-12.16
// Nothing should be selected when empty strings follow ^= or $= or *=
// The test attribute must be unknown in Opera but "safe" for WinRT
// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
if ( div.querySelectorAll("[msallowclip^='']").length ) {
rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
}
// Support: IE8
// Boolean attributes and "value" are not treated correctly
if ( !div.querySelectorAll("[selected]").length ) {
rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
}
// Webkit/Opera - :checked should return selected option elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
// IE8 throws error here and will not see later tests
if ( !div.querySelectorAll(":checked").length ) {
rbuggyQSA.push(":checked");
}
});
assert(function( div ) {
// Support: Windows 8 Native Apps
// The type and name attributes are restricted during .innerHTML assignment
var input = doc.createElement("input");
input.setAttribute( "type", "hidden" );
div.appendChild( input ).setAttribute( "name", "D" );
// Support: IE8
// Enforce case-sensitivity of name attribute
if ( div.querySelectorAll("[name=d]").length ) {
rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
}
// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
// IE8 throws error here and will not see later tests
if ( !div.querySelectorAll(":enabled").length ) {
rbuggyQSA.push( ":enabled", ":disabled" );
}
// Opera 10-11 does not throw on post-comma invalid pseudos
div.querySelectorAll("*,:x");
rbuggyQSA.push(",.*:");
});
}
if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
docElem.webkitMatchesSelector ||
docElem.mozMatchesSelector ||
docElem.oMatchesSelector ||
docElem.msMatchesSelector) )) ) {
assert(function( div ) {
// Check to see if it's possible to do matchesSelector
// on a disconnected node (IE 9)
support.disconnectedMatch = matches.call( div, "div" );
// This should fail with an exception
// Gecko does not error, returns false instead
matches.call( div, "[s!='']:x" );
rbuggyMatches.push( "!=", pseudos );
});
}
rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
/* Contains
---------------------------------------------------------------------- */
hasCompare = rnative.test( docElem.compareDocumentPosition );
// Element contains another
// Purposefully does not implement inclusive descendent
// As in, an element does not contain itself
contains = hasCompare || rnative.test( docElem.contains ) ?
function( a, b ) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!( bup && bup.nodeType === 1 && (
adown.contains ?
adown.contains( bup ) :
a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
));
} :
function( a, b ) {
if ( b ) {
while ( (b = b.parentNode) ) {
if ( b === a ) {
return true;
}
}
}
return false;
};
/* Sorting
---------------------------------------------------------------------- */
// Document order sorting
sortOrder = hasCompare ?
function( a, b ) {
// Flag for duplicate removal
if ( a === b ) {
hasDuplicate = true;
return 0;
}
// Sort on method existence if only one input has compareDocumentPosition
var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
if ( compare ) {
return compare;
}
// Calculate position if both inputs belong to the same document
compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
a.compareDocumentPosition( b ) :
// Otherwise we know they are disconnected
1;
// Disconnected nodes
if ( compare & 1 ||
(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
// Choose the first element that is related to our preferred document
if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
return -1;
}
if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
return 1;
}
// Maintain original order
return sortInput ?
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
0;
}
return compare & 4 ? -1 : 1;
} :
function( a, b ) {
// Exit early if the nodes are identical
if ( a === b ) {
hasDuplicate = true;
return 0;
}
var cur,
i = 0,
aup = a.parentNode,
bup = b.parentNode,
ap = [ a ],
bp = [ b ];
// Parentless nodes are either documents or disconnected
if ( !aup || !bup ) {
return a === doc ? -1 :
b === doc ? 1 :
aup ? -1 :
bup ? 1 :
sortInput ?
( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
0;
// If the nodes are siblings, we can do a quick check
} else if ( aup === bup ) {
return siblingCheck( a, b );
}
// Otherwise we need full lists of their ancestors for comparison
cur = a;
while ( (cur = cur.parentNode) ) {
ap.unshift( cur );
}
cur = b;
while ( (cur = cur.parentNode) ) {
bp.unshift( cur );
}
// Walk down the tree looking for a discrepancy
while ( ap[i] === bp[i] ) {
i++;
}
return i ?
// Do a sibling check if the nodes have a common ancestor
siblingCheck( ap[i], bp[i] ) :
// Otherwise nodes in our document sort first
ap[i] === preferredDoc ? -1 :
bp[i] === preferredDoc ? 1 :
0;
};
return doc;
};
Sizzle.matches = function( expr, elements ) {
return Sizzle( expr, null, null, elements );
};
Sizzle.matchesSelector = function( elem, expr ) {
// Set document vars if needed
if ( ( elem.ownerDocument || elem ) !== document ) {
setDocument( elem );
}
// Make sure that attribute selectors are quoted
expr = expr.replace( rattributeQuotes, "='$1']" );
if ( support.matchesSelector && documentIsHTML &&
( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
try {
var ret = matches.call( elem, expr );
// IE 9's matchesSelector returns false on disconnected nodes
if ( ret || support.disconnectedMatch ||
// As well, disconnected nodes are said to be in a document
// fragment in IE 9
elem.document && elem.document.nodeType !== 11 ) {
return ret;
}
} catch(e) {}
}
return Sizzle( expr, document, null, [ elem ] ).length > 0;
};
Sizzle.contains = function( context, elem ) {
// Set document vars if needed
if ( ( context.ownerDocument || context ) !== document ) {
setDocument( context );
}
return contains( context, elem );
};
Sizzle.attr = function( elem, name ) {
// Set document vars if needed
if ( ( elem.ownerDocument || elem ) !== document ) {
setDocument( elem );
}
var fn = Expr.attrHandle[ name.toLowerCase() ],
// Don't get fooled by Object.prototype properties (jQuery #13807)
val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
fn( elem, name, !documentIsHTML ) :
undefined;
return val !== undefined ?
val :
support.attributes || !documentIsHTML ?
elem.getAttribute( name ) :
(val = elem.getAttributeNode(name)) && val.specified ?
val.value :
null;
};
Sizzle.error = function( msg ) {
throw new Error( "Syntax error, unrecognized expression: " + msg );
};
/**
* Document sorting and removing duplicates
* @param {ArrayLike} results
*/
Sizzle.uniqueSort = function( results ) {
var elem,
duplicates = [],
j = 0,
i = 0;
// Unless we *know* we can detect duplicates, assume their presence
hasDuplicate = !support.detectDuplicates;
sortInput = !support.sortStable && results.slice( 0 );
results.sort( sortOrder );
if ( hasDuplicate ) {
while ( (elem = results[i++]) ) {
if ( elem === results[ i ] ) {
j = duplicates.push( i );
}
}
while ( j-- ) {
results.splice( duplicates[ j ], 1 );
}
}
// Clear input after sorting to release objects
// See https://github.com/jquery/sizzle/pull/225
sortInput = null;
return results;
};
/**
* Utility function for retrieving the text value of an array of DOM nodes
* @param {Array|Element} elem
*/
getText = Sizzle.getText = function( elem ) {
var node,
ret = "",
i = 0,
nodeType = elem.nodeType;
if ( !nodeType ) {
// If no nodeType, this is expected to be an array
while ( (node = elem[i++]) ) {
// Do not traverse comment nodes
ret += getText( node );
}
} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (jQuery #11153)
if ( typeof elem.textContent === "string" ) {
return elem.textContent;
} else {
// Traverse its children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
ret += getText( elem );
}
}
} else if ( nodeType === 3 || nodeType === 4 ) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
return ret;
};
Expr = Sizzle.selectors = {
// Can be adjusted by the user
cacheLength: 50,
createPseudo: markFunction,
match: matchExpr,
attrHandle: {},
find: {},
relative: {
">": { dir: "parentNode", first: true },
" ": { dir: "parentNode" },
"+": { dir: "previousSibling", first: true },
"~": { dir: "previousSibling" }
},
preFilter: {
"ATTR": function( match ) {
match[1] = match[1].replace( runescape, funescape );
// Move the given value to match[3] whether quoted or unquoted
match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
if ( match[2] === "~=" ) {
match[3] = " " + match[3] + " ";
}
return match.slice( 0, 4 );
},
"CHILD": function( match ) {
/* matches from matchExpr["CHILD"]
1 type (only|nth|...)
2 what (child|of-type)
3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
4 xn-component of xn+y argument ([+-]?\d*n|)
5 sign of xn-component
6 x of xn-component
7 sign of y-component
8 y of y-component
*/
match[1] = match[1].toLowerCase();
if ( match[1].slice( 0, 3 ) === "nth" ) {
// nth-* requires argument
if ( !match[3] ) {
Sizzle.error( match[0] );
}
// numeric x and y parameters for Expr.filter.CHILD
// remember that false/true cast respectively to 0/1
match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
// other types prohibit arguments
} else if ( match[3] ) {
Sizzle.error( match[0] );
}
return match;
},
"PSEUDO": function( match ) {
var excess,
unquoted = !match[6] && match[2];
if ( matchExpr["CHILD"].test( match[0] ) ) {
return null;
}
// Accept quoted arguments as-is
if ( match[3] ) {
match[2] = match[4] || match[5] || "";
// Strip excess characters from unquoted arguments
} else if ( unquoted && rpseudo.test( unquoted ) &&
// Get excess from tokenize (recursively)
(excess = tokenize( unquoted, true )) &&
// advance to the next closing parenthesis
(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
// excess is a negative index
match[0] = match[0].slice( 0, excess );
match[2] = unquoted.slice( 0, excess );
}
// Return only captures needed by the pseudo filter method (type and argument)
return match.slice( 0, 3 );
}
},
filter: {
"TAG": function( nodeNameSelector ) {
var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
return nodeNameSelector === "*" ?
function() { return true; } :
function( elem ) {
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
};
},
"CLASS": function( className ) {
var pattern = classCache[ className + " " ];
return pattern ||
(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
classCache( className, function( elem ) {
return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
});
},
"ATTR": function( name, operator, check ) {
return function( elem ) {
var result = Sizzle.attr( elem, name );
if ( result == null ) {
return operator === "!=";
}
if ( !operator ) {
return true;
}
result += "";
return operator === "=" ? result === check :
operator === "!=" ? result !== check :
operator === "^=" ? check && result.indexOf( check ) === 0 :
operator === "*=" ? check && result.indexOf( check ) > -1 :
operator === "$=" ? check && result.slice( -check.length ) === check :
operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
false;
};
},
"CHILD": function( type, what, argument, first, last ) {
var simple = type.slice( 0, 3 ) !== "nth",
forward = type.slice( -4 ) !== "last",
ofType = what === "of-type";
return first === 1 && last === 0 ?
// Shortcut for :nth-*(n)
function( elem ) {
return !!elem.parentNode;
} :
function( elem, context, xml ) {
var cache, outerCache, node, diff, nodeIndex, start,
dir = simple !== forward ? "nextSibling" : "previousSibling",
parent = elem.parentNode,
name = ofType && elem.nodeName.toLowerCase(),
useCache = !xml && !ofType;
if ( parent ) {
// :(first|last|only)-(child|of-type)
if ( simple ) {
while ( dir ) {
node = elem;
while ( (node = node[ dir ]) ) {
if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
return false;
}
}
// Reverse direction for :only-* (if we haven't yet done so)
start = dir = type === "only" && !start && "nextSibling";
}
return true;
}
start = [ forward ? parent.firstChild : parent.lastChild ];
// non-xml :nth-child(...) stores cache data on `parent`
if ( forward && useCache ) {
// Seek `elem` from a previously-cached index
outerCache = parent[ expando ] || (parent[ expando ] = {});
cache = outerCache[ type ] || [];
nodeIndex = cache[0] === dirruns && cache[1];
diff = cache[0] === dirruns && cache[2];
node = nodeIndex && parent.childNodes[ nodeIndex ];
while ( (node = ++nodeIndex && node && node[ dir ] ||
// Fallback to seeking `elem` from the start
(diff = nodeIndex = 0) || start.pop()) ) {
// When found, cache indexes on `parent` and break
if ( node.nodeType === 1 && ++diff && node === elem ) {
outerCache[ type ] = [ dirruns, nodeIndex, diff ];
break;
}
}
// Use previously-cached element index if available
} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
diff = cache[1];
// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
} else {
// Use the same loop as above to seek `elem` from the start
while ( (node = ++nodeIndex && node && node[ dir ] ||
(diff = nodeIndex = 0) || start.pop()) ) {
if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
// Cache the index of each encountered element
if ( useCache ) {
(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
}
if ( node === elem ) {
break;
}
}
}
}
// Incorporate the offset, then check against cycle size
diff -= last;
return diff === first || ( diff % first === 0 && diff / first >= 0 );
}
};
},
"PSEUDO": function( pseudo, argument ) {
// pseudo-class names are case-insensitive
// http://www.w3.org/TR/selectors/#pseudo-classes
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
// Remember that setFilters inherits from pseudos
var args,
fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
Sizzle.error( "unsupported pseudo: " + pseudo );
// The user may use createPseudo to indicate that
// arguments are needed to create the filter function
// just as Sizzle does
if ( fn[ expando ] ) {
return fn( argument );
}
// But maintain support for old signatures
if ( fn.length > 1 ) {
args = [ pseudo, pseudo, "", argument ];
return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
markFunction(function( seed, matches ) {
var idx,
matched = fn( seed, argument ),
i = matched.length;
while ( i-- ) {
idx = indexOf.call( seed, matched[i] );
seed[ idx ] = !( matches[ idx ] = matched[i] );
}
}) :
function( elem ) {
return fn( elem, 0, args );
};
}
return fn;
}
},
pseudos: {
// Potentially complex pseudos
"not": markFunction(function( selector ) {
// Trim the selector passed to compile
// to avoid treating leading and trailing
// spaces as combinators
var input = [],
results = [],
matcher = compile( selector.replace( rtrim, "$1" ) );
return matcher[ expando ] ?
markFunction(function( seed, matches, context, xml ) {
var elem,
unmatched = matcher( seed, null, xml, [] ),
i = seed.length;
// Match elements unmatched by `matcher`
while ( i-- ) {
if ( (elem = unmatched[i]) ) {
seed[i] = !(matches[i] = elem);
}
}
}) :
function( elem, context, xml ) {
input[0] = elem;
matcher( input, null, xml, results );
return !results.pop();
};
}),
"has": markFunction(function( selector ) {
return function( elem ) {
return Sizzle( selector, elem ).length > 0;
};
}),
"contains": markFunction(function( text ) {
return function( elem ) {
return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
};
}),
// "Whether an element is represented by a :lang() selector
// is based solely on the element's language value
// being equal to the identifier C,
// or beginning with the identifier C immediately followed by "-".
// The matching of C against the element's language value is performed case-insensitively.
// The identifier C does not have to be a valid language name."
// http://www.w3.org/TR/selectors/#lang-pseudo
"lang": markFunction( function( lang ) {
// lang value must be a valid identifier
if ( !ridentifier.test(lang || "") ) {
Sizzle.error( "unsupported lang: " + lang );
}
lang = lang.replace( runescape, funescape ).toLowerCase();
return function( elem ) {
var elemLang;
do {
if ( (elemLang = documentIsHTML ?
elem.lang :
elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
elemLang = elemLang.toLowerCase();
return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
}
} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
return false;
};
}),
// Miscellaneous
"target": function( elem ) {
var hash = window.location && window.location.hash;
return hash && hash.slice( 1 ) === elem.id;
},
"root": function( elem ) {
return elem === docElem;
},
"focus": function( elem ) {
return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
},
// Boolean properties
"enabled": function( elem ) {
return elem.disabled === false;
},
"disabled": function( elem ) {
return elem.disabled === true;
},
"checked": function( elem ) {
// In CSS3, :checked should return both checked and selected elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
var nodeName = elem.nodeName.toLowerCase();
return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
},
"selected": function( elem ) {
// Accessing this property makes selected-by-default
// options in Safari work properly
if ( elem.parentNode ) {
elem.parentNode.selectedIndex;
}
return elem.selected === true;
},
// Contents
"empty": function( elem ) {
// http://www.w3.org/TR/selectors/#empty-pseudo
// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
// but not by others (comment: 8; processing instruction: 7; etc.)
// nodeType < 6 works because attributes (2) do not appear as children
for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
if ( elem.nodeType < 6 ) {
return false;
}
}
return true;
},
"parent": function( elem ) {
return !Expr.pseudos["empty"]( elem );
},
// Element/input types
"header": function( elem ) {
return rheader.test( elem.nodeName );
},
"input": function( elem ) {
return rinputs.test( elem.nodeName );
},
"button": function( elem ) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === "button" || name === "button";
},
"text": function( elem ) {
var attr;
return elem.nodeName.toLowerCase() === "input" &&
elem.type === "text" &&
// Support: IE<8
// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
},
// Position-in-collection
"first": createPositionalPseudo(function() {
return [ 0 ];
}),
"last": createPositionalPseudo(function( matchIndexes, length ) {
return [ length - 1 ];
}),
"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
return [ argument < 0 ? argument + length : argument ];
}),
"even": createPositionalPseudo(function( matchIndexes, length ) {
var i = 0;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
}
return matchIndexes;
}),
"odd": createPositionalPseudo(function( matchIndexes, length ) {
var i = 1;
for ( ; i < length; i += 2 ) {
matchIndexes.push( i );
}
return matchIndexes;
}),
"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
var i = argument < 0 ? argument + length : argument;
for ( ; --i >= 0; ) {
matchIndexes.push( i );
}
return matchIndexes;
}),
"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
var i = argument < 0 ? argument + length : argument;
for ( ; ++i < length; ) {
matchIndexes.push( i );
}
return matchIndexes;
})
}
};
Expr.pseudos["nth"] = Expr.pseudos["eq"];
// Add button/input type pseudos
for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
Expr.pseudos[ i ] = createInputPseudo( i );
}
for ( i in { submit: true, reset: true } ) {
Expr.pseudos[ i ] = createButtonPseudo( i );
}
// Easy API for creating new setFilters
function setFilters() {}
setFilters.prototype = Expr.filters = Expr.pseudos;
Expr.setFilters = new setFilters();
tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
var matched, match, tokens, type,
soFar, groups, preFilters,
cached = tokenCache[ selector + " " ];
if ( cached ) {
return parseOnly ? 0 : cached.slice( 0 );
}
soFar = selector;
groups = [];
preFilters = Expr.preFilter;
while ( soFar ) {
// Comma and first run
if ( !matched || (match = rcomma.exec( soFar )) ) {
if ( match ) {
// Don't consume trailing commas as valid
soFar = soFar.slice( match[0].length ) || soFar;
}
groups.push( (tokens = []) );
}
matched = false;
// Combinators
if ( (match = rcombinators.exec( soFar )) ) {
matched = match.shift();
tokens.push({
value: matched,
// Cast descendant combinators to space
type: match[0].replace( rtrim, " " )
});
soFar = soFar.slice( matched.length );
}
// Filters
for ( type in Expr.filter ) {
if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
(match = preFilters[ type ]( match ))) ) {
matched = match.shift();
tokens.push({
value: matched,
type: type,
matches: match
});
soFar = soFar.slice( matched.length );
}
}
if ( !matched ) {
break;
}
}
// Return the length of the invalid excess
// if we're just parsing
// Otherwise, throw an error or return tokens
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error( selector ) :
// Cache the tokens
tokenCache( selector, groups ).slice( 0 );
};
function toSelector( tokens ) {
var i = 0,
len = tokens.length,
selector = "";
for ( ; i < len; i++ ) {
selector += tokens[i].value;
}
return selector;
}
function addCombinator( matcher, combinator, base ) {
var dir = combinator.dir,
checkNonElements = base && dir === "parentNode",
doneName = done++;
return combinator.first ?
// Check against closest ancestor/preceding element
function( elem, context, xml ) {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
return matcher( elem, context, xml );
}
}
} :
// Check against all ancestor/preceding elements
function( elem, context, xml ) {
var oldCache, outerCache,
newCache = [ dirruns, doneName ];
// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
if ( xml ) {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
if ( matcher( elem, context, xml ) ) {
return true;
}
}
}
} else {
while ( (elem = elem[ dir ]) ) {
if ( elem.nodeType === 1 || checkNonElements ) {
outerCache = elem[ expando ] || (elem[ expando ] = {});
if ( (oldCache = outerCache[ dir ]) &&
oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
// Assign to newCache so results back-propagate to previous elements
return (newCache[ 2 ] = oldCache[ 2 ]);
} else {
// Reuse newcache so results back-propagate to previous elements
outerCache[ dir ] = newCache;
// A match means we're done; a fail means we have to keep checking
if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
return true;
}
}
}
}
}
};
}
function elementMatcher( matchers ) {
return matchers.length > 1 ?
function( elem, context, xml ) {
var i = matchers.length;
while ( i-- ) {
if ( !matchers[i]( elem, context, xml ) ) {
return false;
}
}
return true;
} :
matchers[0];
}
function multipleContexts( selector, contexts, results ) {
var i = 0,
len = contexts.length;
for ( ; i < len; i++ ) {
Sizzle( selector, contexts[i], results );
}
return results;
}
function condense( unmatched, map, filter, context, xml ) {
var elem,
newUnmatched = [],
i = 0,
len = unmatched.length,
mapped = map != null;
for ( ; i < len; i++ ) {
if ( (elem = unmatched[i]) ) {
if ( !filter || filter( elem, context, xml ) ) {
newUnmatched.push( elem );
if ( mapped ) {
map.push( i );
}
}
}
}
return newUnmatched;
}
function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
if ( postFilter && !postFilter[ expando ] ) {
postFilter = setMatcher( postFilter );
}
if ( postFinder && !postFinder[ expando ] ) {
postFinder = setMatcher( postFinder, postSelector );
}
return markFunction(function( seed, results, context, xml ) {
var temp, i, elem,
preMap = [],
postMap = [],
preexisting = results.length,
// Get initial elements from seed or context
elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
// Prefilter to get matcher input, preserving a map for seed-results synchronization
matcherIn = preFilter && ( seed || !selector ) ?
condense( elems, preMap, preFilter, context, xml ) :
elems,
matcherOut = matcher ?
// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
// ...intermediate processing is necessary
[] :
// ...otherwise use results directly
results :
matcherIn;
// Find primary matches
if ( matcher ) {
matcher( matcherIn, matcherOut, context, xml );
}
// Apply postFilter
if ( postFilter ) {
temp = condense( matcherOut, postMap );
postFilter( temp, [], context, xml );
// Un-match failing elements by moving them back to matcherIn
i = temp.length;
while ( i-- ) {
if ( (elem = temp[i]) ) {
matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
}
}
}
if ( seed ) {
if ( postFinder || preFilter ) {
if ( postFinder ) {
// Get the final matcherOut by condensing this intermediate into postFinder contexts
temp = [];
i = matcherOut.length;
while ( i-- ) {
if ( (elem = matcherOut[i]) ) {
// Restore matcherIn since elem is not yet a final match
temp.push( (matcherIn[i] = elem) );
}
}
postFinder( null, (matcherOut = []), temp, xml );
}
// Move matched elements from seed to results to keep them synchronized
i = matcherOut.length;
while ( i-- ) {
if ( (elem = matcherOut[i]) &&
(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
seed[temp] = !(results[temp] = elem);
}
}
}
// Add elements to results, through postFinder if defined
} else {
matcherOut = condense(
matcherOut === results ?
matcherOut.splice( preexisting, matcherOut.length ) :
matcherOut
);
if ( postFinder ) {
postFinder( null, results, matcherOut, xml );
} else {
push.apply( results, matcherOut );
}
}
});
}
function matcherFromTokens( tokens ) {
var checkContext, matcher, j,
len = tokens.length,
leadingRelative = Expr.relative[ tokens[0].type ],
implicitRelative = leadingRelative || Expr.relative[" "],
i = leadingRelative ? 1 : 0,
// The foundational matcher ensures that elements are reachable from top-level context(s)
matchContext = addCombinator( function( elem ) {
return elem === checkContext;
}, implicitRelative, true ),
matchAnyContext = addCombinator( function( elem ) {
return indexOf.call( checkContext, elem ) > -1;
}, implicitRelative, true ),
matchers = [ function( elem, context, xml ) {
return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
(checkContext = context).nodeType ?
matchContext( elem, context, xml ) :
matchAnyContext( elem, context, xml ) );
} ];
for ( ; i < len; i++ ) {
if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
} else {
matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
// Return special upon seeing a positional matcher
if ( matcher[ expando ] ) {
// Find the next relative operator (if any) for proper handling
j = ++i;
for ( ; j < len; j++ ) {
if ( Expr.relative[ tokens[j].type ] ) {
break;
}
}
return setMatcher(
i > 1 && elementMatcher( matchers ),
i > 1 && toSelector(
// If the preceding token was a descendant combinator, insert an implicit any-element `*`
tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
).replace( rtrim, "$1" ),
matcher,
i < j && matcherFromTokens( tokens.slice( i, j ) ),
j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
j < len && toSelector( tokens )
);
}
matchers.push( matcher );
}
}
return elementMatcher( matchers );
}
function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
var bySet = setMatchers.length > 0,
byElement = elementMatchers.length > 0,
superMatcher = function( seed, context, xml, results, outermost ) {
var elem, j, matcher,
matchedCount = 0,
i = "0",
unmatched = seed && [],
setMatched = [],
contextBackup = outermostContext,
// We must always have either seed elements or outermost context
elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
// Use integer dirruns iff this is the outermost matcher
dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
len = elems.length;
if ( outermost ) {
outermostContext = context !== document && context;
}
// Add elements passing elementMatchers directly to results
// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
// Support: IE<9, Safari
// Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id
for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
if ( byElement && elem ) {
j = 0;
while ( (matcher = elementMatchers[j++]) ) {
if ( matcher( elem, context, xml ) ) {
results.push( elem );
break;
}
}
if ( outermost ) {
dirruns = dirrunsUnique;
}
}
// Track unmatched elements for set filters
if ( bySet ) {
// They will have gone through all possible matchers
if ( (elem = !matcher && elem) ) {
matchedCount--;
}
// Lengthen the array for every element, matched or not
if ( seed ) {
unmatched.push( elem );
}
}
}
// Apply set filters to unmatched elements
matchedCount += i;
if ( bySet && i !== matchedCount ) {
j = 0;
while ( (matcher = setMatchers[j++]) ) {
matcher( unmatched, setMatched, context, xml );
}
if ( seed ) {
// Reintegrate element matches to eliminate the need for sorting
if ( matchedCount > 0 ) {
while ( i-- ) {
if ( !(unmatched[i] || setMatched[i]) ) {
setMatched[i] = pop.call( results );
}
}
}
// Discard index placeholder values to get only actual matches
setMatched = condense( setMatched );
}
// Add matches to results
push.apply( results, setMatched );
// Seedless set matches succeeding multiple successful matchers stipulate sorting
if ( outermost && !seed && setMatched.length > 0 &&
( matchedCount + setMatchers.length ) > 1 ) {
Sizzle.uniqueSort( results );
}
}
// Override manipulation of globals by nested matchers
if ( outermost ) {
dirruns = dirrunsUnique;
outermostContext = contextBackup;
}
return unmatched;
};
return bySet ?
markFunction( superMatcher ) :
superMatcher;
}
compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
var i,
setMatchers = [],
elementMatchers = [],
cached = compilerCache[ selector + " " ];
if ( !cached ) {
// Generate a function of recursive functions that can be used to check each element
if ( !match ) {
match = tokenize( selector );
}
i = match.length;
while ( i-- ) {
cached = matcherFromTokens( match[i] );
if ( cached[ expando ] ) {
setMatchers.push( cached );
} else {
elementMatchers.push( cached );
}
}
// Cache the compiled function
cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
// Save selector and tokenization
cached.selector = selector;
}
return cached;
};
/**
* A low-level selection function that works with Sizzle's compiled
* selector functions
* @param {String|Function} selector A selector or a pre-compiled
* selector function built with Sizzle.compile
* @param {Element} context
* @param {Array} [results]
* @param {Array} [seed] A set of elements to match against
*/
select = Sizzle.select = function( selector, context, results, seed ) {
var i, tokens, token, type, find,
compiled = typeof selector === "function" && selector,
match = !seed && tokenize( (selector = compiled.selector || selector) );
results = results || [];
// Try to minimize operations if there is no seed and only one group
if ( match.length === 1 ) {
// Take a shortcut and set the context if the root selector is an ID
tokens = match[0] = match[0].slice( 0 );
if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
support.getById && context.nodeType === 9 && documentIsHTML &&
Expr.relative[ tokens[1].type ] ) {
context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
if ( !context ) {
return results;
// Precompiled matchers will still verify ancestry, so step up a level
} else if ( compiled ) {
context = context.parentNode;
}
selector = selector.slice( tokens.shift().value.length );
}
// Fetch a seed set for right-to-left matching
i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
while ( i-- ) {
token = tokens[i];
// Abort if we hit a combinator
if ( Expr.relative[ (type = token.type) ] ) {
break;
}
if ( (find = Expr.find[ type ]) ) {
// Search, expanding context for leading sibling combinators
if ( (seed = find(
token.matches[0].replace( runescape, funescape ),
rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
)) ) {
// If seed is empty or no tokens remain, we can return early
tokens.splice( i, 1 );
selector = seed.length && toSelector( tokens );
if ( !selector ) {
push.apply( results, seed );
return results;
}
break;
}
}
}
}
// Compile and execute a filtering function if one is not provided
// Provide `match` to avoid retokenization if we modified the selector above
( compiled || compile( selector, match ) )(
seed,
context,
!documentIsHTML,
results,
rsibling.test( selector ) && testContext( context.parentNode ) || context
);
return results;
};
// One-time assignments
// Sort stability
support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
// Support: Chrome<14
// Always assume duplicates if they aren't passed to the comparison function
support.detectDuplicates = !!hasDuplicate;
// Initialize against the default document
setDocument();
// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
// Detached nodes confoundingly follow *each other*
support.sortDetached = assert(function( div1 ) {
// Should return 1, but returns 4 (following)
return div1.compareDocumentPosition( document.createElement("div") ) & 1;
});
// Support: IE<8
// Prevent attribute/property "interpolation"
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if ( !assert(function( div ) {
div.innerHTML = "";
return div.firstChild.getAttribute("href") === "#" ;
}) ) {
addHandle( "type|href|height|width", function( elem, name, isXML ) {
if ( !isXML ) {
return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
}
});
}
// Support: IE<9
// Use defaultValue in place of getAttribute("value")
if ( !support.attributes || !assert(function( div ) {
div.innerHTML = "";
div.firstChild.setAttribute( "value", "" );
return div.firstChild.getAttribute( "value" ) === "";
}) ) {
addHandle( "value", function( elem, name, isXML ) {
if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
return elem.defaultValue;
}
});
}
// Support: IE<9
// Use getAttributeNode to fetch booleans when getAttribute lies
if ( !assert(function( div ) {
return div.getAttribute("disabled") == null;
}) ) {
addHandle( booleans, function( elem, name, isXML ) {
var val;
if ( !isXML ) {
return elem[ name ] === true ? name.toLowerCase() :
(val = elem.getAttributeNode( name )) && val.specified ?
val.value :
null;
}
});
}
// EXPOSE
if ( typeof define === "function" && define.amd ) {
define(function() { return Sizzle; });
// Sizzle requires that there be a global window in Common-JS like environments
} else if ( typeof module !== "undefined" && module.exports ) {
module.exports = Sizzle;
} else {
window.Sizzle = Sizzle;
}
// EXPOSE
})( window );
/*!
* LESS - Leaner CSS v1.6.0
* http://lesscss.org
*
* Copyright (c) 2009-2014, Alexis Sellier
* Licensed under the Apache v2 License.
*
*/
/** * @license Apache v2
*/
(function (window, undefined) {//
// Stub out `require` in the browser
//
function require(arg) {
return window.less[arg.split('/')[1]];
};
if (typeof(window.less) === 'undefined' || typeof(window.less.nodeType) !== 'undefined') { window.less = {}; }
less = window.less;
tree = window.less.tree = {};
less.mode = 'browser';
var less, tree;
// Node.js does not have a header file added which defines less
if (less === undefined) {
less = exports;
tree = require('./tree');
less.mode = 'node';
}
//
// less.js - parser
//
// A relatively straight-forward predictive parser.
// There is no tokenization/lexing stage, the input is parsed
// in one sweep.
//
// To make the parser fast enough to run in the browser, several
// optimization had to be made:
//
// - Matching and slicing on a huge input is often cause of slowdowns.
// The solution is to chunkify the input into smaller strings.
// The chunks are stored in the `chunks` var,
// `j` holds the current chunk index, and `currentPos` holds
// the index of the current chunk in relation to `input`.
// This gives us an almost 4x speed-up.
//
// - In many cases, we don't need to match individual tokens;
// for example, if a value doesn't hold any variables, operations
// or dynamic references, the parser can effectively 'skip' it,
// treating it as a literal.
// An example would be '1px solid #000' - which evaluates to itself,
// we don't need to know what the individual components are.
// The drawback, of course is that you don't get the benefits of
// syntax-checking on the CSS. This gives us a 50% speed-up in the parser,
// and a smaller speed-up in the code-gen.
//
//
// Token matching is done with the `$` function, which either takes
// a terminal string or regexp, or a non-terminal function to call.
// It also takes care of moving all the indices forwards.
//
//
less.Parser = function Parser(env) {
var input, // LeSS input string
i, // current index in `input`
j, // current chunk
temp, // temporarily holds a chunk's state, for backtracking
memo, // temporarily holds `i`, when backtracking
furthest, // furthest index the parser has gone to
chunks, // chunkified input
current, // current chunk
currentPos, // index of current chunk, in `input`
parser,
parsers,
rootFilename = env && env.filename;
// Top parser on an import tree must be sure there is one "env"
// which will then be passed around by reference.
if (!(env instanceof tree.parseEnv)) {
env = new tree.parseEnv(env);
}
var imports = this.imports = {
paths: env.paths || [], // Search paths, when importing
queue: [], // Files which haven't been imported yet
files: env.files, // Holds the imported parse trees
contents: env.contents, // Holds the imported file contents
contentsIgnoredChars: env.contentsIgnoredChars, // lines inserted, not in the original less
mime: env.mime, // MIME type of .less files
error: null, // Error in parsing/evaluating an import
push: function (path, currentFileInfo, importOptions, callback) {
var parserImports = this;
this.queue.push(path);
var fileParsedFunc = function (e, root, fullPath) {
parserImports.queue.splice(parserImports.queue.indexOf(path), 1); // Remove the path from the queue
var importedPreviously = fullPath in parserImports.files || fullPath === rootFilename;
parserImports.files[fullPath] = root; // Store the root
if (e && !parserImports.error) { parserImports.error = e; }
callback(e, root, importedPreviously, fullPath);
};
if (less.Parser.importer) {
less.Parser.importer(path, currentFileInfo, fileParsedFunc, env);
} else {
less.Parser.fileLoader(path, currentFileInfo, function(e, contents, fullPath, newFileInfo) {
if (e) {fileParsedFunc(e); return;}
var newEnv = new tree.parseEnv(env);
newEnv.currentFileInfo = newFileInfo;
newEnv.processImports = false;
newEnv.contents[fullPath] = contents;
if (currentFileInfo.reference || importOptions.reference) {
newFileInfo.reference = true;
}
if (importOptions.inline) {
fileParsedFunc(null, contents, fullPath);
} else {
new(less.Parser)(newEnv).parse(contents, function (e, root) {
fileParsedFunc(e, root, fullPath);
});
}
}, env);
}
}
};
function save() { temp = current; memo = currentPos = i; }
function restore() { current = temp; currentPos = i = memo; }
function sync() {
if (i > currentPos) {
current = current.slice(i - currentPos);
currentPos = i;
}
}
function isWhitespace(str, pos) {
var code = str.charCodeAt(pos | 0);
return (code <= 32) && (code === 32 || code === 10 || code === 9);
}
//
// Parse from a token, regexp or string, and move forward if match
//
function $(tok) {
var tokType = typeof tok,
match, length;
// Either match a single character in the input,
// or match a regexp in the current chunk (`current`).
//
if (tokType === "string") {
if (input.charAt(i) !== tok) {
return null;
}
skipWhitespace(1);
return tok;
}
// regexp
sync ();
if (! (match = tok.exec(current))) {
return null;
}
length = match[0].length;
// The match is confirmed, add the match length to `i`,
// and consume any extra white-space characters (' ' || '\n')
// which come after that. The reason for this is that LeSS's
// grammar is mostly white-space insensitive.
//
skipWhitespace(length);
if(typeof(match) === 'string') {
return match;
} else {
return match.length === 1 ? match[0] : match;
}
}
// Specialization of $(tok)
function $re(tok) {
if (i > currentPos) {
current = current.slice(i - currentPos);
currentPos = i;
}
var m = tok.exec(current);
if (!m) {
return null;
}
skipWhitespace(m[0].length);
if(typeof m === "string") {
return m;
}
return m.length === 1 ? m[0] : m;
}
var _$re = $re;
// Specialization of $(tok)
function $char(tok) {
if (input.charAt(i) !== tok) {
return null;
}
skipWhitespace(1);
return tok;
}
function skipWhitespace(length) {
var oldi = i, oldj = j,
curr = i - currentPos,
endIndex = i + current.length - curr,
mem = (i += length),
inp = input,
c;
for (; i < endIndex; i++) {
c = inp.charCodeAt(i);
if (c > 32) {
break;
}
if ((c !== 32) && (c !== 10) && (c !== 9) && (c !== 13)) {
break;
}
}
current = current.slice(length + i - mem + curr);
currentPos = i;
if (!current.length && (j < chunks.length - 1)) {
current = chunks[++j];
}
return oldi !== i || oldj !== j;
}
function expect(arg, msg) {
var result = (typeof arg === "function") ? arg.call(parsers) : $(arg);
if (result) {
return result;
}
error(msg || (typeof(arg) === 'string' ? "expected '" + arg + "' got '" + input.charAt(i) + "'"
: "unexpected token"));
}
// Specialization of expect()
function expectChar(arg, msg) {
if (input.charAt(i) === arg) {
skipWhitespace(1);
return arg;
}
error(msg || "expected '" + arg + "' got '" + input.charAt(i) + "'");
}
function error(msg, type) {
var e = new Error(msg);
e.index = i;
e.type = type || 'Syntax';
throw e;
}
// Same as $(), but don't change the state of the parser,
// just return the match.
function peek(tok) {
if (typeof(tok) === 'string') {
return input.charAt(i) === tok;
} else {
return tok.test(current);
}
}
// Specialization of peek()
function peekChar(tok) {
return input.charAt(i) === tok;
}
function getInput(e, env) {
if (e.filename && env.currentFileInfo.filename && (e.filename !== env.currentFileInfo.filename)) {
return parser.imports.contents[e.filename];
} else {
return input;
}
}
function getLocation(index, inputStream) {
var n = index + 1,
line = null,
column = -1;
while (--n >= 0 && inputStream.charAt(n) !== '\n') {
column++;
}
if (typeof index === 'number') {
line = (inputStream.slice(0, index).match(/\n/g) || "").length;
}
return {
line: line,
column: column
};
}
function getDebugInfo(index, inputStream, env) {
var filename = env.currentFileInfo.filename;
if(less.mode !== 'browser' && less.mode !== 'rhino') {
filename = require('path').resolve(filename);
}
return {
lineNumber: getLocation(index, inputStream).line + 1,
fileName: filename
};
}
function LessError(e, env) {
var input = getInput(e, env),
loc = getLocation(e.index, input),
line = loc.line,
col = loc.column,
callLine = e.call && getLocation(e.call, input).line,
lines = input.split('\n');
this.type = e.type || 'Syntax';
this.message = e.message;
this.filename = e.filename || env.currentFileInfo.filename;
this.index = e.index;
this.line = typeof(line) === 'number' ? line + 1 : null;
this.callLine = callLine + 1;
this.callExtract = lines[callLine];
this.stack = e.stack;
this.column = col;
this.extract = [
lines[line - 1],
lines[line],
lines[line + 1]
];
}
LessError.prototype = new Error();
LessError.prototype.constructor = LessError;
this.env = env = env || {};
// The optimization level dictates the thoroughness of the parser,
// the lower the number, the less nodes it will create in the tree.
// This could matter for debugging, or if you want to access
// the individual nodes in the tree.
this.optimization = ('optimization' in this.env) ? this.env.optimization : 1;
//
// The Parser
//
parser = {
imports: imports,
//
// Parse an input string into an abstract syntax tree,
// @param str A string containing 'less' markup
// @param callback call `callback` when done.
// @param [additionalData] An optional map which can contains vars - a map (key, value) of variables to apply
//
parse: function (str, callback, additionalData) {
var root, line, lines, error = null, globalVars, modifyVars, preText = "";
i = j = currentPos = furthest = 0;
globalVars = (additionalData && additionalData.globalVars) ? less.Parser.serializeVars(additionalData.globalVars) + '\n' : '';
modifyVars = (additionalData && additionalData.modifyVars) ? '\n' + less.Parser.serializeVars(additionalData.modifyVars) : '';
if (globalVars || (additionalData && additionalData.banner)) {
preText = ((additionalData && additionalData.banner) ? additionalData.banner : "") + globalVars;
parser.imports.contentsIgnoredChars[env.currentFileInfo.filename] = preText.length;
}
str = str.replace(/\r\n/g, '\n');
// Remove potential UTF Byte Order Mark
input = str = preText + str.replace(/^\uFEFF/, '') + modifyVars;
parser.imports.contents[env.currentFileInfo.filename] = str;
// Split the input into chunks.
chunks = (function (input) {
var len = input.length, level = 0, parenLevel = 0,
lastOpening, lastClosing, lastMultiComment, lastMultiCommentEndBrace,
chunks = [], emitFrom = 0,
parserCurrentIndex, currentChunkStartIndex, cc, cc2, matched;
function fail(msg, index) {
error = new(LessError)({
index: index || parserCurrentIndex,
type: 'Parse',
message: msg,
filename: env.currentFileInfo.filename
}, env);
}
function emitChunk(force) {
var len = parserCurrentIndex - emitFrom;
if (((len < 512) && !force) || !len) {
return;
}
chunks.push(input.slice(emitFrom, parserCurrentIndex + 1));
emitFrom = parserCurrentIndex + 1;
}
for (parserCurrentIndex = 0; parserCurrentIndex < len; parserCurrentIndex++) {
cc = input.charCodeAt(parserCurrentIndex);
if (((cc >= 97) && (cc <= 122)) || (cc < 34)) {
// a-z or whitespace
continue;
}
switch (cc) {
case 40: // (
parenLevel++; continue;
case 41: // )
parenLevel--; continue;
case 59: // ;
if (!parenLevel) { emitChunk(); }
continue;
case 123: // {
level++; lastOpening = parserCurrentIndex; continue;
case 125: // }
level--; lastClosing = parserCurrentIndex;
if (!level) { emitChunk(); }
continue;
case 92: // \
if (parserCurrentIndex < len - 1) { parserCurrentIndex++; continue; }
return fail("unescaped `\\`");
case 34:
case 39:
case 96: // ", ' and `
matched = 0;
currentChunkStartIndex = parserCurrentIndex;
for (parserCurrentIndex = parserCurrentIndex + 1; parserCurrentIndex < len; parserCurrentIndex++) {
cc2 = input.charCodeAt(parserCurrentIndex);
if (cc2 > 96) { continue; }
if (cc2 == cc) { matched = 1; break; }
if (cc2 == 92) { // \
if (parserCurrentIndex == len - 1) {
return fail("unescaped `\\`");
}
parserCurrentIndex++;
}
}
if (matched) { continue; }
return fail("unmatched `" + String.fromCharCode(cc) + "`", currentChunkStartIndex);
case 47: // /, check for comment
if (parenLevel || (parserCurrentIndex == len - 1)) { continue; }
cc2 = input.charCodeAt(parserCurrentIndex + 1);
if (cc2 == 47) {
// //, find lnfeed
for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len; parserCurrentIndex++) {
cc2 = input.charCodeAt(parserCurrentIndex);
if ((cc2 <= 13) && ((cc2 == 10) || (cc2 == 13))) { break; }
}
} else if (cc2 == 42) {
// /*, find */
lastMultiComment = currentChunkStartIndex = parserCurrentIndex;
for (parserCurrentIndex = parserCurrentIndex + 2; parserCurrentIndex < len - 1; parserCurrentIndex++) {
cc2 = input.charCodeAt(parserCurrentIndex);
if (cc2 == 125) { lastMultiCommentEndBrace = parserCurrentIndex; }
if (cc2 != 42) { continue; }
if (input.charCodeAt(parserCurrentIndex + 1) == 47) { break; }
}
if (parserCurrentIndex == len - 1) {
return fail("missing closing `*/`", currentChunkStartIndex);
}
}
continue;
case 42: // *, check for unmatched */
if ((parserCurrentIndex < len - 1) && (input.charCodeAt(parserCurrentIndex + 1) == 47)) {
return fail("unmatched `/*`");
}
continue;
}
}
if (level !== 0) {
if (level > 0) {
if ((lastMultiComment > lastOpening) && (lastMultiCommentEndBrace > lastMultiComment)) {
return fail("missing closing `}` or `*/`", lastOpening);
} else {
return fail("missing closing `}`", lastOpening);
}
}
return fail("missing opening `{`", lastClosing);
} else if (parenLevel !== 0) {
return fail((parenLevel > 0) ? "missing closing `)`" : "missing opening `(`");
}
emitChunk(true);
return chunks;
})(str);
if (error) {
return callback(new(LessError)(error, env));
}
current = chunks[0];
// Start with the primary rule.
// The whole syntax tree is held under a Ruleset node,
// with the `root` property set to true, so no `{}` are
// output. The callback is called when the input is parsed.
try {
root = new(tree.Ruleset)(null, this.parsers.primary());
root.root = true;
root.firstRoot = true;
} catch (e) {
return callback(new(LessError)(e, env));
}
root.toCSS = (function (evaluate) {
return function (options, variables) {
options = options || {};
var evaldRoot,
css,
evalEnv = new tree.evalEnv(options);
//
// Allows setting variables with a hash, so:
//
// `{ color: new(tree.Color)('#f01') }` will become:
//
// new(tree.Rule)('@color',
// new(tree.Value)([
// new(tree.Expression)([
// new(tree.Color)('#f01')
// ])
// ])
// )
//
if (typeof(variables) === 'object' && !Array.isArray(variables)) {
variables = Object.keys(variables).map(function (k) {
var value = variables[k];
if (! (value instanceof tree.Value)) {
if (! (value instanceof tree.Expression)) {
value = new(tree.Expression)([value]);
}
value = new(tree.Value)([value]);
}
return new(tree.Rule)('@' + k, value, false, null, 0);
});
evalEnv.frames = [new(tree.Ruleset)(null, variables)];
}
try {
var preEvalVisitors = [],
visitors = [
new(tree.joinSelectorVisitor)(),
new(tree.processExtendsVisitor)(),
new(tree.toCSSVisitor)({compress: Boolean(options.compress)})
], i, root = this;
if (options.plugins) {
for(i =0; i < options.plugins.length; i++) {
if (options.plugins[i].isPreEvalVisitor) {
preEvalVisitors.push(options.plugins[i]);
} else {
if (options.plugins[i].isPreVisitor) {
visitors.splice(0, 0, options.plugins[i]);
} else {
visitors.push(options.plugins[i]);
}
}
}
}
for(i = 0; i < preEvalVisitors.length; i++) {
preEvalVisitors[i].run(root);
}
evaldRoot = evaluate.call(root, evalEnv);
for(i = 0; i < visitors.length; i++) {
visitors[i].run(evaldRoot);
}
if (options.sourceMap) {
evaldRoot = new tree.sourceMapOutput(
{
contentsIgnoredCharsMap: parser.imports.contentsIgnoredChars,
writeSourceMap: options.writeSourceMap,
rootNode: evaldRoot,
contentsMap: parser.imports.contents,
sourceMapFilename: options.sourceMapFilename,
sourceMapURL: options.sourceMapURL,
outputFilename: options.sourceMapOutputFilename,
sourceMapBasepath: options.sourceMapBasepath,
sourceMapRootpath: options.sourceMapRootpath,
outputSourceFiles: options.outputSourceFiles,
sourceMapGenerator: options.sourceMapGenerator
});
}
css = evaldRoot.toCSS({
compress: Boolean(options.compress),
dumpLineNumbers: env.dumpLineNumbers,
strictUnits: Boolean(options.strictUnits)});
} catch (e) {
throw new(LessError)(e, env);
}
if (options.cleancss && less.mode === 'node') {
var CleanCSS = require('clean-css'),
cleancssOptions = options.cleancssOptions || {};
if (cleancssOptions.keepSpecialComments === undefined) {
cleancssOptions.keepSpecialComments = "*";
}
cleancssOptions.processImport = false;
cleancssOptions.noRebase = true;
if (cleancssOptions.noAdvanced === undefined) {
cleancssOptions.noAdvanced = true;
}
return new CleanCSS(cleancssOptions).minify(css);
} else if (options.compress) {
return css.replace(/(^(\s)+)|((\s)+$)/g, "");
} else {
return css;
}
};
})(root.eval);
// If `i` is smaller than the `input.length - 1`,
// it means the parser wasn't able to parse the whole
// string, so we've got a parsing error.
//
// We try to extract a \n delimited string,
// showing the line where the parse error occured.
// We split it up into two parts (the part which parsed,
// and the part which didn't), so we can color them differently.
if (i < input.length - 1) {
i = furthest;
var loc = getLocation(i, input);
lines = input.split('\n');
line = loc.line + 1;
error = {
type: "Parse",
message: "Unrecognised input",
index: i,
filename: env.currentFileInfo.filename,
line: line,
column: loc.column,
extract: [
lines[line - 2],
lines[line - 1],
lines[line]
]
};
}
var finish = function (e) {
e = error || e || parser.imports.error;
if (e) {
if (!(e instanceof LessError)) {
e = new(LessError)(e, env);
}
return callback(e);
}
else {
return callback(null, root);
}
};
if (env.processImports !== false) {
new tree.importVisitor(this.imports, finish)
.run(root);
} else {
return finish();
}
},
//
// Here in, the parsing rules/functions
//
// The basic structure of the syntax tree generated is as follows:
//
// Ruleset -> Rule -> Value -> Expression -> Entity
//
// Here's some LESS code:
//
// .class {
// color: #fff;
// border: 1px solid #000;
// width: @w + 4px;
// > .child {...}
// }
//
// And here's what the parse tree might look like:
//
// Ruleset (Selector '.class', [
// Rule ("color", Value ([Expression [Color #fff]]))
// Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
// Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
// Ruleset (Selector [Element '>', '.child'], [...])
// ])
//
// In general, most rules will try to parse a token with the `$()` function, and if the return
// value is truly, will return a new node, of the relevant type. Sometimes, we need to check
// first, before parsing, that's when we use `peek()`.
//
parsers: parsers = {
//
// The `primary` rule is the *entry* and *exit* point of the parser.
// The rules here can appear at any level of the parse tree.
//
// The recursive nature of the grammar is an interplay between the `block`
// rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
// as represented by this simplified grammar:
//
// primary → (ruleset | rule)+
// ruleset → selector+ block
// block → '{' primary '}'
//
// Only at one point is the primary rule not called from the
// block rule: at the root level.
//
primary: function () {
var mixin = this.mixin, $re = _$re, root = [], node;
while (true)
{
node = this.extendRule() || mixin.definition() || this.rule() || this.ruleset() ||
mixin.call() || this.comment() || this.directive();
if (node) {
root.push(node);
} else {
if (!($re(/^[\s\n]+/) || $re(/^;+/))) {
break;
}
}
}
return root;
},
// We create a Comment node for CSS comments `/* */`,
// but keep the LeSS comments `//` silent, by just skipping
// over them.
comment: function () {
var comment;
if (input.charAt(i) !== '/') { return; }
if (input.charAt(i + 1) === '/') {
return new(tree.Comment)($re(/^\/\/.*/), true, i, env.currentFileInfo);
}
comment = $re(/^\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/);
if (comment) {
return new(tree.Comment)(comment, false, i, env.currentFileInfo);
}
},
comments: function () {
var comment, comments = [];
while(true) {
comment = this.comment();
if (!comment) {
break;
}
comments.push(comment);
}
return comments;
},
//
// Entities are tokens which can be found inside an Expression
//
entities: {
//
// A string, which supports escaping " and '
//
// "milky way" 'he\'s the one!'
//
quoted: function () {
var str, j = i, e, index = i;
if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings
if (input.charAt(j) !== '"' && input.charAt(j) !== "'") { return; }
if (e) { $char('~'); }
str = $re(/^"((?:[^"\\\r\n]|\\.)*)"|'((?:[^'\\\r\n]|\\.)*)'/);
if (str) {
return new(tree.Quoted)(str[0], str[1] || str[2], e, index, env.currentFileInfo);
}
},
//
// A catch-all word, such as:
//
// black border-collapse
//
keyword: function () {
var k;
k = $re(/^[_A-Za-z-][_A-Za-z0-9-]*/);
if (k) {
var color = tree.Color.fromKeyword(k);
if (color) {
return color;
}
return new(tree.Keyword)(k);
}
},
//
// A function call
//
// rgb(255, 0, 255)
//
// We also try to catch IE's `alpha()`, but let the `alpha` parser
// deal with the details.
//
// The arguments are parsed with the `entities.arguments` parser.
//
call: function () {
var name, nameLC, args, alpha_ret, index = i;
name = /^([\w-]+|%|progid:[\w\.]+)\(/.exec(current);
if (!name) { return; }
name = name[1];
nameLC = name.toLowerCase();
if (nameLC === 'url') {
return null;
}
i += name.length;
if (nameLC === 'alpha') {
alpha_ret = parsers.alpha();
if(typeof alpha_ret !== 'undefined') {
return alpha_ret;
}
}
$char('('); // Parse the '(' and consume whitespace.
args = this.arguments();
if (! $char(')')) {
return;
}
if (name) { return new(tree.Call)(name, args, index, env.currentFileInfo); }
},
arguments: function () {
var args = [], arg;
while (true) {
arg = this.assignment() || parsers.expression();
if (!arg) {
break;
}
args.push(arg);
if (! $char(',')) {
break;
}
}
return args;
},
literal: function () {
return this.dimension() ||
this.color() ||
this.quoted() ||
this.unicodeDescriptor();
},
// Assignments are argument entities for calls.
// They are present in ie filter properties as shown below.
//
// filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
//
assignment: function () {
var key, value;
key = $re(/^\w+(?=\s?=)/i);
if (!key) {
return;
}
if (!$char('=')) {
return;
}
value = parsers.entity();
if (value) {
return new(tree.Assignment)(key, value);
}
},
//
// Parse url() tokens
//
// We use a specific rule for urls, because they don't really behave like
// standard function calls. The difference is that the argument doesn't have
// to be enclosed within a string, so it can't be parsed as an Expression.
//
url: function () {
var value;
if (input.charAt(i) !== 'u' || !$re(/^url\(/)) {
return;
}
value = this.quoted() || this.variable() ||
$re(/^(?:(?:\\[\(\)'"])|[^\(\)'"])+/) || "";
expectChar(')');
return new(tree.URL)((value.value != null || value instanceof tree.Variable)
? value : new(tree.Anonymous)(value), env.currentFileInfo);
},
//
// A Variable entity, such as `@fink`, in
//
// width: @fink + 2px
//
// We use a different parser for variable definitions,
// see `parsers.variable`.
//
variable: function () {
var name, index = i;
if (input.charAt(i) === '@' && (name = $re(/^@@?[\w-]+/))) {
return new(tree.Variable)(name, index, env.currentFileInfo);
}
},
// A variable entity useing the protective {} e.g. @{var}
variableCurly: function () {
var curly, index = i;
if (input.charAt(i) === '@' && (curly = $re(/^@\{([\w-]+)\}/))) {
return new(tree.Variable)("@" + curly[1], index, env.currentFileInfo);
}
},
//
// A Hexadecimal color
//
// #4F3C2F
//
// `rgb` and `hsl` colors are parsed through the `entities.call` parser.
//
color: function () {
var rgb;
if (input.charAt(i) === '#' && (rgb = $re(/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/))) {
return new(tree.Color)(rgb[1]);
}
},
//
// A Dimension, that is, a number and a unit
//
// 0.5em 95%
//
dimension: function () {
var value, c = input.charCodeAt(i);
//Is the first char of the dimension 0-9, '.', '+' or '-'
if ((c > 57 || c < 43) || c === 47 || c == 44) {
return;
}
value = $re(/^([+-]?\d*\.?\d+)(%|[a-z]+)?/);
if (value) {
return new(tree.Dimension)(value[1], value[2]);
}
},
//
// A unicode descriptor, as is used in unicode-range
//
// U+0?? or U+00A1-00A9
//
unicodeDescriptor: function () {
var ud;
ud = $re(/^U\+[0-9a-fA-F?]+(\-[0-9a-fA-F?]+)?/);
if (ud) {
return new(tree.UnicodeDescriptor)(ud[0]);
}
},
//
// JavaScript code to be evaluated
//
// `window.location.href`
//
javascript: function () {
var str, j = i, e;
if (input.charAt(j) === '~') { j++; e = true; } // Escaped strings
if (input.charAt(j) !== '`') { return; }
if (env.javascriptEnabled !== undefined && !env.javascriptEnabled) {
error("You are using JavaScript, which has been disabled.");
}
if (e) { $char('~'); }
str = $re(/^`([^`]*)`/);
if (str) {
return new(tree.JavaScript)(str[1], i, e);
}
}
},
//
// The variable part of a variable definition. Used in the `rule` parser
//
// @fink:
//
variable: function () {
var name;
if (input.charAt(i) === '@' && (name = $re(/^(@[\w-]+)\s*:/))) { return name[1]; }
},
//
// extend syntax - used to extend selectors
//
extend: function(isRule) {
var elements, e, index = i, option, extendList, extend;
if (!(isRule ? $re(/^&:extend\(/) : $re(/^:extend\(/))) { return; }
do {
option = null;
elements = null;
while (! (option = $re(/^(all)(?=\s*(\)|,))/))) {
e = this.element();
if (!e) { break; }
if (elements) { elements.push(e); } else { elements = [ e ]; }
}
option = option && option[1];
extend = new(tree.Extend)(new(tree.Selector)(elements), option, index);
if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
} while($char(","));
expect(/^\)/);
if (isRule) {
expect(/^;/);
}
return extendList;
},
//
// extendRule - used in a rule to extend all the parent selectors
//
extendRule: function() {
return this.extend(true);
},
//
// Mixins
//
mixin: {
//
// A Mixin call, with an optional argument list
//
// #mixins > .square(#fff);
// .rounded(4px, black);
// .button;
//
// The `while` loop is there because mixins can be
// namespaced, but we only support the child and descendant
// selector for now.
//
call: function () {
var s = input.charAt(i), important = false, index = i,
elements, elem, e, c, args;
if (s !== '.' && s !== '#') { return; }
save(); // stop us absorbing part of an invalid selector
while (true) {
e = $re(/^[#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/);
if (!e) {
break;
}
elem = new(tree.Element)(c, e, i, env.currentFileInfo);
if (elements) { elements.push(elem); } else { elements = [ elem ]; }
c = $char('>');
}
if ($char('(')) {
args = this.args(true).args;
expectChar(')');
}
if (parsers.important()) {
important = true;
}
if (elements && ($char(';') || peekChar('}'))) {
return new(tree.mixin.Call)(elements, args, index, env.currentFileInfo, important);
}
restore();
},
args: function (isCall) {
var parsers = parser.parsers, entities = parsers.entities,
returner = { args:null, variadic: false },
expressions = [], argsSemiColon = [], argsComma = [],
isSemiColonSeperated, expressionContainsNamed, name, nameLoop, value, arg;
while (true) {
if (isCall) {
arg = parsers.expression();
} else {
parsers.comments();
if (input.charAt(i) === '.' && $re(/^\.{3}/)) {
returner.variadic = true;
if ($char(";") && !isSemiColonSeperated) {
isSemiColonSeperated = true;
}
(isSemiColonSeperated ? argsSemiColon : argsComma)
.push({ variadic: true });
break;
}
arg = entities.variable() || entities.literal() || entities.keyword();
}
if (!arg) {
break;
}
nameLoop = null;
if (arg.throwAwayComments) {
arg.throwAwayComments();
}
value = arg;
var val = null;
if (isCall) {
// Variable
if (arg.value.length == 1) {
val = arg.value[0];
}
} else {
val = arg;
}
if (val && val instanceof tree.Variable) {
if ($char(':')) {
if (expressions.length > 0) {
if (isSemiColonSeperated) {
error("Cannot mix ; and , as delimiter types");
}
expressionContainsNamed = true;
}
value = expect(parsers.expression);
nameLoop = (name = val.name);
} else if (!isCall && $re(/^\.{3}/)) {
returner.variadic = true;
if ($char(";") && !isSemiColonSeperated) {
isSemiColonSeperated = true;
}
(isSemiColonSeperated ? argsSemiColon : argsComma)
.push({ name: arg.name, variadic: true });
break;
} else if (!isCall) {
name = nameLoop = val.name;
value = null;
}
}
if (value) {
expressions.push(value);
}
argsComma.push({ name:nameLoop, value:value });
if ($char(',')) {
continue;
}
if ($char(';') || isSemiColonSeperated) {
if (expressionContainsNamed) {
error("Cannot mix ; and , as delimiter types");
}
isSemiColonSeperated = true;
if (expressions.length > 1) {
value = new(tree.Value)(expressions);
}
argsSemiColon.push({ name:name, value:value });
name = null;
expressions = [];
expressionContainsNamed = false;
}
}
returner.args = isSemiColonSeperated ? argsSemiColon : argsComma;
return returner;
},
//
// A Mixin definition, with a list of parameters
//
// .rounded (@radius: 2px, @color) {
// ...
// }
//
// Until we have a finer grained state-machine, we have to
// do a look-ahead, to make sure we don't have a mixin call.
// See the `rule` function for more information.
//
// We start by matching `.rounded (`, and then proceed on to
// the argument list, which has optional default values.
// We store the parameters in `params`, with a `value` key,
// if there is a value, such as in the case of `@radius`.
//
// Once we've got our params list, and a closing `)`, we parse
// the `{...}` block.
//
definition: function () {
var name, params = [], match, ruleset, cond, variadic = false;
if ((input.charAt(i) !== '.' && input.charAt(i) !== '#') ||
peek(/^[^{]*\}/)) {
return;
}
save();
match = $re(/^([#.](?:[\w-]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/);
if (match) {
name = match[1];
var argInfo = this.args(false);
params = argInfo.args;
variadic = argInfo.variadic;
// .mixincall("@{a}");
// looks a bit like a mixin definition.. so we have to be nice and restore
if (!$char(')')) {
furthest = i;
restore();
}
parsers.comments();
if ($re(/^when/)) { // Guard
cond = expect(parsers.conditions, 'expected condition');
}
ruleset = parsers.block();
if (ruleset) {
return new(tree.mixin.Definition)(name, params, ruleset, cond, variadic);
} else {
restore();
}
}
}
},
//
// Entities are the smallest recognized token,
// and can be found inside a rule's value.
//
entity: function () {
var entities = this.entities;
return entities.literal() || entities.variable() || entities.url() ||
entities.call() || entities.keyword() || entities.javascript() ||
this.comment();
},
//
// A Rule terminator. Note that we use `peek()` to check for '}',
// because the `block` rule will be expecting it, but we still need to make sure
// it's there, if ';' was ommitted.
//
end: function () {
return $char(';') || peekChar('}');
},
//
// IE's alpha function
//
// alpha(opacity=88)
//
alpha: function () {
var value;
if (! $re(/^\(opacity=/i)) { return; }
value = $re(/^\d+/) || this.entities.variable();
if (value) {
expectChar(')');
return new(tree.Alpha)(value);
}
},
//
// A Selector Element
//
// div
// + h1
// #socks
// input[type="text"]
//
// Elements are the building blocks for Selectors,
// they are made out of a `Combinator` (see combinator rule),
// and an element name, such as a tag a class, or `*`.
//
element: function () {
var e, c, v;
c = this.combinator();
e = $re(/^(?:\d+\.\d+|\d+)%/) || $re(/^(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/) ||
$char('*') || $char('&') || this.attribute() || $re(/^\([^()@]+\)/) || $re(/^[\.#](?=@)/) ||
this.entities.variableCurly();
if (! e) {
if ($char('(')) {
if ((v = this.selector()) && $char(')')) {
e = new(tree.Paren)(v);
}
}
}
if (e) { return new(tree.Element)(c, e, i, env.currentFileInfo); }
},
//
// Combinators combine elements together, in a Selector.
//
// Because our parser isn't white-space sensitive, special care
// has to be taken, when parsing the descendant combinator, ` `,
// as it's an empty space. We have to check the previous character
// in the input, to see if it's a ` ` character. More info on how
// we deal with this in *combinator.js*.
//
combinator: function () {
var c = input.charAt(i);
if (c === '>' || c === '+' || c === '~' || c === '|') {
i++;
while (isWhitespace(input, i)) { i++; }
return new(tree.Combinator)(c);
} else if (isWhitespace(input, i - 1)) {
return new(tree.Combinator)(" ");
} else {
return new(tree.Combinator)(null);
}
},
//
// A CSS selector (see selector below)
// with less extensions e.g. the ability to extend and guard
//
lessSelector: function () {
return this.selector(true);
},
//
// A CSS Selector
//
// .class > div + h1
// li a:hover
//
// Selectors are made out of one or more Elements, see above.
//
selector: function (isLess) {
var $re = _$re, elements, extendList, c, e, extend, when, condition;
while ((isLess && (extend = this.extend())) || (isLess && (when = $re(/^when/))) || (e = this.element())) {
if (when) {
condition = expect(this.conditions, 'expected condition');
} else if (condition) {
error("CSS guard can only be used at the end of selector");
} else if (extend) {
if (extendList) { extendList.push(extend); } else { extendList = [ extend ]; }
} else {
if (extendList) { error("Extend can only be used at the end of selector"); }
c = input.charAt(i);
if (elements) { elements.push(e); } else { elements = [ e ]; }
e = null;
}
if (c === '{' || c === '}' || c === ';' || c === ',' || c === ')') {
break;
}
}
if (elements) { return new(tree.Selector)(elements, extendList, condition, i, env.currentFileInfo); }
if (extendList) { error("Extend must be used to extend a selector, it cannot be used on its own"); }
},
attribute: function () {
if (! $char('[')) { return; }
var entities = this.entities,
key, val, op;
if (!(key = entities.variableCurly())) {
key = expect(/^(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\.)+/);
}
op = $re(/^[|~*$^]?=/);
if (op) {
val = entities.quoted() || $re(/^[0-9]+%/) || $re(/^[\w-]+/) || entities.variableCurly();
}
expectChar(']');
return new(tree.Attribute)(key, op, val);
},
//
// The `block` rule is used by `ruleset` and `mixin.definition`.
// It's a wrapper around the `primary` rule, with added `{}`.
//
block: function () {
var content;
if ($char('{') && (content = this.primary()) && $char('}')) {
return content;
}
},
//
// div, .class, body > p {...}
//
ruleset: function () {
var selectors, s, rules, debugInfo;
save();
if (env.dumpLineNumbers) {
debugInfo = getDebugInfo(i, input, env);
}
while (true) {
s = this.lessSelector();
if (!s) {
break;
}
if (selectors) { selectors.push(s); } else { selectors = [ s ]; }
this.comments();
if (! $char(',')) { break; }
if (s.condition) {
error("Guards are only currently allowed on a single selector.");
}
this.comments();
}
if (selectors && (rules = this.block())) {
var ruleset = new(tree.Ruleset)(selectors, rules, env.strictImports);
if (env.dumpLineNumbers) {
ruleset.debugInfo = debugInfo;
}
return ruleset;
} else {
// Backtrack
furthest = i;
restore();
}
},
rule: function (tryAnonymous) {
var name, value, c = input.charAt(i), important, merge = false;
save();
if (c === '.' || c === '#' || c === '&') { return; }
name = this.variable() || this.ruleProperty();
if (name) {
// prefer to try to parse first if its a variable or we are compressing
// but always fallback on the other one
value = !tryAnonymous && (env.compress || (name.charAt && (name.charAt(0) === '@'))) ?
(this.value() || this.anonymousValue()) :
(this.anonymousValue() || this.value());
important = this.important();
// a name returned by this.ruleProperty() is always an array of the form:
// ["", "string-1", ..., "string-n", ""] or ["", "string-1", ..., "string-n", "+"]
merge = name.pop && (name.pop() === "+");
if (value && this.end()) {
return new (tree.Rule)(name, value, important, merge, memo, env.currentFileInfo);
} else {
furthest = i;
restore();
if (value && !tryAnonymous) {
return this.rule(true);
}
}
}
},
anonymousValue: function () {
var match;
match = /^([^@+\/'"*`(;{}-]*);/.exec(current);
if (match) {
i += match[0].length - 1;
return new(tree.Anonymous)(match[1]);
}
},
//
// An @import directive
//
// @import "lib";
//
// Depending on our environemnt, importing is done differently:
// In the browser, it's an XHR request, in Node, it would be a
// file-system operation. The function used for importing is
// stored in `import`, which we pass to the Import constructor.
//
"import": function () {
var path, features, index = i;
save();
var dir = $re(/^@import?\s+/);
var options = (dir ? this.importOptions() : null) || {};
if (dir && (path = this.entities.quoted() || this.entities.url())) {
features = this.mediaFeatures();
if ($char(';')) {
features = features && new(tree.Value)(features);
return new(tree.Import)(path, features, options, index, env.currentFileInfo);
}
}
restore();
},
importOptions: function() {
var o, options = {}, optionName, value;
// list of options, surrounded by parens
if (! $char('(')) { return null; }
do {
o = this.importOption();
if (o) {
optionName = o;
value = true;
switch(optionName) {
case "css":
optionName = "less";
value = false;
break;
case "once":
optionName = "multiple";
value = false;
break;
}
options[optionName] = value;
if (! $char(',')) { break; }
}
} while (o);
expectChar(')');
return options;
},
importOption: function() {
var opt = $re(/^(less|css|multiple|once|inline|reference)/);
if (opt) {
return opt[1];
}
},
mediaFeature: function () {
var entities = this.entities, nodes = [], e, p;
do {
e = entities.keyword() || entities.variable();
if (e) {
nodes.push(e);
} else if ($char('(')) {
p = this.property();
e = this.value();
if ($char(')')) {
if (p && e) {
nodes.push(new(tree.Paren)(new(tree.Rule)(p, e, null, null, i, env.currentFileInfo, true)));
} else if (e) {
nodes.push(new(tree.Paren)(e));
} else {
return null;
}
} else { return null; }
}
} while (e);
if (nodes.length > 0) {
return new(tree.Expression)(nodes);
}
},
mediaFeatures: function () {
var entities = this.entities, features = [], e;
do {
e = this.mediaFeature();
if (e) {
features.push(e);
if (! $char(',')) { break; }
} else {
e = entities.variable();
if (e) {
features.push(e);
if (! $char(',')) { break; }
}
}
} while (e);
return features.length > 0 ? features : null;
},
media: function () {
var features, rules, media, debugInfo;
if (env.dumpLineNumbers) {
debugInfo = getDebugInfo(i, input, env);
}
if ($re(/^@media/)) {
features = this.mediaFeatures();
rules = this.block();
if (rules) {
media = new(tree.Media)(rules, features, i, env.currentFileInfo);
if (env.dumpLineNumbers) {
media.debugInfo = debugInfo;
}
return media;
}
}
},
//
// A CSS Directive
//
// @charset "utf-8";
//
directive: function () {
var name, value, rules, nonVendorSpecificName,
hasBlock, hasIdentifier, hasExpression, identifier;
if (input.charAt(i) !== '@') { return; }
value = this['import']() || this.media();
if (value) {
return value;
}
save();
name = $re(/^@[a-z-]+/);
if (!name) { return; }
nonVendorSpecificName = name;
if (name.charAt(1) == '-' && name.indexOf('-', 2) > 0) {
nonVendorSpecificName = "@" + name.slice(name.indexOf('-', 2) + 1);
}
switch(nonVendorSpecificName) {
case "@font-face":
hasBlock = true;
break;
case "@viewport":
case "@top-left":
case "@top-left-corner":
case "@top-center":
case "@top-right":
case "@top-right-corner":
case "@bottom-left":
case "@bottom-left-corner":
case "@bottom-center":
case "@bottom-right":
case "@bottom-right-corner":
case "@left-top":
case "@left-middle":
case "@left-bottom":
case "@right-top":
case "@right-middle":
case "@right-bottom":
hasBlock = true;
break;
case "@host":
case "@page":
case "@document":
case "@supports":
case "@keyframes":
hasBlock = true;
hasIdentifier = true;
break;
case "@namespace":
hasExpression = true;
break;
}
if (hasIdentifier) {
identifier = ($re(/^[^{]+/) || '').trim();
if (identifier) {
name += " " + identifier;
}
}
if (hasBlock) {
rules = this.block();
if (rules) {
return new(tree.Directive)(name, rules, i, env.currentFileInfo);
}
} else {
value = hasExpression ? this.expression() : this.entity();
if (value && $char(';')) {
var directive = new(tree.Directive)(name, value, i, env.currentFileInfo);
if (env.dumpLineNumbers) {
directive.debugInfo = getDebugInfo(i, input, env);
}
return directive;
}
}
restore();
},
//
// A Value is a comma-delimited list of Expressions
//
// font-family: Baskerville, Georgia, serif;
//
// In a Rule, a Value represents everything after the `:`,
// and before the `;`.
//
value: function () {
var e, expressions = [];
do {
e = this.expression();
if (e) {
expressions.push(e);
if (! $char(',')) { break; }
}
} while(e);
if (expressions.length > 0) {
return new(tree.Value)(expressions);
}
},
important: function () {
if (input.charAt(i) === '!') {
return $re(/^! *important/);
}
},
sub: function () {
var a, e;
if ($char('(')) {
a = this.addition();
if (a) {
e = new(tree.Expression)([a]);
expectChar(')');
e.parens = true;
return e;
}
}
},
multiplication: function () {
var m, a, op, operation, isSpaced;
m = this.operand();
if (m) {
isSpaced = isWhitespace(input, i - 1);
while (true) {
if (peek(/^\/[*\/]/)) {
break;
}
op = $char('/') || $char('*');
if (!op) { break; }
a = this.operand();
if (!a) { break; }
m.parensInOp = true;
a.parensInOp = true;
operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
isSpaced = isWhitespace(input, i - 1);
}
return operation || m;
}
},
addition: function () {
var m, a, op, operation, isSpaced;
m = this.multiplication();
if (m) {
isSpaced = isWhitespace(input, i - 1);
while (true) {
op = $re(/^[-+]\s+/) || (!isSpaced && ($char('+') || $char('-')));
if (!op) {
break;
}
a = this.multiplication();
if (!a) {
break;
}
m.parensInOp = true;
a.parensInOp = true;
operation = new(tree.Operation)(op, [operation || m, a], isSpaced);
isSpaced = isWhitespace(input, i - 1);
}
return operation || m;
}
},
conditions: function () {
var a, b, index = i, condition;
a = this.condition();
if (a) {
while (true) {
if (!peek(/^,\s*(not\s*)?\(/) || !$char(',')) {
break;
}
b = this.condition();
if (!b) {
break;
}
condition = new(tree.Condition)('or', condition || a, b, index);
}
return condition || a;
}
},
condition: function () {
var entities = this.entities, index = i, negate = false,
a, b, c, op;
if ($re(/^not/)) { negate = true; }
expectChar('(');
a = this.addition() || entities.keyword() || entities.quoted();
if (a) {
op = $re(/^(?:>=|<=|=<|[<=>])/);
if (op) {
b = this.addition() || entities.keyword() || entities.quoted();
if (b) {
c = new(tree.Condition)(op, a, b, index, negate);
} else {
error('expected expression');
}
} else {
c = new(tree.Condition)('=', a, new(tree.Keyword)('true'), index, negate);
}
expectChar(')');
return $re(/^and/) ? new(tree.Condition)('and', c, this.condition()) : c;
}
},
//
// An operand is anything that can be part of an operation,
// such as a Color, or a Variable
//
operand: function () {
var entities = this.entities,
p = input.charAt(i + 1), negate;
if (input.charAt(i) === '-' && (p === '@' || p === '(')) { negate = $char('-'); }
var o = this.sub() || entities.dimension() ||
entities.color() || entities.variable() ||
entities.call();
if (negate) {
o.parensInOp = true;
o = new(tree.Negative)(o);
}
return o;
},
//
// Expressions either represent mathematical operations,
// or white-space delimited Entities.
//
// 1px solid black
// @var * 2
//
expression: function () {
var entities = [], e, delim;
do {
e = this.addition() || this.entity();
if (e) {
entities.push(e);
// operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
if (!peek(/^\/[\/*]/)) {
delim = $char('/');
if (delim) {
entities.push(new(tree.Anonymous)(delim));
}
}
}
} while (e);
if (entities.length > 0) {
return new(tree.Expression)(entities);
}
},
property: function () {
var name = $re(/^(\*?-?[_a-zA-Z0-9-]+)\s*:/);
if (name) {
return name[1];
}
},
ruleProperty: function () {
var c = current, name = [], index = [], length = 0;
function match(re) {
var a = re.exec(c);
if (a) {
index.push(i + length);
length += a[0].length;
c = c.slice(a[1].length);
return name.push(a[1]);
}
}
match(/^(\*?)/);
while (match(/^((?:[\w-]+)|(?:@\{[\w-]+\}))/)); // !
if ((name.length > 1) && match(/^\s*(\+?)\s*:/)) {
// at last, we have the complete match now. move forward,
// convert @{var}s to tree.Variable(s) and return:
skipWhitespace(length);
for (var k in name) {
if (name[k].charAt(0) === '@') {
name[k] = new tree.Variable('@' + name[k].slice(2, -1),
index[k], env.currentFileInfo);
}
}
return name;
}
}
}
};
return parser;
};
less.Parser.serializeVars = function(vars) {
var s = '';
for (var name in vars) {
if (Object.hasOwnProperty.call(vars, name)) {
var value = vars[name];
s += ((name[0] === '@') ? '' : '@') + name +': '+ value +
((('' + value).slice(-1) === ';') ? '' : ';');
}
}
return s;
};
(function (tree) {
tree.functions = {
rgb: function (r, g, b) {
return this.rgba(r, g, b, 1.0);
},
rgba: function (r, g, b, a) {
var rgb = [r, g, b].map(function (c) { return scaled(c, 255); });
a = number(a);
return new(tree.Color)(rgb, a);
},
hsl: function (h, s, l) {
return this.hsla(h, s, l, 1.0);
},
hsla: function (h, s, l, a) {
function hue(h) {
h = h < 0 ? h + 1 : (h > 1 ? h - 1 : h);
if (h * 6 < 1) { return m1 + (m2 - m1) * h * 6; }
else if (h * 2 < 1) { return m2; }
else if (h * 3 < 2) { return m1 + (m2 - m1) * (2/3 - h) * 6; }
else { return m1; }
}
h = (number(h) % 360) / 360;
s = clamp(number(s)); l = clamp(number(l)); a = clamp(number(a));
var m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
var m1 = l * 2 - m2;
return this.rgba(hue(h + 1/3) * 255,
hue(h) * 255,
hue(h - 1/3) * 255,
a);
},
hsv: function(h, s, v) {
return this.hsva(h, s, v, 1.0);
},
hsva: function(h, s, v, a) {
h = ((number(h) % 360) / 360) * 360;
s = number(s); v = number(v); a = number(a);
var i, f;
i = Math.floor((h / 60) % 6);
f = (h / 60) - i;
var vs = [v,
v * (1 - s),
v * (1 - f * s),
v * (1 - (1 - f) * s)];
var perm = [[0, 3, 1],
[2, 0, 1],
[1, 0, 3],
[1, 2, 0],
[3, 1, 0],
[0, 1, 2]];
return this.rgba(vs[perm[i][0]] * 255,
vs[perm[i][1]] * 255,
vs[perm[i][2]] * 255,
a);
},
hue: function (color) {
return new(tree.Dimension)(Math.round(color.toHSL().h));
},
saturation: function (color) {
return new(tree.Dimension)(Math.round(color.toHSL().s * 100), '%');
},
lightness: function (color) {
return new(tree.Dimension)(Math.round(color.toHSL().l * 100), '%');
},
hsvhue: function(color) {
return new(tree.Dimension)(Math.round(color.toHSV().h));
},
hsvsaturation: function (color) {
return new(tree.Dimension)(Math.round(color.toHSV().s * 100), '%');
},
hsvvalue: function (color) {
return new(tree.Dimension)(Math.round(color.toHSV().v * 100), '%');
},
red: function (color) {
return new(tree.Dimension)(color.rgb[0]);
},
green: function (color) {
return new(tree.Dimension)(color.rgb[1]);
},
blue: function (color) {
return new(tree.Dimension)(color.rgb[2]);
},
alpha: function (color) {
return new(tree.Dimension)(color.toHSL().a);
},
luma: function (color) {
return new(tree.Dimension)(Math.round(color.luma() * color.alpha * 100), '%');
},
saturate: function (color, amount) {
// filter: saturate(3.2);
// should be kept as is, so check for color
if (!color.rgb) {
return null;
}
var hsl = color.toHSL();
hsl.s += amount.value / 100;
hsl.s = clamp(hsl.s);
return hsla(hsl);
},
desaturate: function (color, amount) {
var hsl = color.toHSL();
hsl.s -= amount.value / 100;
hsl.s = clamp(hsl.s);
return hsla(hsl);
},
lighten: function (color, amount) {
var hsl = color.toHSL();
hsl.l += amount.value / 100;
hsl.l = clamp(hsl.l);
return hsla(hsl);
},
darken: function (color, amount) {
var hsl = color.toHSL();
hsl.l -= amount.value / 100;
hsl.l = clamp(hsl.l);
return hsla(hsl);
},
fadein: function (color, amount) {
var hsl = color.toHSL();
hsl.a += amount.value / 100;
hsl.a = clamp(hsl.a);
return hsla(hsl);
},
fadeout: function (color, amount) {
var hsl = color.toHSL();
hsl.a -= amount.value / 100;
hsl.a = clamp(hsl.a);
return hsla(hsl);
},
fade: function (color, amount) {
var hsl = color.toHSL();
hsl.a = amount.value / 100;
hsl.a = clamp(hsl.a);
return hsla(hsl);
},
spin: function (color, amount) {
var hsl = color.toHSL();
var hue = (hsl.h + amount.value) % 360;
hsl.h = hue < 0 ? 360 + hue : hue;
return hsla(hsl);
},
//
// Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
// http://sass-lang.com
//
mix: function (color1, color2, weight) {
if (!weight) {
weight = new(tree.Dimension)(50);
}
var p = weight.value / 100.0;
var w = p * 2 - 1;
var a = color1.toHSL().a - color2.toHSL().a;
var w1 = (((w * a == -1) ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
var w2 = 1 - w1;
var rgb = [color1.rgb[0] * w1 + color2.rgb[0] * w2,
color1.rgb[1] * w1 + color2.rgb[1] * w2,
color1.rgb[2] * w1 + color2.rgb[2] * w2];
var alpha = color1.alpha * p + color2.alpha * (1 - p);
return new(tree.Color)(rgb, alpha);
},
greyscale: function (color) {
return this.desaturate(color, new(tree.Dimension)(100));
},
contrast: function (color, dark, light, threshold) {
// filter: contrast(3.2);
// should be kept as is, so check for color
if (!color.rgb) {
return null;
}
if (typeof light === 'undefined') {
light = this.rgba(255, 255, 255, 1.0);
}
if (typeof dark === 'undefined') {
dark = this.rgba(0, 0, 0, 1.0);
}
//Figure out which is actually light and dark!
if (dark.luma() > light.luma()) {
var t = light;
light = dark;
dark = t;
}
if (typeof threshold === 'undefined') {
threshold = 0.43;
} else {
threshold = number(threshold);
}
if (color.luma() < threshold) {
return light;
} else {
return dark;
}
},
e: function (str) {
return new(tree.Anonymous)(str instanceof tree.JavaScript ? str.evaluated : str);
},
escape: function (str) {
return new(tree.Anonymous)(encodeURI(str.value).replace(/=/g, "%3D").replace(/:/g, "%3A").replace(/#/g, "%23").replace(/;/g, "%3B").replace(/\(/g, "%28").replace(/\)/g, "%29"));
},
'%': function (quoted /* arg, arg, ...*/) {
var args = Array.prototype.slice.call(arguments, 1),
str = quoted.value;
for (var i = 0; i < args.length; i++) {
/*jshint loopfunc:true */
str = str.replace(/%[sda]/i, function(token) {
var value = token.match(/s/i) ? args[i].value : args[i].toCSS();
return token.match(/[A-Z]$/) ? encodeURIComponent(value) : value;
});
}
str = str.replace(/%%/g, '%');
return new(tree.Quoted)('"' + str + '"', str);
},
unit: function (val, unit) {
if(!(val instanceof tree.Dimension)) {
throw { type: "Argument", message: "the first argument to unit must be a number" + (val instanceof tree.Operation ? ". Have you forgotten parenthesis?" : "") };
}
return new(tree.Dimension)(val.value, unit ? unit.toCSS() : "");
},
convert: function (val, unit) {
return val.convertTo(unit.value);
},
round: function (n, f) {
var fraction = typeof(f) === "undefined" ? 0 : f.value;
return _math(function(num) { return num.toFixed(fraction); }, null, n);
},
pi: function () {
return new(tree.Dimension)(Math.PI);
},
mod: function(a, b) {
return new(tree.Dimension)(a.value % b.value, a.unit);
},
pow: function(x, y) {
if (typeof x === "number" && typeof y === "number") {
x = new(tree.Dimension)(x);
y = new(tree.Dimension)(y);
} else if (!(x instanceof tree.Dimension) || !(y instanceof tree.Dimension)) {
throw { type: "Argument", message: "arguments must be numbers" };
}
return new(tree.Dimension)(Math.pow(x.value, y.value), x.unit);
},
_minmax: function (isMin, args) {
args = Array.prototype.slice.call(args);
switch(args.length) {
case 0: throw { type: "Argument", message: "one or more arguments required" };
case 1: return args[0];
}
var i, j, current, currentUnified, referenceUnified, unit,
order = [], // elems only contains original argument values.
values = {}; // key is the unit.toString() for unified tree.Dimension values,
// value is the index into the order array.
for (i = 0; i < args.length; i++) {
current = args[i];
if (!(current instanceof tree.Dimension)) {
order.push(current);
continue;
}
currentUnified = current.unify();
unit = currentUnified.unit.toString();
j = values[unit];
if (j === undefined) {
values[unit] = order.length;
order.push(current);
continue;
}
referenceUnified = order[j].unify();
if ( isMin && currentUnified.value < referenceUnified.value ||
!isMin && currentUnified.value > referenceUnified.value) {
order[j] = current;
}
}
if (order.length == 1) {
return order[0];
}
args = order.map(function (a) { return a.toCSS(this.env); })
.join(this.env.compress ? "," : ", ");
return new(tree.Anonymous)((isMin ? "min" : "max") + "(" + args + ")");
},
min: function () {
return this._minmax(true, arguments);
},
max: function () {
return this._minmax(false, arguments);
},
argb: function (color) {
return new(tree.Anonymous)(color.toARGB());
},
percentage: function (n) {
return new(tree.Dimension)(n.value * 100, '%');
},
color: function (n) {
if (n instanceof tree.Quoted) {
var colorCandidate = n.value,
returnColor;
returnColor = tree.Color.fromKeyword(colorCandidate);
if (returnColor) {
return returnColor;
}
if (/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/.test(colorCandidate)) {
return new(tree.Color)(colorCandidate.slice(1));
}
throw { type: "Argument", message: "argument must be a color keyword or 3/6 digit hex e.g. #FFF" };
} else {
throw { type: "Argument", message: "argument must be a string" };
}
},
iscolor: function (n) {
return this._isa(n, tree.Color);
},
isnumber: function (n) {
return this._isa(n, tree.Dimension);
},
isstring: function (n) {
return this._isa(n, tree.Quoted);
},
iskeyword: function (n) {
return this._isa(n, tree.Keyword);
},
isurl: function (n) {
return this._isa(n, tree.URL);
},
ispixel: function (n) {
return this.isunit(n, 'px');
},
ispercentage: function (n) {
return this.isunit(n, '%');
},
isem: function (n) {
return this.isunit(n, 'em');
},
isunit: function (n, unit) {
return (n instanceof tree.Dimension) && n.unit.is(unit.value || unit) ? tree.True : tree.False;
},
_isa: function (n, Type) {
return (n instanceof Type) ? tree.True : tree.False;
},
tint: function(color, amount) {
return this.mix(this.rgb(255,255,255), color, amount);
},
shade: function(color, amount) {
return this.mix(this.rgb(0, 0, 0), color, amount);
},
extract: function(values, index) {
index = index.value - 1; // (1-based index)
// handle non-array values as an array of length 1
// return 'undefined' if index is invalid
return Array.isArray(values.value)
? values.value[index] : Array(values)[index];
},
length: function(values) {
var n = Array.isArray(values.value) ? values.value.length : 1;
return new tree.Dimension(n);
},
"data-uri": function(mimetypeNode, filePathNode) {
if (typeof window !== 'undefined') {
return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
}
var mimetype = mimetypeNode.value;
var filePath = (filePathNode && filePathNode.value);
var fs = require("fs"),
path = require("path"),
useBase64 = false;
if (arguments.length < 2) {
filePath = mimetype;
}
if (this.env.isPathRelative(filePath)) {
if (this.currentFileInfo.relativeUrls) {
filePath = path.join(this.currentFileInfo.currentDirectory, filePath);
} else {
filePath = path.join(this.currentFileInfo.entryPath, filePath);
}
}
// detect the mimetype if not given
if (arguments.length < 2) {
var mime;
try {
mime = require('mime');
} catch (ex) {
mime = tree._mime;
}
mimetype = mime.lookup(filePath);
// use base 64 unless it's an ASCII or UTF-8 format
var charset = mime.charsets.lookup(mimetype);
useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
if (useBase64) { mimetype += ';base64'; }
}
else {
useBase64 = /;base64$/.test(mimetype);
}
var buf = fs.readFileSync(filePath);
// IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
// and the --ieCompat flag is enabled, return a normal url() instead.
var DATA_URI_MAX_KB = 32,
fileSizeInKB = parseInt((buf.length / 1024), 10);
if (fileSizeInKB >= DATA_URI_MAX_KB) {
if (this.env.ieCompat !== false) {
if (!this.env.silent) {
console.warn("Skipped data-uri embedding of %s because its size (%dKB) exceeds IE8-safe %dKB!", filePath, fileSizeInKB, DATA_URI_MAX_KB);
}
return new tree.URL(filePathNode || mimetypeNode, this.currentFileInfo).eval(this.env);
}
}
buf = useBase64 ? buf.toString('base64')
: encodeURIComponent(buf);
var uri = "'data:" + mimetype + ',' + buf + "'";
return new(tree.URL)(new(tree.Anonymous)(uri));
},
"svg-gradient": function(direction) {
function throwArgumentDescriptor() {
throw { type: "Argument", message: "svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]" };
}
if (arguments.length < 3) {
throwArgumentDescriptor();
}
var stops = Array.prototype.slice.call(arguments, 1),
gradientDirectionSvg,
gradientType = "linear",
rectangleDimension = 'x="0" y="0" width="1" height="1"',
useBase64 = true,
renderEnv = {compress: false},
returner,
directionValue = direction.toCSS(renderEnv),
i, color, position, positionValue, alpha;
switch (directionValue) {
case "to bottom":
gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
break;
case "to right":
gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
break;
case "to bottom right":
gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
break;
case "to top right":
gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
break;
case "ellipse":
case "ellipse at center":
gradientType = "radial";
gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
break;
default:
throw { type: "Argument", message: "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" };
}
returner = '' +
'';
if (useBase64) {
// only works in node, needs interface to what is supported in environment
try {
returner = new Buffer(returner).toString('base64');
} catch(e) {
useBase64 = false;
}
}
returner = "'data:image/svg+xml" + (useBase64 ? ";base64" : "") + "," + returner + "'";
return new(tree.URL)(new(tree.Anonymous)(returner));
}
};
// these static methods are used as a fallback when the optional 'mime' dependency is missing
tree._mime = {
// this map is intentionally incomplete
// if you want more, install 'mime' dep
_types: {
'.htm' : 'text/html',
'.html': 'text/html',
'.gif' : 'image/gif',
'.jpg' : 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png' : 'image/png'
},
lookup: function (filepath) {
var ext = require('path').extname(filepath),
type = tree._mime._types[ext];
if (type === undefined) {
throw new Error('Optional dependency "mime" is required for ' + ext);
}
return type;
},
charsets: {
lookup: function (type) {
// assumes all text types are UTF-8
return type && (/^text\//).test(type) ? 'UTF-8' : '';
}
}
};
// Math
var mathFunctions = {
// name, unit
ceil: null,
floor: null,
sqrt: null,
abs: null,
tan: "",
sin: "",
cos: "",
atan: "rad",
asin: "rad",
acos: "rad"
};
function _math(fn, unit, n) {
if (!(n instanceof tree.Dimension)) {
throw { type: "Argument", message: "argument must be a number" };
}
if (unit == null) {
unit = n.unit;
} else {
n = n.unify();
}
return new(tree.Dimension)(fn(parseFloat(n.value)), unit);
}
// ~ End of Math
// Color Blending
// ref: http://www.w3.org/TR/compositing-1
function colorBlend(mode, color1, color2) {
var ab = color1.alpha, cb, // backdrop
as = color2.alpha, cs, // source
ar, cr, r = []; // result
ar = as + ab * (1 - as);
for (var i = 0; i < 3; i++) {
cb = color1.rgb[i] / 255;
cs = color2.rgb[i] / 255;
cr = mode(cb, cs);
if (ar) {
cr = (as * cs + ab * (cb
- as * (cb + cs - cr))) / ar;
}
r[i] = cr * 255;
}
return new(tree.Color)(r, ar);
}
var colorBlendMode = {
multiply: function(cb, cs) {
return cb * cs;
},
screen: function(cb, cs) {
return cb + cs - cb * cs;
},
overlay: function(cb, cs) {
cb *= 2;
return (cb <= 1)
? colorBlendMode.multiply(cb, cs)
: colorBlendMode.screen(cb - 1, cs);
},
softlight: function(cb, cs) {
var d = 1, e = cb;
if (cs > 0.5) {
e = 1;
d = (cb > 0.25) ? Math.sqrt(cb)
: ((16 * cb - 12) * cb + 4) * cb;
}
return cb - (1 - 2 * cs) * e * (d - cb);
},
hardlight: function(cb, cs) {
return colorBlendMode.overlay(cs, cb);
},
difference: function(cb, cs) {
return Math.abs(cb - cs);
},
exclusion: function(cb, cs) {
return cb + cs - 2 * cb * cs;
},
// non-w3c functions:
average: function(cb, cs) {
return (cb + cs) / 2;
},
negation: function(cb, cs) {
return 1 - Math.abs(cb + cs - 1);
}
};
// ~ End of Color Blending
tree.defaultFunc = {
eval: function () {
var v = this.value_, e = this.error_;
if (e) {
throw e;
}
if (v != null) {
return v ? tree.True : tree.False;
}
},
value: function (v) {
this.value_ = v;
},
error: function (e) {
this.error_ = e;
},
reset: function () {
this.value_ = this.error_ = null;
}
};
function initFunctions() {
var f, tf = tree.functions;
// math
for (f in mathFunctions) {
tf[f] = _math.bind(null, Math[f], mathFunctions[f]);
}
// color blending
for (f in colorBlendMode) {
tf[f] = colorBlend.bind(null, colorBlendMode[f]);
}
// default
f = tree.defaultFunc;
tf.default = f.eval.bind(f);
} initFunctions();
function hsla(color) {
return tree.functions.hsla(color.h, color.s, color.l, color.a);
}
function scaled(n, size) {
if (n instanceof tree.Dimension && n.unit.is('%')) {
return parseFloat(n.value * size / 100);
} else {
return number(n);
}
}
function number(n) {
if (n instanceof tree.Dimension) {
return parseFloat(n.unit.is('%') ? n.value / 100 : n.value);
} else if (typeof(n) === 'number') {
return n;
} else {
throw {
error: "RuntimeError",
message: "color functions take numbers as parameters"
};
}
}
function clamp(val) {
return Math.min(1, Math.max(0, val));
}
tree.functionCall = function(env, currentFileInfo) {
this.env = env;
this.currentFileInfo = currentFileInfo;
};
tree.functionCall.prototype = tree.functions;
})(require('./tree'));
(function (tree) {
tree.colors = {
'aliceblue':'#f0f8ff',
'antiquewhite':'#faebd7',
'aqua':'#00ffff',
'aquamarine':'#7fffd4',
'azure':'#f0ffff',
'beige':'#f5f5dc',
'bisque':'#ffe4c4',
'black':'#000000',
'blanchedalmond':'#ffebcd',
'blue':'#0000ff',
'blueviolet':'#8a2be2',
'brown':'#a52a2a',
'burlywood':'#deb887',
'cadetblue':'#5f9ea0',
'chartreuse':'#7fff00',
'chocolate':'#d2691e',
'coral':'#ff7f50',
'cornflowerblue':'#6495ed',
'cornsilk':'#fff8dc',
'crimson':'#dc143c',
'cyan':'#00ffff',
'darkblue':'#00008b',
'darkcyan':'#008b8b',
'darkgoldenrod':'#b8860b',
'darkgray':'#a9a9a9',
'darkgrey':'#a9a9a9',
'darkgreen':'#006400',
'darkkhaki':'#bdb76b',
'darkmagenta':'#8b008b',
'darkolivegreen':'#556b2f',
'darkorange':'#ff8c00',
'darkorchid':'#9932cc',
'darkred':'#8b0000',
'darksalmon':'#e9967a',
'darkseagreen':'#8fbc8f',
'darkslateblue':'#483d8b',
'darkslategray':'#2f4f4f',
'darkslategrey':'#2f4f4f',
'darkturquoise':'#00ced1',
'darkviolet':'#9400d3',
'deeppink':'#ff1493',
'deepskyblue':'#00bfff',
'dimgray':'#696969',
'dimgrey':'#696969',
'dodgerblue':'#1e90ff',
'firebrick':'#b22222',
'floralwhite':'#fffaf0',
'forestgreen':'#228b22',
'fuchsia':'#ff00ff',
'gainsboro':'#dcdcdc',
'ghostwhite':'#f8f8ff',
'gold':'#ffd700',
'goldenrod':'#daa520',
'gray':'#808080',
'grey':'#808080',
'green':'#008000',
'greenyellow':'#adff2f',
'honeydew':'#f0fff0',
'hotpink':'#ff69b4',
'indianred':'#cd5c5c',
'indigo':'#4b0082',
'ivory':'#fffff0',
'khaki':'#f0e68c',
'lavender':'#e6e6fa',
'lavenderblush':'#fff0f5',
'lawngreen':'#7cfc00',
'lemonchiffon':'#fffacd',
'lightblue':'#add8e6',
'lightcoral':'#f08080',
'lightcyan':'#e0ffff',
'lightgoldenrodyellow':'#fafad2',
'lightgray':'#d3d3d3',
'lightgrey':'#d3d3d3',
'lightgreen':'#90ee90',
'lightpink':'#ffb6c1',
'lightsalmon':'#ffa07a',
'lightseagreen':'#20b2aa',
'lightskyblue':'#87cefa',
'lightslategray':'#778899',
'lightslategrey':'#778899',
'lightsteelblue':'#b0c4de',
'lightyellow':'#ffffe0',
'lime':'#00ff00',
'limegreen':'#32cd32',
'linen':'#faf0e6',
'magenta':'#ff00ff',
'maroon':'#800000',
'mediumaquamarine':'#66cdaa',
'mediumblue':'#0000cd',
'mediumorchid':'#ba55d3',
'mediumpurple':'#9370d8',
'mediumseagreen':'#3cb371',
'mediumslateblue':'#7b68ee',
'mediumspringgreen':'#00fa9a',
'mediumturquoise':'#48d1cc',
'mediumvioletred':'#c71585',
'midnightblue':'#191970',
'mintcream':'#f5fffa',
'mistyrose':'#ffe4e1',
'moccasin':'#ffe4b5',
'navajowhite':'#ffdead',
'navy':'#000080',
'oldlace':'#fdf5e6',
'olive':'#808000',
'olivedrab':'#6b8e23',
'orange':'#ffa500',
'orangered':'#ff4500',
'orchid':'#da70d6',
'palegoldenrod':'#eee8aa',
'palegreen':'#98fb98',
'paleturquoise':'#afeeee',
'palevioletred':'#d87093',
'papayawhip':'#ffefd5',
'peachpuff':'#ffdab9',
'peru':'#cd853f',
'pink':'#ffc0cb',
'plum':'#dda0dd',
'powderblue':'#b0e0e6',
'purple':'#800080',
'red':'#ff0000',
'rosybrown':'#bc8f8f',
'royalblue':'#4169e1',
'saddlebrown':'#8b4513',
'salmon':'#fa8072',
'sandybrown':'#f4a460',
'seagreen':'#2e8b57',
'seashell':'#fff5ee',
'sienna':'#a0522d',
'silver':'#c0c0c0',
'skyblue':'#87ceeb',
'slateblue':'#6a5acd',
'slategray':'#708090',
'slategrey':'#708090',
'snow':'#fffafa',
'springgreen':'#00ff7f',
'steelblue':'#4682b4',
'tan':'#d2b48c',
'teal':'#008080',
'thistle':'#d8bfd8',
'tomato':'#ff6347',
'turquoise':'#40e0d0',
'violet':'#ee82ee',
'wheat':'#f5deb3',
'white':'#ffffff',
'whitesmoke':'#f5f5f5',
'yellow':'#ffff00',
'yellowgreen':'#9acd32'
};
})(require('./tree'));
(function (tree) {
tree.debugInfo = function(env, ctx, lineSeperator) {
var result="";
if (env.dumpLineNumbers && !env.compress) {
switch(env.dumpLineNumbers) {
case 'comments':
result = tree.debugInfo.asComment(ctx);
break;
case 'mediaquery':
result = tree.debugInfo.asMediaQuery(ctx);
break;
case 'all':
result = tree.debugInfo.asComment(ctx) + (lineSeperator || "") + tree.debugInfo.asMediaQuery(ctx);
break;
}
}
return result;
};
tree.debugInfo.asComment = function(ctx) {
return '/* line ' + ctx.debugInfo.lineNumber + ', ' + ctx.debugInfo.fileName + ' */\n';
};
tree.debugInfo.asMediaQuery = function(ctx) {
return '@media -sass-debug-info{filename{font-family:' +
('file://' + ctx.debugInfo.fileName).replace(/([.:/\\])/g, function (a) {
if (a == '\\') {
a = '\/';
}
return '\\' + a;
}) +
'}line{font-family:\\00003' + ctx.debugInfo.lineNumber + '}}\n';
};
tree.find = function (obj, fun) {
for (var i = 0, r; i < obj.length; i++) {
r = fun.call(obj, obj[i]);
if (r) { return r; }
}
return null;
};
tree.jsify = function (obj) {
if (Array.isArray(obj.value) && (obj.value.length > 1)) {
return '[' + obj.value.map(function (v) { return v.toCSS(false); }).join(', ') + ']';
} else {
return obj.toCSS(false);
}
};
tree.toCSS = function (env) {
var strs = [];
this.genCSS(env, {
add: function(chunk, fileInfo, index) {
strs.push(chunk);
},
isEmpty: function () {
return strs.length === 0;
}
});
return strs.join('');
};
tree.outputRuleset = function (env, output, rules) {
var ruleCnt = rules.length, i;
env.tabLevel = (env.tabLevel | 0) + 1;
// Compressed
if (env.compress) {
output.add('{');
for (i = 0; i < ruleCnt; i++) {
rules[i].genCSS(env, output);
}
output.add('}');
env.tabLevel--;
return;
}
// Non-compressed
var tabSetStr = '\n' + Array(env.tabLevel).join(" "), tabRuleStr = tabSetStr + " ";
if (!ruleCnt) {
output.add(" {" + tabSetStr + '}');
} else {
output.add(" {" + tabRuleStr);
rules[0].genCSS(env, output);
for (i = 1; i < ruleCnt; i++) {
output.add(tabRuleStr);
rules[i].genCSS(env, output);
}
output.add(tabSetStr + '}');
}
env.tabLevel--;
};
})(require('./tree'));
(function (tree) {
tree.Alpha = function (val) {
this.value = val;
};
tree.Alpha.prototype = {
type: "Alpha",
accept: function (visitor) {
this.value = visitor.visit(this.value);
},
eval: function (env) {
if (this.value.eval) { return new tree.Alpha(this.value.eval(env)); }
return this;
},
genCSS: function (env, output) {
output.add("alpha(opacity=");
if (this.value.genCSS) {
this.value.genCSS(env, output);
} else {
output.add(this.value);
}
output.add(")");
},
toCSS: tree.toCSS
};
})(require('../tree'));
(function (tree) {
tree.Anonymous = function (string, index, currentFileInfo, mapLines) {
this.value = string.value || string;
this.index = index;
this.mapLines = mapLines;
this.currentFileInfo = currentFileInfo;
};
tree.Anonymous.prototype = {
type: "Anonymous",
eval: function () {
return new tree.Anonymous(this.value, this.index, this.currentFileInfo, this.mapLines);
},
compare: function (x) {
if (!x.toCSS) {
return -1;
}
var left = this.toCSS(),
right = x.toCSS();
if (left === right) {
return 0;
}
return left < right ? -1 : 1;
},
genCSS: function (env, output) {
output.add(this.value, this.currentFileInfo, this.index, this.mapLines);
},
toCSS: tree.toCSS
};
})(require('../tree'));
(function (tree) {
tree.Assignment = function (key, val) {
this.key = key;
this.value = val;
};
tree.Assignment.prototype = {
type: "Assignment",
accept: function (visitor) {
this.value = visitor.visit(this.value);
},
eval: function (env) {
if (this.value.eval) {
return new(tree.Assignment)(this.key, this.value.eval(env));
}
return this;
},
genCSS: function (env, output) {
output.add(this.key + '=');
if (this.value.genCSS) {
this.value.genCSS(env, output);
} else {
output.add(this.value);
}
},
toCSS: tree.toCSS
};
})(require('../tree'));
(function (tree) {
//
// A function call node.
//
tree.Call = function (name, args, index, currentFileInfo) {
this.name = name;
this.args = args;
this.index = index;
this.currentFileInfo = currentFileInfo;
};
tree.Call.prototype = {
type: "Call",
accept: function (visitor) {
if (this.args) {
this.args = visitor.visitArray(this.args);
}
},
//
// When evaluating a function call,
// we either find the function in `tree.functions` [1],
// in which case we call it, passing the evaluated arguments,
// if this returns null or we cannot find the function, we
// simply print it out as it appeared originally [2].
//
// The *functions.js* file contains the built-in functions.
//
// The reason why we evaluate the arguments, is in the case where
// we try to pass a variable to a function, like: `saturate(@color)`.
// The function should receive the value, not the variable.
//
eval: function (env) {
var args = this.args.map(function (a) { return a.eval(env); }),
nameLC = this.name.toLowerCase(),
result, func;
if (nameLC in tree.functions) { // 1.
try {
func = new tree.functionCall(env, this.currentFileInfo);
result = func[nameLC].apply(func, args);
if (result != null) {
return result;
}
} catch (e) {
throw { type: e.type || "Runtime",
message: "error evaluating function `" + this.name + "`" +
(e.message ? ': ' + e.message : ''),
index: this.index, filename: this.currentFileInfo.filename };
}
}
return new tree.Call(this.name, args, this.index, this.currentFileInfo);
},
genCSS: function (env, output) {
output.add(this.name + "(", this.currentFileInfo, this.index);
for(var i = 0; i < this.args.length; i++) {
this.args[i].genCSS(env, output);
if (i + 1 < this.args.length) {
output.add(", ");
}
}
output.add(")");
},
toCSS: tree.toCSS
};
})(require('../tree'));
(function (tree) {
//
// RGB Colors - #ff0014, #eee
//
tree.Color = function (rgb, a) {
//
// The end goal here, is to parse the arguments
// into an integer triplet, such as `128, 255, 0`
//
// This facilitates operations and conversions.
//
if (Array.isArray(rgb)) {
this.rgb = rgb;
} else if (rgb.length == 6) {
this.rgb = rgb.match(/.{2}/g).map(function (c) {
return parseInt(c, 16);
});
} else {
this.rgb = rgb.split('').map(function (c) {
return parseInt(c + c, 16);
});
}
this.alpha = typeof(a) === 'number' ? a : 1;
};
var transparentKeyword = "transparent";
tree.Color.prototype = {
type: "Color",
eval: function () { return this; },
luma: function () { return (0.2126 * this.rgb[0] / 255) + (0.7152 * this.rgb[1] / 255) + (0.0722 * this.rgb[2] / 255); },
genCSS: function (env, output) {
output.add(this.toCSS(env));
},
toCSS: function (env, doNotCompress) {
var compress = env && env.compress && !doNotCompress;
// If we have some transparency, the only way to represent it
// is via `rgba`. Otherwise, we use the hex representation,
// which has better compatibility with older browsers.
// Values are capped between `0` and `255`, rounded and zero-padded.
if (this.alpha < 1) {
if (this.alpha === 0 && this.isTransparentKeyword) {
return transparentKeyword;
}
return "rgba(" + this.rgb.map(function (c) {
return clamp(Math.round(c), 255);
}).concat(clamp(this.alpha, 1))
.join(',' + (compress ? '' : ' ')) + ")";
} else {
var color = this.toRGB();
if (compress) {
var splitcolor = color.split('');
// Convert color to short format
if (splitcolor[1] === splitcolor[2] && splitcolor[3] === splitcolor[4] && splitcolor[5] === splitcolor[6]) {
color = '#' + splitcolor[1] + splitcolor[3] + splitcolor[5];
}
}
return color;
}
},
//
// Operations have to be done per-channel, if not,
// channels will spill onto each other. Once we have
// our result, in the form of an integer triplet,
// we create a new Color node to hold the result.
//
operate: function (env, op, other) {
var rgb = [];
var alpha = this.alpha * (1 - other.alpha) + other.alpha;
for (var c = 0; c < 3; c++) {
rgb[c] = tree.operate(env, op, this.rgb[c], other.rgb[c]);
}
return new(tree.Color)(rgb, alpha);
},
toRGB: function () {
return toHex(this.rgb);
},
toHSL: function () {
var r = this.rgb[0] / 255,
g = this.rgb[1] / 255,
b = this.rgb[2] / 255,
a = this.alpha;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, l = (max + min) / 2, d = max - min;
if (max === min) {
h = s = 0;
} else {
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return { h: h * 360, s: s, l: l, a: a };
},
//Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
toHSV: function () {
var r = this.rgb[0] / 255,
g = this.rgb[1] / 255,
b = this.rgb[2] / 255,
a = this.alpha;
var max = Math.max(r, g, b), min = Math.min(r, g, b);
var h, s, v = max;
var d = max - min;
if (max === 0) {
s = 0;
} else {
s = d / max;
}
if (max === min) {
h = 0;
} else {
switch(max){
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return { h: h * 360, s: s, v: v, a: a };
},
toARGB: function () {
return toHex([this.alpha * 255].concat(this.rgb));
},
compare: function (x) {
if (!x.rgb) {
return -1;
}
return (x.rgb[0] === this.rgb[0] &&
x.rgb[1] === this.rgb[1] &&
x.rgb[2] === this.rgb[2] &&
x.alpha === this.alpha) ? 0 : -1;
}
};
tree.Color.fromKeyword = function(keyword) {
if (tree.colors.hasOwnProperty(keyword)) {
// detect named color
return new(tree.Color)(tree.colors[keyword].slice(1));
}
if (keyword === transparentKeyword) {
var transparent = new(tree.Color)([0, 0, 0], 0);
transparent.isTransparentKeyword = true;
return transparent;
}
};
function toHex(v) {
return '#' + v.map(function (c) {
c = clamp(Math.round(c), 255);
return (c < 16 ? '0' : '') + c.toString(16);
}).join('');
}
function clamp(v, max) {
return Math.min(Math.max(v, 0), max);
}
})(require('../tree'));
(function (tree) {
tree.Comment = function (value, silent, index, currentFileInfo) {
this.value = value;
this.silent = !!silent;
this.currentFileInfo = currentFileInfo;
};
tree.Comment.prototype = {
type: "Comment",
genCSS: function (env, output) {
if (this.debugInfo) {
output.add(tree.debugInfo(env, this), this.currentFileInfo, this.index);
}
output.add(this.value.trim()); //TODO shouldn't need to trim, we shouldn't grab the \n
},
toCSS: tree.toCSS,
isSilent: function(env) {
var isReference = (this.currentFileInfo && this.currentFileInfo.reference && !this.isReferenced),
isCompressed = env.compress && !this.value.match(/^\/\*!/);
return this.silent || isReference || isCompressed;
},
eval: function () { return this; },
markReferenced: function () {
this.isReferenced = true;
}
};
})(require('../tree'));
(function (tree) {
tree.Condition = function (op, l, r, i, negate) {
this.op = op.trim();
this.lvalue = l;
this.rvalue = r;
this.index = i;
this.negate = negate;
};
tree.Condition.prototype = {
type: "Condition",
accept: function (visitor) {
this.lvalue = visitor.visit(this.lvalue);
this.rvalue = visitor.visit(this.rvalue);
},
eval: function (env) {
var a = this.lvalue.eval(env),
b = this.rvalue.eval(env);
var i = this.index, result;
result = (function (op) {
switch (op) {
case 'and':
return a && b;
case 'or':
return a || b;
default:
if (a.compare) {
result = a.compare(b);
} else if (b.compare) {
result = b.compare(a);
} else {
throw { type: "Type",
message: "Unable to perform comparison",
index: i };
}
switch (result) {
case -1: return op === '<' || op === '=<' || op === '<=';
case 0: return op === '=' || op === '>=' || op === '=<' || op === '<=';
case 1: return op === '>' || op === '>=';
}
}
})(this.op);
return this.negate ? !result : result;
}
};
})(require('../tree'));
(function (tree) {
//
// A number with a unit
//
tree.Dimension = function (value, unit) {
this.value = parseFloat(value);
this.unit = (unit && unit instanceof tree.Unit) ? unit :
new(tree.Unit)(unit ? [unit] : undefined);
};
tree.Dimension.prototype = {
type: "Dimension",
accept: function (visitor) {
this.unit = visitor.visit(this.unit);
},
eval: function (env) {
return this;
},
toColor: function () {
return new(tree.Color)([this.value, this.value, this.value]);
},
genCSS: function (env, output) {
if ((env && env.strictUnits) && !this.unit.isSingular()) {
throw new Error("Multiple units in dimension. Correct the units or use the unit function. Bad unit: "+this.unit.toString());
}
var value = this.value,
strValue = String(value);
if (value !== 0 && value < 0.000001 && value > -0.000001) {
// would be output 1e-6 etc.
strValue = value.toFixed(20).replace(/0+$/, "");
}
if (env && env.compress) {
// Zero values doesn't need a unit
if (value === 0 && this.unit.isLength()) {
output.add(strValue);
return;
}
// Float values doesn't need a leading zero
if (value > 0 && value < 1) {
strValue = (strValue).substr(1);
}
}
output.add(strValue);
this.unit.genCSS(env, output);
},
toCSS: tree.toCSS,
// In an operation between two Dimensions,
// we default to the first Dimension's unit,
// so `1px + 2` will yield `3px`.
operate: function (env, op, other) {
/*jshint noempty:false */
var value = tree.operate(env, op, this.value, other.value),
unit = this.unit.clone();
if (op === '+' || op === '-') {
if (unit.numerator.length === 0 && unit.denominator.length === 0) {
unit.numerator = other.unit.numerator.slice(0);
unit.denominator = other.unit.denominator.slice(0);
} else if (other.unit.numerator.length === 0 && unit.denominator.length === 0) {
// do nothing
} else {
other = other.convertTo(this.unit.usedUnits());
if(env.strictUnits && other.unit.toString() !== unit.toString()) {
throw new Error("Incompatible units. Change the units or use the unit function. Bad units: '" + unit.toString() +
"' and '" + other.unit.toString() + "'.");
}
value = tree.operate(env, op, this.value, other.value);
}
} else if (op === '*') {
unit.numerator = unit.numerator.concat(other.unit.numerator).sort();
unit.denominator = unit.denominator.concat(other.unit.denominator).sort();
unit.cancel();
} else if (op === '/') {
unit.numerator = unit.numerator.concat(other.unit.denominator).sort();
unit.denominator = unit.denominator.concat(other.unit.numerator).sort();
unit.cancel();
}
return new(tree.Dimension)(value, unit);
},
compare: function (other) {
if (other instanceof tree.Dimension) {
var a = this.unify(), b = other.unify(),
aValue = a.value, bValue = b.value;
if (bValue > aValue) {
return -1;
} else if (bValue < aValue) {
return 1;
} else {
if (!b.unit.isEmpty() && a.unit.compare(b.unit) !== 0) {
return -1;
}
return 0;
}
} else {
return -1;
}
},
unify: function () {
return this.convertTo({ length: 'm', duration: 's', angle: 'rad' });
},
convertTo: function (conversions) {
var value = this.value, unit = this.unit.clone(),
i, groupName, group, targetUnit, derivedConversions = {}, applyUnit;
if (typeof conversions === 'string') {
for(i in tree.UnitConversions) {
if (tree.UnitConversions[i].hasOwnProperty(conversions)) {
derivedConversions = {};
derivedConversions[i] = conversions;
}
}
conversions = derivedConversions;
}
applyUnit = function (atomicUnit, denominator) {
/*jshint loopfunc:true */
if (group.hasOwnProperty(atomicUnit)) {
if (denominator) {
value = value / (group[atomicUnit] / group[targetUnit]);
} else {
value = value * (group[atomicUnit] / group[targetUnit]);
}
return targetUnit;
}
return atomicUnit;
};
for (groupName in conversions) {
if (conversions.hasOwnProperty(groupName)) {
targetUnit = conversions[groupName];
group = tree.UnitConversions[groupName];
unit.map(applyUnit);
}
}
unit.cancel();
return new(tree.Dimension)(value, unit);
}
};
// http://www.w3.org/TR/css3-values/#absolute-lengths
tree.UnitConversions = {
length: {
'm': 1,
'cm': 0.01,
'mm': 0.001,
'in': 0.0254,
'pt': 0.0254 / 72,
'pc': 0.0254 / 72 * 12
},
duration: {
's': 1,
'ms': 0.001
},
angle: {
'rad': 1/(2*Math.PI),
'deg': 1/360,
'grad': 1/400,
'turn': 1
}
};
tree.Unit = function (numerator, denominator, backupUnit) {
this.numerator = numerator ? numerator.slice(0).sort() : [];
this.denominator = denominator ? denominator.slice(0).sort() : [];
this.backupUnit = backupUnit;
};
tree.Unit.prototype = {
type: "Unit",
clone: function () {
return new tree.Unit(this.numerator.slice(0), this.denominator.slice(0), this.backupUnit);
},
genCSS: function (env, output) {
if (this.numerator.length >= 1) {
output.add(this.numerator[0]);
} else
if (this.denominator.length >= 1) {
output.add(this.denominator[0]);
} else
if ((!env || !env.strictUnits) && this.backupUnit) {
output.add(this.backupUnit);
}
},
toCSS: tree.toCSS,
toString: function () {
var i, returnStr = this.numerator.join("*");
for (i = 0; i < this.denominator.length; i++) {
returnStr += "/" + this.denominator[i];
}
return returnStr;
},
compare: function (other) {
return this.is(other.toString()) ? 0 : -1;
},
is: function (unitString) {
return this.toString() === unitString;
},
isLength: function () {
return Boolean(this.toCSS().match(/px|em|%|in|cm|mm|pc|pt|ex/));
},
isEmpty: function () {
return this.numerator.length === 0 && this.denominator.length === 0;
},
isSingular: function() {
return this.numerator.length <= 1 && this.denominator.length === 0;
},
map: function(callback) {
var i;
for (i = 0; i < this.numerator.length; i++) {
this.numerator[i] = callback(this.numerator[i], false);
}
for (i = 0; i < this.denominator.length; i++) {
this.denominator[i] = callback(this.denominator[i], true);
}
},
usedUnits: function() {
var group, result = {}, mapUnit;
mapUnit = function (atomicUnit) {
/*jshint loopfunc:true */
if (group.hasOwnProperty(atomicUnit) && !result[groupName]) {
result[groupName] = atomicUnit;
}
return atomicUnit;
};
for (var groupName in tree.UnitConversions) {
if (tree.UnitConversions.hasOwnProperty(groupName)) {
group = tree.UnitConversions[groupName];
this.map(mapUnit);
}
}
return result;
},
cancel: function () {
var counter = {}, atomicUnit, i, backup;
for (i = 0; i < this.numerator.length; i++) {
atomicUnit = this.numerator[i];
if (!backup) {
backup = atomicUnit;
}
counter[atomicUnit] = (counter[atomicUnit] || 0) + 1;
}
for (i = 0; i < this.denominator.length; i++) {
atomicUnit = this.denominator[i];
if (!backup) {
backup = atomicUnit;
}
counter[atomicUnit] = (counter[atomicUnit] || 0) - 1;
}
this.numerator = [];
this.denominator = [];
for (atomicUnit in counter) {
if (counter.hasOwnProperty(atomicUnit)) {
var count = counter[atomicUnit];
if (count > 0) {
for (i = 0; i < count; i++) {
this.numerator.push(atomicUnit);
}
} else if (count < 0) {
for (i = 0; i < -count; i++) {
this.denominator.push(atomicUnit);
}
}
}
}
if (this.numerator.length === 0 && this.denominator.length === 0 && backup) {
this.backupUnit = backup;
}
this.numerator.sort();
this.denominator.sort();
}
};
})(require('../tree'));
(function (tree) {
tree.Directive = function (name, value, index, currentFileInfo) {
this.name = name;
if (Array.isArray(value)) {
this.rules = [new(tree.Ruleset)(null, value)];
this.rules[0].allowImports = true;
} else {
this.value = value;
}
this.currentFileInfo = currentFileInfo;
};
tree.Directive.prototype = {
type: "Directive",
accept: function (visitor) {
if (this.rules) {
this.rules = visitor.visitArray(this.rules);
}
if (this.value) {
this.value = visitor.visit(this.value);
}
},
genCSS: function (env, output) {
output.add(this.name, this.currentFileInfo, this.index);
if (this.rules) {
tree.outputRuleset(env, output, this.rules);
} else {
output.add(' ');
this.value.genCSS(env, output);
output.add(';');
}
},
toCSS: tree.toCSS,
eval: function (env) {
var evaldDirective = this;
if (this.rules) {
env.frames.unshift(this);
evaldDirective = new(tree.Directive)(this.name, null, this.index, this.currentFileInfo);
evaldDirective.rules = [this.rules[0].eval(env)];
evaldDirective.rules[0].root = true;
env.frames.shift();
}
return evaldDirective;
},
variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); },
find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); },
rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); },
markReferenced: function () {
var i, rules;
this.isReferenced = true;
if (this.rules) {
rules = this.rules[0].rules;
for (i = 0; i < rules.length; i++) {
if (rules[i].markReferenced) {
rules[i].markReferenced();
}
}
}
}
};
})(require('../tree'));
(function (tree) {
tree.Element = function (combinator, value, index, currentFileInfo) {
this.combinator = combinator instanceof tree.Combinator ?
combinator : new(tree.Combinator)(combinator);
if (typeof(value) === 'string') {
this.value = value.trim();
} else if (value) {
this.value = value;
} else {
this.value = "";
}
this.index = index;
this.currentFileInfo = currentFileInfo;
};
tree.Element.prototype = {
type: "Element",
accept: function (visitor) {
var value = this.value;
this.combinator = visitor.visit(this.combinator);
if (typeof value === "object") {
this.value = visitor.visit(value);
}
},
eval: function (env) {
return new(tree.Element)(this.combinator,
this.value.eval ? this.value.eval(env) : this.value,
this.index,
this.currentFileInfo);
},
genCSS: function (env, output) {
output.add(this.toCSS(env), this.currentFileInfo, this.index);
},
toCSS: function (env) {
var value = (this.value.toCSS ? this.value.toCSS(env) : this.value);
if (value === '' && this.combinator.value.charAt(0) === '&') {
return '';
} else {
return this.combinator.toCSS(env || {}) + value;
}
}
};
tree.Attribute = function (key, op, value) {
this.key = key;
this.op = op;
this.value = value;
};
tree.Attribute.prototype = {
type: "Attribute",
eval: function (env) {
return new(tree.Attribute)(this.key.eval ? this.key.eval(env) : this.key,
this.op, (this.value && this.value.eval) ? this.value.eval(env) : this.value);
},
genCSS: function (env, output) {
output.add(this.toCSS(env));
},
toCSS: function (env) {
var value = this.key.toCSS ? this.key.toCSS(env) : this.key;
if (this.op) {
value += this.op;
value += (this.value.toCSS ? this.value.toCSS(env) : this.value);
}
return '[' + value + ']';
}
};
tree.Combinator = function (value) {
if (value === ' ') {
this.value = ' ';
} else {
this.value = value ? value.trim() : "";
}
};
tree.Combinator.prototype = {
type: "Combinator",
_outputMap: {
'' : '',
' ' : ' ',
':' : ' :',
'+' : ' + ',
'~' : ' ~ ',
'>' : ' > ',
'|' : '|'
},
_outputMapCompressed: {
'' : '',
' ' : ' ',
':' : ' :',
'+' : '+',
'~' : '~',
'>' : '>',
'|' : '|'
},
genCSS: function (env, output) {
output.add((env.compress ? this._outputMapCompressed : this._outputMap)[this.value]);
},
toCSS: tree.toCSS
};
})(require('../tree'));
(function (tree) {
tree.Expression = function (value) { this.value = value; };
tree.Expression.prototype = {
type: "Expression",
accept: function (visitor) {
if (this.value) {
this.value = visitor.visitArray(this.value);
}
},
eval: function (env) {
var returnValue,
inParenthesis = this.parens && !this.parensInOp,
doubleParen = false;
if (inParenthesis) {
env.inParenthesis();
}
if (this.value.length > 1) {
returnValue = new(tree.Expression)(this.value.map(function (e) {
return e.eval(env);
}));
} else if (this.value.length === 1) {
if (this.value[0].parens && !this.value[0].parensInOp) {
doubleParen = true;
}
returnValue = this.value[0].eval(env);
} else {
returnValue = this;
}
if (inParenthesis) {
env.outOfParenthesis();
}
if (this.parens && this.parensInOp && !(env.isMathOn()) && !doubleParen) {
returnValue = new(tree.Paren)(returnValue);
}
return returnValue;
},
genCSS: function (env, output) {
for(var i = 0; i < this.value.length; i++) {
this.value[i].genCSS(env, output);
if (i + 1 < this.value.length) {
output.add(" ");
}
}
},
toCSS: tree.toCSS,
throwAwayComments: function () {
this.value = this.value.filter(function(v) {
return !(v instanceof tree.Comment);
});
}
};
})(require('../tree'));
(function (tree) {
tree.Extend = function Extend(selector, option, index) {
this.selector = selector;
this.option = option;
this.index = index;
this.object_id = tree.Extend.next_id++;
this.parent_ids = [this.object_id];
switch(option) {
case "all":
this.allowBefore = true;
this.allowAfter = true;
break;
default:
this.allowBefore = false;
this.allowAfter = false;
break;
}
};
tree.Extend.next_id = 0;
tree.Extend.prototype = {
type: "Extend",
accept: function (visitor) {
this.selector = visitor.visit(this.selector);
},
eval: function (env) {
return new(tree.Extend)(this.selector.eval(env), this.option, this.index);
},
clone: function (env) {
return new(tree.Extend)(this.selector, this.option, this.index);
},
findSelfSelectors: function (selectors) {
var selfElements = [],
i,
selectorElements;
for(i = 0; i < selectors.length; i++) {
selectorElements = selectors[i].elements;
// duplicate the logic in genCSS function inside the selector node.
// future TODO - move both logics into the selector joiner visitor
if (i > 0 && selectorElements.length && selectorElements[0].combinator.value === "") {
selectorElements[0].combinator.value = ' ';
}
selfElements = selfElements.concat(selectors[i].elements);
}
this.selfSelectors = [{ elements: selfElements }];
}
};
})(require('../tree'));
(function (tree) {
//
// CSS @import node
//
// The general strategy here is that we don't want to wait
// for the parsing to be completed, before we start importing
// the file. That's because in the context of a browser,
// most of the time will be spent waiting for the server to respond.
//
// On creation, we push the import path to our import queue, though
// `import,push`, we also pass it a callback, which it'll call once
// the file has been fetched, and parsed.
//
tree.Import = function (path, features, options, index, currentFileInfo) {
this.options = options;
this.index = index;
this.path = path;
this.features = features;
this.currentFileInfo = currentFileInfo;
if (this.options.less !== undefined || this.options.inline) {
this.css = !this.options.less || this.options.inline;
} else {
var pathValue = this.getPath();
if (pathValue && /css([\?;].*)?$/.test(pathValue)) {
this.css = true;
}
}
};
//
// The actual import node doesn't return anything, when converted to CSS.
// The reason is that it's used at the evaluation stage, so that the rules
// it imports can be treated like any other rules.
//
// In `eval`, we make sure all Import nodes get evaluated, recursively, so
// we end up with a flat structure, which can easily be imported in the parent
// ruleset.
//
tree.Import.prototype = {
type: "Import",
accept: function (visitor) {
if (this.features) {
this.features = visitor.visit(this.features);
}
this.path = visitor.visit(this.path);
if (!this.options.inline && this.root) {
this.root = visitor.visit(this.root);
}
},
genCSS: function (env, output) {
if (this.css) {
output.add("@import ", this.currentFileInfo, this.index);
this.path.genCSS(env, output);
if (this.features) {
output.add(" ");
this.features.genCSS(env, output);
}
output.add(';');
}
},
toCSS: tree.toCSS,
getPath: function () {
if (this.path instanceof tree.Quoted) {
var path = this.path.value;
return (this.css !== undefined || /(\.[a-z]*$)|([\?;].*)$/.test(path)) ? path : path + '.less';
} else if (this.path instanceof tree.URL) {
return this.path.value.value;
}
return null;
},
evalForImport: function (env) {
return new(tree.Import)(this.path.eval(env), this.features, this.options, this.index, this.currentFileInfo);
},
evalPath: function (env) {
var path = this.path.eval(env);
var rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
if (!(path instanceof tree.URL)) {
if (rootpath) {
var pathValue = path.value;
// Add the base path if the import is relative
if (pathValue && env.isPathRelative(pathValue)) {
path.value = rootpath + pathValue;
}
}
path.value = env.normalizePath(path.value);
}
return path;
},
eval: function (env) {
var ruleset, features = this.features && this.features.eval(env);
if (this.skip) { return []; }
if (this.options.inline) {
//todo needs to reference css file not import
var contents = new(tree.Anonymous)(this.root, 0, {filename: this.importedFilename}, true);
return this.features ? new(tree.Media)([contents], this.features.value) : [contents];
} else if (this.css) {
var newImport = new(tree.Import)(this.evalPath(env), features, this.options, this.index);
if (!newImport.css && this.error) {
throw this.error;
}
return newImport;
} else {
ruleset = new(tree.Ruleset)(null, this.root.rules.slice(0));
ruleset.evalImports(env);
return this.features ? new(tree.Media)(ruleset.rules, this.features.value) : ruleset.rules;
}
}
};
})(require('../tree'));
(function (tree) {
tree.JavaScript = function (string, index, escaped) {
this.escaped = escaped;
this.expression = string;
this.index = index;
};
tree.JavaScript.prototype = {
type: "JavaScript",
eval: function (env) {
var result,
that = this,
context = {};
var expression = this.expression.replace(/@\{([\w-]+)\}/g, function (_, name) {
return tree.jsify(new(tree.Variable)('@' + name, that.index).eval(env));
});
try {
expression = new(Function)('return (' + expression + ')');
} catch (e) {
throw { message: "JavaScript evaluation error: " + e.message + " from `" + expression + "`" ,
index: this.index };
}
for (var k in env.frames[0].variables()) {
/*jshint loopfunc:true */
context[k.slice(1)] = {
value: env.frames[0].variables()[k].value,
toJS: function () {
return this.value.eval(env).toCSS();
}
};
}
try {
result = expression.call(context);
} catch (e) {
throw { message: "JavaScript evaluation error: '" + e.name + ': ' + e.message + "'" ,
index: this.index };
}
if (typeof(result) === 'number') {
return new(tree.Dimension)(result);
} else if (typeof(result) === 'string') {
return new(tree.Quoted)('"' + result + '"', result, this.escaped, this.index);
} else if (Array.isArray(result)) {
return new(tree.Anonymous)(result.join(', '));
} else {
return new(tree.Anonymous)(result);
}
}
};
})(require('../tree'));
(function (tree) {
tree.Keyword = function (value) { this.value = value; };
tree.Keyword.prototype = {
type: "Keyword",
eval: function () { return this; },
genCSS: function (env, output) {
output.add(this.value);
},
toCSS: tree.toCSS,
compare: function (other) {
if (other instanceof tree.Keyword) {
return other.value === this.value ? 0 : 1;
} else {
return -1;
}
}
};
tree.True = new(tree.Keyword)('true');
tree.False = new(tree.Keyword)('false');
})(require('../tree'));
(function (tree) {
tree.Media = function (value, features, index, currentFileInfo) {
this.index = index;
this.currentFileInfo = currentFileInfo;
var selectors = this.emptySelectors();
this.features = new(tree.Value)(features);
this.rules = [new(tree.Ruleset)(selectors, value)];
this.rules[0].allowImports = true;
};
tree.Media.prototype = {
type: "Media",
accept: function (visitor) {
if (this.features) {
this.features = visitor.visit(this.features);
}
if (this.rules) {
this.rules = visitor.visitArray(this.rules);
}
},
genCSS: function (env, output) {
output.add('@media ', this.currentFileInfo, this.index);
this.features.genCSS(env, output);
tree.outputRuleset(env, output, this.rules);
},
toCSS: tree.toCSS,
eval: function (env) {
if (!env.mediaBlocks) {
env.mediaBlocks = [];
env.mediaPath = [];
}
var media = new(tree.Media)(null, [], this.index, this.currentFileInfo);
if(this.debugInfo) {
this.rules[0].debugInfo = this.debugInfo;
media.debugInfo = this.debugInfo;
}
var strictMathBypass = false;
if (!env.strictMath) {
strictMathBypass = true;
env.strictMath = true;
}
try {
media.features = this.features.eval(env);
}
finally {
if (strictMathBypass) {
env.strictMath = false;
}
}
env.mediaPath.push(media);
env.mediaBlocks.push(media);
env.frames.unshift(this.rules[0]);
media.rules = [this.rules[0].eval(env)];
env.frames.shift();
env.mediaPath.pop();
return env.mediaPath.length === 0 ? media.evalTop(env) :
media.evalNested(env);
},
variable: function (name) { return tree.Ruleset.prototype.variable.call(this.rules[0], name); },
find: function () { return tree.Ruleset.prototype.find.apply(this.rules[0], arguments); },
rulesets: function () { return tree.Ruleset.prototype.rulesets.apply(this.rules[0]); },
emptySelectors: function() {
var el = new(tree.Element)('', '&', this.index, this.currentFileInfo);
return [new(tree.Selector)([el], null, null, this.index, this.currentFileInfo)];
},
markReferenced: function () {
var i, rules = this.rules[0].rules;
this.isReferenced = true;
for (i = 0; i < rules.length; i++) {
if (rules[i].markReferenced) {
rules[i].markReferenced();
}
}
},
evalTop: function (env) {
var result = this;
// Render all dependent Media blocks.
if (env.mediaBlocks.length > 1) {
var selectors = this.emptySelectors();
result = new(tree.Ruleset)(selectors, env.mediaBlocks);
result.multiMedia = true;
}
delete env.mediaBlocks;
delete env.mediaPath;
return result;
},
evalNested: function (env) {
var i, value,
path = env.mediaPath.concat([this]);
// Extract the media-query conditions separated with `,` (OR).
for (i = 0; i < path.length; i++) {
value = path[i].features instanceof tree.Value ?
path[i].features.value : path[i].features;
path[i] = Array.isArray(value) ? value : [value];
}
// Trace all permutations to generate the resulting media-query.
//
// (a, b and c) with nested (d, e) ->
// a and d
// a and e
// b and c and d
// b and c and e
this.features = new(tree.Value)(this.permute(path).map(function (path) {
path = path.map(function (fragment) {
return fragment.toCSS ? fragment : new(tree.Anonymous)(fragment);
});
for(i = path.length - 1; i > 0; i--) {
path.splice(i, 0, new(tree.Anonymous)("and"));
}
return new(tree.Expression)(path);
}));
// Fake a tree-node that doesn't output anything.
return new(tree.Ruleset)([], []);
},
permute: function (arr) {
if (arr.length === 0) {
return [];
} else if (arr.length === 1) {
return arr[0];
} else {
var result = [];
var rest = this.permute(arr.slice(1));
for (var i = 0; i < rest.length; i++) {
for (var j = 0; j < arr[0].length; j++) {
result.push([arr[0][j]].concat(rest[i]));
}
}
return result;
}
},
bubbleSelectors: function (selectors) {
this.rules = [new(tree.Ruleset)(selectors.slice(0), [this.rules[0]])];
}
};
})(require('../tree'));
(function (tree) {
tree.mixin = {};
tree.mixin.Call = function (elements, args, index, currentFileInfo, important) {
this.selector = new(tree.Selector)(elements);
this.arguments = (args && args.length) ? args : null;
this.index = index;
this.currentFileInfo = currentFileInfo;
this.important = important;
};
tree.mixin.Call.prototype = {
type: "MixinCall",
accept: function (visitor) {
if (this.selector) {
this.selector = visitor.visit(this.selector);
}
if (this.arguments) {
this.arguments = visitor.visitArray(this.arguments);
}
},
eval: function (env) {
var mixins, mixin, args, rules = [], match = false, i, m, f, isRecursive, isOneFound, rule;
var candidates = [], candidate, conditionResult = [], defaultFunc = tree.defaultFunc, defaultUsed = false;
args = this.arguments && this.arguments.map(function (a) {
return { name: a.name, value: a.value.eval(env) };
});
for (i = 0; i < env.frames.length; i++) {
if ((mixins = env.frames[i].find(this.selector)).length > 0) {
isOneFound = true;
// To make `default()` function independent of definition order we have two "subpasses" here.
// At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
// and build candidate list with corresponding flags. Then, when we know all possible matches,
// we make a final decision.
for (m = 0; m < mixins.length; m++) {
mixin = mixins[m];
isRecursive = false;
for(f = 0; f < env.frames.length; f++) {
if ((!(mixin instanceof tree.mixin.Definition)) && mixin === (env.frames[f].originalRuleset || env.frames[f])) {
isRecursive = true;
break;
}
}
if (isRecursive) {
continue;
}
if (mixin.matchArgs(args, env)) {
candidate = {mixin: mixin};
if (mixin.matchCondition) {
for (f = 0; f < 2; f++) {
defaultFunc.value(f);
conditionResult[f] = mixin.matchCondition(args, env);
}
if (conditionResult[0] || conditionResult[1]) {
if (conditionResult[0] != conditionResult[1]) {
if (defaultUsed) {
// todo: ideally, it would make sense to also print the candidate
// mixin definitions that cause the conflict (current one and the
// mixin that set defaultUsed flag). But is there any easy method
// to get their filename/line/index info here?
throw { type: 'Runtime',
message: 'Ambiguous use of `default()` found when matching for `'
+ this.format(args) + '`',
index: this.index, filename: this.currentFileInfo.filename };
}
defaultUsed = true;
candidate.matchIfDefault = true;
candidate.matchIfDefaultValue = conditionResult[1];
}
candidates.push(candidate);
}
}
else {
candidates.push(candidate);
}
match = true;
}
}
defaultFunc.reset();
for (m in candidates) {
candidate = candidates[m];
if (!candidate.matchIfDefault || (candidate.matchIfDefaultValue == (candidates.length == 1))) {
try {
mixin = candidate.mixin;
if (!(mixin instanceof tree.mixin.Definition)) {
mixin = new tree.mixin.Definition("", [], mixin.rules, null, false);
mixin.originalRuleset = mixins[m].originalRuleset || mixins[m];
}
//if (this.important) {
// isImportant = env.isImportant;
// env.isImportant = true;
//}
Array.prototype.push.apply(
rules, mixin.eval(env, args, this.important).rules);
//if (this.important) {
// env.isImportant = isImportant;
//}
} catch (e) {
throw { message: e.message, index: this.index, filename: this.currentFileInfo.filename, stack: e.stack };
}
}
}
if (match) {
if (!this.currentFileInfo || !this.currentFileInfo.reference) {
for (i = 0; i < rules.length; i++) {
rule = rules[i];
if (rule.markReferenced) {
rule.markReferenced();
}
}
}
return rules;
}
}
}
if (isOneFound) {
throw { type: 'Runtime',
message: 'No matching definition was found for `' + this.format(args) + '`',
index: this.index, filename: this.currentFileInfo.filename };
} else {
throw { type: 'Name',
message: this.selector.toCSS().trim() + " is undefined",
index: this.index, filename: this.currentFileInfo.filename };
}
},
format: function (args) {
return this.selector.toCSS().trim() + '(' +
(args ? args.map(function (a) {
var argValue = "";
if (a.name) {
argValue += a.name + ":";
}
if (a.value.toCSS) {
argValue += a.value.toCSS();
} else {
argValue += "???";
}
return argValue;
}).join(', ') : "") + ")";
}
};
tree.mixin.Definition = function (name, params, rules, condition, variadic) {
this.name = name;
this.selectors = [new(tree.Selector)([new(tree.Element)(null, name, this.index, this.currentFileInfo)])];
this.params = params;
this.condition = condition;
this.variadic = variadic;
this.arity = params.length;
this.rules = rules;
this._lookups = {};
this.required = params.reduce(function (count, p) {
if (!p.name || (p.name && !p.value)) { return count + 1; }
else { return count; }
}, 0);
this.parent = tree.Ruleset.prototype;
this.frames = [];
};
tree.mixin.Definition.prototype = {
type: "MixinDefinition",
accept: function (visitor) {
if (this.params && this.params.length) {
this.params = visitor.visitArray(this.params);
}
this.rules = visitor.visitArray(this.rules);
if (this.condition) {
this.condition = visitor.visit(this.condition);
}
},
variable: function (name) { return this.parent.variable.call(this, name); },
variables: function () { return this.parent.variables.call(this); },
find: function () { return this.parent.find.apply(this, arguments); },
rulesets: function () { return this.parent.rulesets.apply(this); },
evalParams: function (env, mixinEnv, args, evaldArguments) {
/*jshint boss:true */
var frame = new(tree.Ruleset)(null, null),
varargs, arg,
params = this.params.slice(0),
i, j, val, name, isNamedFound, argIndex;
mixinEnv = new tree.evalEnv(mixinEnv, [frame].concat(mixinEnv.frames));
if (args) {
args = args.slice(0);
for(i = 0; i < args.length; i++) {
arg = args[i];
if (name = (arg && arg.name)) {
isNamedFound = false;
for(j = 0; j < params.length; j++) {
if (!evaldArguments[j] && name === params[j].name) {
evaldArguments[j] = arg.value.eval(env);
frame.prependRule(new(tree.Rule)(name, arg.value.eval(env)));
isNamedFound = true;
break;
}
}
if (isNamedFound) {
args.splice(i, 1);
i--;
continue;
} else {
throw { type: 'Runtime', message: "Named argument for " + this.name +
' ' + args[i].name + ' not found' };
}
}
}
}
argIndex = 0;
for (i = 0; i < params.length; i++) {
if (evaldArguments[i]) { continue; }
arg = args && args[argIndex];
if (name = params[i].name) {
if (params[i].variadic && args) {
varargs = [];
for (j = argIndex; j < args.length; j++) {
varargs.push(args[j].value.eval(env));
}
frame.prependRule(new(tree.Rule)(name, new(tree.Expression)(varargs).eval(env)));
} else {
val = arg && arg.value;
if (val) {
val = val.eval(env);
} else if (params[i].value) {
val = params[i].value.eval(mixinEnv);
frame.resetCache();
} else {
throw { type: 'Runtime', message: "wrong number of arguments for " + this.name +
' (' + args.length + ' for ' + this.arity + ')' };
}
frame.prependRule(new(tree.Rule)(name, val));
evaldArguments[i] = val;
}
}
if (params[i].variadic && args) {
for (j = argIndex; j < args.length; j++) {
evaldArguments[j] = args[j].value.eval(env);
}
}
argIndex++;
}
return frame;
},
eval: function (env, args, important) {
var _arguments = [],
mixinFrames = this.frames.concat(env.frames),
frame = this.evalParams(env, new(tree.evalEnv)(env, mixinFrames), args, _arguments),
rules, ruleset;
frame.prependRule(new(tree.Rule)('@arguments', new(tree.Expression)(_arguments).eval(env)));
rules = this.rules.slice(0);
ruleset = new(tree.Ruleset)(null, rules);
ruleset.originalRuleset = this;
ruleset = ruleset.eval(new(tree.evalEnv)(env, [this, frame].concat(mixinFrames)));
if (important) {
ruleset = this.parent.makeImportant.apply(ruleset);
}
return ruleset;
},
matchCondition: function (args, env) {
if (this.condition && !this.condition.eval(
new(tree.evalEnv)(env,
[this.evalParams(env, new(tree.evalEnv)(env, this.frames.concat(env.frames)), args, [])] // the parameter variables
.concat(this.frames) // the parent namespace/mixin frames
.concat(env.frames)))) { // the current environment frames
return false;
}
return true;
},
matchArgs: function (args, env) {
var argsLength = (args && args.length) || 0, len;
if (! this.variadic) {
if (argsLength < this.required) { return false; }
if (argsLength > this.params.length) { return false; }
} else {
if (argsLength < (this.required - 1)) { return false; }
}
len = Math.min(argsLength, this.arity);
for (var i = 0; i < len; i++) {
if (!this.params[i].name && !this.params[i].variadic) {
if (args[i].value.eval(env).toCSS() != this.params[i].value.eval(env).toCSS()) {
return false;
}
}
}
return true;
}
};
})(require('../tree'));
(function (tree) {
tree.Negative = function (node) {
this.value = node;
};
tree.Negative.prototype = {
type: "Negative",
accept: function (visitor) {
this.value = visitor.visit(this.value);
},
genCSS: function (env, output) {
output.add('-');
this.value.genCSS(env, output);
},
toCSS: tree.toCSS,
eval: function (env) {
if (env.isMathOn()) {
return (new(tree.Operation)('*', [new(tree.Dimension)(-1), this.value])).eval(env);
}
return new(tree.Negative)(this.value.eval(env));
}
};
})(require('../tree'));
(function (tree) {
tree.Operation = function (op, operands, isSpaced) {
this.op = op.trim();
this.operands = operands;
this.isSpaced = isSpaced;
};
tree.Operation.prototype = {
type: "Operation",
accept: function (visitor) {
this.operands = visitor.visit(this.operands);
},
eval: function (env) {
var a = this.operands[0].eval(env),
b = this.operands[1].eval(env);
if (env.isMathOn()) {
if (a instanceof tree.Dimension && b instanceof tree.Color) {
a = a.toColor();
}
if (b instanceof tree.Dimension && a instanceof tree.Color) {
b = b.toColor();
}
if (!a.operate) {
throw { type: "Operation",
message: "Operation on an invalid type" };
}
return a.operate(env, this.op, b);
} else {
return new(tree.Operation)(this.op, [a, b], this.isSpaced);
}
},
genCSS: function (env, output) {
this.operands[0].genCSS(env, output);
if (this.isSpaced) {
output.add(" ");
}
output.add(this.op);
if (this.isSpaced) {
output.add(" ");
}
this.operands[1].genCSS(env, output);
},
toCSS: tree.toCSS
};
tree.operate = function (env, op, a, b) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
};
})(require('../tree'));
(function (tree) {
tree.Paren = function (node) {
this.value = node;
};
tree.Paren.prototype = {
type: "Paren",
accept: function (visitor) {
this.value = visitor.visit(this.value);
},
genCSS: function (env, output) {
output.add('(');
this.value.genCSS(env, output);
output.add(')');
},
toCSS: tree.toCSS,
eval: function (env) {
return new(tree.Paren)(this.value.eval(env));
}
};
})(require('../tree'));
(function (tree) {
tree.Quoted = function (str, content, escaped, index, currentFileInfo) {
this.escaped = escaped;
this.value = content || '';
this.quote = str.charAt(0);
this.index = index;
this.currentFileInfo = currentFileInfo;
};
tree.Quoted.prototype = {
type: "Quoted",
genCSS: function (env, output) {
if (!this.escaped) {
output.add(this.quote, this.currentFileInfo, this.index);
}
output.add(this.value);
if (!this.escaped) {
output.add(this.quote);
}
},
toCSS: tree.toCSS,
eval: function (env) {
var that = this;
var value = this.value.replace(/`([^`]+)`/g, function (_, exp) {
return new(tree.JavaScript)(exp, that.index, true).eval(env).value;
}).replace(/@\{([\w-]+)\}/g, function (_, name) {
var v = new(tree.Variable)('@' + name, that.index, that.currentFileInfo).eval(env, true);
return (v instanceof tree.Quoted) ? v.value : v.toCSS();
});
return new(tree.Quoted)(this.quote + value + this.quote, value, this.escaped, this.index, this.currentFileInfo);
},
compare: function (x) {
if (!x.toCSS) {
return -1;
}
var left = this.toCSS(),
right = x.toCSS();
if (left === right) {
return 0;
}
return left < right ? -1 : 1;
}
};
})(require('../tree'));
(function (tree) {
tree.Rule = function (name, value, important, merge, index, currentFileInfo, inline) {
this.name = name;
this.value = (value instanceof tree.Value) ? value : new(tree.Value)([value]);
this.important = important ? ' ' + important.trim() : '';
this.merge = merge;
this.index = index;
this.currentFileInfo = currentFileInfo;
this.inline = inline || false;
this.variable = name.charAt && (name.charAt(0) === '@');
};
tree.Rule.prototype = {
type: "Rule",
accept: function (visitor) {
this.value = visitor.visit(this.value);
},
genCSS: function (env, output) {
output.add(this.name + (env.compress ? ':' : ': '), this.currentFileInfo, this.index);
try {
this.value.genCSS(env, output);
}
catch(e) {
e.index = this.index;
e.filename = this.currentFileInfo.filename;
throw e;
}
output.add(this.important + ((this.inline || (env.lastRule && env.compress)) ? "" : ";"), this.currentFileInfo, this.index);
},
toCSS: tree.toCSS,
eval: function (env) {
var strictMathBypass = false;
var name = this.name.map ?
this.name.map( function(v) {
return v.eval ? v.eval(env).value : v;
}).join('') : this.name;
if (name === "font" && !env.strictMath) {
strictMathBypass = true;
env.strictMath = true;
}
try {
return new(tree.Rule)(name,
this.value.eval(env),
this.important,
this.merge,
this.index, this.currentFileInfo, this.inline);
}
catch(e) {
if (e.index === undefined) {
e.index = this.index;
}
throw e;
}
finally {
if (strictMathBypass) {
env.strictMath = false;
}
}
},
makeImportant: function () {
return new(tree.Rule)(this.name,
this.value,
"!important",
this.merge,
this.index, this.currentFileInfo, this.inline);
}
};
})(require('../tree'));
(function (tree) {
tree.Ruleset = function (selectors, rules, strictImports) {
this.selectors = selectors;
this.rules = rules;
this._lookups = {};
this.strictImports = strictImports;
};
tree.Ruleset.prototype = {
type: "Ruleset",
accept: function (visitor) {
if (this.paths) {
visitor.visitArray(this.paths, true);
} else if (this.selectors) {
this.selectors = visitor.visitArray(this.selectors);
}
if (this.rules && this.rules.length) {
this.rules = visitor.visitArray(this.rules);
}
},
eval: function (env) {
var thisSelectors = this.selectors, selectors,
selCnt, i, defaultFunc = tree.defaultFunc;
if (thisSelectors && (selCnt = thisSelectors.length)) {
selectors = [];
defaultFunc.error({
type: "Syntax",
message: "it is currently only allowed in parametric mixin guards,"
});
for (i = 0; i < selCnt; i++) {
selectors.push(thisSelectors[i].eval(env));
}
defaultFunc.reset();
}
var rules = this.rules ? this.rules.slice(0) : null,
ruleset = new(tree.Ruleset)(selectors, rules, this.strictImports),
rule;
ruleset.originalRuleset = this;
ruleset.root = this.root;
ruleset.firstRoot = this.firstRoot;
ruleset.allowImports = this.allowImports;
if(this.debugInfo) {
ruleset.debugInfo = this.debugInfo;
}
// push the current ruleset to the frames stack
var envFrames = env.frames;
envFrames.unshift(ruleset);
// currrent selectors
var envSelectors = env.selectors;
if (!envSelectors) {
env.selectors = envSelectors = [];
}
envSelectors.unshift(this.selectors);
// Evaluate imports
if (ruleset.root || ruleset.allowImports || !ruleset.strictImports) {
ruleset.evalImports(env);
}
// Store the frames around mixin definitions,
// so they can be evaluated like closures when the time comes.
var rsRules = ruleset.rules, rsRuleCnt = rsRules ? rsRules.length : 0;
for (i = 0; i < rsRuleCnt; i++) {
if (rsRules[i] instanceof tree.mixin.Definition) {
rsRules[i].frames = envFrames.slice(0);
}
}
var mediaBlockCount = (env.mediaBlocks && env.mediaBlocks.length) || 0;
// Evaluate mixin calls.
for (i = 0; i < rsRuleCnt; i++) {
if (rsRules[i] instanceof tree.mixin.Call) {
/*jshint loopfunc:true */
rules = rsRules[i].eval(env).filter(function(r) {
if ((r instanceof tree.Rule) && r.variable) {
// do not pollute the scope if the variable is
// already there. consider returning false here
// but we need a way to "return" variable from mixins
return !(ruleset.variable(r.name));
}
return true;
});
rsRules.splice.apply(rsRules, [i, 1].concat(rules));
rsRuleCnt += rules.length - 1;
i += rules.length-1;
ruleset.resetCache();
}
}
// Evaluate everything else
for (i = 0; i < rsRuleCnt; i++) {
rule = rsRules[i];
if (! (rule instanceof tree.mixin.Definition)) {
rsRules[i] = rule.eval ? rule.eval(env) : rule;
}
}
// Pop the stack
envFrames.shift();
envSelectors.shift();
if (env.mediaBlocks) {
for (i = mediaBlockCount; i < env.mediaBlocks.length; i++) {
env.mediaBlocks[i].bubbleSelectors(selectors);
}
}
return ruleset;
},
evalImports: function(env) {
var rules = this.rules, i, importRules;
if (!rules) { return; }
for (i = 0; i < rules.length; i++) {
if (rules[i] instanceof tree.Import) {
importRules = rules[i].eval(env);
if (importRules && importRules.length) {
rules.splice.apply(rules, [i, 1].concat(importRules));
i+= importRules.length-1;
} else {
rules.splice(i, 1, importRules);
}
this.resetCache();
}
}
},
makeImportant: function() {
return new tree.Ruleset(this.selectors, this.rules.map(function (r) {
if (r.makeImportant) {
return r.makeImportant();
} else {
return r;
}
}), this.strictImports);
},
matchArgs: function (args) {
return !args || args.length === 0;
},
matchCondition: function (args, env) {
var lastSelector = this.selectors[this.selectors.length-1];
if (lastSelector.condition &&
!lastSelector.condition.eval(
new(tree.evalEnv)(env,
env.frames))) {
return false;
}
return true;
},
resetCache: function () {
this._rulesets = null;
this._variables = null;
this._lookups = {};
},
variables: function () {
if (!this._variables) {
this._variables = !this.rules ? {} : this.rules.reduce(function (hash, r) {
if (r instanceof tree.Rule && r.variable === true) {
hash[r.name] = r;
}
return hash;
}, {});
}
return this._variables;
},
variable: function (name) {
return this.variables()[name];
},
rulesets: function () {
if (!this.rules) { return null; }
var _Ruleset = tree.Ruleset, _MixinDefinition = tree.mixin.Definition,
filtRules = [], rules = this.rules, cnt = rules.length,
i, rule;
for (i = 0; i < cnt; i++) {
rule = rules[i];
if ((rule instanceof _Ruleset) || (rule instanceof _MixinDefinition)) {
filtRules.push(rule);
}
}
return filtRules;
},
prependRule: function (rule) {
var rules = this.rules;
if (rules) { rules.unshift(rule); } else { this.rules = [ rule ]; }
},
find: function (selector, self) {
self = self || this;
var rules = [], match,
key = selector.toCSS();
if (key in this._lookups) { return this._lookups[key]; }
this.rulesets().forEach(function (rule) {
if (rule !== self) {
for (var j = 0; j < rule.selectors.length; j++) {
match = selector.match(rule.selectors[j]);
if (match) {
if (selector.elements.length > match) {
Array.prototype.push.apply(rules, rule.find(
new(tree.Selector)(selector.elements.slice(match)), self));
} else {
rules.push(rule);
}
break;
}
}
}
});
this._lookups[key] = rules;
return rules;
},
genCSS: function (env, output) {
var i, j,
ruleNodes = [],
rulesetNodes = [],
rulesetNodeCnt,
debugInfo, // Line number debugging
rule,
path;
env.tabLevel = (env.tabLevel || 0);
if (!this.root) {
env.tabLevel++;
}
var tabRuleStr = env.compress ? '' : Array(env.tabLevel + 1).join(" "),
tabSetStr = env.compress ? '' : Array(env.tabLevel).join(" "),
sep;
for (i = 0; i < this.rules.length; i++) {
rule = this.rules[i];
if (rule.rules || (rule instanceof tree.Media) || rule instanceof tree.Directive || (this.root && rule instanceof tree.Comment)) {
rulesetNodes.push(rule);
} else {
ruleNodes.push(rule);
}
}
// If this is the root node, we don't render
// a selector, or {}.
if (!this.root) {
debugInfo = tree.debugInfo(env, this, tabSetStr);
if (debugInfo) {
output.add(debugInfo);
output.add(tabSetStr);
}
var paths = this.paths, pathCnt = paths.length,
pathSubCnt;
sep = env.compress ? ',' : (',\n' + tabSetStr);
for (i = 0; i < pathCnt; i++) {
path = paths[i];
if (!(pathSubCnt = path.length)) { continue; }
if (i > 0) { output.add(sep); }
env.firstSelector = true;
path[0].genCSS(env, output);
env.firstSelector = false;
for (j = 1; j < pathSubCnt; j++) {
path[j].genCSS(env, output);
}
}
output.add((env.compress ? '{' : ' {\n') + tabRuleStr);
}
// Compile rules and rulesets
for (i = 0; i < ruleNodes.length; i++) {
rule = ruleNodes[i];
// @page{ directive ends up with root elements inside it, a mix of rules and rulesets
// In this instance we do not know whether it is the last property
if (i + 1 === ruleNodes.length && (!this.root || rulesetNodes.length === 0 || this.firstRoot)) {
env.lastRule = true;
}
if (rule.genCSS) {
rule.genCSS(env, output);
} else if (rule.value) {
output.add(rule.value.toString());
}
if (!env.lastRule) {
output.add(env.compress ? '' : ('\n' + tabRuleStr));
} else {
env.lastRule = false;
}
}
if (!this.root) {
output.add((env.compress ? '}' : '\n' + tabSetStr + '}'));
env.tabLevel--;
}
sep = (env.compress ? "" : "\n") + (this.root ? tabRuleStr : tabSetStr);
rulesetNodeCnt = rulesetNodes.length;
if (rulesetNodeCnt) {
if (ruleNodes.length && sep) { output.add(sep); }
rulesetNodes[0].genCSS(env, output);
for (i = 1; i < rulesetNodeCnt; i++) {
if (sep) { output.add(sep); }
rulesetNodes[i].genCSS(env, output);
}
}
if (!output.isEmpty() && !env.compress && this.firstRoot) {
output.add('\n');
}
},
toCSS: tree.toCSS,
markReferenced: function () {
for (var s = 0; s < this.selectors.length; s++) {
this.selectors[s].markReferenced();
}
},
joinSelectors: function (paths, context, selectors) {
for (var s = 0; s < selectors.length; s++) {
this.joinSelector(paths, context, selectors[s]);
}
},
joinSelector: function (paths, context, selector) {
var i, j, k,
hasParentSelector, newSelectors, el, sel, parentSel,
newSelectorPath, afterParentJoin, newJoinedSelector,
newJoinedSelectorEmpty, lastSelector, currentElements,
selectorsMultiplied;
for (i = 0; i < selector.elements.length; i++) {
el = selector.elements[i];
if (el.value === '&') {
hasParentSelector = true;
}
}
if (!hasParentSelector) {
if (context.length > 0) {
for (i = 0; i < context.length; i++) {
paths.push(context[i].concat(selector));
}
}
else {
paths.push([selector]);
}
return;
}
// The paths are [[Selector]]
// The first list is a list of comma seperated selectors
// The inner list is a list of inheritance seperated selectors
// e.g.
// .a, .b {
// .c {
// }
// }
// == [[.a] [.c]] [[.b] [.c]]
//
// the elements from the current selector so far
currentElements = [];
// the current list of new selectors to add to the path.
// We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
// by the parents
newSelectors = [[]];
for (i = 0; i < selector.elements.length; i++) {
el = selector.elements[i];
// non parent reference elements just get added
if (el.value !== "&") {
currentElements.push(el);
} else {
// the new list of selectors to add
selectorsMultiplied = [];
// merge the current list of non parent selector elements
// on to the current list of selectors to add
if (currentElements.length > 0) {
this.mergeElementsOnToSelectors(currentElements, newSelectors);
}
// loop through our current selectors
for (j = 0; j < newSelectors.length; j++) {
sel = newSelectors[j];
// if we don't have any parent paths, the & might be in a mixin so that it can be used
// whether there are parents or not
if (context.length === 0) {
// the combinator used on el should now be applied to the next element instead so that
// it is not lost
if (sel.length > 0) {
sel[0].elements = sel[0].elements.slice(0);
sel[0].elements.push(new(tree.Element)(el.combinator, '', 0, el.index, el.currentFileInfo));
}
selectorsMultiplied.push(sel);
}
else {
// and the parent selectors
for (k = 0; k < context.length; k++) {
parentSel = context[k];
// We need to put the current selectors
// then join the last selector's elements on to the parents selectors
// our new selector path
newSelectorPath = [];
// selectors from the parent after the join
afterParentJoin = [];
newJoinedSelectorEmpty = true;
//construct the joined selector - if & is the first thing this will be empty,
// if not newJoinedSelector will be the last set of elements in the selector
if (sel.length > 0) {
newSelectorPath = sel.slice(0);
lastSelector = newSelectorPath.pop();
newJoinedSelector = selector.createDerived(lastSelector.elements.slice(0));
newJoinedSelectorEmpty = false;
}
else {
newJoinedSelector = selector.createDerived([]);
}
//put together the parent selectors after the join
if (parentSel.length > 1) {
afterParentJoin = afterParentJoin.concat(parentSel.slice(1));
}
if (parentSel.length > 0) {
newJoinedSelectorEmpty = false;
// join the elements so far with the first part of the parent
newJoinedSelector.elements.push(new(tree.Element)(el.combinator, parentSel[0].elements[0].value, el.index, el.currentFileInfo));
newJoinedSelector.elements = newJoinedSelector.elements.concat(parentSel[0].elements.slice(1));
}
if (!newJoinedSelectorEmpty) {
// now add the joined selector
newSelectorPath.push(newJoinedSelector);
}
// and the rest of the parent
newSelectorPath = newSelectorPath.concat(afterParentJoin);
// add that to our new set of selectors
selectorsMultiplied.push(newSelectorPath);
}
}
}
// our new selectors has been multiplied, so reset the state
newSelectors = selectorsMultiplied;
currentElements = [];
}
}
// if we have any elements left over (e.g. .a& .b == .b)
// add them on to all the current selectors
if (currentElements.length > 0) {
this.mergeElementsOnToSelectors(currentElements, newSelectors);
}
for (i = 0; i < newSelectors.length; i++) {
if (newSelectors[i].length > 0) {
paths.push(newSelectors[i]);
}
}
},
mergeElementsOnToSelectors: function(elements, selectors) {
var i, sel;
if (selectors.length === 0) {
selectors.push([ new(tree.Selector)(elements) ]);
return;
}
for (i = 0; i < selectors.length; i++) {
sel = selectors[i];
// if the previous thing in sel is a parent this needs to join on to it
if (sel.length > 0) {
sel[sel.length - 1] = sel[sel.length - 1].createDerived(sel[sel.length - 1].elements.concat(elements));
}
else {
sel.push(new(tree.Selector)(elements));
}
}
}
};
})(require('../tree'));
(function (tree) {
tree.Selector = function (elements, extendList, condition, index, currentFileInfo, isReferenced) {
this.elements = elements;
this.extendList = extendList;
this.condition = condition;
this.currentFileInfo = currentFileInfo || {};
this.isReferenced = isReferenced;
if (!condition) {
this.evaldCondition = true;
}
};
tree.Selector.prototype = {
type: "Selector",
accept: function (visitor) {
if (this.elements) {
this.elements = visitor.visitArray(this.elements);
}
if (this.extendList) {
this.extendList = visitor.visitArray(this.extendList);
}
if (this.condition) {
this.condition = visitor.visit(this.condition);
}
},
createDerived: function(elements, extendList, evaldCondition) {
evaldCondition = (evaldCondition != null) ? evaldCondition : this.evaldCondition;
var newSelector = new(tree.Selector)(elements, extendList || this.extendList, this.condition, this.index, this.currentFileInfo, this.isReferenced);
newSelector.evaldCondition = evaldCondition;
return newSelector;
},
match: function (other) {
var elements = this.elements,
len = elements.length,
oelements, olen, i;
oelements = other.elements.map( function(v) {
return v.combinator.value + (v.value.value || v.value);
}).join("").match(/[,\.\w-]([\w-]|(\\.))*/g);
// ^ regexp could be more simple but see test/less/css-escapes.less:17, doh!
if (!oelements) {
return 0;
}
if (oelements[0] === "&") {
oelements.shift();
}
olen = oelements.length;
if (olen === 0 || len < olen) {
return 0;
} else {
for (i = 0; i < olen; i++) {
if (elements[i].value !== oelements[i]) {
return 0;
}
}
}
return olen; // return number of matched elements
},
eval: function (env) {
var evaldCondition = this.condition && this.condition.eval(env),
elements = this.elements, extendList = this.extendList;
elements = elements && elements.map(function (e) { return e.eval(env); });
extendList = extendList && extendList.map(function(extend) { return extend.eval(env); });
return this.createDerived(elements, extendList, evaldCondition);
},
genCSS: function (env, output) {
var i, element;
if ((!env || !env.firstSelector) && this.elements[0].combinator.value === "") {
output.add(' ', this.currentFileInfo, this.index);
}
if (!this._css) {
//TODO caching? speed comparison?
for(i = 0; i < this.elements.length; i++) {
element = this.elements[i];
element.genCSS(env, output);
}
}
},
toCSS: tree.toCSS,
markReferenced: function () {
this.isReferenced = true;
},
getIsReferenced: function() {
return !this.currentFileInfo.reference || this.isReferenced;
},
getIsOutput: function() {
return this.evaldCondition;
}
};
})(require('../tree'));
(function (tree) {
tree.UnicodeDescriptor = function (value) {
this.value = value;
};
tree.UnicodeDescriptor.prototype = {
type: "UnicodeDescriptor",
genCSS: function (env, output) {
output.add(this.value);
},
toCSS: tree.toCSS,
eval: function () { return this; }
};
})(require('../tree'));
(function (tree) {
tree.URL = function (val, currentFileInfo, isEvald) {
this.value = val;
this.currentFileInfo = currentFileInfo;
this.isEvald = isEvald;
};
tree.URL.prototype = {
type: "Url",
accept: function (visitor) {
this.value = visitor.visit(this.value);
},
genCSS: function (env, output) {
output.add("url(");
this.value.genCSS(env, output);
output.add(")");
},
toCSS: tree.toCSS,
eval: function (ctx) {
var val = this.value.eval(ctx),
rootpath;
if (!this.isEvald) {
// Add the base path if the URL is relative
rootpath = this.currentFileInfo && this.currentFileInfo.rootpath;
if (rootpath && typeof val.value === "string" && ctx.isPathRelative(val.value)) {
if (!val.quote) {
rootpath = rootpath.replace(/[\(\)'"\s]/g, function(match) { return "\\"+match; });
}
val.value = rootpath + val.value;
}
val.value = ctx.normalizePath(val.value);
}
return new(tree.URL)(val, this.currentFileInfo, true);
}
};
})(require('../tree'));
(function (tree) {
tree.Value = function (value) {
this.value = value;
};
tree.Value.prototype = {
type: "Value",
accept: function (visitor) {
if (this.value) {
this.value = visitor.visitArray(this.value);
}
},
eval: function (env) {
if (this.value.length === 1) {
return this.value[0].eval(env);
} else {
return new(tree.Value)(this.value.map(function (v) {
return v.eval(env);
}));
}
},
genCSS: function (env, output) {
var i;
for(i = 0; i < this.value.length; i++) {
this.value[i].genCSS(env, output);
if (i+1 < this.value.length) {
output.add((env && env.compress) ? ',' : ', ');
}
}
},
toCSS: tree.toCSS
};
})(require('../tree'));
(function (tree) {
tree.Variable = function (name, index, currentFileInfo) {
this.name = name;
this.index = index;
this.currentFileInfo = currentFileInfo || {};
};
tree.Variable.prototype = {
type: "Variable",
eval: function (env) {
var variable, name = this.name;
if (name.indexOf('@@') === 0) {
name = '@' + new(tree.Variable)(name.slice(1)).eval(env).value;
}
if (this.evaluating) {
throw { type: 'Name',
message: "Recursive variable definition for " + name,
filename: this.currentFileInfo.file,
index: this.index };
}
this.evaluating = true;
variable = tree.find(env.frames, function (frame) {
var v = frame.variable(name);
if (v) {
return v.value.eval(env);
}
});
if (variable) {
this.evaluating = false;
return variable;
} else {
throw { type: 'Name',
message: "variable " + name + " is undefined",
filename: this.currentFileInfo.filename,
index: this.index };
}
}
};
})(require('../tree'));
(function (tree) {
var parseCopyProperties = [
'paths', // option - unmodified - paths to search for imports on
'optimization', // option - optimization level (for the chunker)
'files', // list of files that have been imported, used for import-once
'contents', // map - filename to contents of all the files
'contentsIgnoredChars', // map - filename to lines at the begining of each file to ignore
'relativeUrls', // option - whether to adjust URL's to be relative
'rootpath', // option - rootpath to append to URL's
'strictImports', // option -
'insecure', // option - whether to allow imports from insecure ssl hosts
'dumpLineNumbers', // option - whether to dump line numbers
'compress', // option - whether to compress
'processImports', // option - whether to process imports. if false then imports will not be imported
'syncImport', // option - whether to import synchronously
'javascriptEnabled',// option - whether JavaScript is enabled. if undefined, defaults to true
'mime', // browser only - mime type for sheet import
'useFileCache', // browser only - whether to use the per file session cache
'currentFileInfo' // information about the current file - for error reporting and importing and making urls relative etc.
];
//currentFileInfo = {
// 'relativeUrls' - option - whether to adjust URL's to be relative
// 'filename' - full resolved filename of current file
// 'rootpath' - path to append to normal URLs for this node
// 'currentDirectory' - path to the current file, absolute
// 'rootFilename' - filename of the base file
// 'entryPath' - absolute path to the entry file
// 'reference' - whether the file should not be output and only output parts that are referenced
tree.parseEnv = function(options) {
copyFromOriginal(options, this, parseCopyProperties);
if (!this.contents) { this.contents = {}; }
if (!this.contentsIgnoredChars) { this.contentsIgnoredChars = {}; }
if (!this.files) { this.files = {}; }
if (!this.currentFileInfo) {
var filename = (options && options.filename) || "input";
var entryPath = filename.replace(/[^\/\\]*$/, "");
if (options) {
options.filename = null;
}
this.currentFileInfo = {
filename: filename,
relativeUrls: this.relativeUrls,
rootpath: (options && options.rootpath) || "",
currentDirectory: entryPath,
entryPath: entryPath,
rootFilename: filename
};
}
};
var evalCopyProperties = [
'silent', // whether to swallow errors and warnings
'verbose', // whether to log more activity
'compress', // whether to compress
'yuicompress', // whether to compress with the outside tool yui compressor
'ieCompat', // whether to enforce IE compatibility (IE8 data-uri)
'strictMath', // whether math has to be within parenthesis
'strictUnits', // whether units need to evaluate correctly
'cleancss', // whether to compress with clean-css
'sourceMap', // whether to output a source map
'importMultiple'// whether we are currently importing multiple copies
];
tree.evalEnv = function(options, frames) {
copyFromOriginal(options, this, evalCopyProperties);
this.frames = frames || [];
};
tree.evalEnv.prototype.inParenthesis = function () {
if (!this.parensStack) {
this.parensStack = [];
}
this.parensStack.push(true);
};
tree.evalEnv.prototype.outOfParenthesis = function () {
this.parensStack.pop();
};
tree.evalEnv.prototype.isMathOn = function () {
return this.strictMath ? (this.parensStack && this.parensStack.length) : true;
};
tree.evalEnv.prototype.isPathRelative = function (path) {
return !/^(?:[a-z-]+:|\/)/.test(path);
};
tree.evalEnv.prototype.normalizePath = function( path ) {
var
segments = path.split("/").reverse(),
segment;
path = [];
while (segments.length !== 0 ) {
segment = segments.pop();
switch( segment ) {
case ".":
break;
case "..":
if ((path.length === 0) || (path[path.length - 1] === "..")) {
path.push( segment );
} else {
path.pop();
}
break;
default:
path.push( segment );
break;
}
}
return path.join("/");
};
//todo - do the same for the toCSS env
//tree.toCSSEnv = function (options) {
//};
var copyFromOriginal = function(original, destination, propertiesToCopy) {
if (!original) { return; }
for(var i = 0; i < propertiesToCopy.length; i++) {
if (original.hasOwnProperty(propertiesToCopy[i])) {
destination[propertiesToCopy[i]] = original[propertiesToCopy[i]];
}
}
};
})(require('./tree'));
(function (tree) {
var _visitArgs = { visitDeeper: true },
_hasIndexed = false;
function _noop(node) {
return node;
}
function indexNodeTypes(parent, ticker) {
// add .typeIndex to tree node types for lookup table
var key, child;
for (key in parent) {
child = parent[key];
switch (typeof child) {
case "function":
// ignore bound functions directly on tree which do not have a prototype
// or aren't nodes
if (child.prototype && child.prototype.type) {
child.prototype.typeIndex = ticker++;
}
break;
case "object":
ticker = indexNodeTypes(child, ticker);
break;
}
}
return ticker;
}
tree.visitor = function(implementation) {
this._implementation = implementation;
this._visitFnCache = [];
if (!_hasIndexed) {
indexNodeTypes(tree, 1);
_hasIndexed = true;
}
};
tree.visitor.prototype = {
visit: function(node) {
if (!node) {
return node;
}
var nodeTypeIndex = node.typeIndex;
if (!nodeTypeIndex) {
return node;
}
var visitFnCache = this._visitFnCache,
impl = this._implementation,
aryIndx = nodeTypeIndex << 1,
outAryIndex = aryIndx | 1,
func = visitFnCache[aryIndx],
funcOut = visitFnCache[outAryIndex],
visitArgs = _visitArgs,
fnName;
visitArgs.visitDeeper = true;
if (!func) {
fnName = "visit" + node.type;
func = impl[fnName] || _noop;
funcOut = impl[fnName + "Out"] || _noop;
visitFnCache[aryIndx] = func;
visitFnCache[outAryIndex] = funcOut;
}
if (func !== _noop) {
var newNode = func.call(impl, node, visitArgs);
if (impl.isReplacing) {
node = newNode;
}
}
if (visitArgs.visitDeeper && node && node.accept) {
node.accept(this);
}
if (funcOut != _noop) {
funcOut.call(impl, node);
}
return node;
},
visitArray: function(nodes, nonReplacing) {
if (!nodes) {
return nodes;
}
var cnt = nodes.length, i;
// Non-replacing
if (nonReplacing || !this._implementation.isReplacing) {
for (i = 0; i < cnt; i++) {
this.visit(nodes[i]);
}
return nodes;
}
// Replacing
var out = [];
for (i = 0; i < cnt; i++) {
var evald = this.visit(nodes[i]);
if (!evald.splice) {
out.push(evald);
} else if (evald.length) {
this.flatten(evald, out);
}
}
return out;
},
flatten: function(arr, out) {
if (!out) {
out = [];
}
var cnt, i, item,
nestedCnt, j, nestedItem;
for (i = 0, cnt = arr.length; i < cnt; i++) {
item = arr[i];
if (!item.splice) {
out.push(item);
continue;
}
for (j = 0, nestedCnt = item.length; j < nestedCnt; j++) {
nestedItem = item[j];
if (!nestedItem.splice) {
out.push(nestedItem);
} else if (nestedItem.length) {
this.flatten(nestedItem, out);
}
}
}
return out;
}
};
})(require('./tree'));
(function (tree) {
tree.importVisitor = function(importer, finish, evalEnv) {
this._visitor = new tree.visitor(this);
this._importer = importer;
this._finish = finish;
this.env = evalEnv || new tree.evalEnv();
this.importCount = 0;
};
tree.importVisitor.prototype = {
isReplacing: true,
run: function (root) {
var error;
try {
// process the contents
this._visitor.visit(root);
}
catch(e) {
error = e;
}
this.isFinished = true;
if (this.importCount === 0) {
this._finish(error);
}
},
visitImport: function (importNode, visitArgs) {
var importVisitor = this,
evaldImportNode,
inlineCSS = importNode.options.inline;
if (!importNode.css || inlineCSS) {
try {
evaldImportNode = importNode.evalForImport(this.env);
} catch(e){
if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
// attempt to eval properly and treat as css
importNode.css = true;
// if that fails, this error will be thrown
importNode.error = e;
}
if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) {
importNode = evaldImportNode;
this.importCount++;
var env = new tree.evalEnv(this.env, this.env.frames.slice(0));
if (importNode.options.multiple) {
env.importMultiple = true;
}
this._importer.push(importNode.getPath(), importNode.currentFileInfo, importNode.options, function (e, root, imported, fullPath) {
if (e && !e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; }
if (imported && !env.importMultiple) { importNode.skip = imported; }
var subFinish = function(e) {
importVisitor.importCount--;
if (importVisitor.importCount === 0 && importVisitor.isFinished) {
importVisitor._finish(e);
}
};
if (root) {
importNode.root = root;
importNode.importedFilename = fullPath;
if (!inlineCSS && !importNode.skip) {
new(tree.importVisitor)(importVisitor._importer, subFinish, env)
.run(root);
return;
}
}
subFinish();
});
}
}
visitArgs.visitDeeper = false;
return importNode;
},
visitRule: function (ruleNode, visitArgs) {
visitArgs.visitDeeper = false;
return ruleNode;
},
visitDirective: function (directiveNode, visitArgs) {
this.env.frames.unshift(directiveNode);
return directiveNode;
},
visitDirectiveOut: function (directiveNode) {
this.env.frames.shift();
},
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
this.env.frames.unshift(mixinDefinitionNode);
return mixinDefinitionNode;
},
visitMixinDefinitionOut: function (mixinDefinitionNode) {
this.env.frames.shift();
},
visitRuleset: function (rulesetNode, visitArgs) {
this.env.frames.unshift(rulesetNode);
return rulesetNode;
},
visitRulesetOut: function (rulesetNode) {
this.env.frames.shift();
},
visitMedia: function (mediaNode, visitArgs) {
this.env.frames.unshift(mediaNode.ruleset);
return mediaNode;
},
visitMediaOut: function (mediaNode) {
this.env.frames.shift();
}
};
})(require('./tree'));
(function (tree) {
tree.joinSelectorVisitor = function() {
this.contexts = [[]];
this._visitor = new tree.visitor(this);
};
tree.joinSelectorVisitor.prototype = {
run: function (root) {
return this._visitor.visit(root);
},
visitRule: function (ruleNode, visitArgs) {
visitArgs.visitDeeper = false;
},
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
visitArgs.visitDeeper = false;
},
visitRuleset: function (rulesetNode, visitArgs) {
var context = this.contexts[this.contexts.length - 1],
paths = [], selectors;
this.contexts.push(paths);
if (! rulesetNode.root) {
selectors = rulesetNode.selectors;
if (selectors) {
selectors = selectors.filter(function(selector) { return selector.getIsOutput(); });
rulesetNode.selectors = selectors.length ? selectors : (selectors = null);
if (selectors) { rulesetNode.joinSelectors(paths, context, selectors); }
}
if (!selectors) { rulesetNode.rules = null; }
rulesetNode.paths = paths;
}
},
visitRulesetOut: function (rulesetNode) {
this.contexts.length = this.contexts.length - 1;
},
visitMedia: function (mediaNode, visitArgs) {
var context = this.contexts[this.contexts.length - 1];
mediaNode.rules[0].root = (context.length === 0 || context[0].multiMedia);
}
};
})(require('./tree'));
(function (tree) {
tree.toCSSVisitor = function(env) {
this._visitor = new tree.visitor(this);
this._env = env;
};
tree.toCSSVisitor.prototype = {
isReplacing: true,
run: function (root) {
return this._visitor.visit(root);
},
visitRule: function (ruleNode, visitArgs) {
if (ruleNode.variable) {
return [];
}
return ruleNode;
},
visitMixinDefinition: function (mixinNode, visitArgs) {
return [];
},
visitExtend: function (extendNode, visitArgs) {
return [];
},
visitComment: function (commentNode, visitArgs) {
if (commentNode.isSilent(this._env)) {
return [];
}
return commentNode;
},
visitMedia: function(mediaNode, visitArgs) {
mediaNode.accept(this._visitor);
visitArgs.visitDeeper = false;
if (!mediaNode.rules.length) {
return [];
}
return mediaNode;
},
visitDirective: function(directiveNode, visitArgs) {
if (directiveNode.currentFileInfo.reference && !directiveNode.isReferenced) {
return [];
}
if (directiveNode.name === "@charset") {
// Only output the debug info together with subsequent @charset definitions
// a comment (or @media statement) before the actual @charset directive would
// be considered illegal css as it has to be on the first line
if (this.charset) {
if (directiveNode.debugInfo) {
var comment = new tree.Comment("/* " + directiveNode.toCSS(this._env).replace(/\n/g, "")+" */\n");
comment.debugInfo = directiveNode.debugInfo;
return this._visitor.visit(comment);
}
return [];
}
this.charset = true;
}
return directiveNode;
},
checkPropertiesInRoot: function(rules) {
var ruleNode;
for(var i = 0; i < rules.length; i++) {
ruleNode = rules[i];
if (ruleNode instanceof tree.Rule && !ruleNode.variable) {
throw { message: "properties must be inside selector blocks, they cannot be in the root.",
index: ruleNode.index, filename: ruleNode.currentFileInfo ? ruleNode.currentFileInfo.filename : null};
}
}
},
visitRuleset: function (rulesetNode, visitArgs) {
var rule, rulesets = [];
if (rulesetNode.firstRoot) {
this.checkPropertiesInRoot(rulesetNode.rules);
}
if (! rulesetNode.root) {
if (rulesetNode.paths) {
rulesetNode.paths = rulesetNode.paths
.filter(function(p) {
var i;
if (p[0].elements[0].combinator.value === ' ') {
p[0].elements[0].combinator = new(tree.Combinator)('');
}
for(i = 0; i < p.length; i++) {
if (p[i].getIsReferenced() && p[i].getIsOutput()) {
return true;
}
}
return false;
});
}
// Compile rules and rulesets
var nodeRules = rulesetNode.rules, nodeRuleCnt = nodeRules ? nodeRules.length : 0;
for (var i = 0; i < nodeRuleCnt; ) {
rule = nodeRules[i];
if (rule && rule.rules) {
// visit because we are moving them out from being a child
rulesets.push(this._visitor.visit(rule));
nodeRules.splice(i, 1);
nodeRuleCnt--;
continue;
}
i++;
}
// accept the visitor to remove rules and refactor itself
// then we can decide now whether we want it or not
if (nodeRuleCnt > 0) {
rulesetNode.accept(this._visitor);
} else {
rulesetNode.rules = null;
}
visitArgs.visitDeeper = false;
nodeRules = rulesetNode.rules;
if (nodeRules) {
this._mergeRules(nodeRules);
nodeRules = rulesetNode.rules;
}
if (nodeRules) {
this._removeDuplicateRules(nodeRules);
nodeRules = rulesetNode.rules;
}
// now decide whether we keep the ruleset
if (nodeRules && nodeRules.length > 0 && rulesetNode.paths.length > 0) {
rulesets.splice(0, 0, rulesetNode);
}
} else {
rulesetNode.accept(this._visitor);
visitArgs.visitDeeper = false;
if (rulesetNode.firstRoot || (rulesetNode.rules && rulesetNode.rules.length > 0)) {
rulesets.splice(0, 0, rulesetNode);
}
}
if (rulesets.length === 1) {
return rulesets[0];
}
return rulesets;
},
_removeDuplicateRules: function(rules) {
if (!rules) { return; }
// remove duplicates
var ruleCache = {},
ruleList, rule, i;
for(i = rules.length - 1; i >= 0 ; i--) {
rule = rules[i];
if (rule instanceof tree.Rule) {
if (!ruleCache[rule.name]) {
ruleCache[rule.name] = rule;
} else {
ruleList = ruleCache[rule.name];
if (ruleList instanceof tree.Rule) {
ruleList = ruleCache[rule.name] = [ruleCache[rule.name].toCSS(this._env)];
}
var ruleCSS = rule.toCSS(this._env);
if (ruleList.indexOf(ruleCSS) !== -1) {
rules.splice(i, 1);
} else {
ruleList.push(ruleCSS);
}
}
}
}
},
_mergeRules: function (rules) {
if (!rules) { return; }
var groups = {},
parts,
rule,
key;
for (var i = 0; i < rules.length; i++) {
rule = rules[i];
if ((rule instanceof tree.Rule) && rule.merge) {
key = [rule.name,
rule.important ? "!" : ""].join(",");
if (!groups[key]) {
groups[key] = [];
} else {
rules.splice(i--, 1);
}
groups[key].push(rule);
}
}
Object.keys(groups).map(function (k) {
parts = groups[k];
if (parts.length > 1) {
rule = parts[0];
rule.value = new (tree.Value)(parts.map(function (p) {
return p.value;
}));
}
});
}
};
})(require('./tree'));
(function (tree) {
/*jshint loopfunc:true */
tree.extendFinderVisitor = function() {
this._visitor = new tree.visitor(this);
this.contexts = [];
this.allExtendsStack = [[]];
};
tree.extendFinderVisitor.prototype = {
run: function (root) {
root = this._visitor.visit(root);
root.allExtends = this.allExtendsStack[0];
return root;
},
visitRule: function (ruleNode, visitArgs) {
visitArgs.visitDeeper = false;
},
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
visitArgs.visitDeeper = false;
},
visitRuleset: function (rulesetNode, visitArgs) {
if (rulesetNode.root) {
return;
}
var i, j, extend, allSelectorsExtendList = [], extendList;
// get &:extend(.a); rules which apply to all selectors in this ruleset
var rules = rulesetNode.rules, ruleCnt = rules ? rules.length : 0;
for(i = 0; i < ruleCnt; i++) {
if (rulesetNode.rules[i] instanceof tree.Extend) {
allSelectorsExtendList.push(rules[i]);
rulesetNode.extendOnEveryPath = true;
}
}
// now find every selector and apply the extends that apply to all extends
// and the ones which apply to an individual extend
var paths = rulesetNode.paths;
for(i = 0; i < paths.length; i++) {
var selectorPath = paths[i],
selector = selectorPath[selectorPath.length - 1],
selExtendList = selector.extendList;
extendList = selExtendList ? selExtendList.slice(0).concat(allSelectorsExtendList)
: allSelectorsExtendList;
if (extendList) {
extendList = extendList.map(function(allSelectorsExtend) {
return allSelectorsExtend.clone();
});
}
for(j = 0; j < extendList.length; j++) {
this.foundExtends = true;
extend = extendList[j];
extend.findSelfSelectors(selectorPath);
extend.ruleset = rulesetNode;
if (j === 0) { extend.firstExtendOnThisSelectorPath = true; }
this.allExtendsStack[this.allExtendsStack.length-1].push(extend);
}
}
this.contexts.push(rulesetNode.selectors);
},
visitRulesetOut: function (rulesetNode) {
if (!rulesetNode.root) {
this.contexts.length = this.contexts.length - 1;
}
},
visitMedia: function (mediaNode, visitArgs) {
mediaNode.allExtends = [];
this.allExtendsStack.push(mediaNode.allExtends);
},
visitMediaOut: function (mediaNode) {
this.allExtendsStack.length = this.allExtendsStack.length - 1;
},
visitDirective: function (directiveNode, visitArgs) {
directiveNode.allExtends = [];
this.allExtendsStack.push(directiveNode.allExtends);
},
visitDirectiveOut: function (directiveNode) {
this.allExtendsStack.length = this.allExtendsStack.length - 1;
}
};
tree.processExtendsVisitor = function() {
this._visitor = new tree.visitor(this);
};
tree.processExtendsVisitor.prototype = {
run: function(root) {
var extendFinder = new tree.extendFinderVisitor();
extendFinder.run(root);
if (!extendFinder.foundExtends) { return root; }
root.allExtends = root.allExtends.concat(this.doExtendChaining(root.allExtends, root.allExtends));
this.allExtendsStack = [root.allExtends];
return this._visitor.visit(root);
},
doExtendChaining: function (extendsList, extendsListTarget, iterationCount) {
//
// chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
// the selector we would do normally, but we are also adding an extend with the same target selector
// this means this new extend can then go and alter other extends
//
// this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
// this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
// we look at each selector at a time, as is done in visitRuleset
var extendIndex, targetExtendIndex, matches, extendsToAdd = [], newSelector, extendVisitor = this, selectorPath, extend, targetExtend, newExtend;
iterationCount = iterationCount || 0;
//loop through comparing every extend with every target extend.
// a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
// e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
// and the second is the target.
// the seperation into two lists allows us to process a subset of chains with a bigger set, as is the
// case when processing media queries
for(extendIndex = 0; extendIndex < extendsList.length; extendIndex++){
for(targetExtendIndex = 0; targetExtendIndex < extendsListTarget.length; targetExtendIndex++){
extend = extendsList[extendIndex];
targetExtend = extendsListTarget[targetExtendIndex];
// look for circular references
if( extend.parent_ids.indexOf( targetExtend.object_id ) >= 0 ){ continue; }
// find a match in the target extends self selector (the bit before :extend)
selectorPath = [targetExtend.selfSelectors[0]];
matches = extendVisitor.findMatch(extend, selectorPath);
if (matches.length) {
// we found a match, so for each self selector..
extend.selfSelectors.forEach(function(selfSelector) {
// process the extend as usual
newSelector = extendVisitor.extendSelector(matches, selectorPath, selfSelector);
// but now we create a new extend from it
newExtend = new(tree.Extend)(targetExtend.selector, targetExtend.option, 0);
newExtend.selfSelectors = newSelector;
// add the extend onto the list of extends for that selector
newSelector[newSelector.length-1].extendList = [newExtend];
// record that we need to add it.
extendsToAdd.push(newExtend);
newExtend.ruleset = targetExtend.ruleset;
//remember its parents for circular references
newExtend.parent_ids = newExtend.parent_ids.concat(targetExtend.parent_ids, extend.parent_ids);
// only process the selector once.. if we have :extend(.a,.b) then multiple
// extends will look at the same selector path, so when extending
// we know that any others will be duplicates in terms of what is added to the css
if (targetExtend.firstExtendOnThisSelectorPath) {
newExtend.firstExtendOnThisSelectorPath = true;
targetExtend.ruleset.paths.push(newSelector);
}
});
}
}
}
if (extendsToAdd.length) {
// try to detect circular references to stop a stack overflow.
// may no longer be needed.
this.extendChainCount++;
if (iterationCount > 100) {
var selectorOne = "{unable to calculate}";
var selectorTwo = "{unable to calculate}";
try
{
selectorOne = extendsToAdd[0].selfSelectors[0].toCSS();
selectorTwo = extendsToAdd[0].selector.toCSS();
}
catch(e) {}
throw {message: "extend circular reference detected. One of the circular extends is currently:"+selectorOne+":extend(" + selectorTwo+")"};
}
// now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
return extendsToAdd.concat(extendVisitor.doExtendChaining(extendsToAdd, extendsListTarget, iterationCount+1));
} else {
return extendsToAdd;
}
},
visitRule: function (ruleNode, visitArgs) {
visitArgs.visitDeeper = false;
},
visitMixinDefinition: function (mixinDefinitionNode, visitArgs) {
visitArgs.visitDeeper = false;
},
visitSelector: function (selectorNode, visitArgs) {
visitArgs.visitDeeper = false;
},
visitRuleset: function (rulesetNode, visitArgs) {
if (rulesetNode.root) {
return;
}
var matches, pathIndex, extendIndex, allExtends = this.allExtendsStack[this.allExtendsStack.length-1], selectorsToAdd = [], extendVisitor = this, selectorPath;
// look at each selector path in the ruleset, find any extend matches and then copy, find and replace
for(extendIndex = 0; extendIndex < allExtends.length; extendIndex++) {
for(pathIndex = 0; pathIndex < rulesetNode.paths.length; pathIndex++) {
selectorPath = rulesetNode.paths[pathIndex];
// extending extends happens initially, before the main pass
if (rulesetNode.extendOnEveryPath) { continue; }
var extendList = selectorPath[selectorPath.length-1].extendList;
if (extendList && extendList.length) { continue; }
matches = this.findMatch(allExtends[extendIndex], selectorPath);
if (matches.length) {
allExtends[extendIndex].selfSelectors.forEach(function(selfSelector) {
selectorsToAdd.push(extendVisitor.extendSelector(matches, selectorPath, selfSelector));
});
}
}
}
rulesetNode.paths = rulesetNode.paths.concat(selectorsToAdd);
},
findMatch: function (extend, haystackSelectorPath) {
//
// look through the haystack selector path to try and find the needle - extend.selector
// returns an array of selector matches that can then be replaced
//
var haystackSelectorIndex, hackstackSelector, hackstackElementIndex, haystackElement,
targetCombinator, i,
extendVisitor = this,
needleElements = extend.selector.elements,
potentialMatches = [], potentialMatch, matches = [];
// loop through the haystack elements
for(haystackSelectorIndex = 0; haystackSelectorIndex < haystackSelectorPath.length; haystackSelectorIndex++) {
hackstackSelector = haystackSelectorPath[haystackSelectorIndex];
for(hackstackElementIndex = 0; hackstackElementIndex < hackstackSelector.elements.length; hackstackElementIndex++) {
haystackElement = hackstackSelector.elements[hackstackElementIndex];
// if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
if (extend.allowBefore || (haystackSelectorIndex === 0 && hackstackElementIndex === 0)) {
potentialMatches.push({pathIndex: haystackSelectorIndex, index: hackstackElementIndex, matched: 0, initialCombinator: haystackElement.combinator});
}
for(i = 0; i < potentialMatches.length; i++) {
potentialMatch = potentialMatches[i];
// selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
// then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
// what the resulting combinator will be
targetCombinator = haystackElement.combinator.value;
if (targetCombinator === '' && hackstackElementIndex === 0) {
targetCombinator = ' ';
}
// if we don't match, null our match to indicate failure
if (!extendVisitor.isElementValuesEqual(needleElements[potentialMatch.matched].value, haystackElement.value) ||
(potentialMatch.matched > 0 && needleElements[potentialMatch.matched].combinator.value !== targetCombinator)) {
potentialMatch = null;
} else {
potentialMatch.matched++;
}
// if we are still valid and have finished, test whether we have elements after and whether these are allowed
if (potentialMatch) {
potentialMatch.finished = potentialMatch.matched === needleElements.length;
if (potentialMatch.finished &&
(!extend.allowAfter && (hackstackElementIndex+1 < hackstackSelector.elements.length || haystackSelectorIndex+1 < haystackSelectorPath.length))) {
potentialMatch = null;
}
}
// if null we remove, if not, we are still valid, so either push as a valid match or continue
if (potentialMatch) {
if (potentialMatch.finished) {
potentialMatch.length = needleElements.length;
potentialMatch.endPathIndex = haystackSelectorIndex;
potentialMatch.endPathElementIndex = hackstackElementIndex + 1; // index after end of match
potentialMatches.length = 0; // we don't allow matches to overlap, so start matching again
matches.push(potentialMatch);
}
} else {
potentialMatches.splice(i, 1);
i--;
}
}
}
}
return matches;
},
isElementValuesEqual: function(elementValue1, elementValue2) {
if (typeof elementValue1 === "string" || typeof elementValue2 === "string") {
return elementValue1 === elementValue2;
}
if (elementValue1 instanceof tree.Attribute) {
if (elementValue1.op !== elementValue2.op || elementValue1.key !== elementValue2.key) {
return false;
}
if (!elementValue1.value || !elementValue2.value) {
if (elementValue1.value || elementValue2.value) {
return false;
}
return true;
}
elementValue1 = elementValue1.value.value || elementValue1.value;
elementValue2 = elementValue2.value.value || elementValue2.value;
return elementValue1 === elementValue2;
}
elementValue1 = elementValue1.value;
elementValue2 = elementValue2.value;
if (elementValue1 instanceof tree.Selector) {
if (!(elementValue2 instanceof tree.Selector) || elementValue1.elements.length !== elementValue2.elements.length) {
return false;
}
for(var i = 0; i currentSelectorPathIndex && currentSelectorPathElementIndex > 0) {
path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
currentSelectorPathElementIndex = 0;
currentSelectorPathIndex++;
}
newElements = selector.elements
.slice(currentSelectorPathElementIndex, match.index)
.concat([firstElement])
.concat(replacementSelector.elements.slice(1));
if (currentSelectorPathIndex === match.pathIndex && matchIndex > 0) {
path[path.length - 1].elements =
path[path.length - 1].elements.concat(newElements);
} else {
path = path.concat(selectorPath.slice(currentSelectorPathIndex, match.pathIndex));
path.push(new tree.Selector(
newElements
));
}
currentSelectorPathIndex = match.endPathIndex;
currentSelectorPathElementIndex = match.endPathElementIndex;
if (currentSelectorPathElementIndex >= selectorPath[currentSelectorPathIndex].elements.length) {
currentSelectorPathElementIndex = 0;
currentSelectorPathIndex++;
}
}
if (currentSelectorPathIndex < selectorPath.length && currentSelectorPathElementIndex > 0) {
path[path.length - 1].elements = path[path.length - 1].elements.concat(selectorPath[currentSelectorPathIndex].elements.slice(currentSelectorPathElementIndex));
currentSelectorPathIndex++;
}
path = path.concat(selectorPath.slice(currentSelectorPathIndex, selectorPath.length));
return path;
},
visitRulesetOut: function (rulesetNode) {
},
visitMedia: function (mediaNode, visitArgs) {
var newAllExtends = mediaNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, mediaNode.allExtends));
this.allExtendsStack.push(newAllExtends);
},
visitMediaOut: function (mediaNode) {
this.allExtendsStack.length = this.allExtendsStack.length - 1;
},
visitDirective: function (directiveNode, visitArgs) {
var newAllExtends = directiveNode.allExtends.concat(this.allExtendsStack[this.allExtendsStack.length-1]);
newAllExtends = newAllExtends.concat(this.doExtendChaining(newAllExtends, directiveNode.allExtends));
this.allExtendsStack.push(newAllExtends);
},
visitDirectiveOut: function (directiveNode) {
this.allExtendsStack.length = this.allExtendsStack.length - 1;
}
};
})(require('./tree'));
(function (tree) {
tree.sourceMapOutput = function (options) {
this._css = [];
this._rootNode = options.rootNode;
this._writeSourceMap = options.writeSourceMap;
this._contentsMap = options.contentsMap;
this._contentsIgnoredCharsMap = options.contentsIgnoredCharsMap;
this._sourceMapFilename = options.sourceMapFilename;
this._outputFilename = options.outputFilename;
this._sourceMapURL = options.sourceMapURL;
if (options.sourceMapBasepath) {
this._sourceMapBasepath = options.sourceMapBasepath.replace(/\\/g, '/');
}
this._sourceMapRootpath = options.sourceMapRootpath;
this._outputSourceFiles = options.outputSourceFiles;
this._sourceMapGeneratorConstructor = options.sourceMapGenerator || require("source-map").SourceMapGenerator;
if (this._sourceMapRootpath && this._sourceMapRootpath.charAt(this._sourceMapRootpath.length-1) !== '/') {
this._sourceMapRootpath += '/';
}
this._lineNumber = 0;
this._column = 0;
};
tree.sourceMapOutput.prototype.normalizeFilename = function(filename) {
filename = filename.replace(/\\/g, '/');
if (this._sourceMapBasepath && filename.indexOf(this._sourceMapBasepath) === 0) {
filename = filename.substring(this._sourceMapBasepath.length);
if (filename.charAt(0) === '\\' || filename.charAt(0) === '/') {
filename = filename.substring(1);
}
}
return (this._sourceMapRootpath || "") + filename;
};
tree.sourceMapOutput.prototype.add = function(chunk, fileInfo, index, mapLines) {
//ignore adding empty strings
if (!chunk) {
return;
}
var lines,
sourceLines,
columns,
sourceColumns,
i;
if (fileInfo) {
var inputSource = this._contentsMap[fileInfo.filename];
// remove vars/banner added to the top of the file
if (this._contentsIgnoredCharsMap[fileInfo.filename]) {
// adjust the index
index -= this._contentsIgnoredCharsMap[fileInfo.filename];
if (index < 0) { index = 0; }
// adjust the source
inputSource = inputSource.slice(this._contentsIgnoredCharsMap[fileInfo.filename]);
}
inputSource = inputSource.substring(0, index);
sourceLines = inputSource.split("\n");
sourceColumns = sourceLines[sourceLines.length-1];
}
lines = chunk.split("\n");
columns = lines[lines.length-1];
if (fileInfo) {
if (!mapLines) {
this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + 1, column: this._column},
original: { line: sourceLines.length, column: sourceColumns.length},
source: this.normalizeFilename(fileInfo.filename)});
} else {
for(i = 0; i < lines.length; i++) {
this._sourceMapGenerator.addMapping({ generated: { line: this._lineNumber + i + 1, column: i === 0 ? this._column : 0},
original: { line: sourceLines.length + i, column: i === 0 ? sourceColumns.length : 0},
source: this.normalizeFilename(fileInfo.filename)});
}
}
}
if (lines.length === 1) {
this._column += columns.length;
} else {
this._lineNumber += lines.length - 1;
this._column = columns.length;
}
this._css.push(chunk);
};
tree.sourceMapOutput.prototype.isEmpty = function() {
return this._css.length === 0;
};
tree.sourceMapOutput.prototype.toCSS = function(env) {
this._sourceMapGenerator = new this._sourceMapGeneratorConstructor({ file: this._outputFilename, sourceRoot: null });
if (this._outputSourceFiles) {
for(var filename in this._contentsMap) {
var source = this._contentsMap[filename];
if (this._contentsIgnoredCharsMap[filename]) {
source = source.slice(this._contentsIgnoredCharsMap[filename]);
}
this._sourceMapGenerator.setSourceContent(this.normalizeFilename(filename), source);
}
}
this._rootNode.genCSS(env, this);
if (this._css.length > 0) {
var sourceMapURL,
sourceMapContent = JSON.stringify(this._sourceMapGenerator.toJSON());
if (this._sourceMapURL) {
sourceMapURL = this._sourceMapURL;
} else if (this._sourceMapFilename) {
sourceMapURL = this.normalizeFilename(this._sourceMapFilename);
}
if (this._writeSourceMap) {
this._writeSourceMap(sourceMapContent);
} else {
sourceMapURL = "data:application/json," + encodeURIComponent(sourceMapContent);
}
if (sourceMapURL) {
this._css.push("/*# sourceMappingURL=" + sourceMapURL + " */");
}
}
return this._css.join('');
};
})(require('./tree'));
//
// browser.js - client-side engine
//
/*global less, window, document, XMLHttpRequest, location */
var isFileProtocol = /^(file|chrome(-extension)?|resource|qrc|app):/.test(location.protocol);
less.env = less.env || (location.hostname == '127.0.0.1' ||
location.hostname == '0.0.0.0' ||
location.hostname == 'localhost' ||
(location.port &&
location.port.length > 0) ||
isFileProtocol ? 'development'
: 'production');
var logLevel = {
info: 2,
errors: 1,
none: 0
};
// The amount of logging in the javascript console.
// 2 - Information and errors
// 1 - Errors
// 0 - None
// Defaults to 2
less.logLevel = typeof(less.logLevel) != 'undefined' ? less.logLevel : logLevel.info;
// Load styles asynchronously (default: false)
//
// This is set to `false` by default, so that the body
// doesn't start loading before the stylesheets are parsed.
// Setting this to `true` can result in flickering.
//
less.async = less.async || false;
less.fileAsync = less.fileAsync || false;
// Interval between watch polls
less.poll = less.poll || (isFileProtocol ? 1000 : 1500);
//Setup user functions
if (less.functions) {
for(var func in less.functions) {
less.tree.functions[func] = less.functions[func];
}
}
var dumpLineNumbers = /!dumpLineNumbers:(comments|mediaquery|all)/.exec(location.hash);
if (dumpLineNumbers) {
less.dumpLineNumbers = dumpLineNumbers[1];
}
var typePattern = /^text\/(x-)?less$/;
var cache = null;
var fileCache = {};
function log(str, level) {
if (less.env == 'development' && typeof(console) !== 'undefined' && less.logLevel >= level) {
console.log('less: ' + str);
}
}
function extractId(href) {
return href.replace(/^[a-z-]+:\/+?[^\/]+/, '' ) // Remove protocol & domain
.replace(/^\//, '' ) // Remove root /
.replace(/\.[a-zA-Z]+$/, '' ) // Remove simple extension
.replace(/[^\.\w-]+/g, '-') // Replace illegal characters
.replace(/\./g, ':'); // Replace dots with colons(for valid id)
}
function errorConsole(e, rootHref) {
var template = '{line} {content}';
var filename = e.filename || rootHref;
var errors = [];
var content = (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
" in " + filename + " ";
var errorline = function (e, i, classname) {
if (e.extract[i] !== undefined) {
errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
.replace(/\{class\}/, classname)
.replace(/\{content\}/, e.extract[i]));
}
};
if (e.extract) {
errorline(e, 0, '');
errorline(e, 1, 'line');
errorline(e, 2, '');
content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':\n' +
errors.join('\n');
} else if (e.stack) {
content += e.stack;
}
log(content, logLevel.errors);
}
function createCSS(styles, sheet, lastModified) {
// Strip the query-string
var href = sheet.href || '';
// If there is no title set, use the filename, minus the extension
var id = 'less:' + (sheet.title || extractId(href));
// If this has already been inserted into the DOM, we may need to replace it
var oldCss = document.getElementById(id);
var keepOldCss = false;
// Create a new stylesheet node for insertion or (if necessary) replacement
var css = document.createElement('style');
css.setAttribute('type', 'text/css');
if (sheet.media) {
css.setAttribute('media', sheet.media);
}
css.id = id;
if (css.styleSheet) { // IE
try {
css.styleSheet.cssText = styles;
} catch (e) {
throw new(Error)("Couldn't reassign styleSheet.cssText.");
}
} else {
css.appendChild(document.createTextNode(styles));
// If new contents match contents of oldCss, don't replace oldCss
keepOldCss = (oldCss !== null && oldCss.childNodes.length > 0 && css.childNodes.length > 0 &&
oldCss.firstChild.nodeValue === css.firstChild.nodeValue);
}
var head = document.getElementsByTagName('head')[0];
// If there is no oldCss, just append; otherwise, only append if we need
// to replace oldCss with an updated stylesheet
if (oldCss === null || keepOldCss === false) {
var nextEl = sheet && sheet.nextSibling || null;
if (nextEl) {
nextEl.parentNode.insertBefore(css, nextEl);
} else {
head.appendChild(css);
}
}
if (oldCss && keepOldCss === false) {
oldCss.parentNode.removeChild(oldCss);
}
// Don't update the local store if the file wasn't modified
if (lastModified && cache) {
log('saving ' + href + ' to cache.', logLevel.info);
try {
cache.setItem(href, styles);
cache.setItem(href + ':timestamp', lastModified);
} catch(e) {
//TODO - could do with adding more robust error handling
log('failed to save', logLevel.errors);
}
}
}
function errorHTML(e, rootHref) {
var id = 'less-error-message:' + extractId(rootHref || "");
var template = '
{content}
';
var elem = document.createElement('div'), timer, content, errors = [];
var filename = e.filename || rootHref;
var filenameNoPath = filename.match(/([^\/]+(\?.*)?)$/)[1];
elem.id = id;
elem.className = "less-error-message";
content = '
' + (e.type || "Syntax") + "Error: " + (e.message || 'There is an error in your .less file') +
'
' + '
in ' + filenameNoPath + " ";
var errorline = function (e, i, classname) {
if (e.extract[i] !== undefined) {
errors.push(template.replace(/\{line\}/, (parseInt(e.line, 10) || 0) + (i - 1))
.replace(/\{class\}/, classname)
.replace(/\{content\}/, e.extract[i]));
}
};
if (e.extract) {
errorline(e, 0, '');
errorline(e, 1, 'line');
errorline(e, 2, '');
content += 'on line ' + e.line + ', column ' + (e.column + 1) + ':
' +
'
' + errors.join('') + '
';
} else if (e.stack) {
content += ' ' + e.stack.split('\n').slice(1).join(' ');
}
elem.innerHTML = content;
// CSS for error messages
createCSS([
'.less-error-message ul, .less-error-message li {',
'list-style-type: none;',
'margin-right: 15px;',
'padding: 4px 0;',
'margin: 0;',
'}',
'.less-error-message label {',
'font-size: 12px;',
'margin-right: 15px;',
'padding: 4px 0;',
'color: #cc7777;',
'}',
'.less-error-message pre {',
'color: #dd6666;',
'padding: 4px 0;',
'margin: 0;',
'display: inline-block;',
'}',
'.less-error-message pre.line {',
'color: #ff0000;',
'}',
'.less-error-message h3 {',
'font-size: 20px;',
'font-weight: bold;',
'padding: 15px 0 5px 0;',
'margin: 0;',
'}',
'.less-error-message a {',
'color: #10a',
'}',
'.less-error-message .error {',
'color: red;',
'font-weight: bold;',
'padding-bottom: 2px;',
'border-bottom: 1px dashed red;',
'}'
].join('\n'), { title: 'error-message' });
elem.style.cssText = [
"font-family: Arial, sans-serif",
"border: 1px solid #e00",
"background-color: #eee",
"border-radius: 5px",
"-webkit-border-radius: 5px",
"-moz-border-radius: 5px",
"color: #e00",
"padding: 15px",
"margin-bottom: 15px"
].join(';');
if (less.env == 'development') {
timer = setInterval(function () {
if (document.body) {
if (document.getElementById(id)) {
document.body.replaceChild(elem, document.getElementById(id));
} else {
document.body.insertBefore(elem, document.body.firstChild);
}
clearInterval(timer);
}
}, 10);
}
}
function error(e, rootHref) {
if (!less.errorReporting || less.errorReporting === "html") {
errorHTML(e, rootHref);
} else if (less.errorReporting === "console") {
errorConsole(e, rootHref);
} else if (typeof less.errorReporting === 'function') {
less.errorReporting("add", e, rootHref);
}
}
function removeErrorHTML(path) {
var node = document.getElementById('less-error-message:' + extractId(path));
if (node) {
node.parentNode.removeChild(node);
}
}
function removeErrorConsole(path) {
//no action
}
function removeError(path) {
if (!less.errorReporting || less.errorReporting === "html") {
removeErrorHTML(path);
} else if (less.errorReporting === "console") {
removeErrorConsole(path);
} else if (typeof less.errorReporting === 'function') {
less.errorReporting("remove", path);
}
}
function loadStyles(modifyVars) {
var styles = document.getElementsByTagName('style'),
style;
for (var i = 0; i < styles.length; i++) {
style = styles[i];
if (style.type.match(typePattern)) {
var env = new less.tree.parseEnv(less),
lessText = style.innerHTML || '';
env.filename = document.location.href.replace(/#.*$/, '');
if (modifyVars || less.globalVars) {
env.useFileCache = true;
}
/*jshint loopfunc:true */
// use closure to store current value of i
var callback = (function(style) {
return function (e, cssAST) {
if (e) {
return error(e, "inline");
}
var css = cssAST.toCSS(less);
style.type = 'text/css';
if (style.styleSheet) {
style.styleSheet.cssText = css;
} else {
style.innerHTML = css;
}
};
})(style);
new(less.Parser)(env).parse(lessText, callback, {globalVars: less.globalVars, modifyVars: modifyVars});
}
}
}
function extractUrlParts(url, baseUrl) {
// urlParts[1] = protocol&hostname || /
// urlParts[2] = / if path relative to host base
// urlParts[3] = directories
// urlParts[4] = filename
// urlParts[5] = parameters
var urlPartsRegex = /^((?:[a-z-]+:)?\/+?(?:[^\/\?#]*\/)|([\/\\]))?((?:[^\/\\\?#]*[\/\\])*)([^\/\\\?#]*)([#\?].*)?$/i,
urlParts = url.match(urlPartsRegex),
returner = {}, directories = [], i, baseUrlParts;
if (!urlParts) {
throw new Error("Could not parse sheet href - '"+url+"'");
}
// Stylesheets in IE don't always return the full path
if (!urlParts[1] || urlParts[2]) {
baseUrlParts = baseUrl.match(urlPartsRegex);
if (!baseUrlParts) {
throw new Error("Could not parse page url - '"+baseUrl+"'");
}
urlParts[1] = urlParts[1] || baseUrlParts[1] || "";
if (!urlParts[2]) {
urlParts[3] = baseUrlParts[3] + urlParts[3];
}
}
if (urlParts[3]) {
directories = urlParts[3].replace(/\\/g, "/").split("/");
// extract out . before .. so .. doesn't absorb a non-directory
for(i = 0; i < directories.length; i++) {
if (directories[i] === ".") {
directories.splice(i, 1);
i -= 1;
}
}
for(i = 0; i < directories.length; i++) {
if (directories[i] === ".." && i > 0) {
directories.splice(i-1, 2);
i -= 2;
}
}
}
returner.hostPart = urlParts[1];
returner.directories = directories;
returner.path = urlParts[1] + directories.join("/");
returner.fileUrl = returner.path + (urlParts[4] || "");
returner.url = returner.fileUrl + (urlParts[5] || "");
return returner;
}
function pathDiff(url, baseUrl) {
// diff between two paths to create a relative path
var urlParts = extractUrlParts(url),
baseUrlParts = extractUrlParts(baseUrl),
i, max, urlDirectories, baseUrlDirectories, diff = "";
if (urlParts.hostPart !== baseUrlParts.hostPart) {
return "";
}
max = Math.max(baseUrlParts.directories.length, urlParts.directories.length);
for(i = 0; i < max; i++) {
if (baseUrlParts.directories[i] !== urlParts.directories[i]) { break; }
}
baseUrlDirectories = baseUrlParts.directories.slice(i);
urlDirectories = urlParts.directories.slice(i);
for(i = 0; i < baseUrlDirectories.length-1; i++) {
diff += "../";
}
for(i = 0; i < urlDirectories.length-1; i++) {
diff += urlDirectories[i] + "/";
}
return diff;
}
function getXMLHttpRequest() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
} else {
try {
/*global ActiveXObject */
return new ActiveXObject("MSXML2.XMLHTTP.3.0");
} catch (e) {
log("browser doesn't support AJAX.", logLevel.errors);
return null;
}
}
}
function doXHR(url, type, callback, errback) {
var xhr = getXMLHttpRequest();
var async = isFileProtocol ? less.fileAsync : less.async;
if (typeof(xhr.overrideMimeType) === 'function') {
xhr.overrideMimeType('text/css');
}
log("XHR: Getting '" + url + "'", logLevel.info);
xhr.open('GET', url, async);
xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
xhr.send(null);
function handleResponse(xhr, callback, errback) {
if (xhr.status >= 200 && xhr.status < 300) {
callback(xhr.responseText,
xhr.getResponseHeader("Last-Modified"));
} else if (typeof(errback) === 'function') {
errback(xhr.status, url);
}
}
if (isFileProtocol && !less.fileAsync) {
if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
callback(xhr.responseText);
} else {
errback(xhr.status, url);
}
} else if (async) {
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
handleResponse(xhr, callback, errback);
}
};
} else {
handleResponse(xhr, callback, errback);
}
}
function loadFile(originalHref, currentFileInfo, callback, env, modifyVars) {
if (currentFileInfo && currentFileInfo.currentDirectory && !/^([a-z-]+:)?\//.test(originalHref)) {
originalHref = currentFileInfo.currentDirectory + originalHref;
}
// sheet may be set to the stylesheet for the initial load or a collection of properties including
// some env variables for imports
var hrefParts = extractUrlParts(originalHref, window.location.href);
var href = hrefParts.url;
var newFileInfo = {
currentDirectory: hrefParts.path,
filename: href
};
if (currentFileInfo) {
newFileInfo.entryPath = currentFileInfo.entryPath;
newFileInfo.rootpath = currentFileInfo.rootpath;
newFileInfo.rootFilename = currentFileInfo.rootFilename;
newFileInfo.relativeUrls = currentFileInfo.relativeUrls;
} else {
newFileInfo.entryPath = hrefParts.path;
newFileInfo.rootpath = less.rootpath || hrefParts.path;
newFileInfo.rootFilename = href;
newFileInfo.relativeUrls = env.relativeUrls;
}
if (newFileInfo.relativeUrls) {
if (env.rootpath) {
newFileInfo.rootpath = extractUrlParts(env.rootpath + pathDiff(hrefParts.path, newFileInfo.entryPath)).path;
} else {
newFileInfo.rootpath = hrefParts.path;
}
}
if (env.useFileCache && fileCache[href]) {
try {
var lessText = fileCache[href];
callback(null, lessText, href, newFileInfo, { lastModified: new Date() });
} catch (e) {
callback(e, null, href);
}
return;
}
doXHR(href, env.mime, function (data, lastModified) {
// per file cache
fileCache[href] = data;
// Use remote copy (re-parse)
try {
callback(null, data, href, newFileInfo, { lastModified: lastModified });
} catch (e) {
callback(e, null, href);
}
}, function (status, url) {
callback({ type: 'File', message: "'" + url + "' wasn't found (" + status + ")" }, null, href);
});
}
function loadStyleSheet(sheet, callback, reload, remaining, modifyVars) {
var env = new less.tree.parseEnv(less);
env.mime = sheet.type;
if (modifyVars || less.globalVars) {
env.useFileCache = true;
}
loadFile(sheet.href, null, function(e, data, path, newFileInfo, webInfo) {
if (webInfo) {
webInfo.remaining = remaining;
var css = cache && cache.getItem(path),
timestamp = cache && cache.getItem(path + ':timestamp');
if (!reload && timestamp && webInfo.lastModified &&
(new(Date)(webInfo.lastModified).valueOf() ===
new(Date)(timestamp).valueOf())) {
// Use local copy
createCSS(css, sheet);
webInfo.local = true;
callback(null, null, data, sheet, webInfo, path);
return;
}
}
//TODO add tests around how this behaves when reloading
removeError(path);
if (data) {
env.currentFileInfo = newFileInfo;
new(less.Parser)(env).parse(data, function (e, root) {
if (e) { return callback(e, null, null, sheet); }
try {
callback(e, root, data, sheet, webInfo, path);
} catch (e) {
callback(e, null, null, sheet);
}
}, {modifyVars: modifyVars, globalVars: less.globalVars});
} else {
callback(e, null, null, sheet, webInfo, path);
}
}, env, modifyVars);
}
function loadStyleSheets(callback, reload, modifyVars) {
for (var i = 0; i < less.sheets.length; i++) {
loadStyleSheet(less.sheets[i], callback, reload, less.sheets.length - (i + 1), modifyVars);
}
}
function initRunningMode(){
if (less.env === 'development') {
less.optimization = 0;
less.watchTimer = setInterval(function () {
if (less.watchMode) {
loadStyleSheets(function (e, root, _, sheet, env) {
if (e) {
error(e, sheet.href);
} else if (root) {
createCSS(root.toCSS(less), sheet, env.lastModified);
}
});
}
}, less.poll);
} else {
less.optimization = 3;
}
}
//
// Watch mode
//
less.watch = function () {
if (!less.watchMode ){
less.env = 'development';
initRunningMode();
}
this.watchMode = true;
return true;
};
less.unwatch = function () {clearInterval(less.watchTimer); this.watchMode = false; return false; };
if (/!watch/.test(location.hash)) {
less.watch();
}
if (less.env != 'development') {
try {
cache = (typeof(window.localStorage) === 'undefined') ? null : window.localStorage;
} catch (_) {}
}
//
// Get all tags with the 'rel' attribute set to "stylesheet/less"
//
var links = document.getElementsByTagName('link');
less.sheets = [];
for (var i = 0; i < links.length; i++) {
if (links[i].rel === 'stylesheet/less' || (links[i].rel.match(/stylesheet/) &&
(links[i].type.match(typePattern)))) {
less.sheets.push(links[i]);
}
}
//
// With this function, it's possible to alter variables and re-render
// CSS without reloading less-files
//
less.modifyVars = function(record) {
less.refresh(false, record);
};
less.refresh = function (reload, modifyVars) {
var startTime, endTime;
startTime = endTime = new Date();
loadStyleSheets(function (e, root, _, sheet, env) {
if (e) {
return error(e, sheet.href);
}
if (env.local) {
log("loading " + sheet.href + " from cache.", logLevel.info);
} else {
log("parsed " + sheet.href + " successfully.", logLevel.info);
createCSS(root.toCSS(less), sheet, env.lastModified);
}
log("css for " + sheet.href + " generated in " + (new Date() - endTime) + 'ms', logLevel.info);
if (env.remaining === 0) {
log("css generated in " + (new Date() - startTime) + 'ms', logLevel.info);
}
endTime = new Date();
}, reload, modifyVars);
loadStyles(modifyVars);
};
less.refreshStyles = loadStyles;
less.Parser.fileLoader = loadFile;
less.refresh(less.env === 'development');
// amd.js
//
// Define Less as an AMD module.
if (typeof define === "function" && define.amd) {
define(function () { return less; } );
}
})(window);
;!function(exports, undefined) {
var isArray = Array.isArray ? Array.isArray : function _isArray(obj) {
return Object.prototype.toString.call(obj) === "[object Array]";
};
var defaultMaxListeners = 10;
function init() {
this._events = {};
if (this._conf) {
configure.call(this, this._conf);
}
}
function configure(conf) {
if (conf) {
this._conf = conf;
conf.delimiter && (this.delimiter = conf.delimiter);
conf.maxListeners && (this._events.maxListeners = conf.maxListeners);
conf.wildcard && (this.wildcard = conf.wildcard);
conf.newListener && (this.newListener = conf.newListener);
if (this.wildcard) {
this.listenerTree = {};
}
}
}
function EventEmitter(conf) {
this._events = {};
this.newListener = false;
configure.call(this, conf);
}
//
// Attention, function return type now is array, always !
// It has zero elements if no any matches found and one or more
// elements (leafs) if there are matches
//
function searchListenerTree(handlers, type, tree, i) {
if (!tree) {
return [];
}
var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached,
typeLength = type.length, currentType = type[i], nextType = type[i+1];
if (i === typeLength && tree._listeners) {
//
// If at the end of the event(s) list and the tree has listeners
// invoke those listeners.
//
if (typeof tree._listeners === 'function') {
handlers && handlers.push(tree._listeners);
return [tree];
} else {
for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) {
handlers && handlers.push(tree._listeners[leaf]);
}
return [tree];
}
}
if ((currentType === '*' || currentType === '**') || tree[currentType]) {
//
// If the event emitted is '*' at this part
// or there is a concrete match at this patch
//
if (currentType === '*') {
for (branch in tree) {
if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1));
}
}
return listeners;
} else if(currentType === '**') {
endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*'));
if(endReached && tree._listeners) {
// The next element has a _listeners, add it to the handlers.
listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength));
}
for (branch in tree) {
if (branch !== '_listeners' && tree.hasOwnProperty(branch)) {
if(branch === '*' || branch === '**') {
if(tree[branch]._listeners && !endReached) {
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength));
}
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
} else if(branch === nextType) {
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2));
} else {
// No match on this one, shift into the tree but not in the type array.
listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i));
}
}
}
return listeners;
}
listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1));
}
xTree = tree['*'];
if (xTree) {
//
// If the listener tree will allow any match for this part,
// then recursively explore all branches of the tree
//
searchListenerTree(handlers, type, xTree, i+1);
}
xxTree = tree['**'];
if(xxTree) {
if(i < typeLength) {
if(xxTree._listeners) {
// If we have a listener on a '**', it will catch all, so add its handler.
searchListenerTree(handlers, type, xxTree, typeLength);
}
// Build arrays of matching next branches and others.
for(branch in xxTree) {
if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) {
if(branch === nextType) {
// We know the next element will match, so jump twice.
searchListenerTree(handlers, type, xxTree[branch], i+2);
} else if(branch === currentType) {
// Current node matches, move into the tree.
searchListenerTree(handlers, type, xxTree[branch], i+1);
} else {
isolatedBranch = {};
isolatedBranch[branch] = xxTree[branch];
searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1);
}
}
}
} else if(xxTree._listeners) {
// We have reached the end and still on a '**'
searchListenerTree(handlers, type, xxTree, typeLength);
} else if(xxTree['*'] && xxTree['*']._listeners) {
searchListenerTree(handlers, type, xxTree['*'], typeLength);
}
}
return listeners;
}
function growListenerTree(type, listener) {
type = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
//
// Looks for two consecutive '**', if so, don't add the event at all.
//
for(var i = 0, len = type.length; i+1 < len; i++) {
if(type[i] === '**' && type[i+1] === '**') {
return;
}
}
var tree = this.listenerTree;
var name = type.shift();
while (name) {
if (!tree[name]) {
tree[name] = {};
}
tree = tree[name];
if (type.length === 0) {
if (!tree._listeners) {
tree._listeners = listener;
}
else if(typeof tree._listeners === 'function') {
tree._listeners = [tree._listeners, listener];
}
else if (isArray(tree._listeners)) {
tree._listeners.push(listener);
if (!tree._listeners.warned) {
var m = defaultMaxListeners;
if (typeof this._events.maxListeners !== 'undefined') {
m = this._events.maxListeners;
}
if (m > 0 && tree._listeners.length > m) {
tree._listeners.warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.',
tree._listeners.length);
console.trace();
}
}
}
return true;
}
name = type.shift();
}
return true;
}
// By default EventEmitters will print a warning if more than
// 10 listeners are added to it. This is a useful default which
// helps finding memory leaks.
//
// Obviously not all Emitters should be limited to 10. This function allows
// that to be increased. Set to zero for unlimited.
EventEmitter.prototype.delimiter = '.';
EventEmitter.prototype.setMaxListeners = function(n) {
this._events || init.call(this);
this._events.maxListeners = n;
if (!this._conf) this._conf = {};
this._conf.maxListeners = n;
};
EventEmitter.prototype.event = '';
EventEmitter.prototype.once = function(event, fn) {
this.many(event, 1, fn);
return this;
};
EventEmitter.prototype.many = function(event, ttl, fn) {
var self = this;
if (typeof fn !== 'function') {
throw new Error('many only accepts instances of Function');
}
function listener() {
if (--ttl === 0) {
self.off(event, listener);
}
fn.apply(this, arguments);
}
listener._origin = fn;
this.on(event, listener);
return self;
};
EventEmitter.prototype.emit = function() {
this._events || init.call(this);
var type = arguments[0];
if (type === 'newListener' && !this.newListener) {
if (!this._events.newListener) { return false; }
}
// Loop through the *_all* functions and invoke them.
if (this._all) {
var l = arguments.length;
var args = new Array(l - 1);
for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
for (i = 0, l = this._all.length; i < l; i++) {
this.event = type;
this._all[i].apply(this, args);
}
}
// If there is no 'error' event listener then throw.
if (type === 'error') {
if (!this._all &&
!this._events.error &&
!(this.wildcard && this.listenerTree.error)) {
if (arguments[1] instanceof Error) {
throw arguments[1]; // Unhandled 'error' event
} else {
throw new Error("Uncaught, unspecified 'error' event.");
}
return false;
}
}
var handler;
if(this.wildcard) {
handler = [];
var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
searchListenerTree.call(this, handler, ns, this.listenerTree, 0);
}
else {
handler = this._events[type];
}
if (typeof handler === 'function') {
this.event = type;
if (arguments.length === 1) {
handler.call(this);
}
else if (arguments.length > 1)
switch (arguments.length) {
case 2:
handler.call(this, arguments[1]);
break;
case 3:
handler.call(this, arguments[1], arguments[2]);
break;
// slower
default:
var l = arguments.length;
var args = new Array(l - 1);
for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
handler.apply(this, args);
}
return true;
}
else if (handler) {
var l = arguments.length;
var args = new Array(l - 1);
for (var i = 1; i < l; i++) args[i - 1] = arguments[i];
var listeners = handler.slice();
for (var i = 0, l = listeners.length; i < l; i++) {
this.event = type;
listeners[i].apply(this, args);
}
return (listeners.length > 0) || this._all;
}
else {
return this._all;
}
};
EventEmitter.prototype.on = function(type, listener) {
if (typeof type === 'function') {
this.onAny(type);
return this;
}
if (typeof listener !== 'function') {
throw new Error('on only accepts instances of Function');
}
this._events || init.call(this);
// To avoid recursion in the case that type == "newListeners"! Before
// adding it to the listeners, first emit "newListeners".
this.emit('newListener', type, listener);
if(this.wildcard) {
growListenerTree.call(this, type, listener);
return this;
}
if (!this._events[type]) {
// Optimize the case of one listener. Don't need the extra array object.
this._events[type] = listener;
}
else if(typeof this._events[type] === 'function') {
// Adding the second element, need to change to array.
this._events[type] = [this._events[type], listener];
}
else if (isArray(this._events[type])) {
// If we've already got an array, just append.
this._events[type].push(listener);
// Check for listener leak
if (!this._events[type].warned) {
var m = defaultMaxListeners;
if (typeof this._events.maxListeners !== 'undefined') {
m = this._events.maxListeners;
}
if (m > 0 && this._events[type].length > m) {
this._events[type].warned = true;
console.error('(node) warning: possible EventEmitter memory ' +
'leak detected. %d listeners added. ' +
'Use emitter.setMaxListeners() to increase limit.',
this._events[type].length);
console.trace();
}
}
}
return this;
};
EventEmitter.prototype.onAny = function(fn) {
if(!this._all) {
this._all = [];
}
if (typeof fn !== 'function') {
throw new Error('onAny only accepts instances of Function');
}
// Add the function to the event listener collection.
this._all.push(fn);
return this;
};
EventEmitter.prototype.addListener = EventEmitter.prototype.on;
EventEmitter.prototype.off = function(type, listener) {
if (typeof listener !== 'function') {
throw new Error('removeListener only takes instances of Function');
}
var handlers,leafs=[];
if(this.wildcard) {
var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
}
else {
// does not use listeners(), so no side effect of creating _events[type]
if (!this._events[type]) return this;
handlers = this._events[type];
leafs.push({_listeners:handlers});
}
for (var iLeaf=0; iLeaf 0) {
fns = this._all;
for(i = 0, l = fns.length; i < l; i++) {
if(fn === fns[i]) {
fns.splice(i, 1);
return this;
}
}
} else {
this._all = [];
}
return this;
};
EventEmitter.prototype.removeListener = EventEmitter.prototype.off;
EventEmitter.prototype.removeAllListeners = function(type) {
if (arguments.length === 0) {
!this._events || init.call(this);
return this;
}
if(this.wildcard) {
var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice();
var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0);
for (var iLeaf=0; iLeaf+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g;
// Internal: Get indexes for selector.
//
// selector - String CSS selector
//
// Returns Array of {index, key}.
function parseSelectorIndexes(allIndexes, selector) {
allIndexes = allIndexes.slice(0).concat(allIndexes['default']);
var allIndexesLen = allIndexes.length,
i, j, m, dup, rest = selector,
key, index, indexes = [];
do {
chunker.exec('');
if (m = chunker.exec(rest)) {
rest = m[3];
if (m[2] || !rest) {
for (i = 0; i < allIndexesLen; i++) {
index = allIndexes[i];
if (key = index.selector(m[1])) {
j = indexes.length;
dup = false;
while (j--) {
if (indexes[j].index === index && indexes[j].key === key) {
dup = true;
break;
}
}
if (!dup) {
indexes.push({index: index, key: key});
}
break;
}
}
}
}
} while (m);
return indexes;
}
// Internal: Find first item in Array that is a prototype of `proto`.
//
// ary - Array of objects
// proto - Prototype of expected item in `ary`
//
// Returns object from `ary` if found. Otherwise returns undefined.
function findByPrototype(ary, proto) {
var i, len, item;
for (i = 0, len = ary.length; i < len; i++) {
item = ary[i];
if (proto.isPrototypeOf(item)) {
return item;
}
}
}
// Public: Log when added selector falls under the default index.
//
// This API should not be considered stable. May change between
// minor versions.
//
// obj - {selector, data} Object
//
// SelectorSet.prototype.logDefaultIndexUsed = function(obj) {
// console.warn(obj.selector, "could not be indexed");
// };
//
// Returns nothing.
SelectorSet.prototype.logDefaultIndexUsed = function() {};
// Public: Add selector to set.
//
// selector - String CSS selector
// data - Optional data Object (default: undefined)
//
// Returns nothing.
SelectorSet.prototype.add = function(selector, data) {
var obj, i, indexProto, key, index, objs,
selectorIndexes, selectorIndex,
indexes = this.activeIndexes,
selectors = this.selectors;
if (typeof selector !== 'string') {
return;
}
obj = {
id: this.uid++,
selector: selector,
data: data
};
selectorIndexes = parseSelectorIndexes(this.indexes, selector);
for (i = 0; i < selectorIndexes.length; i++) {
selectorIndex = selectorIndexes[i];
key = selectorIndex.key;
indexProto = selectorIndex.index;
index = findByPrototype(indexes, indexProto);
if (!index) {
index = Object.create(indexProto);
index.map = new Map();
indexes.push(index);
}
if (indexProto === this.indexes['default']) {
this.logDefaultIndexUsed(obj);
}
objs = index.map.get(key);
if (!objs) {
objs = [];
index.map.set(key, objs);
}
objs.push(obj);
}
this.size++;
selectors.push(selector);
};
// Public: Remove selector from set.
//
// selector - String CSS selector
// data - Optional data Object (default: undefined)
//
// Returns nothing.
SelectorSet.prototype.remove = function(selector, data) {
if (typeof selector !== 'string') {
return;
}
var selectorIndexes, selectorIndex, i, j, k, selIndex, objs, obj;
var indexes = this.activeIndexes;
var removedIds = {};
var removeAll = arguments.length === 1;
selectorIndexes = parseSelectorIndexes(this.indexes, selector);
for (i = 0; i < selectorIndexes.length; i++) {
selectorIndex = selectorIndexes[i];
j = indexes.length;
while (j--) {
selIndex = indexes[j];
if (selectorIndex.index.isPrototypeOf(selIndex)) {
objs = selIndex.map.get(selectorIndex.key);
if (objs) {
k = objs.length;
while (k--) {
obj = objs[k];
if (obj.selector === selector && (removeAll || obj.data === data)) {
objs.splice(k, 1);
removedIds[obj.id] = true;
}
}
}
break;
}
}
}
this.size -= Object.keys(removedIds).length;
};
// Sort by id property handler.
//
// a - Selector obj.
// b - Selector obj.
//
// Returns Number.
function sortById(a, b) {
return a.id - b.id;
}
// Public: Find all matching decendants of the context element.
//
// context - An Element
//
// Returns Array of {selector, data, elements} matches.
SelectorSet.prototype.queryAll = function(context) {
if (!this.selectors.length) {
return [];
}
var matches = {}, results = [];
var els = this.querySelectorAll(this.selectors.join(', '), context);
var i, j, len, len2, el, m, match, obj;
for (i = 0, len = els.length; i < len; i++) {
el = els[i];
m = this.matches(el);
for (j = 0, len2 = m.length; j < len2; j++) {
obj = m[j];
if (!matches[obj.id]) {
match = {
id: obj.id,
selector: obj.selector,
data: obj.data,
elements: []
};
matches[obj.id] = match;
results.push(match);
} else {
match = matches[obj.id];
}
match.elements.push(el);
}
}
return results.sort(sortById);
};
// Public: Match element against all selectors in set.
//
// el - An Element
//
// Returns Array of {selector, data} matches.
SelectorSet.prototype.matches = function(el) {
if (!el) {
return [];
}
var i, j, k, len, len2, len3, index, keys, objs, obj, id;
var indexes = this.activeIndexes, matchedIds = {}, matches = [];
for (i = 0, len = indexes.length; i < len; i++) {
index = indexes[i];
keys = index.element(el);
if (keys) {
for (j = 0, len2 = keys.length; j < len2; j++) {
if (objs = index.map.get(keys[j])) {
for (k = 0, len3 = objs.length; k < len3; k++) {
obj = objs[k];
id = obj.id;
if (!matchedIds[id] && this.matchesSelector(el, obj.selector)) {
matchedIds[id] = true;
matches.push(obj);
}
}
}
}
}
}
return matches.sort(sortById);
};
// Public: Expose SelectorSet as a global on window.
window.SelectorSet = SelectorSet;
})(window);
(function() {
var MODULES;
MODULES = {
underscore: this._,
sizzle: this.Sizzle,
less: this.less,
eventemitter2: this.EventEmitter2,
'selector-set': this.SelectorSet
};
this._ = this.__polyfills_originalGlobals['underscore'];
this.less = this.__polyfills_originalGlobals['less'];
this.EventEmitter2 = this.__polyfills_originalGlobals['eventemitter2'];
this.define = function(moduleName, deps, callback) {
var args, depName, first, second, val;
args = (function() {
var _i, _len, _ref, _results;
_results = [];
for (_i = 0, _len = deps.length; _i < _len; _i++) {
depName = deps[_i];
_ref = depName.split('!'), first = _ref[0], second = _ref[1];
depName = second || first;
if (!MODULES[depName]) {
throw new Error('Files are not concatenated based on their dependencies');
}
_results.push(MODULES[depName]);
}
return _results;
})();
val = callback.apply(this, args);
MODULES[moduleName] = val;
return val;
};
}).call(this);
(function() {
var __slice = [].slice;
define('polyfill-path/selector-tree', [], function() {
var CHUNKIFY, SelectorTree, chunker;
chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g;
CHUNKIFY = function(selector) {
var chunks, m, rest;
rest = selector;
chunks = [];
while (true) {
chunker.exec('');
if (m = chunker.exec(rest)) {
rest = m[3];
chunks.push(m[1]);
if (m[2] || !rest) {
break;
}
}
if (!m) {
break;
}
}
return chunks;
};
return SelectorTree = (function() {
function SelectorTree(children, data) {
this.children = children != null ? children : {};
this.data = data != null ? data : [];
}
SelectorTree.prototype._add = function(chunks, datum) {
var first, rest;
if (chunks.length === 0) {
return this.data.push(datum);
} else {
first = chunks[0], rest = 2 <= chunks.length ? __slice.call(chunks, 1) : [];
if (!this.children[first]) {
this.children[first] = new SelectorTree();
}
return this.children[first]._add(rest, datum);
}
};
SelectorTree.prototype.clear = function() {
this.children = {};
return this.data = [];
};
SelectorTree.prototype.add = function(selector, datum) {
var sel, selectors, _i, _len, _results;
selectors = selector.split(',');
_results = [];
for (_i = 0, _len = selectors.length; _i < _len; _i++) {
sel = selectors[_i];
_results.push(this._add(CHUNKIFY(sel), datum));
}
return _results;
};
SelectorTree.prototype.findMatches = function(rootEl, acc, selectorFirst) {
var els, selector, selectorSnippet, tree, _ref;
if (acc == null) {
acc = {};
}
if (selectorFirst == null) {
selectorFirst = '';
}
_ref = this.children;
for (selectorSnippet in _ref) {
tree = _ref[selectorSnippet];
selector = "" + selectorFirst + " " + selectorSnippet;
if (tree.data.length > 0) {
console.log("KLASDJFKLAJF Querying " + selector);
els = rootEl.querySelectorAll(selector);
acc[selector] = {
els: els,
data: tree.data
};
} else {
tree.findMatches(rootEl, acc, selector);
}
}
return acc;
};
SelectorTree.prototype.getSelectors = function(acc, selectorFirst) {
var selector, selectorSnippet, tree, _ref;
if (acc == null) {
acc = {};
}
if (selectorFirst == null) {
selectorFirst = '';
}
_ref = this.children;
for (selectorSnippet in _ref) {
tree = _ref[selectorSnippet];
selector = "" + selectorFirst + " " + selectorSnippet;
if (tree.data.length > 0) {
acc[selector] = tree.data;
} else {
tree.getSelector(acc, selector);
}
}
return acc;
};
SelectorTree.prototype._get = function(chunks) {
var child, first;
if (chunks.length) {
first = chunks.shift();
child = this.children[first];
if (child) {
return child._get(chunks);
} else {
return [];
}
} else {
return this.data;
}
};
SelectorTree.prototype.get = function(selector) {
var chunks;
chunks = CHUNKIFY(selector);
return this._get(chunks);
};
return SelectorTree;
})();
});
}).call(this);
(function() {
var __slice = [].slice,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
define('polyfill-path/fixed-point-runner', ['underscore', 'less', 'eventemitter2', 'selector-set'], function(_, less, EventEmitter, SelectorSet) {
var CSS_FUNCTION_WRAPPER, CSS_SELECTIVITY_COMPARATOR, FixedPointRunner, IMPORTANT_COMPARATOR, IMPORTANT_FIXER, SPECIFICITY_SORT;
CSS_SELECTIVITY_COMPARATOR = function(cls1, cls2) {
var compare, elements1, elements2, isClassOrAttrib, isElementOrPseudo, isIdAttrib;
elements1 = cls1.elements;
elements2 = cls2.elements;
if (!(elements1 || elements2)) {
console.error('BUG: Selectivity Comparator has null elements');
}
compare = function(iterator, els1, els2) {
var x1, x2;
x1 = _.reduce(elements1, iterator, 0);
x2 = _.reduce(elements2, iterator, 0);
if (x1 < x2) {
return -1;
}
if (x1 > x2) {
return 1;
}
return 0;
};
isIdAttrib = function(n, el) {
var _ref, _ref1;
return (_ref = '#' === ((_ref1 = el.value) != null ? _ref1[0] : void 0)) != null ? _ref : n + {
1: n
};
};
isClassOrAttrib = function(n, el) {
if ('.' === el.value[0] || '[' === el.value[0]) {
return n + 1;
}
return n;
};
isElementOrPseudo = function(n, el) {
if ((el.value instanceof less.tree.Attribute) || ':' === el.value[0] || /^[a-zA-Z]/.test(el.value)) {
return n + 1;
}
return n;
};
return compare(isIdAttrib) || compare(isClassOrAttrib) || compare(isElementOrPseudo);
};
SPECIFICITY_SORT = function(autogenClasses) {
var autogenClass, foundDisplayRule, i, newRules, rule, _i, _j, _len, _len1, _ref;
newRules = [];
autogenClasses.sort(CSS_SELECTIVITY_COMPARATOR);
for (_i = 0, _len = autogenClasses.length; _i < _len; _i++) {
autogenClass = autogenClasses[_i];
_ref = autogenClass.rules;
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
rule = _ref[_j];
newRules.push(rule);
}
}
foundDisplayRule = false;
newRules.reverse();
i = 0;
while (i < newRules.length) {
if ('display' === newRules[i].name) {
if (foundDisplayRule) {
newRules.splice(i, 1);
continue;
} else {
foundDisplayRule = true;
}
}
i += 1;
}
return newRules;
};
IMPORTANT_FIXER = function(autogenRules) {
var r, v, _i, _len, _ref, _results;
_results = [];
for (_i = 0, _len = autogenRules.length; _i < _len; _i++) {
r = autogenRules[_i];
if (!r.important) {
v = (_ref = r.value.value) != null ? _ref[0] : void 0;
if (v instanceof less.tree.Anonymous && /\!important$/.test(v.value)) {
r.important = ' !important';
_results.push(v.value = v.value.replace(/\s+\!important$/, ''));
} else {
_results.push(void 0);
}
} else {
_results.push(void 0);
}
}
return _results;
};
IMPORTANT_COMPARATOR = function(a, b) {
if (a.important && b.important) {
return 0;
}
if (a.important) {
return -1;
}
if (b.important) {
return 1;
}
return 0;
};
CSS_FUNCTION_WRAPPER = function(funcName, func) {
return function() {
var ret;
ret = func.apply(this, [this.env].concat(__slice.call(arguments)));
if (ret == null) {
return new less.tree.Call(funcName, _.toArray(arguments));
} else if (ret.toCSS) {
return ret;
} else if (ret instanceof Array) {
return new less.tree.ArrayTreeNode(ret);
} else if (_.isString(ret)) {
return new less.tree.Quoted("'" + ret + "'", ret);
} else if (_.isNumber(ret)) {
return new less.tree.Dimension(ret);
} else {
return new less.tree.Anonymous(ret);
}
};
};
return FixedPointRunner = (function(_super) {
__extends(FixedPointRunner, _super);
function FixedPointRunner(rootNode, plugins, set, removeAutogenClasses) {
var func, funcName, plugin, ruleFunc, ruleName, _i, _len, _ref, _ref1, _ref2;
this.rootNode = rootNode;
this.plugins = plugins;
this.set = set;
this.removeAutogenClasses = removeAutogenClasses;
FixedPointRunner.__super__.constructor.call(this, {
wildcard: true
});
this.squirreledEnv = {};
this.functions = {};
this.lookupClasses = {};
this.lookupCounter = 0;
this.rules = [];
_ref = this.plugins;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
plugin = _ref[_i];
_ref1 = plugin.functions;
for (funcName in _ref1) {
func = _ref1[funcName];
this.functions[funcName] = func.bind(plugin);
}
_ref2 = plugin.rules;
for (ruleName in _ref2) {
ruleFunc = _ref2[ruleName];
this.rules.push({
name: ruleName,
func: ruleFunc.bind(plugin)
});
}
}
}
FixedPointRunner.prototype.lookupAutogenClasses = function(domnode) {
var classes, cls, foundClasses, _i, _len;
if (domnode.className) {
if ('string' === typeof domnode.className) {
classes = domnode.className;
} else {
classes = domnode.className.baseVal;
}
classes = classes.split(' ');
foundClasses = [];
for (_i = 0, _len = classes.length; _i < _len; _i++) {
cls = classes[_i];
if (/^js-polyfill-autoclass-/.test(cls)) {
foundClasses = foundClasses.concat(this.set.get("." + cls));
}
}
return foundClasses;
} else {
return [];
}
};
FixedPointRunner.prototype.tick = function(interestingNodes) {
var domnode, env, _i, _len,
_this = this;
this.emit('tick.start', interestingNodes.length);
this.somethingChanged = 0;
env = new less.tree.evalEnv();
env.state = {};
env.helpers = {
interestingByHref: function(href) {
var id;
if ('#' !== href[0]) {
console.error('ERROR: href must start with a # character');
}
id = href.substring(1);
if (!_this.squirreledEnv[id]) {
console.error('BUG: id was not marked and squirreled before being looked up');
}
return _this.squirreledEnv[id];
},
markInterestingByHref: function(href) {
var id, target, wasAlreadyMarked;
if ('#' !== href[0]) {
console.error('ERROR: href must start with a # character');
}
id = href.substring(1);
wasAlreadyMarked = !!_this.squirreledEnv[id];
if (!wasAlreadyMarked) {
target = document.getElementById(id);
if (target) {
_this.somethingChanged += 1;
target.classList.add('js-polyfill-interesting');
target.classList.add('js-polyfill-target');
} else {
console.warn("ERROR: Invalid target id: #" + id);
}
}
return !wasAlreadyMarked;
},
didSomthingNonIdempotent: function(msg) {
return _this.somethingChanged += 1;
}
};
for (_i = 0, _len = interestingNodes.length; _i < _len; _i++) {
domnode = interestingNodes[_i];
this.evalNode(env, domnode);
}
this.emit('tick.end', this.somethingChanged);
return this.somethingChanged;
};
FixedPointRunner.prototype.pullRulesFromCache = function(domnode) {
var autogenClasses, autogenRule, autogenRules, filteredRules, id, name, _i, _len;
if (domnode.jsPolyfillId) {
filteredRules = this.lookupClasses[domnode.jsPolyfillId];
if (filteredRules) {
return filteredRules;
}
}
id = this.lookupCounter += 1;
autogenClasses = this.lookupAutogenClasses(domnode);
autogenRules = SPECIFICITY_SORT(autogenClasses);
IMPORTANT_FIXER(autogenRules);
autogenRules.sort(IMPORTANT_COMPARATOR);
filteredRules = {};
for (_i = 0, _len = autogenRules.length; _i < _len; _i++) {
autogenRule = autogenRules[_i];
name = autogenRule.name;
if (name instanceof Array) {
name = name.join('');
}
if (filteredRules[name] == null) {
filteredRules[name] = [];
}
filteredRules[name].push(autogenRule);
}
this.lookupClasses[id] = filteredRules;
domnode.jsPolyfillId = id;
return filteredRules;
};
FixedPointRunner.prototype.evalNode = function(env, domnode) {
var autogenRule, autogenRules, beforeSomethingChanged, filteredRules, i, pluginRule, rule, ruleName, ruleValNode, rules, somethingNotCompleted, targetEnv, understood, understoodRules, unused, _i, _j, _k, _len, _len1, _len2, _ref;
this.emit('tick.node', domnode);
somethingNotCompleted = false;
if (domnode.compareDocumentPosition(document.body) & Node.DOCUMENT_POSITION_DISCONNECTED) {
return;
}
env.helpers.contextNode = domnode;
autogenRules = this.pullRulesFromCache(domnode);
_ref = this.rules;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
pluginRule = _ref[_i];
if ('*' === pluginRule.name) {
filteredRules = [];
for (unused in autogenRules) {
rules = autogenRules[unused];
for (_j = 0, _len1 = rules.length; _j < _len1; _j++) {
rule = rules[_j];
filteredRules.push(rule);
}
}
} else {
filteredRules = autogenRules[pluginRule.name];
}
understoodRules = {};
if (filteredRules) {
for (i = _k = 0, _len2 = filteredRules.length; _k < _len2; i = ++_k) {
autogenRule = filteredRules[i];
ruleName = autogenRule.name;
if (ruleName instanceof Array) {
ruleName = ruleName.join('');
}
ruleValNode = autogenRule.value;
if (!ruleName) {
continue;
}
if (ruleName in understoodRules) {
continue;
}
if ('completed' === domnode.getAttribute("data-js-polyfill-rule-" + ruleName)) {
continue;
}
beforeSomethingChanged = this.somethingChanged;
if ('*' === pluginRule.name) {
understood = pluginRule.func(env, ruleName, ruleValNode);
} else {
understood = pluginRule.func(env, ruleValNode);
}
somethingNotCompleted = true;
if (understood) {
understoodRules[ruleName] = true;
domnode.setAttribute("data-js-polyfill-rule-" + ruleName, 'evaluated');
if (understood === 'RULE_COMPLETED') {
this.somethingChanged += 1;
domnode.setAttribute("data-js-polyfill-rule-" + ruleName, 'completed');
} else if (understood === 'NODE_REMOVED') {
if (domnode.jsPolyfillId) {
delete this.lookupClasses[domnode.jsPolyfillId];
}
return;
}
break;
}
if (beforeSomethingChanged !== this.somethingChanged) {
break;
}
}
}
}
if (domnode.classList.contains('js-polyfill-target')) {
targetEnv = {
helpers: _.clone(env.helpers),
state: JSON.parse(JSON.stringify(_.omit(env.state, 'buckets')))
};
targetEnv.helpers.contextNode = domnode;
return this.squirreledEnv[domnode.id] = targetEnv;
} else if (!somethingNotCompleted) {
domnode.classList.remove('js-polyfill-interesting');
if (domnode.jsPolyfillId) {
return delete this.lookupClasses[domnode.jsPolyfillId];
}
}
};
FixedPointRunner.prototype.setUp = function() {
var func, funcName, _ref, _results;
this.emit('runner.start');
_ref = this.functions;
_results = [];
for (funcName in _ref) {
func = _ref[funcName];
_results.push(less.tree.functions[funcName] = CSS_FUNCTION_WRAPPER(funcName, func));
}
return _results;
};
FixedPointRunner.prototype.done = function() {
var className, classes, discardedClasses, el, funcName, node, nonPolyfillClasses, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1;
for (funcName in this.functions) {
delete less.tree.functions[funcName];
}
discardedClasses = ['js-polyfill-evaluated', 'js-polyfill-interesting', 'js-polyfill-target'];
for (_i = 0, _len = discardedClasses.length; _i < _len; _i++) {
className = discardedClasses[_i];
_ref = this.rootNode.querySelectorAll("." + className);
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
el = _ref[_j];
el.classList.remove(className);
}
}
_ref1 = this.rootNode.querySelectorAll('.js-polyfill-autoclass:not(.js-polyfill-autoclass-keep), .js-polyfill-pseudo');
for (_k = 0, _len2 = _ref1.length; _k < _len2; _k++) {
node = _ref1[_k];
if (node.classList) {
if ('string' === typeof node.className) {
classes = node.className;
} else {
classes = node.className.baseVal;
}
classes = classes.split(' ');
nonPolyfillClasses = [];
for (_l = 0, _len3 = classes.length; _l < _len3; _l++) {
className = classes[_l];
if (!/^js-polyfill-/.test(className)) {
nonPolyfillClasses.push(className);
}
if (!this.removeAutogenClasses) {
if (/^js-polyfill-autoclass-/.test(className)) {
nonPolyfillClasses.push(className);
}
}
}
if (nonPolyfillClasses.length > 0) {
node.className = nonPolyfillClasses.join(' ');
} else {
node.removeAttribute('class');
}
}
}
return this.emit('runner.end');
};
FixedPointRunner.prototype.run = function() {
var changes, interestingNodes;
this.setUp();
interestingNodes = this.rootNode.querySelectorAll('.js-polyfill-interesting');
while (changes = this.tick(interestingNodes)) {
interestingNodes = this.rootNode.querySelectorAll('.js-polyfill-interesting');
}
return this.done();
};
return FixedPointRunner;
})(EventEmitter);
});
}).call(this);
(function() {
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
define('polyfill-path/selector-visitor', ['underscore', 'less', 'sizzle', 'eventemitter2'], function(_, less, Sizzle, EventEmitter) {
var AbstractSelectorVisitor, LessVisitor, PSEUDO_CLASSES, _ref;
LessVisitor = (function(_super) {
__extends(LessVisitor, _super);
function LessVisitor(rootNode) {
this.rootNode = rootNode;
this._visitor = new less.tree.visitor(this);
this._frames = [];
}
LessVisitor.prototype.run = function(root) {
return this._visitor.visit(root);
};
LessVisitor.prototype.peek = function() {
return this._frames[this._frames.length - 1];
};
LessVisitor.prototype.push = function(val) {
return this._frames.push(val);
};
LessVisitor.prototype.pop = function() {
return this._frames.pop();
};
return LessVisitor;
})(EventEmitter);
PSEUDO_CLASSES = ['before', 'after', 'outside', 'footnote-call', 'footnote-marker', 'deferred'];
return AbstractSelectorVisitor = (function(_super) {
__extends(AbstractSelectorVisitor, _super);
function AbstractSelectorVisitor() {
_ref = AbstractSelectorVisitor.__super__.constructor.apply(this, arguments);
return _ref;
}
AbstractSelectorVisitor.prototype.isPreEvalVisitor = false;
AbstractSelectorVisitor.prototype.isPreVisitor = false;
AbstractSelectorVisitor.prototype.isReplacing = false;
AbstractSelectorVisitor.prototype.operateOnElements = function(frame, nodes, ruleSet, domSelector, pseudoSelector, originalSelector) {};
AbstractSelectorVisitor.prototype.doSelector = function(node, visitArgs) {
var element, i, isPseudo, sliceIndex, _i, _len, _ref1;
isPseudo = function(name) {
var _ref1;
return _.isString(name) && /^:/.test(name) && (_ref1 = name.replace(/:/g, ''), __indexOf.call(PSEUDO_CLASSES, _ref1) >= 0);
};
sliceIndex = node.elements.length;
_ref1 = node.elements;
for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) {
element = _ref1[i];
if (isPseudo(element.value)) {
sliceIndex = i;
break;
}
}
return {
originalSelector: node,
domSelector: node.createDerived(node.elements.slice(0, sliceIndex)),
pseudoSelector: node.createDerived(node.elements.slice(sliceIndex))
};
};
AbstractSelectorVisitor.prototype.getNodes = function(selectorStr) {
var err;
try {
return this.rootNode.querySelectorAll(selectorStr);
} catch (_error) {
err = _error;
return Sizzle(selectorStr, this.rootNode);
}
};
AbstractSelectorVisitor.prototype.visitRuleset = function(node) {
var context, el, path, sel, selector, selectorStr, _i, _j, _k, _len, _len1, _len2, _ref1, _ref2, _results,
_this = this;
if (node.root) {
return;
}
_ref1 = node.paths;
_results = [];
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
path = _ref1[_i];
context = [];
for (_j = 0, _len1 = path.length; _j < _len1; _j++) {
sel = path[_j];
selector = this.doSelector(sel);
selectorStr = [];
_ref2 = selector.domSelector.elements;
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
el = _ref2[_k];
selectorStr.push(el.toCSS({}));
if (/^[\*]/.test(el.value)) {
selectorStr.push(':not(.js-polyfill-pseudo)');
}
}
selectorStr = selectorStr.join('');
context.push(selectorStr);
}
selectorStr = context.join('');
this.emit('selector.start', selectorStr, node.debugInfo);
this.emit('selector.end', selectorStr, node.debugInfo, function() {
return _this.getNodes(selectorStr);
});
if (__indexOf.call(selectorStr, '@') >= 0 || __indexOf.call(selectorStr, '|') >= 0) {
} else {
this.operateOnElements(null, node, selector.domSelector, selector.pseudoSelector, selector.originalSelector, selectorStr);
}
_results.push(context.pop());
}
return _results;
};
return AbstractSelectorVisitor;
})(LessVisitor);
});
}).call(this);
(function() {
define('polyfill-path/extras', ['underscore', 'sizzle', 'less'], function(_, Sizzle, less) {
var ElementExtras, pseudo, uniqueId, uniqueIdCount, _base, _base1, _base2, _i, _len, _ref;
uniqueIdCount = 0;
uniqueId = function() {
return "id-added-via-x-ensure-id-" + (uniqueIdCount++);
};
_ref = ['deferred', 'pass', 'match'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
pseudo = _ref[_i];
if ((_base = Sizzle.selectors.match)[pseudo] == null) {
_base[pseudo] = RegExp(":?" + pseudo);
}
if ((_base1 = Sizzle.selectors.find)[pseudo] == null) {
_base1[pseudo] = function(match, context, isXML) {
return context;
};
}
if ((_base2 = Sizzle.selectors.pseudos)[pseudo] == null) {
_base2[pseudo] = function(elem) {
return elem;
};
}
}
ElementExtras = (function() {
function ElementExtras() {}
ElementExtras.prototype.functions = {
'x-selector': function(env, selectorNode) {
if (!(selectorNode instanceof less.tree.Quoted)) {
console.warn("ERROR: x-selector(): expects a Quoted");
}
return selectorNode.value;
},
'x-sort': function(env, bucketElementsNode, sortBySelector) {
var domAry, sorted,
_this = this;
if (sortBySelector == null) {
sortBySelector = null;
}
domAry = bucketElementsNode["eval"](env).values;
sorted = _.clone(domAry).sort(function(a, b) {
if (sortBySelector) {
a = a.querySelector(sortBySelector.value);
if (!a) {
console.error('ERROR: Attempting to sort but cannot find selector');
}
a = a != null ? a.textContent.trim() : void 0;
b = b.querySelector(sortBySelector.value);
if (!b) {
console.error('ERROR: Attempting to sort but cannot find selector');
}
b = b != null ? b.textContent.trim() : void 0;
} else {
a = a.textContent.trim();
b = b.textContent.trim();
}
if (a < b) {
return -1;
}
if (a > b) {
return 1;
}
return 0;
});
return sorted;
},
'x-target-is': function(env, targetIdNode, selectorNode) {
var context, href, targetEnv;
if (selectorNode == null) {
selectorNode = null;
}
href = targetIdNode["eval"](env).value;
selectorNode = selectorNode["eval"](env);
if (!(selectorNode instanceof less.tree.Quoted)) {
console.warn("ERROR: x-target-is() expects a Quoted");
}
if (!env.helpers.markInterestingByHref(href)) {
targetEnv = env.helpers.interestingByHref(href);
context = targetEnv.helpers.contextNode;
if (Sizzle.matchesSelector(context, selectorNode.value)) {
return '';
} else {
return null;
}
}
return null;
}
};
ElementExtras.prototype.rules = {
'x-tag-name': function(env, tagNameNode) {
var child, context, newEl, oldTagName, tagName, _j, _len1, _ref1;
tagNameNode = tagNameNode["eval"](env);
if (!(tagNameNode instanceof less.tree.Quoted)) {
console.warn("ERROR: move-to: expects a Quoted");
}
context = env.helpers.contextNode;
oldTagName = context.tagName.toLowerCase();
tagName = tagNameNode.value.toLowerCase();
if (oldTagName !== tagName) {
newEl = document.createElement(tagName);
newEl.className = context.className;
newEl.setAttribute('data-js-polyfill-tagname-orig', oldTagName);
_ref1 = context.childNodes;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
child = _ref1[_j];
newEl.appendChild(child);
}
context.parentNode.replaceChild(newEl, context);
env.helpers.contextNode = newEl;
env.helpers.didSomthingNonIdempotent('x-tag-name');
return 'RULE_COMPLETED';
}
return false;
},
'x-ensure-id': function(env, attributeNameNode) {
attributeNameNode = attributeNameNode["eval"](env);
if (!(attributeNameNode instanceof less.tree.Quoted)) {
console.warn("ERROR: x-ensure-id: expects a Quoted");
}
if (!env.helpers.contextNode.getAttribute(attributeNameNode.value)) {
env.helpers.contextNode.setAttribute(attributeNameNode.value, uniqueId());
env.helpers.didSomthingNonIdempotent('x-ensure-id');
return 'RULE_COMPLETED';
}
return false;
},
'x-attr': function(env, attrsAndVals) {
var setAttr, v, _j, _len1, _ref1, _results,
_this = this;
attrsAndVals = attrsAndVals["eval"](env);
setAttr = function(val) {
var arg, args, attrName, str, _j, _len1;
attrName = _.first(val.value).value;
args = _.rest(val.value);
str = [];
for (_j = 0, _len1 = args.length; _j < _len1; _j++) {
arg = args[_j];
if (arg instanceof less.tree.Quoted) {
str.push(arg.value);
} else if (arg instanceof less.tree.Call) {
if ('content' === arg.name) {
arg.name = 'x-string-set-content';
val = arg["eval"](env);
if (val instanceof less.tree.Call) {
return null;
}
str.push(val.value);
arg.name = 'content';
} else {
return null;
}
} else {
str.push(arg.value);
}
}
return env.helpers.contextNode.setAttribute(attrName, str.join(''));
};
if (attrsAndVals instanceof less.tree.Expression) {
return setAttr(attrsAndVals);
} else if (attrsAndVals instanceof less.tree.Value) {
_ref1 = attrsAndVals.value;
_results = [];
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
v = _ref1[_j];
_results.push(setAttr(v));
}
return _results;
} else {
return console.warn('ERROR: invalid arguments given to "x-attr:"');
}
}
};
return ElementExtras;
})();
return {
ElementExtras: ElementExtras
};
});
}).call(this);
(function() {
define('polyfill-path/plugins', ['underscore', 'sizzle', 'less'], function(_, Sizzle, less) {
var ArrayTreeNode, ContentSet, DisplayNone, MoveTo, StringSet, TargetCounter, TargetText, contentsFuncBuilder;
less.tree.ArrayTreeNode = ArrayTreeNode = (function() {
function ArrayTreeNode(values) {
this.values = values;
}
ArrayTreeNode.prototype["eval"] = function() {
return this;
};
return ArrayTreeNode;
})();
MoveTo = (function() {
function MoveTo() {}
MoveTo.prototype.functions = {
'pending': function(env, bucketNameNode) {
var domAry, footnote, node, pendingPseudo, _i, _j, _len, _len1, _ref;
bucketNameNode = bucketNameNode["eval"](env);
if (!(bucketNameNode instanceof less.tree.Keyword)) {
console.warn("ERROR: pending(): expects a Keyword");
}
domAry = ((_ref = env.state.buckets) != null ? _ref[bucketNameNode.value] : void 0) || [];
for (_i = 0, _len = domAry.length; _i < _len; _i++) {
node = domAry[_i];
pendingPseudo = node.querySelector('.js-polyfill-pseudo[data-js-polyfill-rule-content="pending"]');
if (pendingPseudo) {
return null;
}
}
for (_j = 0, _len1 = domAry.length; _j < _len1; _j++) {
node = domAry[_j];
footnote = node.querySelector('.js-polyfill-pseudo-footnote-call');
if (footnote) {
if (node.nextSibling) {
node.parentNode.insertBefore(footnote, node.nextSibling);
} else {
node.parentNode.append(footnote);
}
}
}
if (env.state.buckets) {
delete env.state.buckets[bucketNameNode.value];
}
return domAry || [];
}
};
MoveTo.prototype.rules = {
'move-to': function(env, bucketNameNode) {
var bucketName, domnode, ruleName, _base, _base1;
bucketNameNode = bucketNameNode["eval"](env);
if (!(bucketNameNode instanceof less.tree.Keyword)) {
console.warn("ERROR: move-to: expects a Keyword");
}
ruleName = 'move-to';
domnode = env.helpers.contextNode;
if ('pending' === domnode.getAttribute('data-js-polyfill-rule-content') || domnode.querySelector("[data-js-polyfill-rule-content='pending']")) {
return false;
}
bucketName = bucketNameNode.value;
if ((_base = env.state).buckets == null) {
_base.buckets = {};
}
if ((_base1 = env.state.buckets)[bucketName] == null) {
_base1[bucketName] = [];
}
env.state.buckets[bucketName].push(env.helpers.contextNode);
return 'RULE_COMPLETED';
}
};
return MoveTo;
})();
DisplayNone = (function() {
function DisplayNone() {}
DisplayNone.prototype.rules = {
'display': function(env, valNode) {
var context;
valNode = valNode["eval"](env);
if (!(valNode instanceof less.tree.Anonymous)) {
return;
}
if ('none' === valNode.value) {
context = env.helpers.contextNode;
context.parentNode.removeChild(context);
env.helpers.didSomthingNonIdempotent('display:none');
return 'NODE_REMOVED';
}
return true;
}
};
return DisplayNone;
})();
TargetCounter = (function() {
function TargetCounter() {}
TargetCounter.prototype.functions = {
'x-parent': function(env, valNode) {
var context;
context = env.helpers.contextNode;
env.helpers.contextNode = context.parentNode;
valNode = valNode["eval"](env);
env.helpers.contextNode = context;
return valNode;
},
'attr': function(env, attrNameNode) {
var context, val;
attrNameNode = attrNameNode["eval"](env);
if (!(attrNameNode instanceof less.tree.Keyword)) {
console.warn("ERROR: attr(): expects a Keyword");
}
context = env.helpers.contextNode;
val = context.getAttribute(attrNameNode.value);
if (val && !isNaN(val)) {
val = parseInt(val);
}
if (!val) {
if (context.classList.contains('js-polyfill-pseudo')) {
if (context.classList.contains('js-polyfill-pseudo-outside')) {
context = context.querySelector(':not(.js-polyfill-pseudo)');
} else {
if (context.parentNode) {
context = context.parentNode;
while (context.classList.contains('js-polyfill-pseudo')) {
context = context.parentNode;
if (!context) {
break;
}
}
}
}
val = context.getAttribute(attrNameNode.value);
if (val && !isNaN(val)) {
val = parseInt(val);
}
}
}
return val;
},
'counter': function(env, counterNameNode, counterType) {
var _ref;
if (counterType == null) {
counterType = null;
}
counterNameNode = counterNameNode["eval"](env);
if (!(counterNameNode instanceof less.tree.Keyword)) {
console.warn("ERROR: counter(): expects a Keyword");
}
return this.numberingStyle((_ref = env.state.counters) != null ? _ref[counterNameNode.value] : void 0, counterType != null ? counterType["eval"](env).value : void 0);
},
'target-counter': function(env, targetIdNode, counterNameNode, counterType) {
var counterName, href, targetEnv, _ref;
if (counterType == null) {
counterType = null;
}
if (!counterNameNode) {
console.error("ERROR: target-counter(): expects a 2nd argument");
return;
}
counterNameNode = counterNameNode["eval"](env);
if (!(counterNameNode instanceof less.tree.Keyword)) {
console.warn("ERROR: target-counter(): expects a Keyword");
}
href = targetIdNode["eval"](env).value;
counterName = counterNameNode.value;
if (!href || href[0] !== '#' || href.length < 2) {
return null;
}
if (!env.helpers.markInterestingByHref(href)) {
targetEnv = env.helpers.interestingByHref(href);
return this.numberingStyle((_ref = targetEnv.state.counters) != null ? _ref[counterName] : void 0, counterType != null ? counterType["eval"](env).value : void 0);
}
return null;
}
};
TargetCounter.prototype.rules = {
'counter-increment': function(env, valNode) {
var counterName, counterValue, counters, countersAndNumbers, _base, _base1;
countersAndNumbers = valNode["eval"](env);
counters = this.parseCounters(countersAndNumbers, 1);
if ((_base = env.state).counters == null) {
_base.counters = {};
}
for (counterName in counters) {
counterValue = counters[counterName];
if ((_base1 = env.state.counters)[counterName] == null) {
_base1[counterName] = 0;
}
env.state.counters[counterName] += counterValue;
}
return true;
},
'counter-reset': function(env, valNode) {
var counterName, counterValue, counters, countersAndNumbers, _base;
countersAndNumbers = valNode["eval"](env);
counters = this.parseCounters(countersAndNumbers, 0);
if ((_base = env.state).counters == null) {
_base.counters = {};
}
for (counterName in counters) {
counterValue = counters[counterName];
env.state.counters[counterName] = counterValue;
}
return true;
}
};
TargetCounter.prototype.parseCounters = function(val, defaultNum) {
var counters, cssStr, i, name, tokens;
cssStr = val.toCSS({});
tokens = cssStr.split(' ');
counters = {};
if ('none' === tokens[0]) {
return counters;
}
i = 0;
while (i < tokens.length) {
name = tokens[i];
if (i === tokens.length - 1) {
val = defaultNum;
} else if (isNaN(parseInt(tokens[i + 1]))) {
val = defaultNum;
} else {
val = parseInt(tokens[i + 1]);
i++;
}
counters[name] = val;
i++;
}
return counters;
};
TargetCounter.prototype.numberingStyle = function(num, style) {
if (num == null) {
num = 0;
}
if (style == null) {
style = 'decimal';
}
switch (style) {
case 'decimal-leading-zero':
if (num < 10) {
return "0" + num;
} else {
return num;
}
break;
case 'lower-roman':
return this.toRoman(num).toLowerCase();
case 'upper-roman':
return this.toRoman(num);
case 'lower-latin':
if (!((1 <= num && num <= 26))) {
console.warn('ERROR: number out of range (must be 1...26)');
return num;
} else {
return String.fromCharCode(num + 96);
}
break;
case 'upper-latin':
if (!((1 <= num && num <= 26))) {
console.warn('ERROR: number out of range (must be 1...26)');
return num;
} else {
return String.fromCharCode(num + 64);
}
break;
case 'decimal':
return num;
default:
console.warn("ERROR: Counter numbering not supported for list type " + style + ". Using decimal.");
return num;
}
};
TargetCounter.prototype.toRoman = function(num) {
var integer, numeral, result, romanNumeralMap, _i, _len, _ref;
romanNumeralMap = [['M', 1000], ['CM', 900], ['D', 500], ['CD', 400], ['C', 100], ['XC', 90], ['L', 50], ['XL', 40], ['X', 10], ['IX', 9], ['V', 5], ['IV', 4], ['I', 1]];
if (!((0 < num && num < 5000))) {
console.warn('ERROR: number out of range (must be 1..4999)');
return "" + num;
}
result = '';
for (_i = 0, _len = romanNumeralMap.length; _i < _len; _i++) {
_ref = romanNumeralMap[_i], numeral = _ref[0], integer = _ref[1];
while (num >= integer) {
result += numeral;
num -= integer;
}
}
return result;
};
return TargetCounter;
})();
contentsFuncBuilder = function(defaultType) {
return function(env, typeNode) {
var child, el, els, evaluated, getContents, selector, text, type, val, _i, _j, _k, _l, _len, _len1, _len2, _len3, _ref, _ref1, _ref2, _ref3, _ref4, _ref5, _ref6,
_this = this;
typeNode = typeNode != null ? typeNode["eval"](env) : void 0;
if ((!typeNode) || typeNode instanceof less.tree.Keyword) {
getContents = function() {
var child, el, _i, _len, _ref, _ref1;
el = env.helpers.contextNode.cloneNode(true);
_ref = _.toArray(el.children);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
if ((_ref1 = child.classList) != null ? _ref1.contains('js-polyfill-pseudo') : void 0) {
el.removeChild(child);
}
}
return el.textContent;
};
type = (typeNode != null ? typeNode.value : void 0) || defaultType;
switch (type) {
case 'contents':
val = getContents();
break;
case 'first-letter':
val = (_ref = getContents()) != null ? _ref.trim()[0] : void 0;
break;
case 'before':
text = [];
_ref1 = env.helpers.contextNode.children;
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
child = _ref1[_i];
if ((_ref2 = child.classList) != null ? _ref2.contains('js-polyfill-pseudo-before') : void 0) {
text.push(child.textContent);
}
}
evaluated = false;
_ref3 = env.helpers.contextNode.children;
for (_j = 0, _len1 = _ref3.length; _j < _len1; _j++) {
child = _ref3[_j];
if (child.classList.contains('js-polyfill-evaluated')) {
evaluated = true;
}
}
if (evaluated) {
val = text.join('');
}
break;
case 'after':
text = [];
_ref4 = env.helpers.contextNode.children;
for (_k = 0, _len2 = _ref4.length; _k < _len2; _k++) {
child = _ref4[_k];
if ((_ref5 = child.classList) != null ? _ref5.contains('js-polyfill-pseudo-after') : void 0) {
text.push(child.textContent);
}
}
evaluated = false;
_ref6 = env.helpers.contextNode.children;
for (_l = 0, _len3 = _ref6.length; _l < _len3; _l++) {
child = _ref6[_l];
if (child.classList.contains('js-polyfill-evaluated')) {
evaluated = true;
}
}
if (evaluated) {
val = text.join('');
}
break;
default:
val = typeNode.toCSS({});
console.error("ERROR: invalid argument to content(). argument=[" + val + "]");
val = '';
}
return val;
} else if (typeNode instanceof less.tree.Quoted) {
selector = typeNode.value;
els = Sizzle(selector, env.helpers.contextNode);
if (!els[0]) {
return;
}
text = (function() {
var _len4, _m, _results;
_results = [];
for (_m = 0, _len4 = els.length; _m < _len4; _m++) {
el = els[_m];
_results.push(el.textContent);
}
return _results;
})();
return text.join('');
} else {
return console.warn("ERROR: content(): expects a Keyword or a Selector String");
}
};
};
TargetText = (function() {
function TargetText() {}
TargetText.prototype.functions = {
'x-target-text-content': contentsFuncBuilder('before'),
'target-text': function(env, targetIdNode, contentTypeNode) {
var contents, href, targetEnv;
if (contentTypeNode == null) {
contentTypeNode = null;
}
href = targetIdNode["eval"](env).value;
if (!env.helpers.markInterestingByHref(href)) {
targetEnv = env.helpers.interestingByHref(href);
if (!(contentTypeNode instanceof less.tree.Call)) {
console.warn("ERROR: target-text() expects a function Call");
}
if ('content' !== contentTypeNode.name) {
console.warn("ERROR: target-text() expects a Call to content()");
}
contentTypeNode.name = 'x-target-text-content';
contents = contentTypeNode["eval"](targetEnv).value;
contentTypeNode.name = 'content';
return contents;
}
return null;
}
};
return TargetText;
})();
StringSet = (function() {
function StringSet() {}
StringSet.prototype.functions = {
'x-string-set-content': contentsFuncBuilder('contents'),
'string': function(env, stringNameNode) {
var str, _ref;
stringNameNode = stringNameNode["eval"](env);
if (!(stringNameNode instanceof less.tree.Keyword)) {
console.warn("ERROR: string(): expects a Keyword");
}
str = (_ref = env.state.strings) != null ? _ref[stringNameNode.value] : void 0;
return str;
}
};
StringSet.prototype.rules = {
'string-set': function(env, stringsAndVals) {
var setString, v, _i, _len, _ref,
_this = this;
stringsAndVals = stringsAndVals["eval"](env);
setString = function(val) {
var arg, args, str, stringName, _base, _i, _len;
stringName = _.first(val.value).value;
args = _.rest(val.value);
str = [];
for (_i = 0, _len = args.length; _i < _len; _i++) {
arg = args[_i];
if (arg instanceof less.tree.Quoted) {
str.push(arg.value);
} else if (arg instanceof less.tree.Call) {
if ('content' === arg.name) {
arg.name = 'x-string-set-content';
val = arg["eval"](env);
if (val instanceof less.tree.Call) {
return null;
}
str.push(val.value);
arg.name = 'content';
} else {
console.warn("ERROR: invalid function used. only content() is acceptable. name=[" + arg.name + "]");
}
} else {
str.push(arg.value);
}
}
if ((_base = env.state).strings == null) {
_base.strings = {};
}
return env.state.strings[stringName] = str.join('');
};
if (stringsAndVals instanceof less.tree.Expression) {
setString(stringsAndVals);
} else if (stringsAndVals instanceof less.tree.Value) {
_ref = stringsAndVals.value;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
v = _ref[_i];
setString(v);
}
} else {
console.warn('ERROR: invalid arguments given to string-set');
return false;
}
return true;
}
};
return StringSet;
})();
ContentSet = (function() {
function ContentSet() {}
ContentSet.prototype.rules = {
'content': function(env, valNode) {
var child, domnode, pseudoAfter, val, values, _i, _j, _len, _len1, _ref, _ref1;
valNode = valNode["eval"](env);
values = this.evaluateValNode(valNode);
if (values) {
domnode = env.helpers.contextNode;
_ref = _.toArray(domnode.childNodes);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
child = _ref[_i];
if (!((_ref1 = child.classList) != null ? _ref1.contains('js-polyfill-pseudo') : void 0)) {
domnode.removeChild(child);
}
}
pseudoAfter = domnode.querySelector('.js-polyfill-pseudo-after');
for (_j = 0, _len1 = values.length; _j < _len1; _j++) {
val = values[_j];
switch (typeof val) {
case 'string':
val = document.createTextNode(val);
break;
case 'number':
val = document.createTextNode(val);
break;
default:
if (val.ELEMENT_NODE) {
} else {
throw new Error('BUG: content rule only supports string, number, and DOM Node objects');
}
}
if (pseudoAfter) {
domnode.insertBefore(val, pseudoAfter);
} else {
domnode.appendChild(val);
}
}
domnode.classList.add('js-polyfill-evaluated');
return 'RULE_COMPLETED';
}
return false;
}
};
ContentSet.prototype.evaluateValNode = function(valNode) {
var el, r, ret, val, vals, _i, _j, _len, _len1, _ref;
ret = [];
if (valNode instanceof less.tree.Expression) {
vals = valNode.value;
} else {
vals = [valNode];
}
for (_i = 0, _len = vals.length; _i < _len; _i++) {
val = vals[_i];
if (val instanceof less.tree.Expression) {
r = this.evaluateValNode(val);
if (r === null) {
return null;
}
ret = ret.concat(r);
} else if (val instanceof less.tree.Quoted) {
ret.push(val.value);
} else if (val instanceof less.tree.Dimension) {
ret.push(val.value);
} else if (val instanceof less.tree.ArrayTreeNode) {
_ref = val.values;
for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) {
el = _ref[_j];
ret.push(el);
}
} else if (val instanceof less.tree.Call) {
return null;
} else if (val instanceof less.tree.URL) {
return null;
} else if (val instanceof less.tree.Comment) {
ret.push('');
} else {
console.warn("BUG: Attempting to set content: to something unknown. [" + val.value + "]");
console.warn(JSON.stringify(val));
return null;
}
}
return ret;
};
return ContentSet;
})();
return {
MoveTo: MoveTo,
DisplayNone: DisplayNone,
TargetCounter: TargetCounter,
TargetText: TargetText,
StringSet: StringSet,
ContentSet: ContentSet
};
});
}).call(this);
(function() {
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
define('polyfill-path/less-converters', ['underscore', 'sizzle', 'cs!polyfill-path/selector-visitor'], function(_, Sizzle, AbstractSelectorVisitor) {
var AutogenClass, PSEUDO_CLASSES, PseudoExpander, freshClass, freshClassIdCounter, slugify;
PSEUDO_CLASSES = ['before', 'after', 'outside', 'footnote-call', 'footnote-marker', 'running', 'deferred'];
slugify = function(str) {
str = str.replace(/\ +/g, ' ');
str = str.replace(/[^A-Za-z0-9>\[\]\(\)\.]/g, '-');
str = str.replace(/[\[\]\(\)]/g, '_');
str = str.replace(/-+/g, '-');
str = str.replace(/>/g, '-child-');
str = str.replace(/\./g, '-dot-');
str = str.replace(/-+/g, '-');
str = str.replace(/^-|-$/g, '');
return str;
};
freshClassIdCounter = 0;
freshClass = function(selectorStr) {
selectorStr = slugify(selectorStr);
return "js-polyfill-autoclass-" + (freshClassIdCounter++) + "-" + selectorStr;
};
AutogenClass = (function() {
function AutogenClass(selectorStr, elements, rules) {
this.selectorStr = selectorStr;
this.elements = elements;
this.rules = rules;
}
return AutogenClass;
})();
PseudoExpander = (function(_super) {
var selectorTestNode;
__extends(PseudoExpander, _super);
PseudoExpander.prototype.PSEUDO_ELEMENT_NAME = 'span';
selectorTestNode = document.createElement('span');
function PseudoExpander(root, set, interestingSet, plugins) {
var plugin, ruleName, _i, _len;
this.set = set;
this.interestingSet = interestingSet;
PseudoExpander.__super__.constructor.apply(this, arguments);
this.interestingRules = [];
for (_i = 0, _len = plugins.length; _i < _len; _i++) {
plugin = plugins[_i];
for (ruleName in plugin.rules || {}) {
this.interestingRules[ruleName] = true;
}
}
}
PseudoExpander.prototype.hasInterestingRules = function(ruleSet) {
var rule, _i, _len, _ref;
if (this.interestingRules['*']) {
return true;
}
_ref = ruleSet.rules;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
rule = _ref[_i];
if (rule.name in this.interestingRules) {
return true;
}
}
};
PseudoExpander.prototype.operateOnElements = function(frame, ruleSet, domSelector, pseudoSelector, originalSelector, selectorStr) {
var autoClass, className, cls, context, e, hasInterestingRules, isBrowserSelector, newClassName, newContexts, node, op, originalContext, outside, pseudoName, pseudoNode, simpleExpand, _i, _j, _len, _len1, _ref;
if (!pseudoSelector.elements.length) {
try {
selectorTestNode.querySelector(selectorStr);
isBrowserSelector = true;
} catch (_error) {
e = _error;
isBrowserSelector = false;
}
if (isBrowserSelector) {
autoClass = new AutogenClass(selectorStr, originalSelector.elements, ruleSet.rules);
this.set.add(selectorStr, autoClass);
}
hasInterestingRules = this.hasInterestingRules(ruleSet);
if (!isBrowserSelector || hasInterestingRules) {
className = freshClass(selectorStr);
_ref = this.getNodes(selectorStr);
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
node = _ref[_i];
if (node.className == null) {
node.className = '';
}
if (node.classList) {
if (!isBrowserSelector) {
node.classList.add('js-polyfill-autoclass-keep');
}
node.classList.add('js-polyfill-autoclass');
node.classList.add('js-polyfill-interesting');
node.classList.add(className);
}
}
selectorStr = "." + className;
autoClass = new AutogenClass(selectorStr, originalSelector.elements, ruleSet.rules);
this.interestingSet.add(selectorStr, autoClass);
if (!isBrowserSelector && !hasInterestingRules) {
return this.set.add(selectorStr, autoClass);
}
}
} else {
newContexts = (function() {
var _j, _k, _len1, _len2, _ref1, _ref2, _results,
_this = this;
_ref1 = this.getNodes(selectorStr);
_results = [];
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
originalContext = _ref1[_j];
context = originalContext;
_ref2 = pseudoSelector.elements;
for (_k = 0, _len2 = _ref2.length; _k < _len2; _k++) {
pseudoNode = _ref2[_k];
pseudoName = pseudoNode.value.replace('::', ':');
simpleExpand = function(op, pseudoName) {
var cls, pseudo;
cls = "js-polyfill-pseudo-" + pseudoName;
pseudo = Sizzle(" > ." + cls + ", > .js-polyfill-pseudo-outside > ." + cls, context)[0];
if (pseudo) {
return context = pseudo;
} else {
pseudo = document.createElement(_this.PSEUDO_ELEMENT_NAME);
pseudo.classList.add('js-polyfill-pseudo');
pseudo.classList.add(cls);
switch (op) {
case 'append':
context.appendChild(pseudo);
break;
case 'prepend':
context.insertBefore(pseudo, context.firstChild);
}
return context = pseudo;
}
};
selectorStr += pseudoName;
switch (pseudoName) {
case ':before':
simpleExpand('prepend', 'before');
break;
case ':after':
simpleExpand('append', 'after');
break;
case ':footnote-marker':
simpleExpand('prepend', 'footnote-marker');
break;
case ':footnote-call':
simpleExpand('append', 'footnote-call');
break;
case ':running':
simpleExpand('append', 'footnote-call');
break;
case ':outside':
op = 'wrap';
pseudoName = 'outside';
cls = "js-polyfill-pseudo-" + pseudoName;
if (context.parentNode.classList.contains(cls)) {
context = context.parentNode;
} else {
outside = document.createElement(this.PSEUDO_ELEMENT_NAME);
outside.classList.add('js-polyfill-pseudo');
outside.classList.add(cls);
context.parentNode.replaceChild(outside, context);
outside.appendChild(context);
context = outside;
}
break;
case ':deferred':
break;
default:
console.error("ERROR: Attempted to expand unsupported pseudo element " + pseudoName);
}
}
_results.push(context);
}
return _results;
}).call(this);
newClassName = freshClass(selectorStr);
for (_j = 0, _len1 = newContexts.length; _j < _len1; _j++) {
context = newContexts[_j];
context.classList.add('js-polyfill-autoclass');
context.classList.add('js-polyfill-interesting');
context.classList.add(newClassName);
}
autoClass = new AutogenClass(selectorStr, pseudoSelector.elements, ruleSet.rules, this.hasInterestingRules(ruleSet));
selectorStr = "." + newClassName;
this.set.add(selectorStr, autoClass);
if (this.hasInterestingRules(ruleSet)) {
return this.interestingSet.add(selectorStr, autoClass);
}
}
};
return PseudoExpander;
})(AbstractSelectorVisitor);
return {
PseudoExpander: PseudoExpander
};
});
}).call(this);
(function() {
var __hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
__slice = [].slice;
define('polyfill-path/index', ['underscore', 'less', 'eventemitter2', 'selector-set', 'cs!polyfill-path/selector-tree', 'cs!polyfill-path/less-converters', 'cs!polyfill-path/plugins', 'cs!polyfill-path/extras', 'cs!polyfill-path/fixed-point-runner', 'cs!polyfill-path/selector-visitor'], function(_, less, EventEmitter, SelectorSet, SelectorTree, LESS_CONVERTERS, PLUGINS, EXTRAS, FixedPointRunner, AbstractSelectorVisitor) {
var CSSPolyfills, ContentSet, DisplayNone, ElementExtras, MoveTo, PseudoExpander, StringSet, TargetCounter, TargetText;
PseudoExpander = LESS_CONVERTERS.PseudoExpander;
MoveTo = PLUGINS.MoveTo;
DisplayNone = PLUGINS.DisplayNone;
TargetCounter = PLUGINS.TargetCounter;
TargetText = PLUGINS.TargetText;
StringSet = PLUGINS.StringSet;
ContentSet = PLUGINS.ContentSet;
ElementExtras = EXTRAS.ElementExtras;
CSSPolyfills = (function(_super) {
__extends(CSSPolyfills, _super);
CSSPolyfills.DEFAULT_PLUGINS = [new MoveTo(), new DisplayNone(), new TargetCounter(), new TargetText(), new StringSet(), new ElementExtras(), new ContentSet()];
function CSSPolyfills(config) {
if (config == null) {
config = {};
}
_.extend(this, config);
_.defaults(this, {
plugins: [],
lessPlugins: [],
pseudoExpanderClass: PseudoExpander,
doNotIncludeDefaultPlugins: false,
removeAutogenClasses: true
});
if (!this.doNotIncludeDefaultPlugins) {
this.plugins = this.plugins.concat(CSSPolyfills.DEFAULT_PLUGINS);
}
}
CSSPolyfills.prototype.runTree = function(rootNode, lessTree, cb) {
var SelectorSet_add, allSet, bindAll, changeLessTree, interestingSet, outputRulesetsToString, pseudoExpander, runFixedPoint,
_this = this;
if (cb == null) {
cb = null;
}
this.emit('start');
if (rootNode.jquery) {
rootNode = rootNode[0];
}
bindAll = function(emitter, eventNames) {
return _.each(eventNames, function(name) {
return emitter.on(name, function() {
return _this.emit.apply(_this, [name].concat(__slice.call(arguments)));
});
});
};
SelectorSet_add = SelectorSet.prototype.add;
SelectorSet.prototype.add = function(sel, data) {
SelectorSet_add.apply(this, arguments);
if (this.addedSelectors == null) {
this.addedSelectors = [];
}
return this.addedSelectors.push({
selector: sel,
data: data
});
};
outputRulesetsToString = function(outputRulesets) {
var allSelectors, autogenClass, comment, cssStrs, env, originalSelectorStr, rule, rules, selector, selectorStr, start, _i, _j, _len, _len1, _ref;
cssStrs = [];
env = new less.tree.evalEnv();
env.compress = false;
env.dumpLineNumbers = 'all';
start = new Date();
allSelectors = outputRulesets.addedSelectors || [];
for (_i = 0, _len = allSelectors.length; _i < _len; _i++) {
_ref = allSelectors[_i], selectorStr = _ref.selector, autogenClass = _ref.data;
rules = autogenClass.rules, selector = autogenClass.selector;
originalSelectorStr = selector;
if (originalSelectorStr === selectorStr) {
comment = '';
} else {
comment = "/* BASED_ON: " + originalSelectorStr + " */";
}
cssStrs.push("" + selectorStr + " { " + comment);
for (_j = 0, _len1 = rules.length; _j < _len1; _j++) {
rule = rules[_j];
cssStrs.push(" " + (rule.toCSS(env)));
}
cssStrs.push("}");
}
return cssStrs.join('\n');
};
allSet = new SelectorSet();
interestingSet = new SelectorTree();
changeLessTree = function(plugins) {
var env;
env = new less.tree.evalEnv();
env.compress = false;
env.dumpLineNumbers = 'all';
env.plugins = plugins;
return lessTree.toCSS(env);
};
runFixedPoint = function(plugins) {
var fixedPointRunner;
fixedPointRunner = new FixedPointRunner(rootNode, plugins, interestingSet, _this.removeAutogenClasses);
bindAll(fixedPointRunner, ['runner.start', 'runner.end', 'tick.start', 'tick.node', 'tick.end']);
fixedPointRunner.run();
return fixedPointRunner;
};
if (this.pseudoExpanderClass) {
pseudoExpander = new this.pseudoExpanderClass(rootNode, allSet, interestingSet, this.plugins);
bindAll(pseudoExpander, ['selector.start', 'selector.end']);
this.lessPlugins.push(pseudoExpander);
}
changeLessTree(this.lessPlugins);
runFixedPoint(this.plugins);
if (typeof cb === "function") {
cb(null, outputRulesetsToString(allSet));
}
return this.emit('end');
};
CSSPolyfills.prototype.run = function(rootNode, cssStyle, filename, cb) {
var _this = this;
return this.parse(cssStyle, filename, function(err, lessTree) {
if (err) {
return typeof cb === "function" ? cb(err, lessTree) : void 0;
}
return _this.runTree(rootNode, lessTree, cb);
});
};
CSSPolyfills.prototype.parse = function(cssStyle, filename, cb) {
var env, parser;
env = {
compress: false,
dumpLineNumbers: 'all',
filename: filename
};
parser = new less.Parser(env);
return parser.parse(cssStyle, cb);
};
return CSSPolyfills;
})(EventEmitter);
CSSPolyfills.less = less;
CSSPolyfills.AbstractSelectorVisitor = AbstractSelectorVisitor;
if (typeof window !== "undefined" && window !== null) {
window.CSSPolyfills = CSSPolyfills;
}
return CSSPolyfills;
});
}).call(this);
(function() {
this.define = this.__polyfills_originalGlobals['define'];
if (typeof this.define === "function") {
this.define('polyfill-path/index', function() {
return this.CSSPolyfills;
});
}
this.__polyfills_originalGlobals = void 0;
}).call(this);