File indexing completed on 2024-12-29 03:43:59

0001 /*
0002     Underscore.js 1.3.3
0003 
0004     SPDX-FileCopyrightText: 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
0005 
0006     SPDX-License-Identifier: MIT
0007 
0008     Portions of Underscore are inspired or borrowed from Prototype,
0009     Oliver Steele's Functional, and John Resig's Micro-Templating.
0010     For all details and documentation:
0011     http://documentcloud.github.com/underscore
0012 */
0013 
0014 var _ = (function() {
0015 
0016   // Baseline setup
0017   // --------------
0018 
0019   // Establish the root object, `window` in the browser, or `global` on the server.
0020   var root = this;
0021 
0022   // Save the previous value of the `_` variable.
0023   var previousUnderscore = root._;
0024 
0025   // Establish the object that gets returned to break out of a loop iteration.
0026   var breaker = {};
0027 
0028   // Save bytes in the minified (but not gzipped) version:
0029   var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
0030 
0031   // Create quick reference variables for speed access to core prototypes.
0032   var slice            = ArrayProto.slice,
0033       unshift          = ArrayProto.unshift,
0034       toString         = ObjProto.toString,
0035       hasOwnProperty   = ObjProto.hasOwnProperty;
0036 
0037   // All **ECMAScript 5** native function implementations that we hope to use
0038   // are declared here.
0039   var
0040     nativeForEach      = ArrayProto.forEach,
0041     nativeMap          = ArrayProto.map,
0042     nativeReduce       = ArrayProto.reduce,
0043     nativeReduceRight  = ArrayProto.reduceRight,
0044     nativeFilter       = ArrayProto.filter,
0045     nativeEvery        = ArrayProto.every,
0046     nativeSome         = ArrayProto.some,
0047     nativeIndexOf      = ArrayProto.indexOf,
0048     nativeLastIndexOf  = ArrayProto.lastIndexOf,
0049     nativeIsArray      = Array.isArray,
0050     nativeKeys         = Object.keys,
0051     nativeBind         = FuncProto.bind;
0052 
0053   // Create a safe reference to the Underscore object for use below.
0054   var _ = function(obj) { return new wrapper(obj); };
0055 
0056   // Export the Underscore object for **Node.js**, with
0057   // backwards-compatibility for the old `require()` API. If we're in
0058   // the browser, add `_` as a global object via a string identifier,
0059   // for Closure Compiler "advanced" mode.
0060   if (typeof exports !== 'undefined') {
0061     if (typeof module !== 'undefined' && module.exports) {
0062       exports = module.exports = _;
0063     }
0064     exports._ = _;
0065   } else {
0066     root['_'] = _;
0067   }
0068 
0069   // Current version.
0070   _.VERSION = '1.3.3';
0071 
0072   // Collection Functions
0073   // --------------------
0074 
0075   // The cornerstone, an `each` implementation, aka `forEach`.
0076   // Handles objects with the built-in `forEach`, arrays, and raw objects.
0077   // Delegates to **ECMAScript 5**'s native `forEach` if available.
0078   var each = _.each = _.forEach = function(obj, iterator, context) {
0079     if (obj == null) return;
0080     if (nativeForEach && obj.forEach === nativeForEach) {
0081       obj.forEach(iterator, context);
0082     } else if (obj.length === +obj.length) {
0083       for (var i = 0, l = obj.length; i < l; i++) {
0084         if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
0085       }
0086     } else {
0087       for (var key in obj) {
0088         if (_.has(obj, key)) {
0089           if (iterator.call(context, obj[key], key, obj) === breaker) return;
0090         }
0091       }
0092     }
0093   };
0094 
0095   // Return the results of applying the iterator to each element.
0096   // Delegates to **ECMAScript 5**'s native `map` if available.
0097   _.map = _.collect = function(obj, iterator, context) {
0098     var results = [];
0099     if (obj == null) return results;
0100     if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
0101     each(obj, function(value, index, list) {
0102       results[results.length] = iterator.call(context, value, index, list);
0103     });
0104     if (obj.length === +obj.length) results.length = obj.length;
0105     return results;
0106   };
0107 
0108   // **Reduce** builds up a single result from a list of values, aka `inject`,
0109   // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
0110   _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
0111     var initial = arguments.length > 2;
0112     if (obj == null) obj = [];
0113     if (nativeReduce && obj.reduce === nativeReduce) {
0114       if (context) iterator = _.bind(iterator, context);
0115       return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
0116     }
0117     each(obj, function(value, index, list) {
0118       if (!initial) {
0119         memo = value;
0120         initial = true;
0121       } else {
0122         memo = iterator.call(context, memo, value, index, list);
0123       }
0124     });
0125     if (!initial) throw new TypeError('Reduce of empty array with no initial value');
0126     return memo;
0127   };
0128 
0129   // The right-associative version of reduce, also known as `foldr`.
0130   // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
0131   _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
0132     var initial = arguments.length > 2;
0133     if (obj == null) obj = [];
0134     if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
0135       if (context) iterator = _.bind(iterator, context);
0136       return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
0137     }
0138     var reversed = _.toArray(obj).reverse();
0139     if (context && !initial) iterator = _.bind(iterator, context);
0140     return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
0141   };
0142 
0143   // Return the first value which passes a truth test. Aliased as `detect`.
0144   _.find = _.detect = function(obj, iterator, context) {
0145     var result;
0146     any(obj, function(value, index, list) {
0147       if (iterator.call(context, value, index, list)) {
0148         result = value;
0149         return true;
0150       }
0151     });
0152     return result;
0153   };
0154 
0155   // Return all the elements that pass a truth test.
0156   // Delegates to **ECMAScript 5**'s native `filter` if available.
0157   // Aliased as `select`.
0158   _.filter = _.select = function(obj, iterator, context) {
0159     var results = [];
0160     if (obj == null) return results;
0161     if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
0162     each(obj, function(value, index, list) {
0163       if (iterator.call(context, value, index, list)) results[results.length] = value;
0164     });
0165     return results;
0166   };
0167 
0168   // Return all the elements for which a truth test fails.
0169   _.reject = function(obj, iterator, context) {
0170     var results = [];
0171     if (obj == null) return results;
0172     each(obj, function(value, index, list) {
0173       if (!iterator.call(context, value, index, list)) results[results.length] = value;
0174     });
0175     return results;
0176   };
0177 
0178   // Determine whether all of the elements match a truth test.
0179   // Delegates to **ECMAScript 5**'s native `every` if available.
0180   // Aliased as `all`.
0181   _.every = _.all = function(obj, iterator, context) {
0182     var result = true;
0183     if (obj == null) return result;
0184     if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
0185     each(obj, function(value, index, list) {
0186       if (!(result = result && iterator.call(context, value, index, list))) return breaker;
0187     });
0188     return !!result;
0189   };
0190 
0191   // Determine if at least one element in the object matches a truth test.
0192   // Delegates to **ECMAScript 5**'s native `some` if available.
0193   // Aliased as `any`.
0194   var any = _.some = _.any = function(obj, iterator, context) {
0195     iterator || (iterator = _.identity);
0196     var result = false;
0197     if (obj == null) return result;
0198     if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
0199     each(obj, function(value, index, list) {
0200       if (result || (result = iterator.call(context, value, index, list))) return breaker;
0201     });
0202     return !!result;
0203   };
0204 
0205   // Determine if a given value is included in the array or object using `===`.
0206   // Aliased as `contains`.
0207   _.include = _.contains = function(obj, target) {
0208     var found = false;
0209     if (obj == null) return found;
0210     if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
0211     found = any(obj, function(value) {
0212       return value === target;
0213     });
0214     return found;
0215   };
0216 
0217   // Invoke a method (with arguments) on every item in a collection.
0218   _.invoke = function(obj, method) {
0219     var args = slice.call(arguments, 2);
0220     return _.map(obj, function(value) {
0221       return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
0222     });
0223   };
0224 
0225   // Convenience version of a common use case of `map`: fetching a property.
0226   _.pluck = function(obj, key) {
0227     return _.map(obj, function(value){ return value[key]; });
0228   };
0229 
0230   // Return the maximum element or (element-based computation).
0231   _.max = function(obj, iterator, context) {
0232     if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.max.apply(Math, obj);
0233     if (!iterator && _.isEmpty(obj)) return -Infinity;
0234     var result = {computed : -Infinity};
0235     each(obj, function(value, index, list) {
0236       var computed = iterator ? iterator.call(context, value, index, list) : value;
0237       computed >= result.computed && (result = {value : value, computed : computed});
0238     });
0239     return result.value;
0240   };
0241 
0242   // Return the minimum element (or element-based computation).
0243   _.min = function(obj, iterator, context) {
0244     if (!iterator && _.isArray(obj) && obj[0] === +obj[0]) return Math.min.apply(Math, obj);
0245     if (!iterator && _.isEmpty(obj)) return Infinity;
0246     var result = {computed : Infinity};
0247     each(obj, function(value, index, list) {
0248       var computed = iterator ? iterator.call(context, value, index, list) : value;
0249       computed < result.computed && (result = {value : value, computed : computed});
0250     });
0251     return result.value;
0252   };
0253 
0254   // Shuffle an array.
0255   _.shuffle = function(obj) {
0256     var shuffled = [], rand;
0257     each(obj, function(value, index, list) {
0258       rand = Math.floor(Math.random() * (index + 1));
0259       shuffled[index] = shuffled[rand];
0260       shuffled[rand] = value;
0261     });
0262     return shuffled;
0263   };
0264 
0265   // Sort the object's values by a criterion produced by an iterator.
0266   _.sortBy = function(obj, val, context) {
0267     var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
0268     return _.pluck(_.map(obj, function(value, index, list) {
0269       return {
0270         value : value,
0271         criteria : iterator.call(context, value, index, list)
0272       };
0273     }).sort(function(left, right) {
0274       var a = left.criteria, b = right.criteria;
0275       if (a === void 0) return 1;
0276       if (b === void 0) return -1;
0277       return a < b ? -1 : a > b ? 1 : 0;
0278     }), 'value');
0279   };
0280 
0281   // Groups the object's values by a criterion. Pass either a string attribute
0282   // to group by, or a function that returns the criterion.
0283   _.groupBy = function(obj, val) {
0284     var result = {};
0285     var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
0286     each(obj, function(value, index) {
0287       var key = iterator(value, index);
0288       (result[key] || (result[key] = [])).push(value);
0289     });
0290     return result;
0291   };
0292 
0293   // Use a comparator function to figure out at what index an object should
0294   // be inserted so as to maintain order. Uses binary search.
0295   _.sortedIndex = function(array, obj, iterator) {
0296     iterator || (iterator = _.identity);
0297     var low = 0, high = array.length;
0298     while (low < high) {
0299       var mid = (low + high) >> 1;
0300       iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
0301     }
0302     return low;
0303   };
0304 
0305   // Safely convert anything iterable into a real, live array.
0306   _.toArray = function(obj) {
0307     if (!obj)                                     return [];
0308     if (_.isArray(obj))                           return slice.call(obj);
0309     if (_.isArguments(obj))                       return slice.call(obj);
0310     if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray();
0311     return _.values(obj);
0312   };
0313 
0314   // Return the number of elements in an object.
0315   _.size = function(obj) {
0316     return _.isArray(obj) ? obj.length : _.keys(obj).length;
0317   };
0318 
0319   // Array Functions
0320   // ---------------
0321 
0322   // Get the first element of an array. Passing **n** will return the first N
0323   // values in the array. Aliased as `head` and `take`. The **guard** check
0324   // allows it to work with `_.map`.
0325   _.first = _.head = _.take = function(array, n, guard) {
0326     return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
0327   };
0328 
0329   // Returns everything but the last entry of the array. Especcialy useful on
0330   // the arguments object. Passing **n** will return all the values in
0331   // the array, excluding the last N. The **guard** check allows it to work with
0332   // `_.map`.
0333   _.initial = function(array, n, guard) {
0334     return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
0335   };
0336 
0337   // Get the last element of an array. Passing **n** will return the last N
0338   // values in the array. The **guard** check allows it to work with `_.map`.
0339   _.last = function(array, n, guard) {
0340     if ((n != null) && !guard) {
0341       return slice.call(array, Math.max(array.length - n, 0));
0342     } else {
0343       return array[array.length - 1];
0344     }
0345   };
0346 
0347   // Returns everything but the first entry of the array. Aliased as `tail`.
0348   // Especially useful on the arguments object. Passing an **index** will return
0349   // the rest of the values in the array from that index onward. The **guard**
0350   // check allows it to work with `_.map`.
0351   _.rest = _.tail = function(array, index, guard) {
0352     return slice.call(array, (index == null) || guard ? 1 : index);
0353   };
0354 
0355   // Trim out all falsy values from an array.
0356   _.compact = function(array) {
0357     return _.filter(array, function(value){ return !!value; });
0358   };
0359 
0360   // Return a completely flattened version of an array.
0361   _.flatten = function(array, shallow) {
0362     return _.reduce(array, function(memo, value) {
0363       if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
0364       memo[memo.length] = value;
0365       return memo;
0366     }, []);
0367   };
0368 
0369   // Return a version of the array that does not contain the specified value(s).
0370   _.without = function(array) {
0371     return _.difference(array, slice.call(arguments, 1));
0372   };
0373 
0374   // Produce a duplicate-free version of the array. If the array has already
0375   // been sorted, you have the option of using a faster algorithm.
0376   // Aliased as `unique`.
0377   _.uniq = _.unique = function(array, isSorted, iterator) {
0378     var initial = iterator ? _.map(array, iterator) : array;
0379     var results = [];
0380     // The `isSorted` flag is irrelevant if the array only contains two elements.
0381     if (array.length < 3) isSorted = true;
0382     _.reduce(initial, function (memo, value, index) {
0383       if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
0384         memo.push(value);
0385         results.push(array[index]);
0386       }
0387       return memo;
0388     }, []);
0389     return results;
0390   };
0391 
0392   // Produce an array that contains the union: each distinct element from all of
0393   // the passed-in arrays.
0394   _.union = function() {
0395     return _.uniq(_.flatten(arguments, true));
0396   };
0397 
0398   // Produce an array that contains every item shared between all the
0399   // passed-in arrays. (Aliased as "intersect" for back-compat.)
0400   _.intersection = _.intersect = function(array) {
0401     var rest = slice.call(arguments, 1);
0402     return _.filter(_.uniq(array), function(item) {
0403       return _.every(rest, function(other) {
0404         return _.indexOf(other, item) >= 0;
0405       });
0406     });
0407   };
0408 
0409   // Take the difference between one array and a number of other arrays.
0410   // Only the elements present in just the first array will remain.
0411   _.difference = function(array) {
0412     var rest = _.flatten(slice.call(arguments, 1), true);
0413     return _.filter(array, function(value){ return !_.include(rest, value); });
0414   };
0415 
0416   // Zip together multiple lists into a single array -- elements that share
0417   // an index go together.
0418   _.zip = function() {
0419     var args = slice.call(arguments);
0420     var length = _.max(_.pluck(args, 'length'));
0421     var results = new Array(length);
0422     for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
0423     return results;
0424   };
0425 
0426   // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
0427   // we need this function. Return the position of the first occurrence of an
0428   // item in an array, or -1 if the item is not included in the array.
0429   // Delegates to **ECMAScript 5**'s native `indexOf` if available.
0430   // If the array is large and already in sort order, pass `true`
0431   // for **isSorted** to use binary search.
0432   _.indexOf = function(array, item, isSorted) {
0433     if (array == null) return -1;
0434     var i, l;
0435     if (isSorted) {
0436       i = _.sortedIndex(array, item);
0437       return array[i] === item ? i : -1;
0438     }
0439     if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
0440     for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
0441     return -1;
0442   };
0443 
0444   // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
0445   _.lastIndexOf = function(array, item) {
0446     if (array == null) return -1;
0447     if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
0448     var i = array.length;
0449     while (i--) if (i in array && array[i] === item) return i;
0450     return -1;
0451   };
0452 
0453   // Generate an integer Array containing an arithmetic progression. A port of
0454   // the native Python `range()` function. See
0455   // [the Python documentation](http://docs.python.org/library/functions.html#range).
0456   _.range = function(start, stop, step) {
0457     if (arguments.length <= 1) {
0458       stop = start || 0;
0459       start = 0;
0460     }
0461     step = arguments[2] || 1;
0462 
0463     var len = Math.max(Math.ceil((stop - start) / step), 0);
0464     var idx = 0;
0465     var range = new Array(len);
0466 
0467     while(idx < len) {
0468       range[idx++] = start;
0469       start += step;
0470     }
0471 
0472     return range;
0473   };
0474 
0475   // Function (ahem) Functions
0476   // ------------------
0477 
0478   // Reusable constructor function for prototype setting.
0479   var ctor = function(){};
0480 
0481   // Create a function bound to a given object (assigning `this`, and arguments,
0482   // optionally). Binding with arguments is also known as `curry`.
0483   // Delegates to **ECMAScript 5**'s native `Function.bind` if available.
0484   // We check for `func.bind` first, to fail fast when `func` is undefined.
0485   _.bind = function bind(func, context) {
0486     var bound, args;
0487     if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
0488     if (!_.isFunction(func)) throw new TypeError;
0489     args = slice.call(arguments, 2);
0490     return bound = function() {
0491       if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
0492       ctor.prototype = func.prototype;
0493       var self = new ctor;
0494       var result = func.apply(self, args.concat(slice.call(arguments)));
0495       if (Object(result) === result) return result;
0496       return self;
0497     };
0498   };
0499 
0500   // Bind all of an object's methods to that object. Useful for ensuring that
0501   // all callbacks defined on an object belong to it.
0502   _.bindAll = function(obj) {
0503     var funcs = slice.call(arguments, 1);
0504     if (funcs.length == 0) funcs = _.functions(obj);
0505     each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
0506     return obj;
0507   };
0508 
0509   // Memoize an expensive function by storing its results.
0510   _.memoize = function(func, hasher) {
0511     var memo = {};
0512     hasher || (hasher = _.identity);
0513     return function() {
0514       var key = hasher.apply(this, arguments);
0515       return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
0516     };
0517   };
0518 
0519   // Delays a function for the given number of milliseconds, and then calls
0520   // it with the arguments supplied.
0521   _.delay = function(func, wait) {
0522     var args = slice.call(arguments, 2);
0523     return setTimeout(function(){ return func.apply(null, args); }, wait);
0524   };
0525 
0526   // Defers a function, scheduling it to run after the current call stack has
0527   // cleared.
0528   _.defer = function(func) {
0529     return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
0530   };
0531 
0532   // Returns a function, that, when invoked, will only be triggered at most once
0533   // during a given window of time.
0534   _.throttle = function(func, wait) {
0535     var context, args, timeout, throttling, more, result;
0536     var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
0537     return function() {
0538       context = this; args = arguments;
0539       var later = function() {
0540         timeout = null;
0541         if (more) func.apply(context, args);
0542         whenDone();
0543       };
0544       if (!timeout) timeout = setTimeout(later, wait);
0545       if (throttling) {
0546         more = true;
0547       } else {
0548         result = func.apply(context, args);
0549       }
0550       whenDone();
0551       throttling = true;
0552       return result;
0553     };
0554   };
0555 
0556   // Returns a function, that, as long as it continues to be invoked, will not
0557   // be triggered. The function will be called after it stops being called for
0558   // N milliseconds. If `immediate` is passed, trigger the function on the
0559   // leading edge, instead of the trailing.
0560   _.debounce = function(func, wait, immediate) {
0561     var timeout;
0562     return function() {
0563       var context = this, args = arguments;
0564       var later = function() {
0565         timeout = null;
0566         if (!immediate) func.apply(context, args);
0567       };
0568       if (immediate && !timeout) func.apply(context, args);
0569       clearTimeout(timeout);
0570       timeout = setTimeout(later, wait);
0571     };
0572   };
0573 
0574   // Returns a function that will be executed at most one time, no matter how
0575   // often you call it. Useful for lazy initialization.
0576   _.once = function(func) {
0577     var ran = false, memo;
0578     return function() {
0579       if (ran) return memo;
0580       ran = true;
0581       return memo = func.apply(this, arguments);
0582     };
0583   };
0584 
0585   // Returns the first function passed as an argument to the second,
0586   // allowing you to adjust arguments, run code before and after, and
0587   // conditionally execute the original function.
0588   _.wrap = function(func, wrapper) {
0589     return function() {
0590       var args = [func].concat(slice.call(arguments, 0));
0591       return wrapper.apply(this, args);
0592     };
0593   };
0594 
0595   // Returns a function that is the composition of a list of functions, each
0596   // consuming the return value of the function that follows.
0597   _.compose = function() {
0598     var funcs = arguments;
0599     return function() {
0600       var args = arguments;
0601       for (var i = funcs.length - 1; i >= 0; i--) {
0602         args = [funcs[i].apply(this, args)];
0603       }
0604       return args[0];
0605     };
0606   };
0607 
0608   // Returns a function that will only be executed after being called N times.
0609   _.after = function(times, func) {
0610     if (times <= 0) return func();
0611     return function() {
0612       if (--times < 1) { return func.apply(this, arguments); }
0613     };
0614   };
0615 
0616   // Object Functions
0617   // ----------------
0618 
0619   // Retrieve the names of an object's properties.
0620   // Delegates to **ECMAScript 5**'s native `Object.keys`
0621   _.keys = nativeKeys || function(obj) {
0622     if (obj !== Object(obj)) throw new TypeError('Invalid object');
0623     var keys = [];
0624     for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
0625     return keys;
0626   };
0627 
0628   // Retrieve the values of an object's properties.
0629   _.values = function(obj) {
0630     return _.map(obj, _.identity);
0631   };
0632 
0633   // Return a sorted list of the function names available on the object.
0634   // Aliased as `methods`
0635   _.functions = _.methods = function(obj) {
0636     var names = [];
0637     for (var key in obj) {
0638       if (_.isFunction(obj[key])) names.push(key);
0639     }
0640     return names.sort();
0641   };
0642 
0643   // Extend a given object with all the properties in passed-in object(s).
0644   _.extend = function(obj) {
0645     each(slice.call(arguments, 1), function(source) {
0646       for (var prop in source) {
0647         obj[prop] = source[prop];
0648       }
0649     });
0650     return obj;
0651   };
0652 
0653   // Return a copy of the object only containing the whitelisted properties.
0654   _.pick = function(obj) {
0655     var result = {};
0656     each(_.flatten(slice.call(arguments, 1)), function(key) {
0657       if (key in obj) result[key] = obj[key];
0658     });
0659     return result;
0660   };
0661 
0662   // Fill in a given object with default properties.
0663   _.defaults = function(obj) {
0664     each(slice.call(arguments, 1), function(source) {
0665       for (var prop in source) {
0666         if (obj[prop] == null) obj[prop] = source[prop];
0667       }
0668     });
0669     return obj;
0670   };
0671 
0672   // Create a (shallow-cloned) duplicate of an object.
0673   _.clone = function(obj) {
0674     if (!_.isObject(obj)) return obj;
0675     return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
0676   };
0677 
0678   // Invokes interceptor with the obj, and then returns obj.
0679   // The primary purpose of this method is to "tap into" a method chain, in
0680   // order to perform operations on intermediate results within the chain.
0681   _.tap = function(obj, interceptor) {
0682     interceptor(obj);
0683     return obj;
0684   };
0685 
0686   // Internal recursive comparison function.
0687   function eq(a, b, stack) {
0688     // Identical objects are equal. `0 === -0`, but they aren't identical.
0689     // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
0690     if (a === b) return a !== 0 || 1 / a == 1 / b;
0691     // A strict comparison is necessary because `null == undefined`.
0692     if (a == null || b == null) return a === b;
0693     // Unwrap any wrapped objects.
0694     if (a._chain) a = a._wrapped;
0695     if (b._chain) b = b._wrapped;
0696     // Invoke a custom `isEqual` method if one is provided.
0697     if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
0698     if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
0699     // Compare `[[Class]]` names.
0700     var className = toString.call(a);
0701     if (className != toString.call(b)) return false;
0702     switch (className) {
0703       // Strings, numbers, dates, and booleans are compared by value.
0704       case '[object String]':
0705         // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
0706         // equivalent to `new String("5")`.
0707         return a == String(b);
0708       case '[object Number]':
0709         // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
0710         // other numeric values.
0711         return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
0712       case '[object Date]':
0713       case '[object Boolean]':
0714         // Coerce dates and booleans to numeric primitive values. Dates are compared by their
0715         // millisecond representations. Note that invalid dates with millisecond representations
0716         // of `NaN` are not equivalent.
0717         return +a == +b;
0718       // RegExps are compared by their source patterns and flags.
0719       case '[object RegExp]':
0720         return a.source == b.source &&
0721                a.global == b.global &&
0722                a.multiline == b.multiline &&
0723                a.ignoreCase == b.ignoreCase;
0724     }
0725     if (typeof a != 'object' || typeof b != 'object') return false;
0726     // Assume equality for cyclic structures. The algorithm for detecting cyclic
0727     // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
0728     var length = stack.length;
0729     while (length--) {
0730       // Linear search. Performance is inversely proportional to the number of
0731       // unique nested structures.
0732       if (stack[length] == a) return true;
0733     }
0734     // Add the first object to the stack of traversed objects.
0735     stack.push(a);
0736     var size = 0, result = true;
0737     // Recursively compare objects and arrays.
0738     if (className == '[object Array]') {
0739       // Compare array lengths to determine if a deep comparison is necessary.
0740       size = a.length;
0741       result = size == b.length;
0742       if (result) {
0743         // Deep compare the contents, ignoring non-numeric properties.
0744         while (size--) {
0745           // Ensure commutative equality for sparse arrays.
0746           if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
0747         }
0748       }
0749     } else {
0750       // Objects with different constructors are not equivalent.
0751       if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) return false;
0752       // Deep compare objects.
0753       for (var key in a) {
0754         if (_.has(a, key)) {
0755           // Count the expected number of properties.
0756           size++;
0757           // Deep compare each member.
0758           if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
0759         }
0760       }
0761       // Ensure that both objects contain the same number of properties.
0762       if (result) {
0763         for (key in b) {
0764           if (_.has(b, key) && !(size--)) break;
0765         }
0766         result = !size;
0767       }
0768     }
0769     // Remove the first object from the stack of traversed objects.
0770     stack.pop();
0771     return result;
0772   }
0773 
0774   // Perform a deep comparison to check if two objects are equal.
0775   _.isEqual = function(a, b) {
0776     return eq(a, b, []);
0777   };
0778 
0779   // Is a given array, string, or object empty?
0780   // An "empty" object has no enumerable own-properties.
0781   _.isEmpty = function(obj) {
0782     if (obj == null) return true;
0783     if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
0784     for (var key in obj) if (_.has(obj, key)) return false;
0785     return true;
0786   };
0787 
0788   // Is a given value a DOM element?
0789   _.isElement = function(obj) {
0790     return !!(obj && obj.nodeType == 1);
0791   };
0792 
0793   // Is a given value an array?
0794   // Delegates to ECMA5's native Array.isArray
0795   _.isArray = nativeIsArray || function(obj) {
0796     return toString.call(obj) == '[object Array]';
0797   };
0798 
0799   // Is a given variable an object?
0800   _.isObject = function(obj) {
0801     return obj === Object(obj);
0802   };
0803 
0804   // Is a given variable an arguments object?
0805   _.isArguments = function(obj) {
0806     return toString.call(obj) == '[object Arguments]';
0807   };
0808   if (!_.isArguments(arguments)) {
0809     _.isArguments = function(obj) {
0810       return !!(obj && _.has(obj, 'callee'));
0811     };
0812   }
0813 
0814   // Is a given value a function?
0815   _.isFunction = function(obj) {
0816     return toString.call(obj) == '[object Function]';
0817   };
0818 
0819   // Is a given value a string?
0820   _.isString = function(obj) {
0821     return toString.call(obj) == '[object String]';
0822   };
0823 
0824   // Is a given value a number?
0825   _.isNumber = function(obj) {
0826     return toString.call(obj) == '[object Number]';
0827   };
0828 
0829   // Is a given object a finite number?
0830   _.isFinite = function(obj) {
0831     return _.isNumber(obj) && isFinite(obj);
0832   };
0833 
0834   // Is the given value `NaN`?
0835   _.isNaN = function(obj) {
0836     // `NaN` is the only value for which `===` is not reflexive.
0837     return obj !== obj;
0838   };
0839 
0840   // Is a given value a boolean?
0841   _.isBoolean = function(obj) {
0842     return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
0843   };
0844 
0845   // Is a given value a date?
0846   _.isDate = function(obj) {
0847     return toString.call(obj) == '[object Date]';
0848   };
0849 
0850   // Is the given value a regular expression?
0851   _.isRegExp = function(obj) {
0852     return toString.call(obj) == '[object RegExp]';
0853   };
0854 
0855   // Is a given value equal to null?
0856   _.isNull = function(obj) {
0857     return obj === null;
0858   };
0859 
0860   // Is a given variable undefined?
0861   _.isUndefined = function(obj) {
0862     return obj === void 0;
0863   };
0864 
0865   // Has own property?
0866   _.has = function(obj, key) {
0867     return hasOwnProperty.call(obj, key);
0868   };
0869 
0870   // Utility Functions
0871   // -----------------
0872 
0873   // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
0874   // previous owner. Returns a reference to the Underscore object.
0875   _.noConflict = function() {
0876     root._ = previousUnderscore;
0877     return this;
0878   };
0879 
0880   // Keep the identity function around for default iterators.
0881   _.identity = function(value) {
0882     return value;
0883   };
0884 
0885   // Run a function **n** times.
0886   _.times = function (n, iterator, context) {
0887     for (var i = 0; i < n; i++) iterator.call(context, i);
0888   };
0889 
0890   // Escape a string for HTML interpolation.
0891   _.escape = function(string) {
0892     return (''+string).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#x27;').replace(/\//g,'&#x2F;');
0893   };
0894 
0895   // If the value of the named property is a function then invoke it;
0896   // otherwise, return it.
0897   _.result = function(object, property) {
0898     if (object == null) return null;
0899     var value = object[property];
0900     return _.isFunction(value) ? value.call(object) : value;
0901   };
0902 
0903   // Add your own custom functions to the Underscore object, ensuring that
0904   // they're correctly added to the OOP wrapper as well.
0905   _.mixin = function(obj) {
0906     each(_.functions(obj), function(name){
0907       addToWrapper(name, _[name] = obj[name]);
0908     });
0909   };
0910 
0911   // Generate a unique integer id (unique within the entire client session).
0912   // Useful for temporary DOM ids.
0913   var idCounter = 0;
0914   _.uniqueId = function(prefix) {
0915     var id = idCounter++;
0916     return prefix ? prefix + id : id;
0917   };
0918 
0919   // By default, Underscore uses ERB-style template delimiters, change the
0920   // following template settings to use alternative delimiters.
0921   _.templateSettings = {
0922     evaluate    : /<%([\s\S]+?)%>/g,
0923     interpolate : /<%=([\s\S]+?)%>/g,
0924     escape      : /<%-([\s\S]+?)%>/g
0925   };
0926 
0927   // When customizing `templateSettings`, if you don't want to define an
0928   // interpolation, evaluation or escaping regex, we need one that is
0929   // guaranteed not to match.
0930   var noMatch = /.^/;
0931 
0932   // Certain characters need to be escaped so that they can be put into a
0933   // string literal.
0934   var escapes = {
0935     '\\': '\\',
0936     "'": "'",
0937     'r': '\r',
0938     'n': '\n',
0939     't': '\t',
0940     'u2028': '\u2028',
0941     'u2029': '\u2029'
0942   };
0943 
0944   for (var p in escapes) escapes[escapes[p]] = p;
0945   var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
0946   var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
0947 
0948   // Within an interpolation, evaluation, or escaping, remove HTML escaping
0949   // that had been previously added.
0950   var unescape = function(code) {
0951     return code.replace(unescaper, function(match, escape) {
0952       return escapes[escape];
0953     });
0954   };
0955 
0956   // JavaScript micro-templating, similar to John Resig's implementation.
0957   // Underscore templating handles arbitrary delimiters, preserves whitespace,
0958   // and correctly escapes quotes within interpolated code.
0959   _.template = function(text, data, settings) {
0960     settings = _.defaults(settings || {}, _.templateSettings);
0961 
0962     // Compile the template source, taking care to escape characters that
0963     // cannot be included in a string literal and then unescape them in code
0964     // blocks.
0965     var source = "__p+='" + text
0966       .replace(escaper, function(match) {
0967         return '\\' + escapes[match];
0968       })
0969       .replace(settings.escape || noMatch, function(match, code) {
0970         return "'+\n_.escape(" + unescape(code) + ")+\n'";
0971       })
0972       .replace(settings.interpolate || noMatch, function(match, code) {
0973         return "'+\n(" + unescape(code) + ")+\n'";
0974       })
0975       .replace(settings.evaluate || noMatch, function(match, code) {
0976         return "';\n" + unescape(code) + "\n;__p+='";
0977       }) + "';\n";
0978 
0979     // If a variable is not specified, place data values in local scope.
0980     if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
0981 
0982     source = "var __p='';" +
0983       "var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n" +
0984       source + "return __p;\n";
0985 
0986     var render = new Function(settings.variable || 'obj', '_', source);
0987     if (data) return render(data, _);
0988     var template = function(data) {
0989       return render.call(this, data, _);
0990     };
0991 
0992     // Provide the compiled function source as a convenience for build time
0993     // precompilation.
0994     template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
0995       source + '}';
0996 
0997     return template;
0998   };
0999 
1000   // Add a "chain" function, which will delegate to the wrapper.
1001   _.chain = function(obj) {
1002     return _(obj).chain();
1003   };
1004 
1005   // The OOP Wrapper
1006   // ---------------
1007 
1008   // If Underscore is called as a function, it returns a wrapped object that
1009   // can be used OO-style. This wrapper holds altered versions of all the
1010   // underscore functions. Wrapped objects may be chained.
1011   var wrapper = function(obj) { this._wrapped = obj; };
1012 
1013   // Expose `wrapper.prototype` as `_.prototype`
1014   _.prototype = wrapper.prototype;
1015 
1016   // Helper function to continue chaining intermediate results.
1017   var result = function(obj, chain) {
1018     return chain ? _(obj).chain() : obj;
1019   };
1020 
1021   // A method to easily add functions to the OOP wrapper.
1022   var addToWrapper = function(name, func) {
1023     wrapper.prototype[name] = function() {
1024       var args = slice.call(arguments);
1025       unshift.call(args, this._wrapped);
1026       return result(func.apply(_, args), this._chain);
1027     };
1028   };
1029 
1030   // Add all of the Underscore functions to the wrapper object.
1031   _.mixin(_);
1032 
1033   // Add all mutator Array functions to the wrapper.
1034   each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
1035     var method = ArrayProto[name];
1036     wrapper.prototype[name] = function() {
1037       var wrapped = this._wrapped;
1038       method.apply(wrapped, arguments);
1039       var length = wrapped.length;
1040       if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
1041       return result(wrapped, this._chain);
1042     };
1043   });
1044 
1045   // Add all accessor Array functions to the wrapper.
1046   each(['concat', 'join', 'slice'], function(name) {
1047     var method = ArrayProto[name];
1048     wrapper.prototype[name] = function() {
1049       return result(method.apply(this._wrapped, arguments), this._chain);
1050     };
1051   });
1052 
1053   // Start chaining a wrapped Underscore object.
1054   wrapper.prototype.chain = function() {
1055     this._chain = true;
1056     return this;
1057   };
1058 
1059   // Extracts the result from a wrapped and chained object.
1060   wrapper.prototype.value = function() {
1061     return this._wrapped;
1062   };
1063   return _;
1064 }).call({});
1065 /**
1066  * Core Emmet object, available in global scope
1067  */
1068 var emmet = (function(global) {
1069         var defaultSyntax = 'html';
1070         var defaultProfile = 'plain';
1071         
1072         if (typeof _ == 'undefined') {
1073                 try {
1074                         // avoid collisions with RequireJS loader
1075                         // also, JS obfuscators tends to translate
1076                         // a["name"] to a.name, which also breaks RequireJS
1077                         _ = global[['require'][0]]('underscore'); // node.js
1078                 } catch (e) {}
1079         }
1080 
1081         if (typeof _ == 'undefined') {
1082                 throw 'Cannot access to Underscore.js lib';
1083         }
1084 
1085         /** List of registered modules */
1086         var modules = {
1087                 _ : _
1088         };
1089         
1090         /**
1091          * Shared empty constructor function to aid in prototype-chain creation.
1092          */
1093         var ctor = function(){};
1094         
1095         /**
1096          * Helper function to correctly set up the prototype chain, for subclasses.
1097          * Similar to `goog.inherits`, but uses a hash of prototype properties and
1098          * class properties to be extended.
1099          * Took it from Backbone.
1100          * @param {Object} parent
1101          * @param {Object} protoProps
1102          * @param {Object} staticProps
1103          * @returns {Object}
1104          */
1105         function inherits(parent, protoProps, staticProps) {
1106                 var child;
1107 
1108                 // The constructor function for the new subclass is either defined by
1109                 // you (the "constructor" property in your `extend` definition), or
1110                 // defaulted by us to simply call the parent's constructor.
1111                 if (protoProps && protoProps.hasOwnProperty('constructor')) {
1112                         child = protoProps.constructor;
1113                 } else {
1114                         child = function() {
1115                                 parent.apply(this, arguments);
1116                         };
1117                 }
1118 
1119                 // Inherit class (static) properties from parent.
1120                 _.extend(child, parent);
1121 
1122                 // Set the prototype chain to inherit from `parent`, without calling
1123                 // `parent`'s constructor function.
1124                 ctor.prototype = parent.prototype;
1125                 child.prototype = new ctor();
1126 
1127                 // Add prototype properties (instance properties) to the subclass,
1128                 // if supplied.
1129                 if (protoProps)
1130                         _.extend(child.prototype, protoProps);
1131 
1132                 // Add static properties to the constructor function, if supplied.
1133                 if (staticProps)
1134                         _.extend(child, staticProps);
1135 
1136                 // Correctly set child's `prototype.constructor`.
1137                 child.prototype.constructor = child;
1138 
1139                 // Set a convenience property in case the parent's prototype is needed
1140                 // later.
1141                 child.__super__ = parent.prototype;
1142 
1143                 return child;
1144         }
1145         
1146         /**
1147          * @type Function Function that loads module definition if it's not defined
1148          */
1149         var moduleLoader = null;
1150         
1151         /**
1152          * Generic Emmet module loader (actually, it doesn’t load anything, just 
1153          * returns module reference). Not using `require` name to avoid conflicts
1154          * with Node.js and RequireJS
1155          */
1156         function r(name) {
1157                 if (!(name in modules) && moduleLoader)
1158                         moduleLoader(name);
1159                 
1160                 return modules[name];
1161         }
1162         
1163         return {
1164                 /**
1165                  * Simple, AMD-like module definition. The module will be added into
1166                  * <code>emmet</code> object and will be available via
1167                  * <code>emmet.require(name)</code> or <code>emmet[name]</code>
1168                  * @param {String} name
1169                  * @param {Function} factory
1170                  * @memberOf emmet
1171                  */
1172                 define: function(name, factory) {
1173                         // do not let redefine existing properties
1174                         if (!(name in modules)) {
1175                                 modules[name] = _.isFunction(factory) 
1176                                         ? this.exec(factory)
1177                                         : factory;
1178                         }
1179                 },
1180                 
1181                 /**
1182                  * Returns reference to Emmet module
1183                  * @param {String} name Module name
1184                  */
1185                 require: r,
1186                 
1187                 /**
1188                  * Helper method that just executes passed function but with all 
1189                  * important arguments like 'require' and '_'
1190                  * @param {Function} fn
1191                  * @param {Object} context Execution context
1192                  */
1193                 exec: function(fn, context) {
1194                         return fn.call(context || global, _.bind(r, this), _, this);
1195                 },
1196                 
1197                 /**
1198                  * The self-propagating extend function for classes.
1199                  * Took it from Backbone 
1200                  * @param {Object} protoProps
1201                  * @param {Object} classProps
1202                  * @returns {Object}
1203                  */
1204                 extend: function(protoProps, classProps) {
1205                         var child = inherits(this, protoProps, classProps);
1206                         child.extend = this.extend;
1207                         // a hack required to WSH inherit `toString` method
1208                         if (protoProps.hasOwnProperty('toString'))
1209                                 child.prototype.toString = protoProps.toString;
1210                         return child;
1211                 },
1212                 
1213                 /**
1214                  * The essential function that expands Emmet abbreviation
1215                  * @param {String} abbr Abbreviation to parse
1216                  * @param {String} syntax Abbreviation's context syntax
1217                  * @param {String} profile Output profile (or its name)
1218                  * @param {Object} contextNode Contextual node where abbreviation is
1219                  * written
1220                  * @return {String}
1221                  */
1222                 expandAbbreviation: function(abbr, syntax, profile, contextNode) {
1223                         if (!abbr) return '';
1224                         
1225                         syntax = syntax || defaultSyntax;
1226 //                      profile = profile || defaultProfile;
1227                         
1228                         var filters = r('filters');
1229                         var parser = r('abbreviationParser');
1230                         
1231                         profile = r('profile').get(profile, syntax);
1232                         r('tabStops').resetTabstopIndex();
1233                         
1234                         var data = filters.extractFromAbbreviation(abbr);
1235                         var outputTree = parser.parse(data[0], {
1236                                 syntax: syntax, 
1237                                 contextNode: contextNode
1238                         });
1239                         
1240                         var filtersList = filters.composeList(syntax, profile, data[1]);
1241                         filters.apply(outputTree, filtersList, profile);
1242                         return outputTree.valueOf();
1243                 },
1244                 
1245                 /**
1246                  * Returns default syntax name used in abbreviation engine
1247                  * @returns {String}
1248                  */
1249                 defaultSyntax: function() {
1250                         return defaultSyntax;
1251                 },
1252                 
1253                 /**
1254                  * Returns default profile name used in abbreviation engine
1255                  * @returns {String}
1256                  */
1257                 defaultProfile: function() {
1258                         return defaultProfile;
1259                 },
1260                 
1261                 /**
1262                  * Log message into console if it exists
1263                  */
1264                 log: function() {
1265                         if (global.console && global.console.log)
1266                                 global.console.log.apply(global.console, arguments);
1267                 },
1268                 
1269                 /**
1270                  * Setups function that should synchronously load undefined modules
1271                  * @param {Function} fn
1272                  */
1273                 setModuleLoader: function(fn) {
1274                         moduleLoader = fn;
1275                 }
1276         };
1277 })(this);
1278 
1279 // export core for Node.JS
1280 if (typeof exports !== 'undefined') {
1281         if (typeof module !== 'undefined' && module.exports) {
1282                 exports = module.exports = emmet;
1283         }
1284         exports.emmet = emmet;
1285 }
1286 
1287 // export as Require.js module
1288 if (typeof define !== 'undefined') {
1289         define(emmet);
1290 }/**
1291  * Emmet abbreviation parser.
1292  * Takes string abbreviation and recursively parses it into a tree. The parsed 
1293  * tree can be transformed into a string representation with 
1294  * <code>toString()</code> method. Note that string representation is defined
1295  * by custom processors (called <i>filters</i>), not by abbreviation parser 
1296  * itself.
1297  * 
1298  * This module can be extended with custom pre-/post-processors to shape-up
1299  * final tree or its representation. Actually, many features of abbreviation 
1300  * engine are defined in other modules as tree processors
1301  * 
1302  * 
1303  * @author Sergey Chikuyonok (serge.che@gmail.com)
1304  * @link http://chikuyonok.ru
1305  * @memberOf __abbreviationParser
1306  * @constructor
1307  * @param {Function} require
1308  * @param {Underscore} _
1309  */
1310 emmet.define('abbreviationParser', function(require, _) {
1311         var reValidName = /^[\w\-\$\:@\!%]+\+?$/i;
1312         var reWord = /[\w\-:\$@]/;
1313         
1314         var pairs = {
1315                 '[': ']',
1316                 '(': ')',
1317                 '{': '}'
1318         };
1319         
1320         var spliceFn = Array.prototype.splice;
1321         
1322         var preprocessors = [];
1323         var postprocessors = [];
1324         var outputProcessors = [];
1325         
1326         /**
1327          * @type AbbreviationNode
1328          */
1329         function AbbreviationNode(parent) {
1330                 /** @type AbbreviationNode */
1331                 this.parent = null;
1332                 this.children = [];
1333                 this._attributes = [];
1334                 
1335                 /** @type String Raw abbreviation for current node */
1336                 this.abbreviation = '';
1337                 this.counter = 1;
1338                 this._name = null;
1339                 this._text = '';
1340                 this.repeatCount = 1;
1341                 this.hasImplicitRepeat = false;
1342                 
1343                 /** Custom data dictionary */
1344                 this._data = {};
1345                 
1346                 // output properties
1347                 this.start = '';
1348                 this.end = '';
1349                 this.content = '';
1350                 this.padding = '';
1351         }
1352         
1353         AbbreviationNode.prototype = {
1354                 /**
1355                  * Adds passed node as child or creates new child
1356                  * @param {AbbreviationNode} child
1357                  * @param {Number} position Index in children array where child should 
1358                  * be inserted
1359                  * @return {AbbreviationNode}
1360                  */
1361                 addChild: function(child, position) {
1362                         child = child || new AbbreviationNode();
1363                         child.parent = this;
1364                         
1365                         if (_.isUndefined(position)) {
1366                                 this.children.push(child);
1367                         } else {
1368                                 this.children.splice(position, 0, child);
1369                         }
1370                         
1371                         return child;
1372                 },
1373                 
1374                 /**
1375                  * Creates a deep copy of current node
1376                  * @returns {AbbreviationNode}
1377                  */
1378                 clone: function() {
1379                         var node = new AbbreviationNode();
1380                         var attrs = ['abbreviation', 'counter', '_name', '_text', 'repeatCount', 'hasImplicitRepeat', 'start', 'end', 'content', 'padding'];
1381                         _.each(attrs, function(a) {
1382                                 node[a] = this[a];
1383                         }, this);
1384                         
1385                         // clone attributes
1386                         node._attributes = _.map(this._attributes, function(attr) {
1387                                 return _.clone(attr);
1388                         });
1389                         
1390                         node._data = _.clone(this._data);
1391                         
1392                         // clone children
1393                         node.children = _.map(this.children, function(child) {
1394                                 child = child.clone();
1395                                 child.parent = node;
1396                                 return child;
1397                         });
1398                         
1399                         return node;
1400                 },
1401                 
1402                 /**
1403                  * Removes current node from parent‘s child list
1404                  * @returns {AbbreviationNode} Current node itself
1405                  */
1406                 remove: function() {
1407                         if (this.parent) {
1408                                 this.parent.children = _.without(this.parent.children, this);
1409                         }
1410                         
1411                         return this;
1412                 },
1413                 
1414                 /**
1415                  * Replaces current node in parent‘s children list with passed nodes
1416                  * @param {AbbreviationNode} node Replacement node or array of nodes
1417                  */
1418                 replace: function() {
1419                         var parent = this.parent;
1420                         var ix = _.indexOf(parent.children, this);
1421                         var items = _.flatten(arguments);
1422                         spliceFn.apply(parent.children, [ix, 1].concat(items));
1423                         
1424                         // update parent
1425                         _.each(items, function(item) {
1426                                 item.parent = parent;
1427                         });
1428                 },
1429                 
1430                 /**
1431                  * Recursively sets <code>property</code> to <code>value</code> of current
1432                  * node and its children 
1433                  * @param {String} name Property to update
1434                  * @param {Object} value New property value
1435                  */
1436                 updateProperty: function(name, value) {
1437                         this[name] = value;
1438                         _.each(this.children, function(child) {
1439                                 child.updateProperty(name, value);
1440                         });
1441                         
1442                         return this;
1443                 },
1444                 
1445                 /**
1446                  * Finds first child node that matches truth test for passed 
1447                  * <code>fn</code> function
1448                  * @param {Function} fn
1449                  * @returns {AbbreviationNode}
1450                  */
1451                 find: function(fn) {
1452                         return this.findAll(fn)[0];
1453 //                      if (!_.isFunction(fn)) {
1454 //                              var elemName = fn.toLowerCase();
1455 //                              fn = function(item) {return item.name().toLowerCase() == elemName;};
1456 //                      }
1457 //                      
1458 //                      var result = null;
1459 //                      _.find(this.children, function(child) {
1460 //                              if (fn(child)) {
1461 //                                      return result = child;
1462 //                              }
1463 //                              
1464 //                              return result = child.find(fn);
1465 //                      });
1466 //                      
1467 //                      return result;
1468                 },
1469                 
1470                 /**
1471                  * Finds all child nodes that matches truth test for passed 
1472                  * <code>fn</code> function
1473                  * @param {Function} fn
1474                  * @returns {Array}
1475                  */
1476                 findAll: function(fn) {
1477                         if (!_.isFunction(fn)) {
1478                                 var elemName = fn.toLowerCase();
1479                                 fn = function(item) {return item.name().toLowerCase() == elemName;};
1480                         }
1481                                 
1482                         var result = [];
1483                         _.each(this.children, function(child) {
1484                                 if (fn(child))
1485                                         result.push(child);
1486                                 
1487                                 result = result.concat(child.findAll(fn));
1488                         });
1489                         
1490                         return _.compact(result);
1491                 },
1492                 
1493                 /**
1494                  * Sets/gets custom data
1495                  * @param {String} name
1496                  * @param {Object} value
1497                  * @returns {Object}
1498                  */
1499                 data: function(name, value) {
1500                         if (arguments.length == 2) {
1501                                 this._data[name] = value;
1502                                 
1503                                 if (name == 'resource' && require('elements').is(value, 'snippet')) {
1504                                         // setting snippet as matched resource: update `content`
1505                                         // property with snippet value
1506                                         this.content = value.data;
1507                                         if (this._text) {
1508                                                 this.content = require('abbreviationUtils')
1509                                                         .insertChildContent(value.data, this._text);
1510                                         }
1511                                 }
1512                         }
1513                         
1514                         return this._data[name];
1515                 },
1516                 
1517                 /**
1518                  * Returns name of current node
1519                  * @returns {String}
1520                  */
1521                 name: function() {
1522                         var res = this.matchedResource();
1523                         if (require('elements').is(res, 'element')) {
1524                                 return res.name;
1525                         }
1526                         
1527                         return this._name;
1528                 },
1529                 
1530                 /**
1531                  * Returns list of attributes for current node
1532                  * @returns {Array}
1533                  */
1534                 attributeList: function() {
1535                         var attrs = [];
1536                         
1537                         var res = this.matchedResource();
1538                         if (require('elements').is(res, 'element') && _.isArray(res.attributes)) {
1539                                 attrs = attrs.concat(res.attributes);
1540                         }
1541                         
1542                         return optimizeAttributes(attrs.concat(this._attributes));
1543                 },
1544                 
1545                 /**
1546                  * Returns or sets attribute value
1547                  * @param {String} name Attribute name
1548                  * @param {String} value New attribute value
1549                  * @returns {String}
1550                  */
1551                 attribute: function(name, value) {
1552                         if (arguments.length == 2) {
1553                                 // modifying attribute
1554                                 var ix = _.indexOf(_.pluck(this._attributes, 'name'), name.toLowerCase());
1555                                 if (~ix) {
1556                                         this._attributes[ix].value = value;
1557                                 } else {
1558                                         this._attributes.push({
1559                                                 name: name,
1560                                                 value: value
1561                                         });
1562                                 }
1563                         }
1564                         
1565                         return (_.find(this.attributeList(), function(attr) {
1566                                 return attr.name == name;
1567                         }) || {}).value;
1568                 },
1569                 
1570                 /**
1571                  * Returns reference to the matched <code>element</code>, if any.
1572                  * See {@link elements} module for a list of available elements
1573                  * @returns {Object}
1574                  */
1575                 matchedResource: function() {
1576                         return this.data('resource');
1577                 },
1578                 
1579                 /**
1580                  * Returns index of current node in parent‘s children list
1581                  * @returns {Number}
1582                  */
1583                 index: function() {
1584                         return this.parent ? _.indexOf(this.parent.children, this) : -1;
1585                 },
1586                 
1587                 /**
1588                  * Sets how many times current element should be repeated
1589                  * @private
1590                  */
1591                 _setRepeat: function(count) {
1592                         if (count) {
1593                                 this.repeatCount = parseInt(count, 10) || 1;
1594                         } else {
1595                                 this.hasImplicitRepeat = true;
1596                         }
1597                 },
1598                 
1599                 /**
1600                  * Sets abbreviation that belongs to current node
1601                  * @param {String} abbr
1602                  */
1603                 setAbbreviation: function(abbr) {
1604                         abbr = abbr || '';
1605                         
1606                         var that = this;
1607                         
1608                         // find multiplier
1609                         abbr = abbr.replace(/\*(\d+)?$/, function(str, repeatCount) {
1610                                 that._setRepeat(repeatCount);
1611                                 return '';
1612                         });
1613                         
1614                         this.abbreviation = abbr;
1615                         
1616                         var abbrText = extractText(abbr);
1617                         if (abbrText) {
1618                                 abbr = abbrText.element;
1619                                 this.content = this._text = abbrText.text;
1620                         }
1621                         
1622                         var abbrAttrs = parseAttributes(abbr);
1623                         if (abbrAttrs) {
1624                                 abbr = abbrAttrs.element;
1625                                 this._attributes = abbrAttrs.attributes;
1626                         }
1627                         
1628                         this._name = abbr;
1629                         
1630                         // validate name
1631                         if (this._name && !reValidName.test(this._name)) {
1632                                 throw 'Invalid abbreviation';
1633                         }
1634                 },
1635                 
1636                 /**
1637                  * Returns string representation of current node
1638                  * @return {String}
1639                  */
1640                 valueOf: function() {
1641                         var utils = require('utils');
1642                         
1643                         var start = this.start;
1644                         var end = this.end;
1645                         var content = this.content;
1646                         
1647                         // apply output processors
1648                         var node = this;
1649                         _.each(outputProcessors, function(fn) {
1650                                 start = fn(start, node, 'start');
1651                                 content = fn(content, node, 'content');
1652                                 end = fn(end, node, 'end');
1653                         });
1654                         
1655                         
1656                         var innerContent = _.map(this.children, function(child) {
1657                                 return child.valueOf();
1658                         }).join('');
1659                         
1660                         content = require('abbreviationUtils').insertChildContent(content, innerContent, {
1661                                 keepVariable: false
1662                         });
1663                         
1664                         return start + utils.padString(content, this.padding) + end;
1665                 },
1666 
1667                 toString: function() {
1668                         return this.valueOf();
1669                 },
1670                 
1671                 /**
1672                  * Check if current node contains children with empty <code>expr</code>
1673                  * property
1674                  * @return {Boolean}
1675                  */
1676                 hasEmptyChildren: function() {
1677                         return !!_.find(this.children, function(child) {
1678                                 return child.isEmpty();
1679                         });
1680                 },
1681                 
1682                 /**
1683                  * Check if current node has implied name that should be resolved
1684                  * @returns {Boolean}
1685                  */
1686                 hasImplicitName: function() {
1687                         return !this._name && !this.isTextNode();
1688                 },
1689                 
1690                 /**
1691                  * Indicates that current element is a grouping one, e.g. has no 
1692                  * representation but serves as a container for other nodes
1693                  * @returns {Boolean}
1694                  */
1695                 isGroup: function() {
1696                         return !this.abbreviation;
1697                 },
1698                 
1699                 /**
1700                  * Indicates empty node (i.e. without abbreviation). It may be a 
1701                  * grouping node and should not be outputted
1702                  * @return {Boolean}
1703                  */
1704                 isEmpty: function() {
1705                         return !this.abbreviation && !this.children.length;
1706                 },
1707                 
1708                 /**
1709                  * Indicates that current node should be repeated
1710                  * @returns {Boolean}
1711                  */
1712                 isRepeating: function() {
1713                         return this.repeatCount > 1 || this.hasImplicitRepeat;
1714                 },
1715                 
1716                 /**
1717                  * Check if current node is a text-only node
1718                  * @return {Boolean}
1719                  */
1720                 isTextNode: function() {
1721                         return !this.name() && !this.attributeList().length;
1722                 },
1723                 
1724                 /**
1725                  * Indicates whether this node may be used to build elements or snippets
1726                  * @returns {Boolean}
1727                  */
1728                 isElement: function() {
1729                         return !this.isEmpty() && !this.isTextNode();
1730                 },
1731                 
1732                 /**
1733                  * Returns latest and deepest child of current tree
1734                  * @returns {AbbreviationNode}
1735                  */
1736                 deepestChild: function() {
1737                         if (!this.children.length)
1738                                 return null;
1739                                 
1740                         var deepestChild = this;
1741                         while (deepestChild.children.length) {
1742                                 deepestChild = _.last(deepestChild.children);
1743                         }
1744                         
1745                         return deepestChild;
1746                 }
1747         };
1748         
1749         /**
1750          * Returns stripped string: a string without first and last character.
1751          * Used for “unquoting” strings
1752          * @param {String} str
1753          * @returns {String}
1754          */
1755         function stripped(str) {
1756                 return str.substring(1, str.length - 1);
1757         }
1758         
1759         function consumeQuotedValue(stream, quote) {
1760                 var ch;
1761                 while ((ch = stream.next())) {
1762                         if (ch === quote)
1763                                 return true;
1764                         
1765                         if (ch == '\\')
1766                                 continue;
1767                 }
1768                 
1769                 return false;
1770         }
1771         
1772         /**
1773          * Parses abbreviation into a tree
1774          * @param {String} abbr
1775          * @returns {AbbreviationNode}
1776          */
1777         function parseAbbreviation(abbr) {
1778                 abbr = require('utils').trim(abbr);
1779                 
1780                 var root = new AbbreviationNode();
1781                 var context = root.addChild(), ch;
1782                 
1783                 /** @type StringStream */
1784                 var stream = require('stringStream').create(abbr);
1785                 var loopProtector = 1000, multiplier;
1786                 var addChild = function(child) {
1787                         context.addChild(child);
1788                 };
1789 
1790                 var consumeAbbr = function() {
1791                         stream.start = stream.pos;
1792                         stream.eatWhile(function(c) {
1793                                 if (c == '[' || c == '{') {
1794                                         if (stream.skipToPair(c, pairs[c])) {
1795                                                 stream.backUp(1);
1796                                                 return true;
1797                                         }
1798                                         
1799                                         throw 'Invalid abbreviation: mo matching "' + pairs[c] + '" found for character at ' + stream.pos;
1800                                 }
1801                                 
1802                                 if (c == '+') {
1803                                         // let's see if this is an expando marker
1804                                         stream.next();
1805                                         var isMarker = stream.eol() ||  ~'+>^*'.indexOf(stream.peek());
1806                                         stream.backUp(1);
1807                                         return isMarker;
1808                                 }
1809                                 
1810                                 return c != '(' && isAllowedChar(c);
1811                         });
1812                 };
1813                 
1814                 while (!stream.eol() && --loopProtector > 0) {
1815                         ch = stream.peek();
1816                         
1817                         switch (ch) {
1818                                 case '(': // abbreviation group
1819                                         stream.start = stream.pos;
1820                                         if (stream.skipToPair('(', ')')) {
1821                                                 var inner = parseAbbreviation(stripped(stream.current()));
1822                                                 if ((multiplier = stream.match(/^\*(\d+)?/, true))) {
1823                                                         context._setRepeat(multiplier[1]);
1824                                                 }
1825                                                 
1826                                                 _.each(inner.children, addChild);
1827                                         } else {
1828                                                 throw 'Invalid abbreviation: mo matching ")" found for character at ' + stream.pos;
1829                                         }
1830                                         break;
1831                                         
1832                                 case '>': // child operator
1833                                         context = context.addChild();
1834                                         stream.next();
1835                                         break;
1836                                         
1837                                 case '+': // sibling operator
1838                                         context = context.parent.addChild();
1839                                         stream.next();
1840                                         break;
1841                                         
1842                                 case '^': // climb up operator
1843                                         var parent = context.parent || context;
1844                                         context = (parent.parent || parent).addChild();
1845                                         stream.next();
1846                                         break;
1847                                         
1848                                 default: // consume abbreviation
1849                                         consumeAbbr();
1850                                         context.setAbbreviation(stream.current());
1851                                         stream.start = stream.pos;
1852                         }
1853                 }
1854                 
1855                 if (loopProtector < 1)
1856                         throw 'Endless loop detected';
1857                 
1858                 return root;
1859         }
1860         
1861         /**
1862          * Extract attributes and their values from attribute set: 
1863          * <code>[attr col=3 title="Quoted string"]</code>
1864          * @param {String} attrSet
1865          * @returns {Array}
1866          */
1867         function extractAttributes(attrSet, attrs) {
1868                 attrSet = require('utils').trim(attrSet);
1869                 var result = [];
1870                 
1871                 /** @type StringStream */
1872                 var stream = require('stringStream').create(attrSet);
1873                 stream.eatSpace();
1874                 
1875                 while (!stream.eol()) {
1876                         stream.start = stream.pos;
1877                         if (stream.eatWhile(reWord)) {
1878                                 var attrName = stream.current();
1879                                 var attrValue = '';
1880                                 if (stream.peek() == '=') {
1881                                         stream.next();
1882                                         stream.start = stream.pos;
1883                                         var quote = stream.peek();
1884                                         
1885                                         if (quote == '"' || quote == "'") {
1886                                                 stream.next();
1887                                                 if (consumeQuotedValue(stream, quote)) {
1888                                                         attrValue = stream.current();
1889                                                         // strip quotes
1890                                                         attrValue = attrValue.substring(1, attrValue.length - 1);
1891                                                 } else {
1892                                                         throw 'Invalid attribute value';
1893                                                 }
1894                                         } else if (stream.eatWhile(/[^\s\]]/)) {
1895                                                 attrValue = stream.current();
1896                                         } else {
1897                                                 throw 'Invalid attribute value';
1898                                         }
1899                                 }
1900                                 
1901                                 result.push({
1902                                         name: attrName, 
1903                                         value: attrValue
1904                                 });
1905                                 stream.eatSpace();
1906                         } else {
1907                                 break;
1908                         }
1909                 }
1910                 
1911                 return result;
1912         }
1913         
1914         /**
1915          * Parses tag attributes extracted from abbreviation. If attributes found, 
1916          * returns object with <code>element</code> and <code>attributes</code>
1917          * properties
1918          * @param {String} abbr
1919          * @returns {Object} Returns <code>null</code> if no attributes found in 
1920          * abbreviation
1921          */
1922         function parseAttributes(abbr) {
1923                 /*
1924                  * Example of incoming data:
1925                  * #header
1926                  * .some.data
1927                  * .some.data#header
1928                  * [attr]
1929                  * #item[attr=Hello other="World"].class
1930                  */
1931                 var result = [];
1932                 var attrMap = {'#': 'id', '.': 'class'};
1933                 var nameEnd = null;
1934                 
1935                 /** @type StringStream */
1936                 var stream = require('stringStream').create(abbr);
1937                 while (!stream.eol()) {
1938                         switch (stream.peek()) {
1939                                 case '#': // id
1940                                 case '.': // class
1941                                         if (nameEnd === null)
1942                                                 nameEnd = stream.pos;
1943                                         
1944                                         var attrName = attrMap[stream.peek()];
1945                                         
1946                                         stream.next();
1947                                         stream.start = stream.pos;
1948                                         stream.eatWhile(reWord);
1949                                         result.push({
1950                                                 name: attrName, 
1951                                                 value: stream.current()
1952                                         });
1953                                         break;
1954                                 case '[': //begin attribute set
1955                                         if (nameEnd === null)
1956                                                 nameEnd = stream.pos;
1957                                         
1958                                         stream.start = stream.pos;
1959                                         if (!stream.skipToPair('[', ']')) 
1960                                                 throw 'Invalid attribute set definition';
1961                                         
1962                                         result = result.concat(
1963                                                 extractAttributes(stripped(stream.current()))
1964                                         );
1965                                         break;
1966                                 default:
1967                                         stream.next();
1968                         }
1969                 }
1970                 
1971                 if (!result.length)
1972                         return null;
1973                 
1974                 return {
1975                         element: abbr.substring(0, nameEnd),
1976                         attributes: optimizeAttributes(result)
1977                 };
1978         }
1979         
1980         /**
1981          * Optimize attribute set: remove duplicates and merge class attributes
1982          * @param attrs
1983          */
1984         function optimizeAttributes(attrs) {
1985                 // clone all attributes to make sure that original objects are 
1986                 // not modified
1987                 attrs  = _.map(attrs, function(attr) {
1988                         return _.clone(attr);
1989                 });
1990                 
1991                 var lookup = {};
1992                 return _.filter(attrs, function(attr) {
1993                         if (!(attr.name in lookup)) {
1994                                 return lookup[attr.name] = attr;
1995                         }
1996                         
1997                         var la = lookup[attr.name];
1998                         
1999                         if (attr.name.toLowerCase() == 'class') {
2000                                 la.value += (la.value.length ? ' ' : '') + attr.value;
2001                         } else {
2002                                 la.value = attr.value;
2003                         }
2004                         
2005                         return false;
2006                 });
2007         }
2008         
2009         /**
2010          * Extract text data from abbreviation: if <code>a{hello}</code> abbreviation
2011          * is passed, returns object <code>{element: 'a', text: 'hello'}</code>.
2012          * If nothing found, returns <code>null</code>
2013          * @param {String} abbr
2014          * 
2015          */
2016         function extractText(abbr) {
2017                 if (!~abbr.indexOf('{'))
2018                         return null;
2019                 
2020                 /** @type StringStream */
2021                 var stream = require('stringStream').create(abbr);
2022                 while (!stream.eol()) {
2023                         switch (stream.peek()) {
2024                                 case '[':
2025                                 case '(':
2026                                         stream.skipToPair(stream.peek(), pairs[stream.peek()]); break;
2027                                         
2028                                 case '{':
2029                                         stream.start = stream.pos;
2030                                         stream.skipToPair('{', '}');
2031                                         return {
2032                                                 element: abbr.substring(0, stream.start),
2033                                                 text: stripped(stream.current())
2034                                         };
2035                                         
2036                                 default:
2037                                         stream.next();
2038                         }
2039                 }
2040         }
2041         
2042         /**
2043          * “Un-rolls“ contents of current node: recursively replaces all repeating 
2044          * children with their repeated clones
2045          * @param {AbbreviationNode} node
2046          * @returns {AbbreviationNode}
2047          */
2048         function unroll(node) {
2049                 for (var i = node.children.length - 1, j, child, maxCount; i >= 0; i--) {
2050                         child = node.children[i];
2051                         
2052                         if (child.isRepeating()) {
2053                                 maxCount = j = child.repeatCount;
2054                                 child.repeatCount = 1;
2055                                 child.updateProperty('counter', 1);
2056                                 child.updateProperty('maxCount', maxCount);
2057                                 while (--j > 0) {
2058                                         child.parent.addChild(child.clone(), i + 1)
2059                                                 .updateProperty('counter', j + 1)
2060                                                 .updateProperty('maxCount', maxCount);
2061                                 }
2062                         }
2063                 }
2064                 
2065                 // to keep proper 'counter' property, we need to walk
2066                 // on children once again
2067                 _.each(node.children, unroll);
2068                 
2069                 return node;
2070         }
2071         
2072         /**
2073          * Optimizes tree node: replaces empty nodes with their children
2074          * @param {AbbreviationNode} node
2075          * @return {AbbreviationNode}
2076          */
2077         function squash(node) {
2078                 for (var i = node.children.length - 1; i >= 0; i--) {
2079                         /** @type AbbreviationNode */
2080                         var n = node.children[i];
2081                         if (n.isGroup()) {
2082                                 n.replace(squash(n).children);
2083                         } else if (n.isEmpty()) {
2084                                 n.remove();
2085                         }
2086                 }
2087                 
2088                 _.each(node.children, squash);
2089                 
2090                 return node;
2091         }
2092         
2093         function isAllowedChar(ch) {
2094                 var charCode = ch.charCodeAt(0);
2095                 var specialChars = '#.*:$-_!@|%';
2096                 
2097                 return (charCode > 64 && charCode < 91)       // uppercase letter
2098                                 || (charCode > 96 && charCode < 123)  // lowercase letter
2099                                 || (charCode > 47 && charCode < 58)   // number
2100                                 || specialChars.indexOf(ch) != -1;    // special character
2101         }
2102         
2103         // XXX add counter replacer function as output processor
2104         outputProcessors.push(function(text, node) {
2105                 return require('utils').replaceCounter(text, node.counter, node.maxCount);
2106         });
2107         
2108         return {
2109                 /**
2110                  * Parses abbreviation into tree with respect of groups, 
2111                  * text nodes and attributes. Each node of the tree is a single 
2112                  * abbreviation. Tree represents actual structure of the outputted 
2113                  * result
2114                  * @memberOf abbreviationParser
2115                  * @param {String} abbr Abbreviation to parse
2116                  * @param {Object} options Additional options for parser and processors
2117                  * 
2118                  * @return {AbbreviationNode}
2119                  */
2120                 parse: function(abbr, options) {
2121                         options = options || {};
2122                         
2123                         var tree = parseAbbreviation(abbr);
2124                         
2125                         if (options.contextNode) {
2126                                 // add info about context node –
2127                                 // a parent XHTML node in editor inside which abbreviation is 
2128                                 // expanded
2129                                 tree._name = options.contextNode.name;
2130                                 var attrLookup = {};
2131                                 _.each(tree._attributes, function(attr) {
2132                                         attrLookup[attr.name] = attr;
2133                                 });
2134                                 
2135                                 _.each(options.contextNode.attributes, function(attr) {
2136                                         if (attr.name in attrLookup) {
2137                                                 attrLookup[attr.name].value = attr.value;
2138                                         } else {
2139                                                 attr = _.clone(attr);
2140                                                 tree._attributes.push(attr);
2141                                                 attrLookup[attr.name] = attr;
2142                                         }
2143                                 });
2144                         }
2145                         
2146                         
2147                         // apply preprocessors
2148                         _.each(preprocessors, function(fn) {
2149                                 fn(tree, options);
2150                         });
2151                         
2152                         tree = squash(unroll(tree));
2153                         
2154                         // apply postprocessors
2155                         _.each(postprocessors, function(fn) {
2156                                 fn(tree, options);
2157                         });
2158                         
2159                         return tree;
2160                 },
2161                 
2162                 AbbreviationNode: AbbreviationNode,
2163                 
2164                 /**
2165                  * Add new abbreviation preprocessor. <i>Preprocessor</i> is a function
2166                  * that applies to a parsed abbreviation tree right after it get parsed.
2167                  * The passed tree is in unoptimized state.
2168                  * @param {Function} fn Preprocessor function. This function receives
2169                  * two arguments: parsed abbreviation tree (<code>AbbreviationNode</code>)
2170                  * and <code>options</code> hash that was passed to <code>parse</code>
2171                  * method
2172                  */
2173                 addPreprocessor: function(fn) {
2174                         if (!_.include(preprocessors, fn))
2175                                 preprocessors.push(fn);
2176                 },
2177                 
2178                 /**
2179                  * Removes registered preprocessor
2180                  */
2181                 removeFilter: function(fn) {
2182                         _.without(preprocessors, fn);
2183                 },
2184                 
2185                 /**
2186                  * Adds new abbreviation postprocessor. <i>Postprocessor</i> is a 
2187                  * functinon that applies to <i>optimized</i> parsed abbreviation tree
2188                  * right before it returns from <code>parse()</code> method
2189                  * @param {Function} fn Postprocessor function. This function receives
2190                  * two arguments: parsed abbreviation tree (<code>AbbreviationNode</code>)
2191                  * and <code>options</code> hash that was passed to <code>parse</code>
2192                  * method
2193                  */
2194                 addPostprocessor: function(fn) {
2195                         if (!_.include(postprocessors, fn))
2196                                 postprocessors.push(fn);
2197                 },
2198                 
2199                 /**
2200                  * Removes registered postprocessor function
2201                  */
2202                 removePostprocessor: function(fn) {
2203                         postprocessors = _.without(postprocessors, fn);
2204                 },
2205                 
2206                 /**
2207                  * Registers output postprocessor. <i>Output processor</i> is a 
2208                  * function that applies to output part (<code>start</code>, 
2209                  * <code>end</code> and <code>content</code>) when 
2210                  * <code>AbbreviationNode.toString()</code> method is called
2211                  */
2212                 addOutputProcessor: function(fn) {
2213                         if (!_.include(outputProcessors, fn))
2214                                 outputProcessors.push(fn);
2215                 },
2216                 
2217                 /**
2218                  * Removes registered output processor
2219                  */
2220                 removeOutputProcessor: function(fn) {
2221                         outputProcessors = _.without(outputProcessors, fn);
2222                 },
2223                 
2224                 /**
2225                  * Check if passed symbol is valid symbol for abbreviation expression
2226                  * @param {String} ch
2227                  * @return {Boolean}
2228                  */
2229                 isAllowedChar: function(ch) {
2230                         ch = String(ch); // convert Java object to JS
2231                         return isAllowedChar(ch) || ~'>+^[](){}'.indexOf(ch);
2232                 }
2233         };
2234 });/**
2235  * Processor function that matches parsed <code>AbbreviationNode</code>
2236  * against resources defined in <code>resource</code> module
2237  * @param {Function} require
2238  * @param {Underscore} _
2239  */ 
2240 emmet.exec(function(require, _) {
2241         /**
2242          * Finds matched resources for child nodes of passed <code>node</code> 
2243          * element. A matched resource is a reference to <i>snippets.json</i> entry
2244          * that describes output of parsed node 
2245          * @param {AbbreviationNode} node
2246          * @param {String} syntax
2247          */
2248         function matchResources(node, syntax) {
2249                 var resources = require('resources');
2250                 var elements = require('elements');
2251                 var parser = require('abbreviationParser');
2252                 
2253                 // do a shallow copy because the children list can be modified during
2254                 // resource matching
2255                 _.each(_.clone(node.children), /** @param {AbbreviationNode} child */ function(child) {
2256                         var r = resources.getMatchedResource(child, syntax);
2257                         if (_.isString(r)) {
2258                                 child.data('resource', elements.create('snippet', r));
2259                         } else if (elements.is(r, 'reference')) {
2260                                 // it’s a reference to another abbreviation:
2261                                 // parse it and insert instead of current child
2262                                 /** @type AbbreviationNode */
2263                                 var subtree = parser.parse(r.data, {
2264                                         syntax: syntax
2265                                 });
2266                                 
2267                                 // if context element should be repeated, check if we need to 
2268                                 // transfer repeated element to specific child node
2269                                 if (child.repeatCount > 1) {
2270                                         var repeatedChildren = subtree.findAll(function(node) {
2271                                                 return node.hasImplicitRepeat;
2272                                         });
2273                                         
2274                                         _.each(repeatedChildren, function(node) {
2275                                                 node.repeatCount = child.repeatCount;
2276                                                 node.hasImplicitRepeat = false;
2277                                         });
2278                                 }
2279                                 
2280                                 // move child‘s children into the deepest child of new subtree
2281                                 var deepestChild = subtree.deepestChild();
2282                                 if (deepestChild) {
2283                                         _.each(child.children, function(c) {
2284                                                 deepestChild.addChild(c);
2285                                         });
2286                                 }
2287                                 
2288                                 // copy current attributes to children
2289                                 _.each(subtree.children, function(node) {
2290                                         _.each(child.attributeList(), function(attr) {
2291                                                 node.attribute(attr.name, attr.value);
2292                                         });
2293                                 });
2294                                 
2295                                 child.replace(subtree.children);
2296                         } else {
2297                                 child.data('resource', r);
2298                         }
2299                         
2300                         matchResources(child, syntax);
2301                 });
2302         }
2303         
2304         // XXX register abbreviation filter that creates references to resources
2305         // on abbreviation nodes
2306         /**
2307          * @param {AbbreviationNode} tree
2308          */
2309         require('abbreviationParser').addPreprocessor(function(tree, options) {
2310                 var syntax = options.syntax || emmet.defaultSyntax();
2311                 matchResources(tree, syntax);
2312         });
2313         
2314 });/**
2315  * Pasted content abbreviation processor. A pasted content is a content that
2316  * should be inserted into implicitly repeated abbreviation nodes.
2317  * This processor powers “Wrap With Abbreviation” action
2318  * @param {Function} require
2319  * @param {Underscore} _
2320  */
2321 emmet.exec(function(require, _) {
2322         var parser = require('abbreviationParser');
2323         var outputPlaceholder = '$#';
2324         
2325         /**
2326          * Locates output placeholders inside text
2327          * @param {String} text
2328          * @returns {Array} Array of ranges of output placeholder in text
2329          */
2330         function locateOutputPlaceholder(text) {
2331                 var range = require('range');
2332                 var result = [];
2333                 
2334                 /** @type StringStream */
2335                 var stream = require('stringStream').create(text);
2336                 
2337                 while (!stream.eol()) {
2338                         if (stream.peek() == '\\') {
2339                                 stream.next();
2340                         } else {
2341                                 stream.start = stream.pos;
2342                                 if (stream.match(outputPlaceholder, true)) {
2343                                         result.push(range.create(stream.start, outputPlaceholder));
2344                                         continue;
2345                                 }
2346                         }
2347                         stream.next();
2348                 }
2349                 
2350                 return result;
2351         }
2352         
2353         /**
2354          * Replaces output placeholders inside <code>source</code> with 
2355          * <code>value</code>
2356          * @param {String} source
2357          * @param {String} value
2358          * @returns {String}
2359          */
2360         function replaceOutputPlaceholders(source, value) {
2361                 var utils = require('utils');
2362                 var ranges = locateOutputPlaceholder(source);
2363                 
2364                 ranges.reverse();
2365                 _.each(ranges, function(r) {
2366                         source = utils.replaceSubstring(source, value, r);
2367                 });
2368                 
2369                 return source;
2370         }
2371         
2372         /**
2373          * Check if parsed node contains output placeholder – a target where
2374          * pasted content should be inserted
2375          * @param {AbbreviationNode} node
2376          * @returns {Boolean}
2377          */
2378         function hasOutputPlaceholder(node) {
2379                 if (locateOutputPlaceholder(node.content).length)
2380                         return true;
2381                 
2382                 // check if attributes contains placeholder
2383                 return !!_.find(node.attributeList(), function(attr) {
2384                         return !!locateOutputPlaceholder(attr.value).length;
2385                 });
2386         }
2387         
2388         /**
2389          * Insert pasted content into correct positions of parsed node
2390          * @param {AbbreviationNode} node
2391          * @param {String} content
2392          * @param {Boolean} overwrite Overwrite node content if no value placeholders
2393          * found instead of appending to existing content
2394          */
2395         function insertPastedContent(node, content, overwrite) {
2396                 var nodesWithPlaceholders = node.findAll(function(item) {
2397                         return hasOutputPlaceholder(item);
2398                 });
2399                 
2400                 if (hasOutputPlaceholder(node))
2401                         nodesWithPlaceholders.unshift(node);
2402                 
2403                 if (nodesWithPlaceholders.length) {
2404                         _.each(nodesWithPlaceholders, function(item) {
2405                                 item.content = replaceOutputPlaceholders(item.content, content);
2406                                 _.each(item._attributes, function(attr) {
2407                                         attr.value = replaceOutputPlaceholders(attr.value, content);
2408                                 });
2409                         });
2410                 } else {
2411                         // on output placeholders in subtree, insert content in the deepest
2412                         // child node
2413                         var deepest = node.deepestChild() || node;
2414                         if (overwrite) {
2415                                 deepest.content = content;
2416                         } else {
2417                                 deepest.content = require('abbreviationUtils').insertChildContent(deepest.content, content);
2418                         }
2419                 }
2420         }
2421         
2422         /**
2423          * @param {AbbreviationNode} tree
2424          * @param {Object} options
2425          */
2426         parser.addPreprocessor(function(tree, options) {
2427                 if (options.pastedContent) {
2428                         var utils = require('utils');
2429                         var lines = _.map(utils.splitByLines(options.pastedContent, true), utils.trim);
2430                         
2431                         // set repeat count for implicitly repeated elements before
2432                         // tree is unrolled
2433                         tree.findAll(function(item) {
2434                                 if (item.hasImplicitRepeat) {
2435                                         item.data('paste', lines);
2436                                         return item.repeatCount = lines.length;
2437                                 }
2438                         });
2439                 }
2440         });
2441         
2442         /**
2443          * @param {AbbreviationNode} tree
2444          * @param {Object} options
2445          */
2446         parser.addPostprocessor(function(tree, options) {
2447                 // for each node with pasted content, update text data
2448                 var targets = tree.findAll(function(item) {
2449                         var pastedContentObj = item.data('paste');
2450                         var pastedContent = '';
2451                         if (_.isArray(pastedContentObj)) {
2452                                 pastedContent = pastedContentObj[item.counter - 1];
2453                         } else if (_.isFunction(pastedContentObj)) {
2454                                 pastedContent = pastedContentObj(item.counter - 1, item.content);
2455                         } else if (pastedContentObj) {
2456                                 pastedContent = pastedContentObj;
2457                         }
2458                         
2459                         if (pastedContent) {
2460                                 insertPastedContent(item, pastedContent, !!item.data('pasteOverwrites'));
2461                         }
2462                         
2463                         item.data('paste', null);
2464                         return !!pastedContentObj;
2465                 });
2466                 
2467                 if (!targets.length && options.pastedContent) {
2468                         // no implicitly repeated elements, put pasted content in
2469                         // the deepest child
2470                         insertPastedContent(tree, options.pastedContent);
2471                 }
2472         });
2473 });/**
2474  * Resolves tag names in abbreviations with implied name
2475  */
2476 emmet.exec(function(require, _) {
2477         /**
2478          * Resolves implicit node names in parsed tree
2479          * @param {AbbreviationNode} tree
2480          */
2481         function resolveNodeNames(tree) {
2482                 var tagName = require('tagName');
2483                 _.each(tree.children, function(node) {
2484                         if (node.hasImplicitName() || node.data('forceNameResolving')) {
2485                                 node._name = tagName.resolve(node.parent.name());
2486                         }
2487                         resolveNodeNames(node);
2488                 });
2489                 
2490                 return tree;
2491         }
2492         
2493         require('abbreviationParser').addPostprocessor(resolveNodeNames);
2494 });/**
2495  * @author Stoyan Stefanov
2496  * @link https://github.com/stoyan/etc/tree/master/cssex
2497  */
2498 
2499 emmet.define('cssParser', function(require, _) {
2500 var walker, tokens = [], isOp, isNameChar, isDigit;
2501         
2502         // walks around the source
2503         walker = {
2504                 lines: null,
2505                 total_lines: 0,
2506                 linenum: -1,
2507                 line: '',
2508                 ch: '',
2509                 chnum: -1,
2510                 init: function (source) {
2511                         var me = walker;
2512                 
2513                         // source, yumm
2514                         me.lines = source
2515                                 .replace(/\r\n/g, '\n')
2516                                 .replace(/\r/g, '\n')
2517                                 .split('\n');
2518                         me.total_lines = me.lines.length;
2519                 
2520                         // reset
2521                         me.chnum = -1;
2522                         me.linenum = -1;
2523                         me.ch = '';
2524                         me.line = '';
2525                 
2526                         // advance
2527                         me.nextLine();
2528                         me.nextChar();
2529                 },
2530                 nextLine: function () {
2531                         var me = this;
2532                         me.linenum += 1;
2533                         if (me.total_lines <= me.linenum) {
2534                                 me.line = false;
2535                         } else {
2536                                 me.line = me.lines[me.linenum];
2537                         }
2538                         if (me.chnum !== -1) {
2539                                 me.chnum = 0;
2540                         }
2541                         return me.line;
2542                 }, 
2543                 nextChar: function () {
2544                         var me = this;
2545                         me.chnum += 1;
2546                         while (me.line.charAt(me.chnum) === '') {
2547                                 if (this.nextLine() === false) {
2548                                         me.ch = false;
2549                                         return false; // end of source
2550                                 }
2551                                 me.chnum = -1;
2552                                 me.ch = '\n';
2553                                 return '\n';
2554                         }
2555                         me.ch = me.line.charAt(me.chnum);
2556                         return me.ch;
2557                 },
2558                 peek: function() {
2559                         return this.line.charAt(this.chnum + 1);
2560                 }
2561         };
2562 
2563         // utility helpers
2564         isNameChar = function (c) {
2565                 // be more tolerate for name tokens: allow & character for LESS syntax
2566                 return (c == '&' || c === '_' || c === '-' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
2567         };
2568 
2569         isDigit = function (ch) {
2570                 return (ch !== false && ch >= '0' && ch <= '9');
2571         };  
2572 
2573         isOp = (function () {
2574                 var opsa = "{}[]()+*=.,;:>~|\\%$#@^!".split(''),
2575                         opsmatcha = "*^|$~".split(''),
2576                         ops = {},
2577                         opsmatch = {},
2578                         i = 0;
2579                 for (; i < opsa.length; i += 1) {
2580                         ops[opsa[i]] = true;
2581                 }
2582                 for (i = 0; i < opsmatcha.length; i += 1) {
2583                         opsmatch[opsmatcha[i]] = true;
2584                 }
2585                 return function (ch, matchattr) {
2586                         if (matchattr) {
2587                                 return !!opsmatch[ch];
2588                         }
2589                         return !!ops[ch];
2590                 };
2591         }());
2592         
2593         // shorthands
2594         function isset(v) {
2595                 return typeof v !== 'undefined';
2596         }
2597         function getConf() {
2598                 return {
2599                         'char': walker.chnum,
2600                         line: walker.linenum
2601                 };
2602         }
2603 
2604 
2605         // creates token objects and pushes them to a list
2606         function tokener(value, type, conf) {
2607                 var w = walker, c = conf || {};
2608                 tokens.push({
2609                         charstart: isset(c['char']) ? c['char'] : w.chnum,
2610                         charend:   isset(c.charend) ? c.charend : w.chnum,
2611                         linestart: isset(c.line)    ? c.line    : w.linenum,
2612                         lineend:   isset(c.lineend) ? c.lineend : w.linenum,
2613                         value:     value,
2614                         type:      type || value
2615                 });
2616         }
2617         
2618         // oops
2619         function error(m, config) { 
2620                 var w = walker,
2621                         conf = config || {},
2622                         c = isset(conf['char']) ? conf['char'] : w.chnum,
2623                         l = isset(conf.line) ? conf.line : w.linenum;
2624                 return {
2625                         name: "ParseError",
2626                         message: m + " at line " + (l + 1) + ' char ' + (c + 1),
2627                         walker: w,
2628                         tokens: tokens
2629                 };
2630         }
2631 
2632 
2633         // token handlers follow for:
2634         // white space, comment, string, identifier, number, operator
2635         function white() {
2636         
2637                 var c = walker.ch,
2638                         token = '',
2639                         conf = getConf();
2640         
2641                 while (c === " " || c === "\t") {
2642                         token += c;
2643                         c = walker.nextChar();
2644                 }
2645         
2646                 tokener(token, 'white', conf);
2647         
2648         }
2649 
2650         function comment() {
2651         
2652                 var w = walker,
2653                         c = w.ch,
2654                         token = c,
2655                         cnext,
2656                         conf = getConf();    
2657          
2658                 cnext = w.nextChar();
2659 
2660                 if (cnext === '/') {
2661                         // inline comment in SCSS and such
2662                         token += cnext;
2663                         var pk = w.peek();
2664                         while (pk && pk !== '\n') {
2665                                 token += cnext;
2666                                 cnext = w.nextChar();
2667                                 pk = w.peek();
2668                         }
2669                 } else if (cnext === '*') {
2670                         // multiline CSS commment
2671                         while (!(c === "*" && cnext === "/")) {
2672                                 token += cnext;
2673                                 c = cnext;
2674                                 cnext = w.nextChar();        
2675                         }            
2676                 } else {
2677                         // oops, not a comment, just a /
2678                         conf.charend = conf['char'];
2679                         conf.lineend = conf.line;
2680                         return tokener(token, token, conf);
2681                 }
2682                 
2683                 token += cnext;
2684                 w.nextChar();
2685                 tokener(token, 'comment', conf);
2686         }
2687 
2688         function str() {
2689                 var w = walker,
2690                         c = w.ch,
2691                         q = c,
2692                         token = c,
2693                         cnext,
2694                         conf = getConf();
2695         
2696                 c = w.nextChar();
2697         
2698                 while (c !== q) {
2699                         
2700                         if (c === '\n') {
2701                                 cnext = w.nextChar();
2702                                 if (cnext === "\\") {
2703                                         token += c + cnext;
2704                                 } else {
2705                                         // end of line with no \ escape = bad
2706                                         throw error("Unterminated string", conf);
2707                                 }
2708                         } else {
2709                                 if (c === "\\") {
2710                                         token += c + w.nextChar();
2711                                 } else {
2712                                         token += c;
2713                                 }
2714                         }
2715                 
2716                         c = w.nextChar();
2717                 
2718                 }
2719                 token += c;
2720                 w.nextChar();
2721                 tokener(token, 'string', conf);
2722         }
2723         
2724         function brace() {
2725                 var w = walker,
2726                         c = w.ch,
2727                         depth = 0,
2728                         token = c,
2729                         conf = getConf();
2730         
2731                 c = w.nextChar();
2732         
2733                 while (c !== ')' && !depth) {
2734                         if (c === '(') {
2735                                 depth++;
2736                         } else if (c === ')') {
2737                                 depth--;
2738                         } else if (c === false) {
2739                                 throw error("Unterminated brace", conf);
2740                         }
2741                         
2742                         token += c;
2743                         c = w.nextChar();
2744                 }
2745                 
2746                 token += c;
2747                 w.nextChar();
2748                 tokener(token, 'brace', conf);
2749         }
2750 
2751         function identifier(pre) {
2752                 var w = walker,
2753                         c = w.ch,
2754                         conf = getConf(),
2755                         token = (pre) ? pre + c : c;
2756                         
2757                 c = w.nextChar();
2758         
2759                 if (pre) { // adjust token position
2760                         conf['char'] -= pre.length;
2761                 }
2762                 
2763                 while (isNameChar(c) || isDigit(c)) {
2764                         token += c;
2765                         c = w.nextChar();
2766                 }
2767         
2768                 tokener(token, 'identifier', conf);    
2769         }
2770 
2771         function num() {
2772                 var w = walker,
2773                         c = w.ch,
2774                         conf = getConf(),
2775                         token = c,
2776                         point = token === '.',
2777                         nondigit;
2778                 
2779                 c = w.nextChar();
2780                 nondigit = !isDigit(c);
2781         
2782                 // .2px or .classname?
2783                 if (point && nondigit) {
2784                         // meh, NaN, could be a class name, so it's an operator for now
2785                         conf.charend = conf['char'];
2786                         conf.lineend = conf.line;
2787                         return tokener(token, '.', conf);    
2788                 }
2789                 
2790                 // -2px or -moz-something
2791                 if (token === '-' && nondigit) {
2792                         return identifier('-');
2793                 }
2794         
2795                 while (c !== false && (isDigit(c) || (!point && c === '.'))) { // not end of source && digit or first instance of .
2796                         if (c === '.') {
2797                                 point = true;
2798                         }
2799                         token += c;
2800                         c = w.nextChar();
2801                 }
2802 
2803                 tokener(token, 'number', conf);    
2804         
2805         }
2806 
2807         function op() {
2808                 var w = walker,
2809                         c = w.ch,
2810                         conf = getConf(),
2811                         token = c,
2812                         next = w.nextChar();
2813                         
2814                 if (next === "=" && isOp(token, true)) {
2815                         token += next;
2816                         tokener(token, 'match', conf);
2817                         w.nextChar();
2818                         return;
2819                 } 
2820                 
2821                 conf.charend = conf['char'] + 1;
2822                 conf.lineend = conf.line;    
2823                 tokener(token, token, conf);
2824         }
2825 
2826 
2827         // call the appropriate handler based on the first character in a token suspect
2828         function tokenize() {
2829 
2830                 var ch = walker.ch;
2831         
2832                 if (ch === " " || ch === "\t") {
2833                         return white();
2834                 }
2835 
2836                 if (ch === '/') {
2837                         return comment();
2838                 } 
2839 
2840                 if (ch === '"' || ch === "'") {
2841                         return str();
2842                 }
2843                 
2844                 if (ch === '(') {
2845                         return brace();
2846                 }
2847         
2848                 if (ch === '-' || ch === '.' || isDigit(ch)) { // tricky - char: minus (-1px) or dash (-moz-stuff)
2849                         return num();
2850                 }
2851         
2852                 if (isNameChar(ch)) {
2853                         return identifier();
2854                 }
2855 
2856                 if (isOp(ch)) {
2857                         return op();
2858                 }
2859                 
2860                 if (ch === "\n") {
2861                         tokener("line");
2862                         walker.nextChar();
2863                         return;
2864                 }
2865                 
2866                 throw error("Unrecognized character");
2867         }
2868         
2869         /**
2870          * Returns newline character at specified position in content
2871          * @param {String} content
2872          * @param {Number} pos
2873          * @return {String}
2874          */
2875         function getNewline(content, pos) {
2876                 return content.charAt(pos) == '\r' && content.charAt(pos + 1) == '\n' 
2877                         ? '\r\n' 
2878                         : content.charAt(pos);
2879         }
2880 
2881         return {
2882                 /**
2883                  * @param source
2884                  * @returns
2885                  * @memberOf emmet.cssParser
2886                  */
2887                 lex: function (source) {
2888                         walker.init(source);
2889                         tokens = [];
2890                         while (walker.ch !== false) {
2891                                 tokenize();            
2892                         }
2893                         return tokens;
2894                 },
2895                 
2896                 /**
2897                  * Tokenizes CSS source
2898                  * @param {String} source
2899                  * @returns {Array}
2900                  */
2901                 parse: function(source) {
2902                         // transform tokens
2903                         var pos = 0;
2904                         return _.map(this.lex(source), function(token) {
2905                                 if (token.type == 'line') {
2906                                         token.value = getNewline(source, pos);
2907                                 }
2908                                 
2909                                 return {
2910                                         type: token.type,
2911                                         start: pos,
2912                                         end: (pos += token.value.length)
2913                                 };
2914                         });
2915                 },
2916                 
2917                 toSource: function (toks) {
2918                         var i = 0, max = toks.length, t, src = '';
2919                         for (; i < max; i += 1) {
2920                                 t = toks[i];
2921                                 if (t.type === 'line') {
2922                                         src += '\n';
2923                                 } else {
2924                                         src += t.value;
2925                                 }
2926                         }
2927                         return src;
2928                 }
2929         };
2930 });/**
2931  * HTML tokenizer by Marijn Haverbeke
2932  * http://codemirror.net/
2933  * @constructor
2934  * @memberOf __xmlParseDefine
2935  * @param {Function} require
2936  * @param {Underscore} _
2937  */
2938 emmet.define('xmlParser', function(require, _) {
2939         var Kludges = {
2940                 autoSelfClosers : {},
2941                 implicitlyClosed : {},
2942                 contextGrabbers : {},
2943                 doNotIndent : {},
2944                 allowUnquoted : true,
2945                 allowMissing : true
2946         };
2947 
2948         // Return variables for tokenizers
2949         var tagName = null, type = null;
2950 
2951         function inText(stream, state) {
2952                 function chain(parser) {
2953                         state.tokenize = parser;
2954                         return parser(stream, state);
2955                 }
2956 
2957                 var ch = stream.next();
2958                 if (ch == "<") {
2959                         if (stream.eat("!")) {
2960                                 if (stream.eat("[")) {
2961                                         if (stream.match("CDATA["))
2962                                                 return chain(inBlock("atom", "]]>"));
2963                                         else
2964                                                 return null;
2965                                 } else if (stream.match("--"))
2966                                         return chain(inBlock("comment", "-->"));
2967                                 else if (stream.match("DOCTYPE", true, true)) {
2968                                         stream.eatWhile(/[\w\._\-]/);
2969                                         return chain(doctype(1));
2970                                 } else
2971                                         return null;
2972                         } else if (stream.eat("?")) {
2973                                 stream.eatWhile(/[\w\._\-]/);
2974                                 state.tokenize = inBlock("meta", "?>");
2975                                 return "meta";
2976                         } else {
2977                                 type = stream.eat("/") ? "closeTag" : "openTag";
2978                                 stream.eatSpace();
2979                                 tagName = "";
2980                                 var c;
2981                                 while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/)))
2982                                         tagName += c;
2983                                 state.tokenize = inTag;
2984                                 return "tag";
2985                         }
2986                 } else if (ch == "&") {
2987                         var ok;
2988                         if (stream.eat("#")) {
2989                                 if (stream.eat("x")) {
2990                                         ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
2991                                 } else {
2992                                         ok = stream.eatWhile(/[\d]/) && stream.eat(";");
2993                                 }
2994                         } else {
2995                                 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
2996                         }
2997                         return ok ? "atom" : "error";
2998                 } else {
2999                         stream.eatWhile(/[^&<]/);
3000                         return "text";
3001                 }
3002         }
3003 
3004         function inTag(stream, state) {
3005                 var ch = stream.next();
3006                 if (ch == ">" || (ch == "/" && stream.eat(">"))) {
3007                         state.tokenize = inText;
3008                         type = ch == ">" ? "endTag" : "selfcloseTag";
3009                         return "tag";
3010                 } else if (ch == "=") {
3011                         type = "equals";
3012                         return null;
3013                 } else if (/[\'\"]/.test(ch)) {
3014                         state.tokenize = inAttribute(ch);
3015                         return state.tokenize(stream, state);
3016                 } else {
3017                         stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
3018                         return "word";
3019                 }
3020         }
3021 
3022         function inAttribute(quote) {
3023                 return function(stream, state) {
3024                         while (!stream.eol()) {
3025                                 if (stream.next() == quote) {
3026                                         state.tokenize = inTag;
3027                                         break;
3028                                 }
3029                         }
3030                         return "string";
3031                 };
3032         }
3033 
3034         function inBlock(style, terminator) {
3035                 return function(stream, state) {
3036                         while (!stream.eol()) {
3037                                 if (stream.match(terminator)) {
3038                                         state.tokenize = inText;
3039                                         break;
3040                                 }
3041                                 stream.next();
3042                         }
3043                         return style;
3044                 };
3045         }
3046         
3047         function doctype(depth) {
3048                 return function(stream, state) {
3049                         var ch;
3050                         while ((ch = stream.next()) !== null) {
3051                                 if (ch == "<") {
3052                                         state.tokenize = doctype(depth + 1);
3053                                         return state.tokenize(stream, state);
3054                                 } else if (ch == ">") {
3055                                         if (depth == 1) {
3056                                                 state.tokenize = inText;
3057                                                 break;
3058                                         } else {
3059                                                 state.tokenize = doctype(depth - 1);
3060                                                 return state.tokenize(stream, state);
3061                                         }
3062                                 }
3063                         }
3064                         return "meta";
3065                 };
3066         }
3067 
3068         var curState = null, setStyle;
3069         function pass() {
3070                 for (var i = arguments.length - 1; i >= 0; i--)
3071                         curState.cc.push(arguments[i]);
3072         }
3073         
3074         function cont() {
3075                 pass.apply(null, arguments);
3076                 return true;
3077         }
3078 
3079         function pushContext(tagName, startOfLine) {
3080                 var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) 
3081                         || (curState.context && curState.context.noIndent);
3082                 curState.context = {
3083                         prev : curState.context,
3084                         tagName : tagName,
3085                         indent : curState.indented,
3086                         startOfLine : startOfLine,
3087                         noIndent : noIndent
3088                 };
3089         }
3090         
3091         function popContext() {
3092                 if (curState.context)
3093                         curState.context = curState.context.prev;
3094         }
3095 
3096         function element(type) {
3097                 if (type == "openTag") {
3098                         curState.tagName = tagName;
3099                         return cont(attributes, endtag(curState.startOfLine));
3100                 } else if (type == "closeTag") {
3101                         var err = false;
3102                         if (curState.context) {
3103                                 if (curState.context.tagName != tagName) {
3104                                         if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) {
3105                                                 popContext();
3106                                         }
3107                                         err = !curState.context || curState.context.tagName != tagName;
3108                                 }
3109                         } else {
3110                                 err = true;
3111                         }
3112                         
3113                         if (err)
3114                                 setStyle = "error";
3115                         return cont(endclosetag(err));
3116                 }
3117                 return cont();
3118         }
3119         
3120         function endtag(startOfLine) {
3121                 return function(type) {
3122                         if (type == "selfcloseTag"
3123                                         || (type == "endTag" && Kludges.autoSelfClosers
3124                                                         .hasOwnProperty(curState.tagName
3125                                                                         .toLowerCase()))) {
3126                                 maybePopContext(curState.tagName.toLowerCase());
3127                                 return cont();
3128                         }
3129                         if (type == "endTag") {
3130                                 maybePopContext(curState.tagName.toLowerCase());
3131                                 pushContext(curState.tagName, startOfLine);
3132                                 return cont();
3133                         }
3134                         return cont();
3135                 };
3136         }
3137         
3138         function endclosetag(err) {
3139                 return function(type) {
3140                         if (err)
3141                                 setStyle = "error";
3142                         if (type == "endTag") {
3143                                 popContext();
3144                                 return cont();
3145                         }
3146                         setStyle = "error";
3147                         return cont(arguments.callee);
3148                 };
3149         }
3150         
3151         function maybePopContext(nextTagName) {
3152                 var parentTagName;
3153                 while (true) {
3154                         if (!curState.context) {
3155                                 return;
3156                         }
3157                         parentTagName = curState.context.tagName.toLowerCase();
3158                         if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName)
3159                                         || !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
3160                                 return;
3161                         }
3162                         popContext();
3163                 }
3164         }
3165 
3166         function attributes(type) {
3167                 if (type == "word") {
3168                         setStyle = "attribute";
3169                         return cont(attribute, attributes);
3170                 }
3171                 if (type == "endTag" || type == "selfcloseTag")
3172                         return pass();
3173                 setStyle = "error";
3174                 return cont(attributes);
3175         }
3176         
3177         function attribute(type) {
3178                 if (type == "equals")
3179                         return cont(attvalue, attributes);
3180                 if (!Kludges.allowMissing)
3181                         setStyle = "error";
3182                 return (type == "endTag" || type == "selfcloseTag") ? pass()
3183                                 : cont();
3184         }
3185         
3186         function attvalue(type) {
3187                 if (type == "string")
3188                         return cont(attvaluemaybe);
3189                 if (type == "word" && Kludges.allowUnquoted) {
3190                         setStyle = "string";
3191                         return cont();
3192                 }
3193                 setStyle = "error";
3194                 return (type == "endTag" || type == "selfCloseTag") ? pass()
3195                                 : cont();
3196         }
3197         
3198         function attvaluemaybe(type) {
3199                 if (type == "string")
3200                         return cont(attvaluemaybe);
3201                 else
3202                         return pass();
3203         }
3204         
3205         function startState() {
3206                 return {
3207                         tokenize : inText,
3208                         cc : [],
3209                         indented : 0,
3210                         startOfLine : true,
3211                         tagName : null,
3212                         context : null
3213                 };
3214         }
3215         
3216         function token(stream, state) {
3217                 if (stream.sol()) {
3218                         state.startOfLine = true;
3219                         state.indented = 0;
3220                 }
3221                 
3222                 if (stream.eatSpace())
3223                         return null;
3224 
3225                 setStyle = type = tagName = null;
3226                 var style = state.tokenize(stream, state);
3227                 state.type = type;
3228                 if ((style || type) && style != "comment") {
3229                         curState = state;
3230                         while (true) {
3231                                 var comb = state.cc.pop() || element;
3232                                 if (comb(type || style))
3233                                         break;
3234                         }
3235                 }
3236                 state.startOfLine = false;
3237                 return setStyle || style;
3238         }
3239 
3240         return {
3241                 /**
3242                  * @memberOf emmet.xmlParser
3243                  * @returns
3244                  */
3245                 parse: function(data, offset) {
3246                         offset = offset || 0;
3247                         var state = startState();
3248                         var stream = require('stringStream').create(data);
3249                         var tokens = [];
3250                         while (!stream.eol()) {
3251                                 tokens.push({
3252                                         type: token(stream, state),
3253                                         start: stream.start + offset,
3254                                         end: stream.pos + offset
3255                                 });
3256                                 stream.start = stream.pos;
3257                         }
3258                         
3259                         return tokens;
3260                 }               
3261         };
3262 });
3263 /*!
3264  * string_score.js: String Scoring Algorithm 0.1.10 
3265  *
3266  * http://joshaven.com/string_score
3267  * https://github.com/joshaven/string_score
3268  *
3269  * Copyright (C) 2009-2011 Joshaven Potter <yourtech@gmail.com>
3270  * Special thanks to all of the contributors listed here https://github.com/joshaven/string_score
3271  * MIT license: http://www.opensource.org/licenses/mit-license.php
3272  *
3273  * Date: Tue Mar 1 2011
3274 */
3275 
3276 /**
3277  * Scores a string against another string.
3278  *  'Hello World'.score('he');     //=> 0.5931818181818181
3279  *  'Hello World'.score('Hello');  //=> 0.7318181818181818
3280  */
3281 emmet.define('string-score', function(require, _) {
3282         return {
3283                 score: function(string, abbreviation, fuzziness) {
3284                         // If the string is equal to the abbreviation, perfect match.
3285                           if (string == abbreviation) {return 1;}
3286                           //if it's not a perfect match and is empty return 0
3287                           if(abbreviation == "") {return 0;}
3288 
3289                           var total_character_score = 0,
3290                               abbreviation_length = abbreviation.length,
3291                               string_length = string.length,
3292                               start_of_string_bonus,
3293                               abbreviation_score,
3294                               fuzzies=1,
3295                               final_score;
3296                           
3297                           // Walk through abbreviation and add up scores.
3298                           for (var i = 0,
3299                                  character_score/* = 0*/,
3300                                  index_in_string/* = 0*/,
3301                                  c/* = ''*/,
3302                                  index_c_lowercase/* = 0*/,
3303                                  index_c_uppercase/* = 0*/,
3304                                  min_index/* = 0*/;
3305                              i < abbreviation_length;
3306                              ++i) {
3307                             
3308                             // Find the first case-insensitive match of a character.
3309                             c = abbreviation.charAt(i);
3310                             
3311                             index_c_lowercase = string.indexOf(c.toLowerCase());
3312                             index_c_uppercase = string.indexOf(c.toUpperCase());
3313                             min_index = Math.min(index_c_lowercase, index_c_uppercase);
3314                             index_in_string = (min_index > -1) ? min_index : Math.max(index_c_lowercase, index_c_uppercase);
3315                             
3316                             if (index_in_string === -1) { 
3317                               if (fuzziness) {
3318                                 fuzzies += 1-fuzziness;
3319                                 continue;
3320                               } else {
3321                                 return 0;
3322                               }
3323                             } else {
3324                               character_score = 0.1;
3325                             }
3326                             
3327                             // Set base score for matching 'c'.
3328                             
3329                             // Same case bonus.
3330                             if (string[index_in_string] === c) { 
3331                               character_score += 0.1; 
3332                             }
3333                             
3334                             // Consecutive letter & start-of-string Bonus
3335                             if (index_in_string === 0) {
3336                               // Increase the score when matching first character of the remainder of the string
3337                               character_score += 0.6;
3338                               if (i === 0) {
3339                                 // If match is the first character of the string
3340                                 // & the first character of abbreviation, add a
3341                                 // start-of-string match bonus.
3342                                 start_of_string_bonus = 1; //true;
3343                               }
3344                             }
3345                             else {
3346                           // Acronym Bonus
3347                           // Weighing Logic: Typing the first character of an acronym is as if you
3348                           // preceded it with two perfect character matches.
3349                           if (string.charAt(index_in_string - 1) === ' ') {
3350                             character_score += 0.8; // * Math.min(index_in_string, 5); // Cap bonus at 0.4 * 5
3351                           }
3352                             }
3353                             
3354                             // Left trim the already matched part of the string
3355                             // (forces sequential matching).
3356                             string = string.substring(index_in_string + 1, string_length);
3357                             
3358                             total_character_score += character_score;
3359                           } // end of for loop
3360                           
3361                           // Uncomment to weigh smaller words higher.
3362                           // return total_character_score / string_length;
3363                           
3364                           abbreviation_score = total_character_score / abbreviation_length;
3365                           //percentage_of_matched_string = abbreviation_length / string_length;
3366                           //word_score = abbreviation_score * percentage_of_matched_string;
3367                           
3368                           // Reduce penalty for longer strings.
3369                           //final_score = (word_score + abbreviation_score) / 2;
3370                           final_score = ((abbreviation_score * (abbreviation_length / string_length)) + abbreviation_score) / 2;
3371                           
3372                           final_score = final_score / fuzzies;
3373                           
3374                           if (start_of_string_bonus && (final_score + 0.15 < 1)) {
3375                             final_score += 0.15;
3376                           }
3377                           
3378                           return final_score;
3379                 }
3380         };
3381 });/**
3382  * Utility module for Emmet
3383  * @param {Function} require
3384  * @param {Underscore} _
3385  */
3386 emmet.define('utils', function(require, _) {
3387         /** 
3388          * Special token used as a placeholder for caret positions inside 
3389          * generated output 
3390          */
3391         var caretPlaceholder = '${0}';
3392         
3393         return {
3394                 /** @memberOf utils */
3395                 reTag: /<\/?[\w:\-]+(?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*\s*(\/?)>$/,
3396                 
3397                 /**
3398                  * Test if passed string ends with XHTML tag. This method is used for testing
3399                  * '>' character: it belongs to tag or it's a part of abbreviation? 
3400                  * @param {String} str
3401                  * @return {Boolean}
3402                  */
3403                 endsWithTag: function(str) {
3404                         return this.reTag.test(str);
3405                 },
3406                 
3407                 /**
3408                  * Check if passed symbol is a number
3409                  * @param {String} ch
3410                  * @returns {Boolean}
3411                  */
3412                 isNumeric: function(ch) {
3413                         if (typeof(ch) == 'string')
3414                                 ch = ch.charCodeAt(0);
3415                                 
3416                         return (ch && ch > 47 && ch < 58);
3417                 },
3418                 
3419                 /**
3420                  * Trim whitespace from string
3421                  * @param {String} text
3422                  * @return {String}
3423                  */
3424                 trim: function(text) {
3425                         return (text || "").replace(/^\s+|\s+$/g, "");
3426                 },
3427                 
3428                 /**
3429                  * Returns newline character
3430                  * @returns {String}
3431                  */
3432                 getNewline: function() {
3433                         var res = require('resources');
3434                         if (!res) {
3435                                 return '\n';
3436                         }
3437                         
3438                         var nl = res.getVariable('newline');
3439                         return _.isString(nl) ? nl : '\n';
3440                 },
3441                 
3442                 /**
3443                  * Sets new newline character that will be used in output
3444                  * @param {String} str
3445                  */
3446                 setNewline: function(str) {
3447                         var res = require('resources');
3448                         res.setVariable('newline', str);
3449                         res.setVariable('nl', str);
3450                 },
3451                 
3452                 /**
3453                  * Split text into lines. Set <code>remove_empty</code> to true to filter
3454                  * empty lines
3455                  * @param {String} text Text to split
3456                  * @param {Boolean} removeEmpty Remove empty lines from result
3457                  * @return {Array}
3458                  */
3459                 splitByLines: function(text, removeEmpty) {
3460                         // IE fails to split string by regexp, 
3461                         // need to normalize newlines first
3462                         // Also, Mozilla's Rhiho JS engine has a weird newline bug
3463                         var nl = this.getNewline();
3464                         var lines = (text || '')
3465                                 .replace(/\r\n/g, '\n')
3466                                 .replace(/\n\r/g, '\n')
3467                                 .replace(/\r/g, '\n')
3468                                 .replace(/\n/g, nl)
3469                                 .split(nl);
3470                         
3471                         if (removeEmpty) {
3472                                 lines = _.filter(lines, function(line) {
3473                                         return line.length && !!this.trim(line);
3474                                 }, this);
3475                         }
3476                         
3477                         return lines;
3478                 },
3479                 
3480                 /**
3481                  * Normalizes newline character: replaces newlines in <code>text</code> 
3482                  * with newline defined in preferences
3483                  * @param {String} text
3484                  * @returns {String}
3485                  */
3486                 normalizeNewline: function(text) {
3487                         return this.splitByLines(text).join(this.getNewline());
3488                 },
3489                 
3490                 /**
3491                  * Repeats string <code>howMany</code> times
3492                  * @param {String} str
3493                  * @param {Number} how_many
3494                  * @return {String}
3495                  */
3496                 repeatString: function(str, howMany) {
3497                         var result = [];
3498                         
3499                         for (var i = 0; i < howMany; i++) 
3500                                 result.push(str);
3501                                 
3502                         return result.join('');
3503                 },
3504                 
3505                 /**
3506                  * Returns list of paddings that should be used to align passed string
3507                  * @param {Array} strings
3508                  * @returns {Array}
3509                  */
3510                 getStringsPads: function(strings) {
3511                         var lengths = _.map(strings, function(s) {
3512                                 return _.isString(s) ? s.length : +s;
3513                         });
3514                         
3515                         var max = _.max(lengths);
3516                         return _.map(lengths, function(l) {
3517                                 var pad = max - l;
3518                                 return pad ? this.repeatString(' ', pad) : '';
3519                         }, this);
3520                 },
3521                 
3522                 /**
3523                  * Indents text with padding
3524                  * @param {String} text Text to indent
3525                  * @param {String} pad Padding size (number) or padding itself (string)
3526                  * @return {String}
3527                  */
3528                 padString: function(text, pad) {
3529                         var padStr = (_.isNumber(pad)) 
3530                                 ? this.repeatString(require('resources').getVariable('indentation') || '\t', pad) 
3531                                 : pad;
3532                                 
3533                         var result = [];
3534                         
3535                         var lines = this.splitByLines(text);
3536                         var nl = this.getNewline();
3537                                 
3538                         result.push(lines[0]);
3539                         for (var j = 1; j < lines.length; j++) 
3540                                 result.push(nl + padStr + lines[j]);
3541                                 
3542                         return result.join('');
3543                 },
3544                 
3545                 /**
3546                  * Pad string with zeroes
3547                  * @param {String} str String to pad
3548                  * @param {Number} pad Desired string length
3549                  * @return {String}
3550                  */
3551                 zeroPadString: function(str, pad) {
3552                         var padding = '';
3553                         var il = str.length;
3554                                 
3555                         while (pad > il++) padding += '0';
3556                         return padding + str; 
3557                 },
3558                 
3559                 /**
3560                  * Removes padding at the beginning of each text's line
3561                  * @param {String} text
3562                  * @param {String} pad
3563                  */
3564                 unindentString: function(text, pad) {
3565                         var lines = this.splitByLines(text);
3566                         for (var i = 0; i < lines.length; i++) {
3567                                 if (lines[i].search(pad) === 0)
3568                                         lines[i] = lines[i].substr(pad.length);
3569                         }
3570                         
3571                         return lines.join(this.getNewline());
3572                 },
3573                 
3574                 /**
3575                  * Replaces unescaped symbols in <code>str</code>. For example, the '$' symbol
3576                  * will be replaced in 'item$count', but not in 'item\$count'.
3577                  * @param {String} str Original string
3578                  * @param {String} symbol Symbol to replace
3579                  * @param {String} replace Symbol replacement. Might be a function that 
3580                  * returns new value
3581                  * @return {String}
3582                  */
3583                 replaceUnescapedSymbol: function(str, symbol, replace) {
3584                         var i = 0;
3585                         var il = str.length;
3586                         var sl = symbol.length;
3587                         var matchCount = 0;
3588                                 
3589                         while (i < il) {
3590                                 if (str.charAt(i) == '\\') {
3591                                         // escaped symbol, skip next character
3592                                         i += sl + 1;
3593                                 } else if (str.substr(i, sl) == symbol) {
3594                                         // have match
3595                                         var curSl = sl;
3596                                         matchCount++;
3597                                         var newValue = replace;
3598                                         if (_.isFunction(replace)) {
3599                                                 var replaceData = replace(str, symbol, i, matchCount);
3600                                                 if (replaceData) {
3601                                                         curSl = replaceData[0].length;
3602                                                         newValue = replaceData[1];
3603                                                 } else {
3604                                                         newValue = false;
3605                                                 }
3606                                         }
3607                                         
3608                                         if (newValue === false) { // skip replacement
3609                                                 i++;
3610                                                 continue;
3611                                         }
3612                                         
3613                                         str = str.substring(0, i) + newValue + str.substring(i + curSl);
3614                                         // adjust indexes
3615                                         il = str.length;
3616                                         i += newValue.length;
3617                                 } else {
3618                                         i++;
3619                                 }
3620                         }
3621                         
3622                         return str;
3623                 },
3624                 
3625                 /**
3626                  * Replace variables like ${var} in string
3627                  * @param {String} str
3628                  * @param {Object} vars Variable set (defaults to variables defined in 
3629                  * <code>snippets.json</code>) or variable resolver (<code>Function</code>)
3630                  * @return {String}
3631                  */
3632                 replaceVariables: function(str, vars) {
3633                         vars = vars || {};
3634                         var resolver = _.isFunction(vars) ? vars : function(str, p1) {
3635                                 return p1 in vars ? vars[p1] : null;
3636                         };
3637                         
3638                         var res = require('resources');
3639                         return require('tabStops').processText(str, {
3640                                 variable: function(data) {
3641                                         var newValue = resolver(data.token, data.name, data);
3642                                         if (newValue === null) {
3643                                                 // try to find variable in resources
3644                                                 newValue = res.getVariable(data.name);
3645                                         }
3646                                         
3647                                         if (newValue === null || _.isUndefined(newValue))
3648                                                 // nothing found, return token itself
3649                                                 newValue = data.token;
3650                                         return newValue;
3651                                 }
3652                         });
3653                 },
3654                 
3655                 /**
3656                  * Replaces '$' character in string assuming it might be escaped with '\'
3657                  * @param {String} str String where character should be replaced
3658                  * @param {String} value New value
3659                  * @return {String}
3660                  */
3661                 replaceCounter: function(str, value, total) {
3662                         var symbol = '$';
3663                         // in case we received strings from Java, convert the to native strings
3664                         str = String(str);
3665                         value = String(value);
3666                         
3667                         if (/^\-?\d+$/.test(value)) {
3668                                 value = +value;
3669                         }
3670                         
3671                         var that = this;
3672                         
3673                         return this.replaceUnescapedSymbol(str, symbol, function(str, symbol, pos, matchNum){
3674                                 if (str.charAt(pos + 1) == '{' || that.isNumeric(str.charAt(pos + 1)) ) {
3675                                         // it's a variable, skip it
3676                                         return false;
3677                                 }
3678                                 
3679                                 // replace sequense of $ symbols with padded number  
3680                                 var j = pos + 1;
3681                                 while(str.charAt(j) == '$' && str.charAt(j + 1) != '{') j++;
3682                                 var pad = j - pos;
3683                                 
3684                                 // get counter base
3685                                 var base = 0, decrement = false, m;
3686                                 if ((m = str.substr(j).match(/^@(\-?)(\d*)/))) {
3687                                         j += m[0].length;
3688                                         
3689                                         if (m[1]) {
3690                                                 decrement = true;
3691                                         }
3692                                         
3693                                         base = parseInt(m[2] || 1, 10) - 1;
3694                                 }
3695                                 
3696                                 if (decrement && total && _.isNumber(value)) {
3697                                         value = total - value + 1;
3698                                 }
3699                                 
3700                                 value += base;
3701                                 
3702                                 return [str.substring(pos, j), that.zeroPadString(value + '', pad)];
3703                         });
3704                 },
3705                 
3706                 /**
3707                  * Check if string matches against <code>reTag</code> regexp. This 
3708                  * function may be used to test if provided string contains HTML tags
3709                  * @param {String} str
3710                  * @returns {Boolean}
3711                  */
3712                 matchesTag: function(str) {
3713                         return this.reTag.test(str || '');
3714                 },
3715                 
3716                 /**
3717                  * Escapes special characters used in Emmet, like '$', '|', etc.
3718                  * Use this method before passing to actions like "Wrap with Abbreviation"
3719                  * to make sure that existing special characters won't be altered
3720                  * @param {String} text
3721                  * @return {String}
3722                  */
3723                 escapeText: function(text) {
3724                         return text.replace(/([\$\\])/g, '\\$1');
3725                 },
3726                 
3727                 /**
3728                  * Unescapes special characters used in Emmet, like '$', '|', etc.
3729                  * @param {String} text
3730                  * @return {String}
3731                  */
3732                 unescapeText: function(text) {
3733                         return text.replace(/\\(.)/g, '$1');
3734                 },
3735                 
3736                 /**
3737                  * Returns caret placeholder
3738                  * @returns {String}
3739                  */
3740                 getCaretPlaceholder: function() {
3741                         return _.isFunction(caretPlaceholder) 
3742                                 ? caretPlaceholder.apply(this, arguments)
3743                                 : caretPlaceholder;
3744                 },
3745                 
3746                 /**
3747                  * Sets new representation for carets in generated output
3748                  * @param {String} value New caret placeholder. Might be a 
3749                  * <code>Function</code>
3750                  */
3751                 setCaretPlaceholder: function(value) {
3752                         caretPlaceholder = value;
3753                 },
3754                 
3755                 /**
3756                  * Returns line padding
3757                  * @param {String} line
3758                  * @return {String}
3759                  */
3760                 getLinePadding: function(line) {
3761                         return (line.match(/^(\s+)/) || [''])[0];
3762                 },
3763                 
3764                 /**
3765                  * Helper function that returns padding of line of <code>pos</code>
3766                  * position in <code>content</code>
3767                  * @param {String} content
3768                  * @param {Number} pos
3769                  * @returns {String}
3770                  */
3771                 getLinePaddingFromPosition: function(content, pos) {
3772                         var lineRange = this.findNewlineBounds(content, pos);
3773                         return this.getLinePadding(lineRange.substring(content));
3774                 },
3775                 
3776                 /**
3777                  * Escape special regexp chars in string, making it usable for creating dynamic
3778                  * regular expressions
3779                  * @param {String} str
3780                  * @return {String}
3781                  */
3782                 escapeForRegexp: function(str) {
3783                         var specials = new RegExp("[.*+?|()\\[\\]{}\\\\]", "g"); // .*+?|()[]{}\
3784                         return str.replace(specials, "\\$&");
3785                 },
3786                 
3787                 /**
3788                  * Make decimal number look good: convert it to fixed precision end remove
3789                  * traling zeroes 
3790                  * @param {Number} num
3791                  * @param {Number} fracion Fraction numbers (default is 2)
3792                  * @return {String}
3793                  */
3794                 prettifyNumber: function(num, fraction) {
3795                         return num.toFixed(typeof fraction == 'undefined' ? 2 : fraction).replace(/\.?0+$/, '');
3796                 },
3797                 
3798                 /**
3799                  * Replace substring of <code>str</code> with <code>value</code>
3800                  * @param {String} str String where to replace substring
3801                  * @param {String} value New substring value
3802                  * @param {Number} start Start index of substring to replace. May also
3803                  * be a <code>Range</code> object: in this case, the <code>end</code>
3804                  * argument is not required
3805                  * @param {Number} end End index of substring to replace. If ommited, 
3806                  * <code>start</code> argument is used
3807                  */
3808                 replaceSubstring: function(str, value, start, end) {
3809                         if (_.isObject(start) && 'end' in start) {
3810                                 end = start.end;
3811                                 start = start.start;
3812                         }
3813                         
3814                         if (_.isString(end))
3815                                 end = start + end.length;
3816                         
3817                         if (_.isUndefined(end))
3818                                 end = start;
3819                         
3820                         if (start < 0 || start > str.length)
3821                                 return str;
3822                         
3823                         return str.substring(0, start) + value + str.substring(end);
3824                 },
3825                 
3826                 /**
3827                  * Narrows down text range, adjusting selection to non-space characters
3828                  * @param {String} text
3829                  * @param {Number} start Starting range in <code>text</code> where 
3830                  * slection should be adjusted. Can also be any object that is accepted
3831                  * by <code>Range</code> class
3832                  * @return {Range}
3833                  */
3834                 narrowToNonSpace: function(text, start, end) {
3835                         var range = require('range').create(start, end);
3836                         
3837                         var reSpace = /[\s\n\r\u00a0]/;
3838                         // narrow down selection until first non-space character
3839                         while (range.start < range.end) {
3840                                 if (!reSpace.test(text.charAt(range.start)))
3841                                         break;
3842                                         
3843                                 range.start++;
3844                         }
3845                         
3846                         while (range.end > range.start) {
3847                                 range.end--;
3848                                 if (!reSpace.test(text.charAt(range.end))) {
3849                                         range.end++;
3850                                         break;
3851                                 }
3852                         }
3853                         
3854                         return range;
3855                 },
3856                 
3857                 /**
3858                  * Find start and end index of text line for <code>from</code> index
3859                  * @param {String} text 
3860                  * @param {Number} from
3861                  */
3862                 findNewlineBounds: function(text, from) {
3863                         var len = text.length,
3864                                 start = 0,
3865                                 end = len - 1, 
3866                                 ch;
3867 
3868                         
3869                         // search left
3870                         for (var i = from - 1; i > 0; i--) {
3871                                 ch = text.charAt(i);
3872                                 if (ch == '\n' || ch == '\r') {
3873                                         start = i + 1;
3874                                         break;
3875                                 }
3876                         }
3877                         // search right
3878                         for (var j = from; j < len; j++) {
3879                                 ch = text.charAt(j);
3880                                 if (ch == '\n' || ch == '\r') {
3881                                         end = j;
3882                                         break;
3883                                 }
3884                         }
3885                         
3886                         return require('range').create(start, end - start);
3887                 },
3888 
3889                 /**
3890                  * Deep merge of two or more objects. Taken from jQuery.extend()
3891                  */
3892                 deepMerge: function() {
3893                         var options, name, src, copy, copyIsArray, clone,
3894                                 target = arguments[0] || {},
3895                                 i = 1,
3896                                 length = arguments.length;
3897 
3898 
3899                         // Handle case when target is a string or something (possible in deep copy)
3900                         if (!_.isObject(target) && !_.isFunction(target)) {
3901                                 target = {};
3902                         }
3903 
3904                         for ( ; i < length; i++ ) {
3905                                 // Only deal with non-null/undefined values
3906                                 if ( (options = arguments[ i ]) !== null ) {
3907                                         // Extend the base object
3908                                         for ( name in options ) {
3909                                                 src = target[ name ];
3910                                                 copy = options[ name ];
3911 
3912                                                 // Prevent never-ending loop
3913                                                 if ( target === copy ) {
3914                                                         continue;
3915                                                 }
3916 
3917                                                 // Recurse if we're merging plain objects or arrays
3918                                                 if ( copy && ( _.isObject(copy) || (copyIsArray = _.isArray(copy)) ) ) {
3919                                                         if ( copyIsArray ) {
3920                                                                 copyIsArray = false;
3921                                                                 clone = src && _.isArray(src) ? src : [];
3922 
3923                                                         } else {
3924                                                                 clone = src && _.isObject(src) ? src : {};
3925                                                         }
3926 
3927                                                         // Never move original objects, clone them
3928                                                         target[ name ] = this.deepMerge(clone, copy );
3929 
3930                                                 // Don't bring in undefined values
3931                                                 } else if ( copy !== undefined ) {
3932                                                         target[ name ] = copy;
3933                                                 }
3934                                         }
3935                                 }
3936                         }
3937 
3938                         // Return the modified object
3939                         return target;
3940                 }
3941         };
3942 });
3943 /**
3944  * Helper module to work with ranges
3945  * @constructor
3946  * @memberOf __rangeDefine
3947  * @param {Function} require
3948  * @param {Underscore} _
3949  */
3950 emmet.define('range', function(require, _) {
3951         function cmp(a, b, op) {
3952                 switch (op) {
3953                         case 'eq':
3954                         case '==':
3955                                 return a === b;
3956                         case 'lt':
3957                         case '<':
3958                                 return a < b;
3959                         case 'lte':
3960                         case '<=':
3961                                 return a <= b;
3962                         case 'gt':
3963                         case '>':
3964                                 return a > b;
3965                         case 'gte':
3966                         case '>=':
3967                                 return a >= b;
3968                 }
3969         }
3970         
3971         
3972         /**
3973          * @type Range
3974          * @constructor
3975          * @param {Object} start
3976          * @param {Number} len
3977          */
3978         function Range(start, len) {
3979                 if (_.isObject(start) && 'start' in start) {
3980                         // create range from object stub
3981                         this.start = Math.min(start.start, start.end);
3982                         this.end = Math.max(start.start, start.end);
3983                 } else if (_.isArray(start)) {
3984                         this.start = start[0];
3985                         this.end = start[1];
3986                 } else {
3987                         len = _.isString(len) ? len.length : +len;
3988                         this.start = start;
3989                         this.end = start + len;
3990                 }
3991         }
3992         
3993         Range.prototype = {
3994                 length: function() {
3995                         return Math.abs(this.end - this.start);
3996                 },
3997                 
3998                 /**
3999                  * Returns <code>true</code> if passed range is equals to current one
4000                  * @param {Range} range
4001                  * @returns {Boolean}
4002                  */
4003                 equal: function(range) {
4004                         return this.cmp(range, 'eq', 'eq');
4005 //                      return this.start === range.start && this.end === range.end;
4006                 },
4007                 
4008                 /**
4009                  * Shifts indexes position with passed <code>delat</code>
4010                  * @param {Number} delta
4011                  * @returns {Range} range itself
4012                  */
4013                 shift: function(delta) {
4014                         this.start += delta;
4015                         this.end += delta;
4016                         return this;
4017                 },
4018                 
4019                 /**
4020                  * Check if two ranges are overlapped
4021                  * @param {Range} range
4022                  * @returns {Boolean}
4023                  */
4024                 overlap: function(range) {
4025                         return range.start <= this.end && range.end >= this.start;
4026                 },
4027                 
4028                 /**
4029                  * Finds intersection of two ranges
4030                  * @param {Range} range
4031                  * @returns {Range} <code>null</code> if ranges does not overlap
4032                  */
4033                 intersection: function(range) {
4034                         if (this.overlap(range)) {
4035                                 var start = Math.max(range.start, this.start);
4036                                 var end = Math.min(range.end, this.end);
4037                                 return new Range(start, end - start);
4038                         }
4039                         
4040                         return null;
4041                 },
4042                 
4043                 /**
4044                  * Returns the union of the thow ranges.
4045                  * @param {Range} range
4046                  * @returns {Range} <code>null</code> if ranges are not overlapped
4047                  */
4048                 union: function(range) {
4049                         if (this.overlap(range)) {
4050                                 var start = Math.min(range.start, this.start);
4051                                 var end = Math.max(range.end, this.end);
4052                                 return new Range(start, end - start);
4053                         }
4054                         
4055                         return null;
4056                 },
4057                 
4058                 /**
4059                  * Returns a Boolean value that indicates whether a specified position 
4060                  * is in a given range.
4061                  * @param {Number} loc
4062                  */
4063                 inside: function(loc) {
4064                         return this.cmp(loc, 'lte', 'gt');
4065 //                      return this.start <= loc && this.end > loc;
4066                 },
4067                 
4068                 /**
4069                  * Returns a Boolean value that indicates whether a specified position 
4070                  * is in a given range, but not equals bounds.
4071                  * @param {Number} loc
4072                  */
4073                 contains: function(loc) {
4074                         return this.cmp(loc, 'lt', 'gt');
4075                 },
4076                 
4077                 /**
4078                  * Check if current range completely includes specified one
4079                  * @param {Range} r
4080                  * @returns {Boolean} 
4081                  */
4082                 include: function(r) {
4083                         return this.cmp(r, 'lte', 'gte');
4084 //                      return this.start <= r.start && this.end >= r.end;
4085                 },
4086                 
4087                 /**
4088                  * Low-level comparision method
4089                  * @param {Number} loc
4090                  * @param {String} left Left comparison operator
4091                  * @param {String} right Right comaprison operator
4092                  */
4093                 cmp: function(loc, left, right) {
4094                         var a, b;
4095                         if (loc instanceof Range) {
4096                                 a = loc.start;
4097                                 b = loc.end;
4098                         } else {
4099                                 a = b = loc;
4100                         }
4101                         
4102                         return cmp(this.start, a, left || '<=') && cmp(this.end, b, right || '>');
4103                 },
4104                 
4105                 /**
4106                  * Returns substring of specified <code>str</code> for current range
4107                  * @param {String} str
4108                  * @returns {String}
4109                  */
4110                 substring: function(str) {
4111                         return this.length() > 0 
4112                                 ? str.substring(this.start, this.end) 
4113                                 : '';
4114                 },
4115                 
4116                 /**
4117                  * Creates copy of current range
4118                  * @returns {Range}
4119                  */
4120                 clone: function() {
4121                         return new Range(this.start, this.length());
4122                 },
4123                 
4124                 /**
4125                  * @returns {Array}
4126                  */
4127                 toArray: function() {
4128                         return [this.start, this.end];
4129                 },
4130                 
4131                 toString: function() {
4132                         return this.valueOf();
4133                 },
4134 
4135                 valueOf: function() {
4136                         return '{' + this.start + ', ' + this.length() + '}';
4137                 }
4138         };
4139         
4140         return {
4141                 /**
4142                  * Creates new range object instance
4143                  * @param {Object} start Range start or array with 'start' and 'end'
4144                  * as two first indexes or object with 'start' and 'end' properties
4145                  * @param {Number} len Range length or string to produce range from
4146                  * @returns {Range}
4147                  * @memberOf emmet.range
4148                  */
4149                 create: function(start, len) {
4150                         if (_.isUndefined(start) || start === null)
4151                                 return null;
4152                         
4153                         if (start instanceof Range)
4154                                 return start;
4155                         
4156                         if (_.isObject(start) && 'start' in start && 'end' in start) {
4157                                 len = start.end - start.start;
4158                                 start = start.start;
4159                         }
4160                                 
4161                         return new Range(start, len);
4162                 },
4163                 
4164                 /**
4165                  * <code>Range</code> object factory, the same as <code>this.create()</code>
4166                  * but last argument represents end of range, not length
4167                  * @returns {Range}
4168                  */
4169                 create2: function(start, end) {
4170                         if (_.isNumber(start) && _.isNumber(end)) {
4171                                 end -= start;
4172                         }
4173                         
4174                         return this.create(start, end);
4175                 }
4176         };
4177 });/**
4178  * Utility module that provides ordered storage of function handlers. 
4179  * Many Emmet modules' functionality can be extended/overridden by custom
4180  * function. This modules provides unified storage of handler functions, their 
4181  * management and execution
4182  * 
4183  * @constructor
4184  * @memberOf __handlerListDefine
4185  * @param {Function} require
4186  * @param {Underscore} _
4187  */
4188 emmet.define('handlerList', function(require, _) {
4189         /**
4190          * @type HandlerList
4191          * @constructor
4192          */
4193         function HandlerList() {
4194                 this._list = [];
4195         }
4196         
4197         HandlerList.prototype = {
4198                 /**
4199                  * Adds function handler
4200                  * @param {Function} fn Handler
4201                  * @param {Object} options Handler options. Possible values are:<br><br>
4202                  * <b>order</b> : (<code>Number</code>) – order in handler list. Handlers
4203                  * with higher order value will be executed earlier.
4204                  */
4205                 add: function(fn, options) {
4206                         this._list.push(_.extend({order: 0}, options || {}, {fn: fn}));
4207                 },
4208                 
4209                 /**
4210                  * Removes handler from list
4211                  * @param {Function} fn
4212                  */
4213                 remove: function(fn) {
4214                         this._list = _.without(this._list, _.find(this._list, function(item) {
4215                                 return item.fn === fn;
4216                         }));
4217                 },
4218                 
4219                 /**
4220                  * Returns ordered list of handlers. By default, handlers 
4221                  * with the same <code>order</code> option returned in reverse order, 
4222                  * i.e. the latter function was added into the handlers list, the higher 
4223                  * it will be in the returned array 
4224                  * @returns {Array}
4225                  */
4226                 list: function() {
4227                         return _.sortBy(this._list, 'order').reverse();
4228                 },
4229                 
4230                 /**
4231                  * Returns ordered list of handler functions
4232                  * @returns {Array}
4233                  */
4234                 listFn: function() {
4235                         return _.pluck(this.list(), 'fn');
4236                 },
4237                 
4238                 /**
4239                  * Executes handler functions in their designated order. If function
4240                  * returns <code>skipVal</code>, meaning that function was unable to 
4241                  * handle passed <code>args</code>, the next function will be executed
4242                  * and so on.
4243                  * @param {Object} skipValue If function returns this value, execute 
4244                  * next handler.
4245                  * @param {Array} args Arguments to pass to handler function
4246                  * @returns {Boolean} Whether any of registered handlers performed
4247                  * successfully  
4248                  */
4249                 exec: function(skipValue, args) {
4250                         args = args || [];
4251                         var result = null;
4252                         _.find(this.list(), function(h) {
4253                                 result = h.fn.apply(h, args);
4254                                 if (result !== skipValue)
4255                                         return true;
4256                         });
4257                         
4258                         return result;
4259                 }
4260         };
4261         
4262         return {
4263                 /**
4264                  * Factory method that produces <code>HandlerList</code> instance
4265                  * @returns {HandlerList}
4266                  * @memberOf handlerList
4267                  */
4268                 create: function() {
4269                         return new HandlerList();
4270                 }
4271         };
4272 });/**
4273  * Helper class for convenient token iteration
4274  */
4275 emmet.define('tokenIterator', function(require, _) {
4276         /**
4277          * @type TokenIterator
4278          * @param {Array} tokens
4279          * @type TokenIterator
4280          * @constructor
4281          */
4282         function TokenIterator(tokens) {
4283                 /** @type Array */
4284                 this.tokens = tokens;
4285                 this._position = 0;
4286                 this.reset();
4287         }
4288         
4289         TokenIterator.prototype = {
4290                 next: function() {
4291                         if (this.hasNext()) {
4292                                 var token = this.tokens[++this._i];
4293                                 this._position = token.start;
4294                                 return token;
4295                         }
4296                         
4297                         return null;
4298                 },
4299                 
4300                 current: function() {
4301                         return this.tokens[this._i];
4302                 },
4303                 
4304                 position: function() {
4305                         return this._position;
4306                 },
4307                 
4308                 hasNext: function() {
4309                         return this._i < this._il - 1;
4310                 },
4311                 
4312                 reset: function() {
4313                         this._i = -1;
4314                         this._il = this.tokens.length;
4315                 },
4316                 
4317                 item: function() {
4318                         return this.tokens[this._i];
4319                 },
4320                 
4321                 itemNext: function() {
4322                         return this.tokens[this._i + 1];
4323                 },
4324                 
4325                 itemPrev: function() {
4326                         return this.tokens[this._i - 1];
4327                 },
4328                 
4329                 nextUntil: function(type, callback) {
4330                         var token;
4331                         var test = _.isString(type) 
4332                                 ? function(t){return t.type == type;} 
4333                                 : type;
4334                         
4335                         while ((token = this.next())) {
4336                                 if (callback)
4337                                         callback.call(this, token);
4338                                 if (test.call(this, token))
4339                                         break;
4340                         }
4341                 }
4342         };
4343         
4344         return {
4345                 create: function(tokens) {
4346                         return new TokenIterator(tokens);
4347                 }
4348         };
4349 });/**
4350  * A trimmed version of CodeMirror's StringStream module for string parsing
4351  */
4352 emmet.define('stringStream', function(require, _) {
4353         /**
4354          * @type StringStream
4355          * @constructor
4356          * @param {String} string
4357          */
4358         function StringStream(string) {
4359                 this.pos = this.start = 0;
4360                 this.string = string;
4361         }
4362         
4363         StringStream.prototype = {
4364                 /**
4365                  * Returns true only if the stream is at the end of the line.
4366                  * @returns {Boolean}
4367                  */
4368                 eol: function() {
4369                         return this.pos >= this.string.length;
4370                 },
4371                 
4372                 /**
4373                  * Returns true only if the stream is at the start of the line
4374                  * @returns {Boolean}
4375                  */
4376                 sol: function() {
4377                         return this.pos === 0;
4378                 },
4379                 
4380                 /**
4381                  * Returns the next character in the stream without advancing it. 
4382                  * Will return <code>undefined</code> at the end of the line.
4383                  * @returns {String}
4384                  */
4385                 peek: function() {
4386                         return this.string.charAt(this.pos);
4387                 },
4388                 
4389                 /**
4390                  * Returns the next character in the stream and advances it.
4391                  * Also returns <code>undefined</code> when no more characters are available.
4392                  * @returns {String}
4393                  */
4394                 next: function() {
4395                         if (this.pos < this.string.length)
4396                                 return this.string.charAt(this.pos++);
4397                 },
4398                 
4399                 /**
4400                  * match can be a character, a regular expression, or a function that
4401                  * takes a character and returns a boolean. If the next character in the
4402                  * stream 'matches' the given argument, it is consumed and returned.
4403                  * Otherwise, undefined is returned.
4404                  * @param {Object} match
4405                  * @returns {String}
4406                  */
4407                 eat: function(match) {
4408                         var ch = this.string.charAt(this.pos), ok;
4409                         if (typeof match == "string")
4410                                 ok = ch == match;
4411                         else
4412                                 ok = ch && (match.test ? match.test(ch) : match(ch));
4413                         
4414                         if (ok) {
4415                                 ++this.pos;
4416                                 return ch;
4417                         }
4418                 },
4419                 
4420                 /**
4421                  * Repeatedly calls <code>eat</code> with the given argument, until it
4422                  * fails. Returns <code>true</code> if any characters were eaten.
4423                  * @param {Object} match
4424                  * @returns {Boolean}
4425                  */
4426                 eatWhile: function(match) {
4427                         var start = this.pos;
4428                         while (this.eat(match)) {}
4429                         return this.pos > start;
4430                 },
4431                 
4432                 /**
4433                  * Shortcut for <code>eatWhile</code> when matching white-space.
4434                  * @returns {Boolean}
4435                  */
4436                 eatSpace: function() {
4437                         var start = this.pos;
4438                         while (/[\s\u00a0]/.test(this.string.charAt(this.pos)))
4439                                 ++this.pos;
4440                         return this.pos > start;
4441                 },
4442                 
4443                 /**
4444                  * Moves the position to the end of the line.
4445                  */
4446                 skipToEnd: function() {
4447                         this.pos = this.string.length;
4448                 },
4449                 
4450                 /**
4451                  * Skips to the next occurrence of the given character, if found on the
4452                  * current line (doesn't advance the stream if the character does not
4453                  * occur on the line). Returns true if the character was found.
4454                  * @param {String} ch
4455                  * @returns {Boolean}
4456                  */
4457                 skipTo: function(ch) {
4458                         var found = this.string.indexOf(ch, this.pos);
4459                         if (found > -1) {
4460                                 this.pos = found;
4461                                 return true;
4462                         }
4463                 },
4464                 
4465                 /**
4466                  * Skips to <code>close</code> character which is pair to <code>open</code>
4467                  * character, considering possible pair nesting. This function is used
4468                  * to consume pair of characters, like opening and closing braces
4469                  * @param {String} open
4470                  * @param {String} close
4471                  * @returns {Boolean} Returns <code>true</code> if pair was successfully
4472                  * consumed
4473                  */
4474                 skipToPair: function(open, close) {
4475                         var braceCount = 0, ch;
4476                         var pos = this.pos, len = this.string.length;
4477                         while (pos < len) {
4478                                 ch = this.string.charAt(pos++);
4479                                 if (ch == open) {
4480                                         braceCount++;
4481                                 } else if (ch == close) {
4482                                         braceCount--;
4483                                         if (braceCount < 1) {
4484                                                 this.pos = pos;
4485                                                 return true;
4486                                         }
4487                                 }
4488                         }
4489                         
4490                         return false;
4491                 },
4492                 
4493                 /**
4494                  * Backs up the stream n characters. Backing it up further than the
4495                  * start of the current token will cause things to break, so be careful.
4496                  * @param {Number} n
4497                  */
4498                 backUp : function(n) {
4499                         this.pos -= n;
4500                 },
4501                 
4502                 /**
4503                  * Act like a multi-character <code>eat</code>—if <code>consume</code> is true or
4504                  * not given—or a look-ahead that doesn't update the stream position—if
4505                  * it is false. <code>pattern</code> can be either a string or a
4506                  * regular expression starting with ^. When it is a string,
4507                  * <code>caseInsensitive</code> can be set to true to make the match
4508                  * case-insensitive. When successfully matching a regular expression,
4509                  * the returned value will be the array returned by <code>match</code>,
4510                  * in case you need to extract matched groups.
4511                  * 
4512                  * @param {RegExp} pattern
4513                  * @param {Boolean} consume
4514                  * @param {Boolean} caseInsensitive
4515                  * @returns
4516                  */
4517                 match: function(pattern, consume, caseInsensitive) {
4518                         if (typeof pattern == "string") {
4519                                 var cased = caseInsensitive
4520                                         ? function(str) {return str.toLowerCase();}
4521                                         : function(str) {return str;};
4522                                 
4523                                 if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
4524                                         if (consume !== false)
4525                                                 this.pos += pattern.length;
4526                                         return true;
4527                                 }
4528                         } else {
4529                                 var match = this.string.slice(this.pos).match(pattern);
4530                                 if (match && consume !== false)
4531                                         this.pos += match[0].length;
4532                                 return match;
4533                         }
4534                 },
4535                 
4536                 /**
4537                  * Get the string between the start of the current token and the 
4538                  * current stream position.
4539                  * @returns {String}
4540                  */
4541                 current: function() {
4542                         return this.string.slice(this.start, this.pos);
4543                 }
4544         };
4545         
4546         return {
4547                 create: function(string) {
4548                         return new StringStream(string);
4549                 }
4550         };
4551 });/**
4552  * Parsed resources (snippets, abbreviations, variables, etc.) for Emmet.
4553  * Contains convenient method to get access for snippets with respect of 
4554  * inheritance. Also provides ability to store data in different vocabularies
4555  * ('system' and 'user') for fast and safe resource update
4556  * @author Sergey Chikuyonok (serge.che@gmail.com)
4557  * @link http://chikuyonok.ru
4558  * 
4559  * @param {Function} require
4560  * @param {Underscore} _
4561  */
4562 emmet.define('resources', function(require, _) {
4563         var VOC_SYSTEM = 'system';
4564         var VOC_USER = 'user';
4565         
4566         var cache = {};
4567                 
4568         /** Regular expression for XML tag matching */
4569         var reTag = /^<(\w+\:?[\w\-]*)((?:\s+[\w\:\-]+\s*=\s*(['"]).*?\3)*)\s*(\/?)>/;
4570                 
4571         var systemSettings = {};
4572         var userSettings = {};
4573         
4574         /** @type HandlerList List of registered abbreviation resolvers */
4575         var resolvers = require('handlerList').create();
4576         
4577         /**
4578          * Normalizes caret plceholder in passed text: replaces | character with
4579          * default caret placeholder
4580          * @param {String} text
4581          * @returns {String}
4582          */
4583         function normalizeCaretPlaceholder(text) {
4584                 var utils = require('utils');
4585                 return utils.replaceUnescapedSymbol(text, '|', utils.getCaretPlaceholder());
4586         }
4587         
4588         function parseItem(name, value, type) {
4589                 value = normalizeCaretPlaceholder(value);
4590                 
4591                 if (type == 'snippets') {
4592                         return require('elements').create('snippet', value);
4593                 }
4594                 
4595                 if (type == 'abbreviations') {
4596                         return parseAbbreviation(name, value);
4597                 }
4598         }
4599         
4600         /**
4601          * Parses single abbreviation
4602          * @param {String} key Abbreviation name
4603          * @param {String} value Abbreviation value
4604          * @return {Object}
4605          */
4606         function parseAbbreviation(key, value) {
4607                 key = require('utils').trim(key);
4608                 var elements = require('elements');
4609                 var m;
4610                 if ((m = reTag.exec(value))) {
4611                         return elements.create('element', m[1], m[2], m[4] == '/');
4612                 } else {
4613                         // assume it's reference to another abbreviation
4614                         return elements.create('reference', value);
4615                 }
4616         }
4617         
4618         /**
4619          * Normalizes snippet key name for better fuzzy search
4620          * @param {String} str
4621          * @returns {String}
4622          */
4623         function normalizeName(str) {
4624                 return str.replace(/:$/, '').replace(/:/g, '-');
4625         }
4626         
4627         return {
4628                 /**
4629                  * Sets new unparsed data for specified settings vocabulary
4630                  * @param {Object} data
4631                  * @param {String} type Vocabulary type ('system' or 'user')
4632                  * @memberOf resources
4633                  */
4634                 setVocabulary: function(data, type) {
4635                         cache = {};
4636                         if (type == VOC_SYSTEM)
4637                                 systemSettings = data;
4638                         else
4639                                 userSettings = data;
4640                 },
4641                 
4642                 /**
4643                  * Returns resource vocabulary by its name
4644                  * @param {String} name Vocabulary name ('system' or 'user')
4645                  * @return {Object}
4646                  */
4647                 getVocabulary: function(name) {
4648                         return name == VOC_SYSTEM ? systemSettings : userSettings;
4649                 },
4650                 
4651                 /**
4652                  * Returns resource (abbreviation, snippet, etc.) matched for passed 
4653                  * abbreviation
4654                  * @param {AbbreviationNode} node
4655                  * @param {String} syntax
4656                  * @returns {Object}
4657                  */
4658                 getMatchedResource: function(node, syntax) {
4659                         return resolvers.exec(null, _.toArray(arguments)) 
4660                                 || this.findSnippet(syntax, node.name());
4661                 },
4662                 
4663                 /**
4664                  * Returns variable value
4665                  * @return {String}
4666                  */
4667                 getVariable: function(name) {
4668                         return (this.getSection('variables') || {})[name];
4669                 },
4670                 
4671                 /**
4672                  * Store runtime variable in user storage
4673                  * @param {String} name Variable name
4674                  * @param {String} value Variable value
4675                  */
4676                 setVariable: function(name, value){
4677                         var voc = this.getVocabulary('user') || {};
4678                         if (!('variables' in voc))
4679                                 voc.variables = {};
4680                                 
4681                         voc.variables[name] = value;
4682                         this.setVocabulary(voc, 'user');
4683                 },
4684                 
4685                 /**
4686                  * Check if there are resources for specified syntax
4687                  * @param {String} syntax
4688                  * @return {Boolean}
4689                  */
4690                 hasSyntax: function(syntax) {
4691                         return syntax in this.getVocabulary(VOC_USER) 
4692                                 || syntax in this.getVocabulary(VOC_SYSTEM);
4693                 },
4694                 
4695                 /**
4696                  * Registers new abbreviation resolver.
4697                  * @param {Function} fn Abbreviation resolver which will receive 
4698                  * abbreviation as first argument and should return parsed abbreviation
4699                  * object if abbreviation has handled successfully, <code>null</code>
4700                  * otherwise
4701                  * @param {Object} options Options list as described in 
4702                  * {@link HandlerList#add()} method
4703                  */
4704                 addResolver: function(fn, options) {
4705                         resolvers.add(fn, options);
4706                 },
4707                 
4708                 removeResolver: function(fn) {
4709                         resolvers.remove(fn);
4710                 },
4711                 
4712                 /**
4713                  * Returns actual section data, merged from both
4714                  * system and user data
4715                  * @param {String} name Section name (syntax)
4716                  * @param {String} ...args Subsections
4717                  * @returns
4718                  */
4719                 getSection: function(name) {
4720                         if (!name)
4721                                 return null;
4722                         
4723                         if (!(name in cache)) {
4724                                 cache[name] = require('utils').deepMerge({}, systemSettings[name], userSettings[name]);
4725                         }
4726                         
4727                         var data = cache[name], subsections = _.rest(arguments), key;
4728                         while (data && (key = subsections.shift())) {
4729                                 if (key in data) {
4730                                         data = data[key];
4731                                 } else {
4732                                         return null;
4733                                 }
4734                         }
4735                         
4736                         return data;
4737                 },
4738                 
4739                 /**
4740                  * Recursively searches for a item inside top level sections (syntaxes)
4741                  * with respect of `extends` attribute
4742                  * @param {String} topSection Top section name (syntax)
4743                  * @param {String} subsection Inner section name
4744                  * @returns {Object}
4745                  */
4746                 findItem: function(topSection, subsection) {
4747                         var data = this.getSection(topSection);
4748                         while (data) {
4749                                 if (subsection in data)
4750                                         return data[subsection];
4751                                 
4752                                 data = this.getSection(data['extends']);
4753                         }
4754                 },
4755                 
4756                 /**
4757                  * Recursively searches for a snippet definition inside syntax section.
4758                  * Definition is searched inside `snippets` and `abbreviations` 
4759                  * subsections  
4760                  * @param {String} syntax Top-level section name (syntax)
4761                  * @param {String} name Snippet name
4762                  * @returns {Object}
4763                  */
4764                 findSnippet: function(syntax, name, memo) {
4765                         if (!syntax || !name)
4766                                 return null;
4767                         
4768                         memo = memo || [];
4769                         
4770                         var names = [name];
4771                         // create automatic aliases to properties with colons,
4772                         // e.g. pos-a == pos:a
4773                         if (~name.indexOf('-'))
4774                                 names.push(name.replace(/\-/g, ':'));
4775                         
4776                         var data = this.getSection(syntax), matchedItem = null;
4777                         _.find(['snippets', 'abbreviations'], function(sectionName) {
4778                                 var data = this.getSection(syntax, sectionName);
4779                                 if (data) {
4780                                         return _.find(names, function(n) {
4781                                                 if (data[n])
4782                                                         return matchedItem = parseItem(n, data[n], sectionName);
4783                                         });
4784                                 }
4785                         }, this);
4786                         
4787                         memo.push(syntax);
4788                         if (!matchedItem && data['extends'] && !_.include(memo, data['extends'])) {
4789                                 // try to find item in parent syntax section
4790                                 return this.findSnippet(data['extends'], name, memo);
4791                         }
4792                         
4793                         return matchedItem;
4794                 },
4795                 
4796                 /**
4797                  * Performs fuzzy search of snippet definition
4798                  * @param {String} syntax Top-level section name (syntax)
4799                  * @param {String} name Snippet name
4800                  * @returns
4801                  */
4802                 fuzzyFindSnippet: function(syntax, name, minScore) {
4803                         minScore = minScore || 0.3;
4804                         
4805                         var payload = this.getAllSnippets(syntax);
4806                         var sc = require('string-score');
4807                         
4808                         name = normalizeName(name);
4809                         var scores = _.map(payload, function(value, key) {
4810                                 return {
4811                                         key: key,
4812                                         score: sc.score(value.nk, name, 0.1)
4813                                 };
4814                         });
4815                         
4816                         var result = _.last(_.sortBy(scores, 'score'));
4817                         if (result && result.score >= minScore) {
4818                                 var k = result.key;
4819                                 return payload[k].parsedValue;
4820 //                              return parseItem(k, payload[k].value, payload[k].type);
4821                         }
4822                 },
4823                 
4824                 /**
4825                  * Returns plain dictionary of all available abbreviations and snippets
4826                  * for specified syntax with respect of inheritance
4827                  * @param {String} syntax
4828                  * @returns {Object}
4829                  */
4830                 getAllSnippets: function(syntax) {
4831                         var cacheKey = 'all-' + syntax;
4832                         if (!cache[cacheKey]) {
4833                                 var stack = [], sectionKey = syntax;
4834                                 var memo = [];
4835                                 
4836                                 do {
4837                                         var section = this.getSection(sectionKey);
4838                                         if (!section)
4839                                                 break;
4840                                         
4841                                         _.each(['snippets', 'abbreviations'], function(sectionName) {
4842                                                 var stackItem = {};
4843                                                 _.each(section[sectionName] || null, function(v, k) {
4844                                                         stackItem[k] = {
4845                                                                 nk: normalizeName(k),
4846                                                                 value: v,
4847                                                                 parsedValue: parseItem(k, v, sectionName),
4848                                                                 type: sectionName
4849                                                         };
4850                                                 });
4851                                                 
4852                                                 stack.push(stackItem);
4853                                         });
4854                                         
4855                                         memo.push(sectionKey);
4856                                         sectionKey = section['extends'];
4857                                 } while (sectionKey && !_.include(memo, sectionKey));
4858                                 
4859                                 
4860                                 cache[cacheKey] = _.extend.apply(_, stack.reverse());
4861                         }
4862                         
4863                         return cache[cacheKey];
4864                 }
4865         };
4866 });/**
4867  * Module describes and performs Emmet actions. The actions themselves are
4868  * defined in <i>actions</i> folder
4869  * @param {Function} require
4870  * @param {Underscore} _
4871  */
4872 emmet.define('actions', function(require, _) {
4873         var actions = {};
4874         
4875         /**
4876          * “Humanizes” action name, makes it more readable for people
4877          * @param {String} name Action name (like 'expand_abbreviation')
4878          * @return Humanized name (like 'Expand Abbreviation')
4879          */
4880         function humanizeActionName(name) {
4881                 return require('utils').trim(name.charAt(0).toUpperCase() 
4882                         + name.substring(1).replace(/_[a-z]/g, function(str) {
4883                                 return ' ' + str.charAt(1).toUpperCase();
4884                         }));
4885         }
4886         
4887         return {
4888                 /**
4889                  * Registers new action
4890                  * @param {String} name Action name
4891                  * @param {Function} fn Action function
4892                  * @param {Object} options Custom action options:<br>
4893                  * <b>label</b> : (<code>String</code>) – Human-readable action name. 
4894                  * May contain '/' symbols as submenu separators<br>
4895                  * <b>hidden</b> : (<code>Boolean</code>) – Indicates whether action
4896                  * should be displayed in menu (<code>getMenu()</code> method)
4897                  * 
4898                  * @memberOf actions
4899                  */
4900                 add: function(name, fn, options) {
4901                         name = name.toLowerCase();
4902                         options = options || {};
4903                         if (!options.label) {
4904                                 options.label = humanizeActionName(name);
4905                         }
4906                         
4907                         actions[name] = {
4908                                 name: name,
4909                                 fn: fn,
4910                                 options: options
4911                         };
4912                 },
4913                 
4914                 /**
4915                  * Returns action object
4916                  * @param {String} name Action name
4917                  * @returns {Object}
4918                  */
4919                 get: function(name) {
4920                         return actions[name.toLowerCase()];
4921                 },
4922                 
4923                 /**
4924                  * Runs Emmet action. For list of available actions and their
4925                  * arguments see <i>actions</i> folder.
4926                  * @param {String} name Action name 
4927                  * @param {Array} args Additional arguments. It may be array of arguments
4928                  * or inline arguments. The first argument should be <code>IEmmetEditor</code> instance
4929                  * @returns {Boolean} Status of performed operation, <code>true</code>
4930                  * means action was performed successfully.
4931                  * @example
4932                  * emmet.require('actions').run('expand_abbreviation', editor);  
4933                  * emmet.require('actions').run('wrap_with_abbreviation', [editor, 'div']);  
4934                  */
4935                 run: function(name, args) {
4936                         if (!_.isArray(args)) {
4937                                 args = _.rest(arguments);
4938                         }
4939                         
4940                         var action = this.get(name);
4941                         if (action) {
4942                                 return action.fn.apply(emmet, args);
4943                         } else {
4944                                 emmet.log('Action "%s" is not defined', name);
4945                                 return false;
4946                         }
4947                 },
4948                 
4949                 /**
4950                  * Returns all registered actions as object
4951                  * @returns {Object}
4952                  */
4953                 getAll: function() {
4954                         return actions;
4955                 },
4956                 
4957                 /**
4958                  * Returns all registered actions as array
4959                  * @returns {Array}
4960                  */
4961                 getList: function() {
4962                         return _.values(this.getAll());
4963                 },
4964                 
4965                 /**
4966                  * Returns actions list as structured menu. If action has <i>label</i>,
4967                  * it will be splitted by '/' symbol into submenus (for example: 
4968                  * CSS/Reflect Value) and grouped with other items
4969                  * @param {Array} skipActions List of action identifiers that should be 
4970                  * skipped from menu
4971                  * @returns {Array}
4972                  */
4973                 getMenu: function(skipActions) {
4974                         var result = [];
4975                         skipActions = skipActions || [];
4976                         _.each(this.getList(), function(action) {
4977                                 if (action.options.hidden || _.include(skipActions, action.name))
4978                                         return;
4979                                 
4980                                 var actionName = humanizeActionName(action.name);
4981                                 var ctx = result;
4982                                 if (action.options.label) {
4983                                         var parts = action.options.label.split('/');
4984                                         actionName = parts.pop();
4985                                         
4986                                         // create submenus, if needed
4987                                         var menuName, submenu;
4988                                         while ((menuName = parts.shift())) {
4989                                                 submenu = _.find(ctx, function(item) {
4990                                                         return item.type == 'submenu' && item.name == menuName;
4991                                                 });
4992                                                 
4993                                                 if (!submenu) {
4994                                                         submenu = {
4995                                                                 name: menuName,
4996                                                                 type: 'submenu',
4997                                                                 items: []
4998                                                         };
4999                                                         ctx.push(submenu);
5000                                                 }
5001                                                 
5002                                                 ctx = submenu.items;
5003                                         }
5004                                 }
5005                                 
5006                                 ctx.push({
5007                                         type: 'action',
5008                                         name: action.name,
5009                                         label: actionName
5010                                 });
5011                         });
5012                         
5013                         return result;
5014                 },
5015 
5016                 /**
5017                  * Returns action name associated with menu item title
5018                  * @param {String} title
5019                  * @returns {String}
5020                  */
5021                 getActionNameForMenuTitle: function(title, menu) {
5022                         var item = null;
5023                         _.find(menu || this.getMenu(), function(val) {
5024                                 if (val.type == 'action') {
5025                                         if (val.label == title || val.name == title) {
5026                                                 return item = val.name;
5027                                         }
5028                                 } else {
5029                                         return item = this.getActionNameForMenuTitle(title, val.items);
5030                                 }
5031                         }, this);
5032                         
5033                         return item || null;
5034                 }
5035         };
5036 });/**
5037  * Output profile module.
5038  * Profile defines how XHTML output data should look like
5039  * @param {Function} require
5040  * @param {Underscore} _
5041  */
5042 emmet.define('profile', function(require, _) {
5043         var profiles = {};
5044         
5045         var defaultProfile = {
5046                 tag_case: 'asis',
5047                 attr_case: 'asis',
5048                 attr_quotes: 'double',
5049                 
5050                 // each tag on new line
5051                 tag_nl: 'decide',
5052                 
5053                 // with tag_nl === true, defines if leaf node (e.g. node with no children)
5054                 // should have formatted line breaks
5055                 tag_nl_leaf: false,
5056                 
5057                 place_cursor: true,
5058                 
5059                 // indent tags
5060                 indent: true,
5061                 
5062                 // how many inline elements should be to force line break 
5063                 // (set to 0 to disable)
5064                 inline_break: 3,
5065                 
5066                 // use self-closing style for writing empty elements, e.g. <br /> or <br>
5067                 self_closing_tag: 'xhtml',
5068                 
5069                 // Profile-level output filters, re-defines syntax filters 
5070                 filters: '',
5071                 
5072                 // Additional filters applied to abbreviation.
5073                 // Unlike "filters", this preference doesn't override default filters
5074                 // but add the instead every time given profile is chosen
5075                 extraFilters: ''
5076         };
5077         
5078         /**
5079          * @constructor
5080          * @type OutputProfile
5081          * @param {Object} options
5082          */
5083         function OutputProfile(options) {
5084                 _.extend(this, defaultProfile, options);
5085         }
5086         
5087         OutputProfile.prototype = {
5088                 /**
5089                  * Transforms tag name case depending on current profile settings
5090                  * @param {String} name String to transform
5091                  * @returns {String}
5092                  */
5093                 tagName: function(name) {
5094                         return stringCase(name, this.tag_case);
5095                 },
5096                 
5097                 /**
5098                  * Transforms attribute name case depending on current profile settings 
5099                  * @param {String} name String to transform
5100                  * @returns {String}
5101                  */
5102                 attributeName: function(name) {
5103                         return stringCase(name, this.attr_case);
5104                 },
5105                 
5106                 /**
5107                  * Returns quote character for current profile
5108                  * @returns {String}
5109                  */
5110                 attributeQuote: function() {
5111                         return this.attr_quotes == 'single' ? "'" : '"';
5112                 },
5113                 
5114                 /**
5115                  * Returns self-closing tag symbol for current profile
5116                  * @returns {String}
5117                  */
5118                 selfClosing: function() {
5119                         if (this.self_closing_tag == 'xhtml')
5120                                 return ' /';
5121                         
5122                         if (this.self_closing_tag === true)
5123                                 return '/';
5124                         
5125                         return '';
5126                 },
5127                 
5128                 /**
5129                  * Returns cursor token based on current profile settings
5130                  * @returns {String}
5131                  */
5132                 cursor: function() {
5133                         return this.place_cursor ? require('utils').getCaretPlaceholder() : '';
5134                 }
5135         };
5136         
5137         /**
5138          * Helper function that converts string case depending on 
5139          * <code>caseValue</code> 
5140          * @param {String} str String to transform
5141          * @param {String} caseValue Case value: can be <i>lower</i>, 
5142          * <i>upper</i> and <i>leave</i>
5143          * @returns {String}
5144          */
5145         function stringCase(str, caseValue) {
5146                 switch (String(caseValue || '').toLowerCase()) {
5147                         case 'lower':
5148                                 return str.toLowerCase();
5149                         case 'upper':
5150                                 return str.toUpperCase();
5151                 }
5152                 
5153                 return str;
5154         }
5155         
5156         /**
5157          * Creates new output profile
5158          * @param {String} name Profile name
5159          * @param {Object} options Profile options
5160          */
5161         function createProfile(name, options) {
5162                 return profiles[name.toLowerCase()] = new OutputProfile(options);
5163         }
5164         
5165         function createDefaultProfiles() {
5166                 createProfile('xhtml');
5167                 createProfile('html', {self_closing_tag: false});
5168                 createProfile('xml', {self_closing_tag: true, tag_nl: true});
5169                 createProfile('plain', {tag_nl: false, indent: false, place_cursor: false});
5170                 createProfile('line', {tag_nl: false, indent: false, extraFilters: 's'});
5171         }
5172         
5173         createDefaultProfiles();
5174         
5175         return  {
5176                 /**
5177                  * Creates new output profile and adds it into internal dictionary
5178                  * @param {String} name Profile name
5179                  * @param {Object} options Profile options
5180                  * @memberOf emmet.profile
5181                  * @returns {Object} New profile
5182                  */
5183                 create: function(name, options) {
5184                         if (arguments.length == 2)
5185                                 return createProfile(name, options);
5186                         else
5187                                 // create profile object only
5188                                 return new OutputProfile(_.defaults(name || {}, defaultProfile));
5189                 },
5190                 
5191                 /**
5192                  * Returns profile by its name. If profile wasn't found, returns
5193                  * 'plain' profile
5194                  * @param {String} name Profile name. Might be profile itself
5195                  * @param {String} syntax. Optional. Current editor syntax. If defined,
5196                  * profile is searched in resources first, then in predefined profiles
5197                  * @returns {Object}
5198                  */
5199                 get: function(name, syntax) {
5200                         if (!name && syntax) {
5201                                 // search in user resources first
5202                                 var profile = require('resources').findItem(syntax, 'profile');
5203                                 if (profile) {
5204                                         name = profile;
5205                                 }
5206                         }
5207                         
5208                         if (!name) {
5209                                 return profiles.plain;
5210                         }
5211                         
5212                         if (name instanceof OutputProfile) {
5213                                 return name;
5214                         }
5215                         
5216                         if (_.isString(name) && name.toLowerCase() in profiles) {
5217                                 return profiles[name.toLowerCase()];
5218                         }
5219                         
5220                         return this.create(name);
5221                 },
5222                 
5223                 /**
5224                  * Deletes profile with specified name
5225                  * @param {String} name Profile name
5226                  */
5227                 remove: function(name) {
5228                         name = (name || '').toLowerCase();
5229                         if (name in profiles)
5230                                 delete profiles[name];
5231                 },
5232                 
5233                 /**
5234                  * Resets all user-defined profiles
5235                  */
5236                 reset: function() {
5237                         profiles = {};
5238                         createDefaultProfiles();
5239                 },
5240                 
5241                 /**
5242                  * Helper function that converts string case depending on 
5243                  * <code>caseValue</code> 
5244                  * @param {String} str String to transform
5245                  * @param {String} caseValue Case value: can be <i>lower</i>, 
5246                  * <i>upper</i> and <i>leave</i>
5247                  * @returns {String}
5248                  */
5249                 stringCase: stringCase
5250         };
5251 });/**
5252  * Utility module used to prepare text for pasting into back-end editor
5253  * @param {Function} require
5254  * @param {Underscore} _
5255  * @author Sergey Chikuyonok (serge.che@gmail.com) <http://chikuyonok.ru>
5256  */
5257 emmet.define('editorUtils', function(require, _) {
5258         return  {
5259                 /**
5260                  * Check if cursor is placed inside XHTML tag
5261                  * @param {String} html Contents of the document
5262                  * @param {Number} caretPos Current caret position inside tag
5263                  * @return {Boolean}
5264                  */
5265                 isInsideTag: function(html, caretPos) {
5266                         var reTag = /^<\/?\w[\w\:\-]*.*?>/;
5267                         
5268                         // search left to find opening brace
5269                         var pos = caretPos;
5270                         while (pos > -1) {
5271                                 if (html.charAt(pos) == '<') 
5272                                         break;
5273                                 pos--;
5274                         }
5275                         
5276                         if (pos != -1) {
5277                                 var m = reTag.exec(html.substring(pos));
5278                                 if (m && caretPos > pos && caretPos < pos + m[0].length)
5279                                         return true;
5280                         }
5281                         
5282                         return false;
5283                 },
5284                 
5285                 /**
5286                  * Sanitizes incoming editor data and provides default values for
5287                  * output-specific info
5288                  * @param {IEmmetEditor} editor
5289                  * @param {String} syntax
5290                  * @param {String} profile
5291                  */
5292                 outputInfo: function(editor, syntax, profile) {
5293                         // most of this code makes sense for Java/Rhino environment
5294                         // because string that comes from Java are not actually JS string
5295                         // but Java String object so the have to be explicitly converted
5296                         // to native string
5297                         profile = profile || editor.getProfileName();
5298                         return  {
5299                                 /** @memberOf outputInfo */
5300                                 syntax: String(syntax || editor.getSyntax()),
5301                                 profile: profile || null,
5302                                 content: String(editor.getContent())
5303                         };
5304                 },
5305                 
5306                 /**
5307                  * Unindent content, thus preparing text for tag wrapping
5308                  * @param {IEmmetEditor} editor Editor instance
5309                  * @param {String} text
5310                  * @return {String}
5311                  */
5312                 unindent: function(editor, text) {
5313                         return require('utils').unindentString(text, this.getCurrentLinePadding(editor));
5314                 },
5315                 
5316                 /**
5317                  * Returns padding of current editor's line
5318                  * @param {IEmmetEditor} Editor instance
5319                  * @return {String}
5320                  */
5321                 getCurrentLinePadding: function(editor) {
5322                         return require('utils').getLinePadding(editor.getCurrentLine());
5323                 }
5324         };
5325 });
5326 /**
5327  * Utility methods for Emmet actions
5328  * @param {Function} require
5329  * @param {Underscore} _
5330  * @author Sergey Chikuyonok (serge.che@gmail.com) <http://chikuyonok.ru>
5331  */
5332 emmet.define('actionUtils', function(require, _) {
5333         return {
5334                 mimeTypes: {
5335                         'gif' : 'image/gif',
5336                         'png' : 'image/png',
5337                         'jpg' : 'image/jpeg',
5338                         'jpeg': 'image/jpeg',
5339                         'svg' : 'image/svg+xml',
5340                         'html': 'text/html',
5341                         'htm' : 'text/html'
5342                 },
5343                 
5344                 /**
5345                  * Extracts abbreviations from text stream, starting from the end
5346                  * @param {String} str
5347                  * @return {String} Abbreviation or empty string
5348                  * @memberOf emmet.actionUtils
5349                  */
5350                 extractAbbreviation: function(str) {
5351                         var curOffset = str.length;
5352                         var startIndex = -1;
5353                         var groupCount = 0;
5354                         var braceCount = 0;
5355                         var textCount = 0;
5356                         
5357                         var utils = require('utils');
5358                         var parser = require('abbreviationParser');
5359                         
5360                         while (true) {
5361                                 curOffset--;
5362                                 if (curOffset < 0) {
5363                                         // moved to the beginning of the line
5364                                         startIndex = 0;
5365                                         break;
5366                                 }
5367                                 
5368                                 var ch = str.charAt(curOffset);
5369                                 
5370                                 if (ch == ']') {
5371                                         braceCount++;
5372                                 } else if (ch == '[') {
5373                                         if (!braceCount) { // unexpected brace
5374                                                 startIndex = curOffset + 1;
5375                                                 break;
5376                                         }
5377                                         braceCount--;
5378                                 } else if (ch == '}') {
5379                                         textCount++;
5380                                 } else if (ch == '{') {
5381                                         if (!textCount) { // unexpected brace
5382                                                 startIndex = curOffset + 1;
5383                                                 break;
5384                                         }
5385                                         textCount--;
5386                                 } else if (ch == ')') {
5387                                         groupCount++;
5388                                 } else if (ch == '(') {
5389                                         if (!groupCount) { // unexpected brace
5390                                                 startIndex = curOffset + 1;
5391                                                 break;
5392                                         }
5393                                         groupCount--;
5394                                 } else {
5395                                         if (braceCount || textCount) 
5396                                                 // respect all characters inside attribute sets or text nodes
5397                                                 continue;
5398                                         else if (!parser.isAllowedChar(ch) || (ch == '>' && utils.endsWithTag(str.substring(0, curOffset + 1)))) {
5399                                                 // found stop symbol
5400                                                 startIndex = curOffset + 1;
5401                                                 break;
5402                                         }
5403                                 }
5404                         }
5405                         
5406                         if (startIndex != -1 && !textCount && !braceCount && !groupCount) 
5407                                 // found something, remove some invalid symbols from the 
5408                                 // beginning and return abbreviation
5409                                 return str.substring(startIndex).replace(/^[\*\+\>\^]+/, '');
5410                         else
5411                                 return '';
5412                 },
5413                 
5414                 /**
5415                  * Gets image size from image byte stream.
5416                  * @author http://romeda.org/rePublish/
5417                  * @param {String} stream Image byte stream (use <code>IEmmetFile.read()</code>)
5418                  * @return {Object} Object with <code>width</code> and <code>height</code> properties
5419                  */
5420                 getImageSize: function(stream) {
5421                         var pngMagicNum = "\x89PNG\r\n\x1A\n",
5422                                 jpgMagicNum = "\xFF\xD8",
5423                                 gifMagicNum = "GIF8",
5424                                 pos = 0,
5425                                 nextByte = function() {
5426                                         return stream.charCodeAt(pos++);
5427                                 };
5428                 
5429                         if (stream.substr(0, 8) === pngMagicNum) {
5430                                 // PNG. Easy peasy.
5431                                 pos = stream.indexOf('IHDR') + 4;
5432                         
5433                                 return {
5434                                         width:  (nextByte() << 24) | (nextByte() << 16) | (nextByte() <<  8) | nextByte(),
5435                                         height: (nextByte() << 24) | (nextByte() << 16) | (nextByte() <<  8) | nextByte()
5436                                 };
5437                         
5438                         } else if (stream.substr(0, 4) === gifMagicNum) {
5439                                 pos = 6;
5440                         
5441                                 return {
5442                                         width:  nextByte() | (nextByte() << 8),
5443                                         height: nextByte() | (nextByte() << 8)
5444                                 };
5445                         
5446                         } else if (stream.substr(0, 2) === jpgMagicNum) {
5447                                 pos = 2;
5448                         
5449                                 var l = stream.length;
5450                                 while (pos < l) {
5451                                         if (nextByte() != 0xFF) return;
5452                                 
5453                                         var marker = nextByte();
5454                                         if (marker == 0xDA) break;
5455                                 
5456                                         var size = (nextByte() << 8) | nextByte();
5457                                 
5458                                         if (marker >= 0xC0 && marker <= 0xCF && !(marker & 0x4) && !(marker & 0x8)) {
5459                                                 pos += 1;
5460                                                 return {
5461                                                         height: (nextByte() << 8) | nextByte(),
5462                                                         width: (nextByte() << 8) | nextByte()
5463                                                 };
5464                                 
5465                                         } else {
5466                                                 pos += size - 2;
5467                                         }
5468                                 }
5469                         }
5470                 },
5471                 
5472                 /**
5473                  * Captures context XHTML element from editor under current caret position.
5474                  * This node can be used as a helper for abbreviation extraction
5475                  * @param {IEmmetEditor} editor
5476                  * @returns {Object}
5477                  */
5478                 captureContext: function(editor) {
5479                         var allowedSyntaxes = {'html': 1, 'xml': 1, 'xsl': 1};
5480                         var syntax = String(editor.getSyntax());
5481                         if (syntax in allowedSyntaxes) {
5482                                 var content = String(editor.getContent());
5483                                 var tag = require('htmlMatcher').find(content, editor.getCaretPos());
5484                                 
5485                                 if (tag && tag.type == 'tag') {
5486                                         var startTag = tag.open;
5487                                         var contextNode = {
5488                                                 name: startTag.name,
5489                                                 attributes: []
5490                                         };
5491                                         
5492                                         // parse attributes
5493                                         var tagTree = require('xmlEditTree').parse(startTag.range.substring(content));
5494                                         if (tagTree) {
5495                                                 contextNode.attributes = _.map(tagTree.getAll(), function(item) {
5496                                                         return {
5497                                                                 name: item.name(),
5498                                                                 value: item.value()
5499                                                         };
5500                                                 });
5501                                         }
5502                                         
5503                                         return contextNode;
5504                                 }
5505                         }
5506                         
5507                         return null;
5508                 },
5509                 
5510                 /**
5511                  * Find expression bounds in current editor at caret position. 
5512                  * On each character a <code>fn</code> function will be called and must 
5513                  * return <code>true</code> if current character meets requirements, 
5514                  * <code>false</code> otherwise
5515                  * @param {IEmmetEditor} editor
5516                  * @param {Function} fn Function to test each character of expression
5517                  * @return {Range}
5518                  */
5519                 findExpressionBounds: function(editor, fn) {
5520                         var content = String(editor.getContent());
5521                         var il = content.length;
5522                         var exprStart = editor.getCaretPos() - 1;
5523                         var exprEnd = exprStart + 1;
5524                                 
5525                         // start by searching left
5526                         while (exprStart >= 0 && fn(content.charAt(exprStart), exprStart, content)) exprStart--;
5527                         
5528                         // then search right
5529                         while (exprEnd < il && fn(content.charAt(exprEnd), exprEnd, content)) exprEnd++;
5530                         
5531                         if (exprEnd > exprStart) {
5532                                 return require('range').create([++exprStart, exprEnd]);
5533                         }
5534                 },
5535                 
5536                 /**
5537                  * @param {IEmmetEditor} editor
5538                  * @param {Object} data
5539                  * @returns {Boolean}
5540                  */
5541                 compoundUpdate: function(editor, data) {
5542                         if (data) {
5543                                 var sel = editor.getSelectionRange();
5544                                 editor.replaceContent(data.data, data.start, data.end, true);
5545                                 editor.createSelection(data.caret, data.caret + sel.end - sel.start);
5546                                 return true;
5547                         }
5548                         
5549                         return false;
5550                 },
5551                 
5552                 /**
5553                  * Common syntax detection method for editors that doesn’t provide any
5554                  * info about current syntax scope. 
5555                  * @param {IEmmetEditor} editor Current editor
5556                  * @param {String} hint Any syntax hint that editor can provide 
5557                  * for syntax detection. Default is 'html'
5558                  * @returns {String} 
5559                  */
5560                 detectSyntax: function(editor, hint) {
5561                         var syntax = hint || 'html';
5562                         
5563                         if (!require('resources').hasSyntax(syntax)) {
5564                                 syntax = 'html';
5565                         }
5566                         
5567                         if (syntax == 'html' && (this.isStyle(editor) || this.isInlineCSS(editor))) {
5568                                 syntax = 'css';
5569                         }
5570 
5571                         if (syntax == 'styl') {
5572                                 syntax = 'stylus';
5573                         }
5574                         
5575                         return syntax;
5576                 },
5577                 
5578                 /**
5579                  * Common method for detecting output profile
5580                  * @param {IEmmetEditor} editor
5581                  * @returns {String}
5582                  */
5583                 detectProfile: function(editor) {
5584                         var syntax = editor.getSyntax();
5585                         
5586                         // get profile from syntax definition
5587                         var profile = require('resources').findItem(syntax, 'profile');
5588                         if (profile) {
5589                                 return profile;
5590                         }
5591                         
5592                         switch(syntax) {
5593                                 case 'xml':
5594                                 case 'xsl':
5595                                         return 'xml';
5596                                 case 'css':
5597                                         if (this.isInlineCSS(editor)) {
5598                                                 return 'line';
5599                                         }
5600                                         break;
5601                                 case 'html':
5602                                         profile = require('resources').getVariable('profile');
5603                                         if (!profile) { // no forced profile, guess from content
5604                                                 // html or xhtml?
5605                                                 profile = this.isXHTML(editor) ? 'xhtml': 'html';
5606                                         }
5607 
5608                                         return profile;
5609                         }
5610 
5611                         return 'xhtml';
5612                 },
5613                 
5614                 /**
5615                  * Tries to detect if current document is XHTML one.
5616                  * @param {IEmmetEditor} editor
5617                  * @returns {Boolean}
5618                  */
5619                 isXHTML: function(editor) {
5620                         return editor.getContent().search(/<!DOCTYPE[^>]+XHTML/i) != -1;
5621                 },
5622                 
5623                 /**
5624                  * Check if current caret position is inside &lt;style&gt; tag
5625                  * @param {IEmmetEditor} editor
5626                  * @returns
5627                  */
5628                 isStyle: function(editor) {
5629                         var content = String(editor.getContent());
5630                         var caretPos = editor.getCaretPos();
5631                         var tag = require('htmlMatcher').tag(content, caretPos);
5632                         return tag && tag.open.name.toLowerCase() == 'style' 
5633                                 && tag.innerRange.cmp(caretPos, 'lte', 'gte');
5634                 },
5635                 
5636                 /**
5637                  * Check if current caret position is inside "style" attribute of HTML
5638                  * element
5639                  * @param {IEmmetEditor} editor
5640                  * @returns {Boolean}
5641                  */
5642                 isInlineCSS: function(editor) {
5643                         var content = String(editor.getContent());
5644                         var caretPos = editor.getCaretPos();
5645                         var tree = require('xmlEditTree').parseFromPosition(content, caretPos, true);
5646                         if (tree) {
5647                                 var attr = tree.itemFromPosition(caretPos, true);
5648                                 return attr && attr.name().toLowerCase() == 'style' 
5649                                         && attr.valueRange(true).cmp(caretPos, 'lte', 'gte');
5650                         }
5651             
5652             return false;
5653                 }
5654         };
5655 });/**
5656  * Utility functions to work with <code>AbbreviationNode</code> as HTML element
5657  * @param {Function} require
5658  * @param {Underscore} _
5659  */
5660 emmet.define('abbreviationUtils', function(require, _) {
5661         return {
5662                 /**
5663                  * Test if passed node is unary (no closing tag)
5664                  * @param {AbbreviationNode} node
5665                  * @return {Boolean}
5666                  */
5667                 isUnary: function(node) {
5668                         if (node.children.length || node._text || this.isSnippet(node)) {
5669                                 return false;
5670                         }
5671                         
5672                         var r = node.matchedResource();
5673                         return r && r.is_empty;
5674                 },
5675                 
5676                 /**
5677                  * Test if passed node is inline-level (like &lt;strong&gt;, &lt;img&gt;)
5678                  * @param {AbbreviationNode} node
5679                  * @return {Boolean}
5680                  */
5681                 isInline: function(node) {
5682                         return node.isTextNode() 
5683                                 || !node.name() 
5684                                 || require('tagName').isInlineLevel(node.name());
5685                 },
5686                 
5687                 /**
5688                  * Test if passed node is block-level
5689                  * @param {AbbreviationNode} node
5690                  * @return {Boolean}
5691                  */
5692                 isBlock: function(node) {
5693                         return this.isSnippet(node) || !this.isInline(node);
5694                 },
5695                 
5696                 /**
5697                  * Test if given node is a snippet
5698                  * @param {AbbreviationNode} node
5699                  * @return {Boolean}
5700                  */
5701                 isSnippet: function(node) {
5702                         return require('elements').is(node.matchedResource(), 'snippet');
5703                 },
5704                 
5705                 /**
5706                  * This function tests if passed node content contains HTML tags. 
5707                  * This function is mostly used for output formatting
5708                  * @param {AbbreviationNode} node
5709                  * @returns {Boolean}
5710                  */
5711                 hasTagsInContent: function(node) {
5712                         return require('utils').matchesTag(node.content);
5713                 },
5714                 
5715                 /**
5716                  * Test if current element contains block-level children
5717                  * @param {AbbreviationNode} node
5718                  * @return {Boolean}
5719                  */
5720                 hasBlockChildren: function(node) {
5721                         return (this.hasTagsInContent(node) && this.isBlock(node)) 
5722                                 || _.any(node.children, function(child) {
5723                                         return this.isBlock(child);
5724                                 }, this);
5725                 },
5726                 
5727                 /**
5728                  * Utility function that inserts content instead of <code>${child}</code>
5729                  * variables on <code>text</code>
5730                  * @param {String} text Text where child content should be inserted
5731                  * @param {String} childContent Content to insert
5732                  * @param {Object} options
5733                  * @returns {String
5734                  */
5735                 insertChildContent: function(text, childContent, options) {
5736                         options = _.extend({
5737                                 keepVariable: true,
5738                                 appendIfNoChild: true
5739                         }, options || {});
5740                         
5741                         var childVariableReplaced = false;
5742                         var utils = require('utils');
5743                         text = utils.replaceVariables(text, function(variable, name, data) {
5744                                 var output = variable;
5745                                 if (name == 'child') {
5746                                         // add correct indentation
5747                                         output = utils.padString(childContent, utils.getLinePaddingFromPosition(text, data.start));
5748                                         childVariableReplaced = true;
5749                                         if (options.keepVariable)
5750                                                 output += variable;
5751                                 }
5752                                 
5753                                 return output;
5754                         });
5755                         
5756                         if (!childVariableReplaced && options.appendIfNoChild) {
5757                                 text += childContent;
5758                         }
5759                         
5760                         return text;
5761                 }
5762         };
5763 });/**
5764  * @author Sergey Chikuyonok (serge.che@gmail.com)
5765  * @link http://chikuyonok.ru
5766  */
5767 emmet.define('base64', function(require, _) {
5768         var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
5769         
5770         return {
5771                 /**
5772                  * Encodes data using base64 algorithm
5773                  * @author Tyler Akins (http://rumkin.com)
5774                  * @param {String} input
5775                  * @returns {String}
5776                  * @memberOf emmet.base64
5777                  */
5778                 encode : function(input) {
5779                         var output = [];
5780                         var chr1, chr2, chr3, enc1, enc2, enc3, enc4, cdp1, cdp2, cdp3;
5781                         var i = 0, il = input.length, b64 = chars;
5782 
5783                         while (i < il) {
5784 
5785                                 cdp1 = input.charCodeAt(i++);
5786                                 cdp2 = input.charCodeAt(i++);
5787                                 cdp3 = input.charCodeAt(i++);
5788 
5789                                 chr1 = cdp1 & 0xff;
5790                                 chr2 = cdp2 & 0xff;
5791                                 chr3 = cdp3 & 0xff;
5792 
5793                                 enc1 = chr1 >> 2;
5794                                 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
5795                                 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
5796                                 enc4 = chr3 & 63;
5797 
5798                                 if (isNaN(cdp2)) {
5799                                         enc3 = enc4 = 64;
5800                                 } else if (isNaN(cdp3)) {
5801                                         enc4 = 64;
5802                                 }
5803 
5804                                 output.push(b64.charAt(enc1) + b64.charAt(enc2) + b64.charAt(enc3) + b64.charAt(enc4));
5805                         }
5806 
5807                         return output.join('');
5808                 },
5809 
5810                 /**
5811                  * Decodes string using MIME base64 algorithm
5812                  * 
5813                  * @author Tyler Akins (http://rumkin.com)
5814                  * @param {String} data
5815                  * @return {String}
5816                  */
5817                 decode : function(data) {
5818                         var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, tmpArr = [];
5819                         var b64 = chars, il = data.length;
5820 
5821                         if (!data) {
5822                                 return data;
5823                         }
5824 
5825                         data += '';
5826 
5827                         do { // unpack four hexets into three octets using index points in b64
5828                                 h1 = b64.indexOf(data.charAt(i++));
5829                                 h2 = b64.indexOf(data.charAt(i++));
5830                                 h3 = b64.indexOf(data.charAt(i++));
5831                                 h4 = b64.indexOf(data.charAt(i++));
5832 
5833                                 bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
5834 
5835                                 o1 = bits >> 16 & 0xff;
5836                                 o2 = bits >> 8 & 0xff;
5837                                 o3 = bits & 0xff;
5838 
5839                                 if (h3 == 64) {
5840                                         tmpArr[ac++] = String.fromCharCode(o1);
5841                                 } else if (h4 == 64) {
5842                                         tmpArr[ac++] = String.fromCharCode(o1, o2);
5843                                 } else {
5844                                         tmpArr[ac++] = String.fromCharCode(o1, o2, o3);
5845                                 }
5846                         } while (i < il);
5847 
5848                         return tmpArr.join('');
5849                 }
5850         };
5851 });/**
5852  * HTML matcher: takes string and searches for HTML tag pairs for given position 
5853  * 
5854  * Unlike “classic” matchers, it parses content from the specified 
5855  * position, not from the start, so it may work even outside HTML documents
5856  * (for example, inside strings of programming languages like JavaScript, Python 
5857  * etc.)
5858  * @constructor
5859  * @memberOf __htmlMatcherDefine
5860  */
5861 emmet.define('htmlMatcher', function(require, _) {
5862         // Regular Expressions for parsing tags and attributes
5863         var reOpenTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
5864         var reCloseTag = /^<\/([\w\:\-]+)[^>]*>/;
5865         
5866         function openTag(i, match) {
5867                 return {
5868                         name: match[1],
5869                         selfClose: !!match[3],
5870                         /** @type Range */
5871                         range: require('range').create(i, match[0]),
5872                         type: 'open'
5873                 };
5874         }
5875         
5876         function closeTag(i, match) {
5877                 return {
5878                         name: match[1],
5879                         /** @type Range */
5880                         range: require('range').create(i, match[0]),
5881                         type: 'close'
5882                 };
5883         }
5884         
5885         function comment(i, match) {
5886                 return {
5887                         /** @type Range */
5888                         range: require('range').create(i, _.isNumber(match) ? match - i : match[0]),
5889                         type: 'comment'
5890                 };
5891         }
5892         
5893         /**
5894          * Creates new tag matcher session
5895          * @param {String} text
5896          */
5897         function createMatcher(text) {
5898                 var memo = {}, m;
5899                 return {
5900                         /**
5901                          * Test if given position matches opening tag
5902                          * @param {Number} i
5903                          * @returns {Object} Matched tag object
5904                          */
5905                         open: function(i) {
5906                                 var m = this.matches(i);
5907                                 return m && m.type == 'open' ? m : null;
5908                         },
5909                         
5910                         /**
5911                          * Test if given position matches closing tag
5912                          * @param {Number} i
5913                          * @returns {Object} Matched tag object
5914                          */
5915                         close: function(i) {
5916                                 var m = this.matches(i);
5917                                 return m && m.type == 'close' ? m : null;
5918                         },
5919                         
5920                         /**
5921                          * Matches either opening or closing tag for given position
5922                          * @param i
5923                          * @returns
5924                          */
5925                         matches: function(i) {
5926                                 var key = 'p' + i;
5927                                 
5928                                 if (!(key in memo)) {
5929                                         if (text.charAt(i) == '<') {
5930                                                 var substr = text.slice(i);
5931                                                 if ((m = substr.match(reOpenTag))) {
5932                                                         memo[key] = openTag(i, m);
5933                                                 } else if ((m = substr.match(reCloseTag))) {
5934                                                         memo[key] = closeTag(i, m);
5935                                                 } else {
5936                                                         // remember that given position contains no valid tag
5937                                                         memo[key] = false;
5938                                                 }
5939                                         }
5940                                 }
5941                                 
5942                                 return memo[key];
5943                         },
5944                         
5945                         /**
5946                          * Returns original text
5947                          * @returns {String}
5948                          */
5949                         text: function() {
5950                                 return text;
5951                         }
5952                 };
5953         }
5954         
5955         function matches(text, pos, pattern) {
5956                 return text.substring(pos, pos + pattern.length) == pattern;
5957         }
5958         
5959         /**
5960          * Search for closing pair of opening tag
5961          * @param {Object} open Open tag instance
5962          * @param {Object} matcher Matcher instance
5963          */
5964         function findClosingPair(open, matcher) {
5965                 var stack = [], tag = null;
5966                 var text = matcher.text();
5967                 
5968                 for (var pos = open.range.end, len = text.length; pos < len; pos++) {
5969                         if (matches(text, pos, '<!--')) {
5970                                 // skip to end of comment
5971                                 for (var j = pos; j < len; j++) {
5972                                         if (matches(text, j, '-->')) {
5973                                                 pos = j + 3;
5974                                                 break;
5975                                         }
5976                                 }
5977                         }
5978                         
5979                         if ((tag = matcher.matches(pos))) {
5980                                 if (tag.type == 'open' && !tag.selfClose) {
5981                                         stack.push(tag.name);
5982                                 } else if (tag.type == 'close') {
5983                                         if (!stack.length) { // found valid pair?
5984                                                 return tag.name == open.name ? tag : null;
5985                                         }
5986                                         
5987                                         // check if current closing tag matches previously opened one
5988                                         if (_.last(stack) == tag.name) {
5989                                                 stack.pop();
5990                                         } else {
5991                                                 var found = false;
5992                                                 while (stack.length && !found) {
5993                                                         var last = stack.pop();
5994                                                         if (last == tag.name) {
5995                                                                 found = true;
5996                                                         }
5997                                                 }
5998                                                 
5999                                                 if (!stack.length && !found) {
6000                                                         return tag.name == open.name ? tag : null;
6001                                                 }
6002                                         }
6003                                 }
6004                         }
6005                         
6006                 }
6007         }
6008         
6009         return {
6010                 /**
6011                  * Main function: search for tag pair in <code>text</code> for given 
6012                  * position
6013                  * @memberOf htmlMatcher
6014                  * @param {String} text 
6015                  * @param {Number} pos
6016                  * @returns {Object}
6017                  */
6018                 find: function(text, pos) {
6019                         var range = require('range');
6020                         var matcher = createMatcher(text); 
6021                         var open = null, close = null;
6022                         var j, jl;
6023                         
6024                         for (var i = pos; i >= 0; i--) {
6025                                 if ((open = matcher.open(i))) {
6026                                         // found opening tag
6027                                         if (open.selfClose) {
6028                                                 if (open.range.cmp(pos, 'lt', 'gt')) {
6029                                                         // inside self-closing tag, found match
6030                                                         break;
6031                                                 }
6032                                                 
6033                                                 // outside self-closing tag, continue
6034                                                 continue;
6035                                         }
6036                                         
6037                                         close = findClosingPair(open, matcher);
6038                                         if (close) {
6039                                                 // found closing tag.
6040                                                 var r = range.create2(open.range.start, close.range.end);
6041                                                 if (r.contains(pos)) {
6042                                                         break;
6043                                                 }
6044                                         } else if (open.range.contains(pos)) {
6045                                                 // we inside empty HTML tag like <br>
6046                                                 break;
6047                                         }
6048                                         
6049                                         open = null;
6050                                 } else if (matches(text, i, '-->')) {
6051                                         // skip back to comment start
6052                                         for (j = i - 1; j >= 0; j--) {
6053                                                 if (matches(text, j, '-->')) {
6054                                                         // found another comment end, do nothing
6055                                                         break;
6056                                                 } else if (matches(text, j, '<!--')) {
6057                                                         i = j;
6058                                                         break;
6059                                                 }
6060                                         }
6061                                 } else if (matches(text, i, '<!--')) {
6062                                         // we're inside comment, match it
6063                                         for (j = i + 4, jl = text.length; j < jl; j++) {
6064                                                 if (matches(text, j, '-->')) {
6065                                                         j += 3;
6066                                                         break;
6067                                                 }
6068                                         }
6069                                         
6070                                         open = comment(i, j);
6071                                         break;
6072                                 }
6073                         }
6074                         
6075                         if (open) {
6076                                 var outerRange = null;
6077                                 var innerRange = null;
6078                                 
6079                                 if (close) {
6080                                         outerRange = range.create2(open.range.start, close.range.end);
6081                                         innerRange = range.create2(open.range.end, close.range.start);
6082                                 } else {
6083                                         outerRange = innerRange = range.create2(open.range.start, open.range.end);
6084                                 }
6085                                 
6086                                 if (open.type == 'comment') {
6087                                         // adjust positions of inner range for comment
6088                                         var _c = outerRange.substring(text);
6089                                         innerRange.start += _c.length - _c.replace(/^<\!--\s*/, '').length;
6090                                         innerRange.end -= _c.length - _c.replace(/\s*-->$/, '').length;
6091                                 }
6092                                 
6093                                 return {
6094                                         open: open,
6095                                         close: close,
6096                                         type: open.type == 'comment' ? 'comment' : 'tag',
6097                                         innerRange: innerRange,
6098                                         innerContent: function() {
6099                                                 return this.innerRange.substring(text);
6100                                         },
6101                                         outerRange: outerRange,
6102                                         outerContent: function() {
6103                                                 return this.outerRange.substring(text);
6104                                         },
6105                                         range: !innerRange.length() || !innerRange.cmp(pos, 'lte', 'gte') ? outerRange : innerRange,
6106                                         content: function() {
6107                                                 return this.range.substring(text);
6108                                         },
6109                                         source: text
6110                                 };
6111                         }
6112                 },
6113                 
6114                 /**
6115                  * The same as <code>find()</code> method, but restricts matched result 
6116                  * to <code>tag</code> type
6117                  * @param {String} text 
6118                  * @param {Number} pos
6119                  * @returns {Object}
6120                  */
6121                 tag: function(text, pos) {
6122                         var result = this.find(text, pos);
6123                         if (result && result.type == 'tag') {
6124                                 return result;
6125                         }
6126                 }
6127         };
6128 });/**
6129  * Utility module for handling tabstops tokens generated by Emmet's 
6130  * "Expand Abbreviation" action. The main <code>extract</code> method will take
6131  * raw text (for example: <i>${0} some ${1:text}</i>), find all tabstops 
6132  * occurrences, replace them with tokens suitable for your editor of choice and 
6133  * return object with processed text and list of found tabstops and their ranges.
6134  * For sake of portability (Objective-C/Java) the tabstops list is a plain 
6135  * sorted array with plain objects.
6136  * 
6137  * Placeholders with the same are meant to be <i>linked</i> in your editor.
6138  * @param {Function} require
6139  * @param {Underscore} _  
6140  */
6141 emmet.define('tabStops', function(require, _) {
6142         /**
6143          * Global placeholder value, automatically incremented by 
6144          * <code>variablesResolver()</code> function
6145          */
6146         var startPlaceholderNum = 100;
6147         
6148         var tabstopIndex = 0;
6149         
6150         var defaultOptions = {
6151                 replaceCarets: false,
6152                 escape: function(ch) {
6153                         return '\\' + ch;
6154                 },
6155                 tabstop: function(data) {
6156                         return data.token;
6157                 },
6158                 variable: function(data) {
6159                         return data.token;
6160                 }
6161         };
6162         
6163         // XXX register output processor that will upgrade tabstops of parsed node
6164         // in order to prevent tabstop index conflicts
6165         require('abbreviationParser').addOutputProcessor(function(text, node, type) {
6166                 var maxNum = 0;
6167                 var tabstops = require('tabStops');
6168                 var utils = require('utils');
6169                 
6170                 var tsOptions = {
6171                         tabstop: function(data) {
6172                                 var group = parseInt(data.group, 10);
6173                                 if (group === 0)
6174                                         return '${0}';
6175                                 
6176                                 if (group > maxNum) maxNum = group;
6177                                 if (data.placeholder) {
6178                                         // respect nested placeholders
6179                                         var ix = group + tabstopIndex;
6180                                         var placeholder = tabstops.processText(data.placeholder, tsOptions);
6181                                         return '${' + ix + ':' + placeholder + '}';
6182                                 } else {
6183                                         return '${' + (group + tabstopIndex) + '}';
6184                                 }
6185                         }
6186                 };
6187                 
6188                 // upgrade tabstops
6189                 text = tabstops.processText(text, tsOptions);
6190                 
6191                 // resolve variables
6192                 text = utils.replaceVariables(text, tabstops.variablesResolver(node));
6193                 
6194                 tabstopIndex += maxNum + 1;
6195                 return text;
6196         });
6197         
6198         return {
6199                 /**
6200                  * Main function that looks for a tabstops in provided <code>text</code>
6201                  * and returns a processed version of <code>text</code> with expanded 
6202                  * placeholders and list of tabstops found.
6203                  * @param {String} text Text to process
6204                  * @param {Object} options List of processor options:<br>
6205                  * 
6206                  * <b>replaceCarets</b> : <code>Boolean</code> — replace all default
6207                  * caret placeholders (like <i>{%::emmet-caret::%}</i>) with <i>${0:caret}</i><br>
6208                  * 
6209                  * <b>escape</b> : <code>Function</code> — function that handle escaped
6210                  * characters (mostly '$'). By default, it returns the character itself 
6211                  * to be displayed as is in output, but sometimes you will use 
6212                  * <code>extract</code> method as intermediate solution for further 
6213                  * processing and want to keep character escaped. Thus, you should override
6214                  * <code>escape</code> method to return escaped symbol (e.g. '\\$')<br>
6215                  * 
6216                  * <b>tabstop</b> : <code>Function</code> – a tabstop handler. Receives 
6217                  * a single argument – an object describing token: its position, number 
6218                  * group, placeholder and token itself. Should return a replacement 
6219                  * string that will appear in final output
6220                  * 
6221                  * <b>variable</b> : <code>Function</code> – variable handler. Receives 
6222                  * a single argument – an object describing token: its position, name 
6223                  * and original token itself. Should return a replacement 
6224                  * string that will appear in final output
6225                  * 
6226                  * @returns {Object} Object with processed <code>text</code> property
6227                  * and array of <code>tabstops</code> found
6228                  * @memberOf tabStops
6229                  */
6230                 extract: function(text, options) {
6231                         // prepare defaults
6232                         var utils = require('utils');
6233                         var placeholders = {carets: ''};
6234                         var marks = [];
6235                         
6236                         options = _.extend({}, defaultOptions, options, {
6237                                 tabstop: function(data) {
6238                                         var token = data.token;
6239                                         var ret = '';
6240                                         if (data.placeholder == 'cursor') {
6241                                                 marks.push({
6242                                                         start: data.start,
6243                                                         end: data.start + token.length,
6244                                                         group: 'carets',
6245                                                         value: ''
6246                                                 });
6247                                         } else {
6248                                                 // unify placeholder value for single group
6249                                                 if ('placeholder' in data)
6250                                                         placeholders[data.group] = data.placeholder;
6251                                                 
6252                                                 if (data.group in placeholders)
6253                                                         ret = placeholders[data.group];
6254                                                 
6255                                                 marks.push({
6256                                                         start: data.start,
6257                                                         end: data.start + token.length,
6258                                                         group: data.group,
6259                                                         value: ret
6260                                                 });
6261                                         }
6262                                         
6263                                         return token;
6264                                 }
6265                         });
6266                         
6267                         if (options.replaceCarets) {
6268                                 text = text.replace(new RegExp( utils.escapeForRegexp( utils.getCaretPlaceholder() ), 'g'), '${0:cursor}');
6269                         }
6270                         
6271                         // locate tabstops and unify group's placeholders
6272                         text = this.processText(text, options);
6273                         
6274                         // now, replace all tabstops with placeholders
6275                         var buf = '', lastIx = 0;
6276                         var tabStops = _.map(marks, function(mark) {
6277                                 buf += text.substring(lastIx, mark.start);
6278                                 
6279                                 var pos = buf.length;
6280                                 var ph = placeholders[mark.group] || '';
6281                                 
6282                                 buf += ph;
6283                                 lastIx = mark.end;
6284                                 
6285                                 return {
6286                                         group: mark.group,
6287                                         start: pos,
6288                                         end:  pos + ph.length
6289                                 };
6290                         });
6291                         
6292                         buf += text.substring(lastIx);
6293                         
6294                         return {
6295                                 text: buf,
6296                                 tabstops: _.sortBy(tabStops, 'start')
6297                         };
6298                 },
6299                 
6300                 /**
6301                  * Text processing routine. Locates escaped characters and tabstops and
6302                  * replaces them with values returned by handlers defined in 
6303                  * <code>options</code>
6304                  * @param {String} text
6305                  * @param {Object} options See <code>extract</code> method options 
6306                  * description
6307                  * @returns {String}
6308                  */
6309                 processText: function(text, options) {
6310                         options = _.extend({}, defaultOptions, options);
6311                         
6312                         var buf = '';
6313                         /** @type StringStream */
6314                         var stream = require('stringStream').create(text);
6315                         var ch, m, a;
6316                         
6317                         while ((ch = stream.next())) {
6318                                 if (ch == '\\' && !stream.eol()) {
6319                                         // handle escaped character
6320                                         buf += options.escape(stream.next());
6321                                         continue;
6322                                 }
6323                                 
6324                                 a = ch;
6325                                 
6326                                 if (ch == '$') {
6327                                         // looks like a tabstop
6328                                         stream.start = stream.pos - 1;
6329                                         
6330                                         if ((m = stream.match(/^[0-9]+/))) {
6331                                                 // it's $N
6332                                                 a = options.tabstop({
6333                                                         start: buf.length, 
6334                                                         group: stream.current().substr(1),
6335                                                         token: stream.current()
6336                                                 });
6337                                         } else if ((m = stream.match(/^\{([a-z_\-][\w\-]*)\}/))) {
6338                                                 // ${variable}
6339                                                 a = options.variable({
6340                                                         start: buf.length, 
6341                                                         name: m[1],
6342                                                         token: stream.current()
6343                                                 });
6344                                         } else if ((m = stream.match(/^\{([0-9]+)(:.+?)?\}/, false))) {
6345                                                 // ${N:value} or ${N} placeholder
6346                                                 // parse placeholder, including nested ones
6347                                                 stream.skipToPair('{', '}');
6348                                                 
6349                                                 var obj = {
6350                                                         start: buf.length, 
6351                                                         group: m[1],
6352                                                         token: stream.current()
6353                                                 };
6354                                                 
6355                                                 var placeholder = obj.token.substring(obj.group.length + 2, obj.token.length - 1);
6356                                                 
6357                                                 if (placeholder) {
6358                                                         obj.placeholder = placeholder.substr(1);
6359                                                 }
6360                                                 
6361                                                 a = options.tabstop(obj);
6362                                         }
6363                                 }
6364                                 
6365                                 buf += a;
6366                         }
6367                         
6368                         return buf;
6369                 },
6370                 
6371                 /**
6372                  * Upgrades tabstops in output node in order to prevent naming conflicts
6373                  * @param {AbbreviationNode} node
6374                  * @param {Number} offset Tab index offset
6375                  * @returns {Number} Maximum tabstop index in element
6376                  */
6377                 upgrade: function(node, offset) {
6378                         var maxNum = 0;
6379                         var options = {
6380                                 tabstop: function(data) {
6381                                         var group = parseInt(data.group, 10);
6382                                         if (group > maxNum) maxNum = group;
6383                                                 
6384                                         if (data.placeholder)
6385                                                 return '${' + (group + offset) + ':' + data.placeholder + '}';
6386                                         else
6387                                                 return '${' + (group + offset) + '}';
6388                                 }
6389                         };
6390                         
6391                         _.each(['start', 'end', 'content'], function(p) {
6392                                 node[p] = this.processText(node[p], options);
6393                         }, this);
6394                         
6395                         return maxNum;
6396                 },
6397                 
6398                 /**
6399                  * Helper function that produces a callback function for 
6400                  * <code>replaceVariables()</code> method from {@link utils}
6401                  * module. This callback will replace variable definitions (like 
6402                  * ${var_name}) with their value defined in <i>resource</i> module,
6403                  * or outputs tabstop with variable name otherwise.
6404                  * @param {AbbreviationNode} node Context node
6405                  * @returns {Function}
6406                  */
6407                 variablesResolver: function(node) {
6408                         var placeholderMemo = {};
6409                         var res = require('resources');
6410                         return function(str, varName) {
6411                                 // do not mark `child` variable as placeholder – it‘s a reserved
6412                                 // variable name
6413                                 if (varName == 'child')
6414                                         return str;
6415                                 
6416                                 if (varName == 'cursor')
6417                                         return require('utils').getCaretPlaceholder();
6418                                 
6419                                 var attr = node.attribute(varName);
6420                                 if (!_.isUndefined(attr) && attr !== str) {
6421                                         return attr;
6422                                 }
6423                                 
6424                                 var varValue = res.getVariable(varName);
6425                                 if (varValue)
6426                                         return varValue;
6427                                 
6428                                 // output as placeholder
6429                                 if (!placeholderMemo[varName])
6430                                         placeholderMemo[varName] = startPlaceholderNum++;
6431                                         
6432                                 return '${' + placeholderMemo[varName] + ':' + varName + '}';
6433                         };
6434                 },
6435                 
6436                 /**
6437                  * Resets global tabstop index. When parsed tree is converted to output
6438                  * string (<code>AbbreviationNode.toString()</code>), all tabstops 
6439                  * defined in snippets and elements are upgraded in order to prevent
6440                  * naming conflicts of nested. For example, <code>${1}</code> of a node
6441                  * should not be linked with the same placehilder of the child node.
6442                  * By default, <code>AbbreviationNode.toString()</code> automatically
6443                  * upgrades tabstops of the same index for each node and writes maximum
6444                  * tabstop index into the <code>tabstopIndex</code> variable. To keep
6445                  * this variable at reasonable value, it is recommended to call 
6446                  * <code>resetTabstopIndex()</code> method each time you expand variable 
6447                  * @returns
6448                  */
6449                 resetTabstopIndex: function() {
6450                         tabstopIndex = 0;
6451                         startPlaceholderNum = 100;
6452                 }
6453         };
6454 });/**
6455  * Common module's preferences storage. This module 
6456  * provides general storage for all module preferences, their description and
6457  * default values.<br><br>
6458  * 
6459  * This module can also be used to list all available properties to create 
6460  * UI for updating properties
6461  * 
6462  * @memberOf __preferencesDefine
6463  * @constructor
6464  * @param {Function} require
6465  * @param {Underscore} _ 
6466  */
6467 emmet.define('preferences', function(require, _) {
6468         var preferences = {};
6469         var defaults = {};
6470         var _dbgDefaults = null;
6471         var _dbgPreferences = null;
6472 
6473         function toBoolean(val) {
6474                 if (_.isString(val)) {
6475                         val = val.toLowerCase();
6476                         return val == 'yes' || val == 'true' || val == '1';
6477                 }
6478 
6479                 return !!val;
6480         }
6481         
6482         function isValueObj(obj) {
6483                 return _.isObject(obj) 
6484                         && 'value' in obj 
6485                         && _.keys(obj).length < 3;
6486         }
6487         
6488         return {
6489                 /**
6490                  * Creates new preference item with default value
6491                  * @param {String} name Preference name. You can also pass object
6492                  * with many options
6493                  * @param {Object} value Preference default value
6494                  * @param {String} description Item textual description
6495                  * @memberOf preferences
6496                  */
6497                 define: function(name, value, description) {
6498                         var prefs = name;
6499                         if (_.isString(name)) {
6500                                 prefs = {};
6501                                 prefs[name] = {
6502                                         value: value,
6503                                         description: description
6504                                 };
6505                         }
6506                         
6507                         _.each(prefs, function(v, k) {
6508                                 defaults[k] = isValueObj(v) ? v : {value: v};
6509                         });
6510                 },
6511                 
6512                 /**
6513                  * Updates preference item value. Preference value should be defined
6514                  * first with <code>define</code> method.
6515                  * @param {String} name Preference name. You can also pass object
6516                  * with many options
6517                  * @param {Object} value Preference default value
6518                  * @memberOf preferences
6519                  */
6520                 set: function(name, value) {
6521                         var prefs = name;
6522                         if (_.isString(name)) {
6523                                 prefs = {};
6524                                 prefs[name] = value;
6525                         }
6526                         
6527                         _.each(prefs, function(v, k) {
6528                                 if (!(k in defaults)) {
6529                                         throw 'Property "' + k + '" is not defined. You should define it first with `define` method of current module';
6530                                 }
6531                                 
6532                                 // do not set value if it equals to default value
6533                                 if (v !== defaults[k].value) {
6534                                         // make sure we have value of correct type
6535                                         switch (typeof defaults[k].value) {
6536                                                 case 'boolean':
6537                                                         v = toBoolean(v);
6538                                                         break;
6539                                                 case 'number':
6540                                                         v = parseInt(v + '', 10) || 0;
6541                                                         break;
6542                                                 default: // convert to string
6543                                                         if (v !== null) {
6544                                                                 v += '';
6545                                                         }
6546                                         }
6547 
6548                                         preferences[k] = v;
6549                                 } else if  (k in preferences) {
6550                                         delete preferences[k];
6551                                 }
6552                         });
6553                 },
6554                 
6555                 /**
6556                  * Returns preference value
6557                  * @param {String} name
6558                  * @returns {String} Returns <code>undefined</code> if preference is 
6559                  * not defined
6560                  */
6561                 get: function(name) {
6562                         if (name in preferences)
6563                                 return preferences[name];
6564                         
6565                         if (name in defaults)
6566                                 return defaults[name].value;
6567                         
6568                         return void 0;
6569                 },
6570                 
6571                 /**
6572                  * Returns comma-separated preference value as array of values
6573                  * @param {String} name
6574                  * @returns {Array} Returns <code>undefined</code> if preference is 
6575                  * not defined, <code>null</code> if string cannot be converted to array
6576                  */
6577                 getArray: function(name) {
6578                         var val = this.get(name);
6579                         if (_.isUndefined(val) || val === null || val === '')  {
6580                                 return null;
6581                         }
6582 
6583                         val = _.map(val.split(','), require('utils').trim);
6584                         if (!val.length) {
6585                                 return null;
6586                         }
6587                         
6588                         return val;
6589                 },
6590                 
6591                 /**
6592                  * Returns comma and colon-separated preference value as dictionary
6593                  * @param {String} name
6594                  * @returns {Object}
6595                  */
6596                 getDict: function(name) {
6597                         var result = {};
6598                         _.each(this.getArray(name), function(val) {
6599                                 var parts = val.split(':');
6600                                 result[parts[0]] = parts[1];
6601                         });
6602                         
6603                         return result;
6604                 },
6605                 
6606                 /**
6607                  * Returns description of preference item
6608                  * @param {String} name Preference name
6609                  * @returns {Object}
6610                  */
6611                 description: function(name) {
6612                         return name in defaults ? defaults[name].description : void 0;
6613                 },
6614                 
6615                 /**
6616                  * Completely removes specified preference(s)
6617                  * @param {String} name Preference name (or array of names)
6618                  */
6619                 remove: function(name) {
6620                         if (!_.isArray(name))
6621                                 name = [name];
6622                         
6623                         _.each(name, function(key) {
6624                                 if (key in preferences)
6625                                         delete preferences[key];
6626                                 
6627                                 if (key in defaults)
6628                                         delete defaults[key];
6629                         });
6630                 },
6631                 
6632                 /**
6633                  * Returns sorted list of all available properties
6634                  * @returns {Array}
6635                  */
6636                 list: function() {
6637                         return _.map(_.keys(defaults).sort(), function(key) {
6638                                 return {
6639                                         name: key,
6640                                         value: this.get(key),
6641                                         type: typeof defaults[key].value,
6642                                         description: defaults[key].description
6643                                 };
6644                         }, this);
6645                 },
6646                 
6647                 /**
6648                  * Loads user-defined preferences from JSON
6649                  * @param {Object} json
6650                  * @returns
6651                  */
6652                 load: function(json) {
6653                         _.each(json, function(value, key) {
6654                                 this.set(key, value);
6655                         }, this);
6656                 },
6657 
6658                 /**
6659                  * Returns hash of user-modified preferences
6660                  * @returns {Object}
6661                  */
6662                 exportModified: function() {
6663                         return _.clone(preferences);
6664                 },
6665                 
6666                 /**
6667                  * Reset to defaults
6668                  * @returns
6669                  */
6670                 reset: function() {
6671                         preferences = {};
6672                 },
6673                 
6674                 /**
6675                  * For unit testing: use empty storage
6676                  */
6677                 _startTest: function() {
6678                         _dbgDefaults = defaults;
6679                         _dbgPreferences = preferences;
6680                         defaults = {};
6681                         preferences = {};
6682                 },
6683                 
6684                 /**
6685                  * For unit testing: restore original storage
6686                  */
6687                 _stopTest: function() {
6688                         defaults = _dbgDefaults;
6689                         preferences = _dbgPreferences;
6690                 }
6691         };
6692 });/**
6693  * Module for handling filters
6694  * @param {Function} require
6695  * @param {Underscore} _
6696  * @author Sergey Chikuyonok (serge.che@gmail.com) <http://chikuyonok.ru>
6697  */
6698 emmet.define('filters', function(require, _) {
6699         /** List of registered filters */
6700         var registeredFilters = {};
6701         
6702         /** Filters that will be applied for unknown syntax */
6703         var basicFilters = 'html';
6704         
6705         function list(filters) {
6706                 if (!filters)
6707                         return [];
6708                 
6709                 if (_.isString(filters))
6710                         return filters.split(/[\|,]/g);
6711                 
6712                 return filters;
6713         }
6714         
6715         return  {
6716                 /**
6717                  * Register new filter
6718                  * @param {String} name Filter name
6719                  * @param {Function} fn Filter function
6720                  */
6721                 add: function(name, fn) {
6722                         registeredFilters[name] = fn;
6723                 },
6724                 
6725                 /**
6726                  * Apply filters for final output tree
6727                  * @param {AbbreviationNode} tree Output tree
6728                  * @param {Array} filters List of filters to apply. Might be a 
6729                  * <code>String</code>
6730                  * @param {Object} profile Output profile, defined in <i>profile</i> 
6731                  * module. Filters defined it profile are not used, <code>profile</code>
6732                  * is passed to filter function
6733                  * @memberOf emmet.filters
6734                  * @returns {AbbreviationNode}
6735                  */
6736                 apply: function(tree, filters, profile) {
6737                         var utils = require('utils');
6738                         profile = require('profile').get(profile);
6739                         
6740                         _.each(list(filters), function(filter) {
6741                                 var name = utils.trim(filter.toLowerCase());
6742                                 if (name && name in registeredFilters) {
6743                                         tree = registeredFilters[name](tree, profile);
6744                                 }
6745                         });
6746                         
6747                         return tree;
6748                 },
6749                 
6750                 /**
6751                  * Composes list of filters that should be applied to a tree, based on 
6752                  * passed data
6753                  * @param {String} syntax Syntax name ('html', 'css', etc.)
6754                  * @param {Object} profile Output profile
6755                  * @param {String} additionalFilters List or pipe-separated
6756                  * string of additional filters to apply
6757                  * @returns {Array}
6758                  */
6759                 composeList: function(syntax, profile, additionalFilters) {
6760                         profile = require('profile').get(profile);
6761                         var filters = list(profile.filters || require('resources').findItem(syntax, 'filters') || basicFilters);
6762                         
6763                         if (profile.extraFilters) {
6764                                 filters = filters.concat(list(profile.extraFilters));
6765                         }
6766                                 
6767                         if (additionalFilters) {
6768                                 filters = filters.concat(list(additionalFilters));
6769                         }
6770                                 
6771                         if (!filters || !filters.length) {
6772                                 // looks like unknown syntax, apply basic filters
6773                                 filters = list(basicFilters);
6774                         }
6775                                 
6776                         return filters;
6777                 },
6778                 
6779                 /**
6780                  * Extracts filter list from abbreviation
6781                  * @param {String} abbr
6782                  * @returns {Array} Array with cleaned abbreviation and list of 
6783                  * extracted filters
6784                  */
6785                 extractFromAbbreviation: function(abbr) {
6786                         var filters = '';
6787                         abbr = abbr.replace(/\|([\w\|\-]+)$/, function(str, p1){
6788                                 filters = p1;
6789                                 return '';
6790                         });
6791                         
6792                         return [abbr, list(filters)];
6793                 }
6794         };
6795 });/**
6796  * Module that contains factories for element types used by Emmet
6797  * @param {Function} require
6798  * @param {Underscore} _
6799  */
6800 emmet.define('elements', function(require, _) {
6801         var factories = {};
6802         var reAttrs = /([\w\-:]+)\s*=\s*(['"])(.*?)\2/g;
6803         
6804         var result = {
6805                 /**
6806                  * Create new element factory
6807                  * @param {String} name Element identifier
6808                  * @param {Function} factory Function that produces element of specified 
6809                  * type. The object generated by this factory is automatically 
6810                  * augmented with <code>type</code> property pointing to element
6811                  * <code>name</code>
6812                  * @memberOf elements
6813                  */
6814                 add: function(name, factory) {
6815                         var that = this;
6816                         factories[name] = function() {
6817                                 var elem = factory.apply(that, arguments);
6818                                 if (elem)
6819                                         elem.type = name;
6820                                 
6821                                 return elem;
6822                         };
6823                 },
6824                 
6825                 /**
6826                  * Returns factory for specified name
6827                  * @param {String} name
6828                  * @returns {Function}
6829                  */
6830                 get: function(name) {
6831                         return factories[name];
6832                 },
6833                 
6834                 /**
6835                  * Creates new element with specified type
6836                  * @param {String} name
6837                  * @returns {Object}
6838                  */
6839                 create: function(name) {
6840                         var args = [].slice.call(arguments, 1);
6841                         var factory = this.get(name);
6842                         return factory ? factory.apply(this, args) : null;
6843                 },
6844                 
6845                 /**
6846                  * Check if passed element is of specified type
6847                  * @param {Object} elem
6848                  * @param {String} type
6849                  * @returns {Boolean}
6850                  */
6851                 is: function(elem, type) {
6852                         return elem && elem.type === type;
6853                 }
6854         };
6855         
6856         // register resource references
6857         function commonFactory(value) {
6858                 return {data: value};
6859         }
6860         
6861         /**
6862          * Element factory
6863          * @param {String} elementName Name of output element
6864          * @param {String} attrs Attributes definition. You may also pass
6865          * <code>Array</code> where each contains object with <code>name</code> 
6866          * and <code>value</code> properties, or <code>Object</code>
6867          * @param {Boolean} isEmpty Is expanded element should be empty
6868          */
6869         result.add('element', function(elementName, attrs, isEmpty) {
6870                 var ret = {
6871                         /** @memberOf __emmetDataElement */
6872                         name: elementName,
6873                         is_empty: !!isEmpty
6874                 };
6875                 
6876                 if (attrs) {
6877                         ret.attributes = [];
6878                         if (_.isArray(attrs)) {
6879                                 ret.attributes = attrs;
6880                         } else if (_.isString(attrs)) {
6881                                 var m;
6882                                 while ((m = reAttrs.exec(attrs))) {
6883                                         ret.attributes.push({
6884                                                 name: m[1],
6885                                                 value: m[3]
6886                                         });
6887                                 }
6888                         } else {
6889                                 _.each(attrs, function(value, name) {
6890                                         ret.attributes.push({
6891                                                 name: name, 
6892                                                 value: value
6893                                         });
6894                                 });
6895                         }
6896                 }
6897                 
6898                 return ret;
6899         });
6900         
6901         result.add('snippet', commonFactory);
6902         result.add('reference', commonFactory);
6903         result.add('empty', function() {
6904                 return {};
6905         });
6906         
6907         return result;
6908 });/**
6909  * Abstract implementation of edit tree interface.
6910  * Edit tree is a named container of editable “name-value” child elements, 
6911  * parsed from <code>source</code>. This container provides convenient methods
6912  * for editing/adding/removing child elements. All these update actions are
6913  * instantly reflected in the <code>source</code> code with respect of formatting.
6914  * <br><br>
6915  * For example, developer can create an edit tree from CSS rule and add or 
6916  * remove properties from it–all changes will be immediately reflected in the 
6917  * original source.
6918  * <br><br>
6919  * All classes defined in this module should be extended the same way as in
6920  * Backbone framework: using <code>extend</code> method to create new class and 
6921  * <code>initialize</code> method to define custom class constructor.
6922  * 
6923  * @example
6924  * <pre><code>
6925  * var MyClass = require('editTree').EditElement.extend({
6926  *     initialize: function() {
6927  *     // constructor code here
6928  *   }
6929  * });
6930  * 
6931  * var elem = new MyClass(); 
6932  * </code></pre>
6933  * 
6934  * 
6935  * @param {Function} require
6936  * @param {Underscore} _
6937  * @constructor
6938  * @memberOf __editTreeDefine
6939  */
6940 emmet.define('editTree', function(require, _, core) {
6941         var range = require('range').create;
6942         
6943         /**
6944          * Named container of edited source
6945          * @type EditContainer
6946          * @param {String} source
6947          * @param {Object} options
6948          */
6949         function EditContainer(source, options) {
6950                 this.options = _.extend({offset: 0}, options);
6951                 /**
6952                  * Source code of edited structure. All changes in the structure are 
6953                  * immediately reflected into this property
6954                  */
6955                 this.source = source;
6956                 
6957                 /** 
6958                  * List of all editable children
6959                  * @private 
6960                  */
6961                 this._children = [];
6962                 
6963                 /**
6964                  * Hash of all positions of container
6965                  * @private
6966                  */
6967                 this._positions = {
6968                         name: 0
6969                 };
6970                 
6971                 this.initialize.apply(this, arguments);
6972         }
6973         
6974         /**
6975          * The self-propagating extend function for classes.
6976          * @type Function
6977          */
6978         EditContainer.extend = core.extend;
6979         
6980         EditContainer.prototype = {
6981                 /**
6982                  * Child class constructor
6983                  */
6984                 initialize: function() {},
6985                 
6986                 /**
6987                  * Replace substring of tag's source
6988                  * @param {String} value
6989                  * @param {Number} start
6990                  * @param {Number} end
6991                  * @private
6992                  */
6993                 _updateSource: function(value, start, end) {
6994                         // create modification range
6995                         var r = range(start, _.isUndefined(end) ? 0 : end - start);
6996                         var delta = value.length - r.length();
6997                         
6998                         var update = function(obj) {
6999                                 _.each(obj, function(v, k) {
7000                                         if (v >= r.end)
7001                                                 obj[k] += delta;
7002                                 });
7003                         };
7004                         
7005                         // update affected positions of current container
7006                         update(this._positions);
7007                         
7008                         // update affected positions of children
7009                         _.each(this.list(), function(item) {
7010                                 update(item._positions);
7011                         });
7012                         
7013                         this.source = require('utils').replaceSubstring(this.source, value, r);
7014                 },
7015                         
7016                         
7017                 /**
7018                  * Adds new attribute 
7019                  * @param {String} name Property name
7020                  * @param {String} value Property value
7021                  * @param {Number} pos Position at which to insert new property. By 
7022                  * default the property is inserted at the end of rule 
7023                  * @returns {EditElement} Newly created element
7024                  */
7025                 add: function(name, value, pos) {
7026                         // this is abstract implementation
7027                         var item = new EditElement(name, value);
7028                         this._children.push(item);
7029                         return item;
7030                 },
7031                 
7032                 /**
7033                  * Returns attribute object
7034                  * @param {String} name Attribute name or its index
7035                  * @returns {EditElement}
7036                  */
7037                 get: function(name) {
7038                         if (_.isNumber(name))
7039                                 return this.list()[name];
7040                         
7041                         if (_.isString(name))
7042                                 return _.find(this.list(), function(prop) {
7043                                         return prop.name() === name;
7044                                 });
7045                         
7046                         return name;
7047                 },
7048                 
7049                 /**
7050                  * Returns all children by name or indexes
7051                  * @param {Object} name Element name(s) or indexes (<code>String</code>,
7052                  * <code>Array</code>, <code>Number</code>)
7053                  * @returns {Array}
7054                  */
7055                 getAll: function(name) {
7056                         if (!_.isArray(name))
7057                                 name = [name];
7058                         
7059                         // split names and indexes
7060                         var names = [], indexes = [];
7061                         _.each(name, function(item) {
7062                                 if (_.isString(item))
7063                                         names.push(item);
7064                                 else if (_.isNumber(item))
7065                                         indexes.push(item);
7066                         });
7067                         
7068                         return _.filter(this.list(), function(attribute, i) {
7069                                 return _.include(indexes, i) || _.include(names, attribute.name());
7070                         });
7071                 },
7072                 
7073                 /**
7074                  * Returns or updates element value. If such element doesn't exists,
7075                  * it will be created automatically and added at the end of child list.
7076                  * @param {String} name Element name or its index
7077                  * @param {String} value New element value
7078                  * @returns {String}
7079                  */
7080                 value: function(name, value, pos) {
7081                         var element = this.get(name);
7082                         if (element)
7083                                 return element.value(value);
7084                         
7085                         if (!_.isUndefined(value)) {
7086                                 // no such element — create it
7087                                 return this.add(name, value, pos);
7088                         }
7089                 },
7090                 
7091                 /**
7092                  * Returns all values of child elements found by <code>getAll()</code>
7093                  * method
7094                  * @param {Object} name Element name(s) or indexes (<code>String</code>,
7095                  * <code>Array</code>, <code>Number</code>)
7096                  * @returns {Array}
7097                  */
7098                 values: function(name) {
7099                         return _.map(this.getAll(name), function(element) {
7100                                 return element.value();
7101                         });
7102                 },
7103                 
7104                 /**
7105                  * Remove child element
7106                  * @param {String} name Property name or its index
7107                  */
7108                 remove: function(name) {
7109                         var element = this.get(name);
7110                         if (element) {
7111                                 this._updateSource('', element.fullRange());
7112                                 this._children = _.without(this._children, element);
7113                         }
7114                 },
7115                 
7116                 /**
7117                  * Returns list of all editable child elements
7118                  * @returns {Array}
7119                  */
7120                 list: function() {
7121                         return this._children;
7122                 },
7123                 
7124                 /**
7125                  * Returns index of editble child in list
7126                  * @param {Object} item
7127                  * @returns {Number}
7128                  */
7129                 indexOf: function(item) {
7130                         return _.indexOf(this.list(), this.get(item));
7131                 },
7132                 
7133                 /**
7134                  * Sets or gets container name
7135                  * @param {String} val New name. If not passed, current 
7136                  * name is returned
7137                  * @return {String}
7138                  */
7139                 name: function(val) {
7140                         if (!_.isUndefined(val) && this._name !== (val = String(val))) {
7141                                 this._updateSource(val, this._positions.name, this._positions.name + this._name.length);
7142                                 this._name = val;
7143                         }
7144                         
7145                         return this._name;
7146                 },
7147                 
7148                 /**
7149                  * Returns name range object
7150                  * @param {Boolean} isAbsolute Return absolute range (with respect of 
7151                  * rule offset)
7152                  * @returns {Range}
7153                  */
7154                 nameRange: function(isAbsolute) {
7155                         return range(this._positions.name + (isAbsolute ? this.options.offset : 0), this.name());
7156                 },
7157                 
7158                 /**
7159                  * Returns range of current source
7160                  * @param {Boolean} isAbsolute
7161                  */
7162                 range: function(isAbsolute) {
7163                         return range(isAbsolute ? this.options.offset : 0, this.valueOf());
7164                 },
7165                 
7166                 /**
7167                  * Returns element that belongs to specified position
7168                  * @param {Number} pos
7169                  * @param {Boolean} isAbsolute
7170                  * @returns {EditElement}
7171                  */
7172                 itemFromPosition: function(pos, isAbsolute) {
7173                         return _.find(this.list(), function(elem) {
7174                                 return elem.range(isAbsolute).inside(pos);
7175                         });
7176                 },
7177                 
7178                 /**
7179                  * Returns source code of current container 
7180                  * @returns {String}
7181                  */
7182                 toString: function() {
7183                         return this.valueOf();
7184                 },
7185 
7186                 valueOf: function() {
7187                         return this.source;
7188                 }
7189         };
7190         
7191         /**
7192          * @param {EditContainer} parent
7193          * @param {Object} nameToken
7194          * @param {Object} valueToken
7195          */
7196         function EditElement(parent, nameToken, valueToken) {
7197                 /** @type EditContainer */
7198                 this.parent = parent;
7199                 
7200                 this._name = nameToken.value;
7201                 this._value = valueToken ? valueToken.value : '';
7202                 
7203                 this._positions = {
7204                         name: nameToken.start,
7205                         value: valueToken ? valueToken.start : -1
7206                 };
7207                 
7208                 this.initialize.apply(this, arguments);
7209         }
7210         
7211         /**
7212          * The self-propagating extend function for classes.
7213          * @type Function
7214          */
7215         EditElement.extend = core.extend;
7216         
7217         EditElement.prototype = {
7218                 /**
7219                  * Child class constructor
7220                  */
7221                 initialize: function() {},
7222                 
7223                 /**
7224                  * Make position absolute
7225                  * @private
7226                  * @param {Number} num
7227                  * @param {Boolean} isAbsolute
7228                  * @returns {Boolean}
7229                  */
7230                 _pos: function(num, isAbsolute) {
7231                         return num + (isAbsolute ? this.parent.options.offset : 0);
7232                 },
7233                         
7234                 /**
7235                  * Sets of gets element value
7236                  * @param {String} val New element value. If not passed, current 
7237                  * value is returned
7238                  * @returns {String}
7239                  */
7240                 value: function(val) {
7241                         if (!_.isUndefined(val) && this._value !== (val = String(val))) {
7242                                 this.parent._updateSource(val, this.valueRange());
7243                                 this._value = val;
7244                         }
7245                         
7246                         return this._value;
7247                 },
7248                 
7249                 /**
7250                  * Sets of gets element name
7251                  * @param {String} val New element name. If not passed, current 
7252                  * name is returned
7253                  * @returns {String}
7254                  */
7255                 name: function(val) {
7256                         if (!_.isUndefined(val) && this._name !== (val = String(val))) {
7257                                 this.parent._updateSource(val, this.nameRange());
7258                                 this._name = val;
7259                         }
7260                         
7261                         return this._name;
7262                 },
7263                 
7264                 /**
7265                  * Returns position of element name token
7266                  * @param {Boolean} isAbsolute Return absolute position
7267                  * @returns {Number}
7268                  */
7269                 namePosition: function(isAbsolute) {
7270                         return this._pos(this._positions.name, isAbsolute);
7271                 },
7272                 
7273                 /**
7274                  * Returns position of element value token
7275                  * @param {Boolean} isAbsolute Return absolute position
7276                  * @returns {Number}
7277                  */
7278                 valuePosition: function(isAbsolute) {
7279                         return this._pos(this._positions.value, isAbsolute);
7280                 },
7281                 
7282                 /**
7283                  * Returns element name
7284                  * @param {Boolean} isAbsolute Return absolute range 
7285                  * @returns {Range}
7286                  */
7287                 range: function(isAbsolute) {
7288                         return range(this.namePosition(isAbsolute), this.valueOf());
7289                 },
7290                 
7291                 /**
7292                  * Returns full element range, including possible indentation
7293                  * @param {Boolean} isAbsolute Return absolute range
7294                  * @returns {Range}
7295                  */
7296                 fullRange: function(isAbsolute) {
7297                         return this.range(isAbsolute);
7298                 },
7299                 
7300                 /**
7301                  * Returns element name range
7302                  * @param {Boolean} isAbsolute Return absolute range
7303                  * @returns {Range}
7304                  */
7305                 nameRange: function(isAbsolute) {
7306                         return range(this.namePosition(isAbsolute), this.name());
7307                 },
7308                 
7309                 /**
7310                  * Returns element value range
7311                  * @param {Boolean} isAbsolute Return absolute range
7312                  * @returns {Range}
7313                  */
7314                 valueRange: function(isAbsolute) {
7315                         return range(this.valuePosition(isAbsolute), this.value());
7316                 },
7317                 
7318                 /**
7319                  * Returns current element string representation
7320                  * @returns {String}
7321                  */
7322                 toString: function() {
7323                         return this.valueOf();
7324                 },
7325                 
7326                 valueOf: function() {
7327                         return this.name() + this.value();
7328                 }
7329         };
7330         
7331         return {
7332                 EditContainer: EditContainer,
7333                 EditElement: EditElement,
7334                 
7335                 /**
7336                  * Creates token that can be fed to <code>EditElement</code>
7337                  * @param {Number} start
7338                  * @param {String} value
7339                  * @param {String} type
7340                  * @returns
7341                  */
7342                 createToken: function(start, value, type) {
7343                         var obj = {
7344                                 start: start || 0,
7345                                 value: value || '',
7346                                 type: type
7347                         };
7348                         
7349                         obj.end = obj.start + obj.value.length;
7350                         return obj;
7351                 }
7352         };
7353 });/**
7354  * CSS EditTree is a module that can parse a CSS rule into a tree with 
7355  * convenient methods for adding, modifying and removing CSS properties. These 
7356  * changes can be written back to string with respect of code formatting.
7357  * 
7358  * @memberOf __cssEditTreeDefine
7359  * @constructor
7360  * @param {Function} require
7361  * @param {Underscore} _ 
7362  */
7363 emmet.define('cssEditTree', function(require, _) {
7364         var defaultOptions = {
7365                 styleBefore: '\n\t',
7366                 styleSeparator: ': ',
7367                 offset: 0
7368         };
7369         
7370         var WHITESPACE_REMOVE_FROM_START = 1;
7371         var WHITESPACE_REMOVE_FROM_END   = 2;
7372         
7373         /**
7374          * Returns range object
7375          * @param {Number} start
7376          * @param {Number} len 
7377          * @returns {Range}
7378          */
7379         function range(start, len) {
7380                 return require('range').create(start, len);
7381         }
7382         
7383         /**
7384          * Removes whitespace tokens from the array ends
7385          * @param {Array} tokens
7386          * @param {Number} mask Mask indicating from which end whitespace should be 
7387          * removed 
7388          * @returns {Array}
7389          */
7390         function trimWhitespaceTokens(tokens, mask) {
7391                 mask = mask || (WHITESPACE_REMOVE_FROM_START | WHITESPACE_REMOVE_FROM_END);
7392                 var whitespace = ['white', 'line'];
7393                 
7394                 if ((mask & WHITESPACE_REMOVE_FROM_END) == WHITESPACE_REMOVE_FROM_END)
7395                         while (tokens.length && _.include(whitespace, _.last(tokens).type)) {
7396                                 tokens.pop();
7397                         }
7398                 
7399                 if ((mask & WHITESPACE_REMOVE_FROM_START) == WHITESPACE_REMOVE_FROM_START)
7400                         while (tokens.length && _.include(whitespace, tokens[0].type)) {
7401                                 tokens.shift();
7402                         }
7403                 
7404                 return tokens;
7405         }
7406         
7407         /**
7408          * Helper function that searches for selector range for <code>CSSEditRule</code>
7409          * @param {TokenIterator} it
7410          * @returns {Range}
7411          */
7412         function findSelectorRange(it) {
7413                 var tokens = [], token;
7414                 var start = it.position(), end;
7415                 
7416                 while ((token = it.next())) {
7417                         if (token.type == '{')
7418                                 break;
7419                         tokens.push(token);
7420                 }
7421                 
7422                 trimWhitespaceTokens(tokens);
7423                 
7424                 if (tokens.length) {
7425                         start = tokens[0].start;
7426                         end = _.last(tokens).end;
7427                 } else {
7428                         end = start;
7429                 }
7430                 
7431                 return range(start, end - start);
7432         }
7433         
7434         /**
7435          * Helper function that searches for CSS property value range next to
7436          * iterator's current position  
7437          * @param {TokenIterator} it
7438          * @returns {Range}
7439          */
7440         function findValueRange(it) {
7441                 // find value start position
7442                 var skipTokens = ['white', 'line', ':'];
7443                 var tokens = [], token, start, end;
7444                 
7445                 it.nextUntil(function() {
7446                         return !_.include(skipTokens, this.itemNext().type);
7447                 });
7448                 
7449                 start = it.current().end;
7450                 // consume value
7451                 while ((token = it.next())) {
7452                         if (token.type == '}' || token.type == ';') {
7453                                 // found value end
7454                                 trimWhitespaceTokens(tokens, WHITESPACE_REMOVE_FROM_START 
7455                                                 | (token.type == '}' ? WHITESPACE_REMOVE_FROM_END : 0));
7456                                 
7457                                 if (tokens.length) {
7458                                         start = tokens[0].start;
7459                                         end = _.last(tokens).end;
7460                                 } else {
7461                                         end = start;
7462                                 }
7463                                 
7464                                 return range(start, end - start);
7465                         }
7466                         
7467                         tokens.push(token);
7468                 }
7469                 
7470                 // reached the end of tokens list
7471                 if (tokens.length) {
7472                         return range(tokens[0].start, _.last(tokens).end - tokens[0].start);
7473                 }
7474         }
7475         
7476         /**
7477          * Finds parts of complex CSS value
7478          * @param {String} str
7479          * @returns {Array} Returns list of <code>Range</code>'s
7480          */
7481         function findParts(str) {
7482                 /** @type StringStream */
7483                 var stream = require('stringStream').create(str);
7484                 var ch;
7485                 var result = [];
7486                 var sep = /[\s\u00a0,]/;
7487                 
7488                 var add = function() {
7489                         stream.next();
7490                         result.push(range(stream.start, stream.current()));
7491                         stream.start = stream.pos;
7492                 };
7493                 
7494                 // skip whitespace
7495                 stream.eatSpace();
7496                 stream.start = stream.pos;
7497                 
7498                 while ((ch = stream.next())) {
7499                         if (ch == '"' || ch == "'") {
7500                                 stream.next();
7501                                 if (!stream.skipTo(ch)) break;
7502                                 add();
7503                         } else if (ch == '(') {
7504                                 // function found, may have nested function
7505                                 stream.backUp(1);
7506                                 if (!stream.skipToPair('(', ')')) break;
7507                                 stream.backUp(1);
7508                                 add();
7509                         } else {
7510                                 if (sep.test(ch)) {
7511                                         result.push(range(stream.start, stream.current().length - 1));
7512                                         stream.eatWhile(sep);
7513                                         stream.start = stream.pos;
7514                                 }
7515                         }
7516                 }
7517                 
7518                 add();
7519                 
7520                 return _.chain(result)
7521                         .filter(function(item) {
7522                                 return !!item.length();
7523                         })
7524                         .uniq(false, function(item) {
7525                                 return item.toString();
7526                         })
7527                         .value();
7528         }
7529         
7530         /**
7531          * A bit hacky way to identify invalid CSS property definition: when user
7532          * starts writing new abbreviation in CSS rule, he actually creates invalid
7533          * CSS property definition and this method tries to identify such abbreviation
7534          * and prevent it from being added to CSS edit tree 
7535          * @param {TokenIterator} it
7536          */
7537         function isValidIdentifier(it) {
7538 //              return true;
7539                 var tokens = it.tokens;
7540                 for (var i = it._i + 1, il = tokens.length; i < il; i++) {
7541                         if (tokens[i].type == ':')
7542                                 return true;
7543                         
7544                         if (tokens[i].type == 'identifier' || tokens[i].type == 'line')
7545                                 return false;
7546                 }
7547                 
7548                 return false;
7549         }
7550         
7551         /**
7552          * @class
7553          * @extends EditContainer
7554          */
7555         var CSSEditContainer = require('editTree').EditContainer.extend({
7556                 initialize: function(source, options) {
7557                         _.defaults(this.options, defaultOptions);
7558                         var editTree = require('editTree');
7559                         
7560                         /** @type TokenIterator */
7561                         var it = require('tokenIterator').create(
7562                                         require('cssParser').parse(source));
7563                         
7564                         var selectorRange = findSelectorRange(it);
7565                         this._positions.name = selectorRange.start;
7566                         this._name = selectorRange.substring(source);
7567                         
7568                         if (!it.current() || it.current().type != '{')
7569                                 throw 'Invalid CSS rule';
7570                         
7571                         this._positions.contentStart = it.position() + 1;
7572                         
7573                         // consume properties
7574                         var propertyRange, valueRange, token;
7575                         while ((token = it.next())) {
7576                                 if (token.type == 'identifier' && isValidIdentifier(it)) {
7577                                         propertyRange = range(token);
7578                                         valueRange = findValueRange(it);
7579                                         var end = (it.current() && it.current().type == ';') 
7580                                                 ? range(it.current())
7581                                                 : range(valueRange.end, 0);
7582                                         this._children.push(new CSSEditElement(this,
7583                                                         editTree.createToken(propertyRange.start, propertyRange.substring(source)),
7584                                                         editTree.createToken(valueRange.start, valueRange.substring(source)),
7585                                                         editTree.createToken(end.start, end.substring(source))
7586                                                         ));
7587                                 }
7588                         }
7589                         
7590                         this._saveStyle();
7591                 },
7592                 
7593                 /**
7594                  * Remembers all styles of properties
7595                  * @private
7596                  */
7597                 _saveStyle: function() {
7598                         var start = this._positions.contentStart;
7599                         var source = this.source;
7600                         var utils = require('utils');
7601                         
7602                         _.each(this.list(), /** @param {CSSEditProperty} p */ function(p) {
7603                                 p.styleBefore = source.substring(start, p.namePosition());
7604                                 // a small hack here:
7605                                 // Sometimes users add empty lines before properties to logically
7606                                 // separate groups of properties. In this case, a blind copy of
7607                                 // characters between rules may lead to undesired behavior,
7608                                 // especially when current rule is duplicated or used as a donor
7609                                 // to create new rule.
7610                                 // To solve this issue, we‘ll take only last newline indentation
7611                                 var lines = utils.splitByLines(p.styleBefore);
7612                                 if (lines.length > 1) {
7613                                         p.styleBefore = '\n' + _.last(lines);
7614                                 }
7615                                 
7616                                 p.styleSeparator = source.substring(p.nameRange().end, p.valuePosition());
7617                                 
7618                                 // graceful and naive comments removal 
7619                                 p.styleBefore = _.last(p.styleBefore.split('*/'));
7620                                 p.styleSeparator = p.styleSeparator.replace(/\/\*.*?\*\//g, '');
7621                                 
7622                                 start = p.range().end;
7623                         });
7624                 },
7625                 
7626                 /**
7627                  * Adds new CSS property 
7628                  * @param {String} name Property name
7629                  * @param {String} value Property value
7630                  * @param {Number} pos Position at which to insert new property. By 
7631                  * default the property is inserted at the end of rule 
7632                  * @returns {CSSEditProperty}
7633                  */
7634                 add: function(name, value, pos) {
7635                         var list = this.list();
7636                         var start = this._positions.contentStart;
7637                         var styles = _.pick(this.options, 'styleBefore', 'styleSeparator');
7638                         var editTree = require('editTree');
7639                         
7640                         if (_.isUndefined(pos))
7641                                 pos = list.length;
7642                         
7643                         /** @type CSSEditProperty */
7644                         var donor = list[pos];
7645                         if (donor) {
7646                                 start = donor.fullRange().start;
7647                         } else if ((donor = list[pos - 1])) {
7648                                 // make sure that donor has terminating semicolon
7649                                 donor.end(';');
7650                                 start = donor.range().end;
7651                         }
7652                         
7653                         if (donor) {
7654                                 styles = _.pick(donor, 'styleBefore', 'styleSeparator');
7655                         }
7656                         
7657                         var nameToken = editTree.createToken(start + styles.styleBefore.length, name);
7658                         var valueToken = editTree.createToken(nameToken.end + styles.styleSeparator.length, value);
7659                         
7660                         var property = new CSSEditElement(this, nameToken, valueToken,
7661                                         editTree.createToken(valueToken.end, ';'));
7662                         
7663                         _.extend(property, styles);
7664                         
7665                         // write new property into the source
7666                         this._updateSource(property.styleBefore + property.toString(), start);
7667                         
7668                         // insert new property
7669                         this._children.splice(pos, 0, property);
7670                         return property;
7671                 }
7672         });
7673         
7674         /**
7675          * @class
7676          * @type CSSEditElement
7677          * @constructor
7678          */
7679         var CSSEditElement = require('editTree').EditElement.extend({
7680                 initialize: function(rule, name, value, end) {
7681                         this.styleBefore = rule.options.styleBefore;
7682                         this.styleSeparator = rule.options.styleSeparator;
7683                         
7684                         this._end = end.value;
7685                         this._positions.end = end.start;
7686                 },
7687                 
7688                 /**
7689                  * Returns ranges of complex value parts
7690                  * @returns {Array} Returns <code>null</code> if value is not complex
7691                  */
7692                 valueParts: function(isAbsolute) {
7693                         var parts = findParts(this.value());
7694                         if (isAbsolute) {
7695                                 var offset = this.valuePosition(true);
7696                                 _.each(parts, function(p) {
7697                                         p.shift(offset);
7698                                 });
7699                         }
7700                         
7701                         return parts;
7702                 },
7703                 
7704                 /**
7705                  * Sets of gets property end value (basically, it's a semicolon)
7706                  * @param {String} val New end value. If not passed, current 
7707                  * value is returned
7708                  */
7709                 end: function(val) {
7710                         if (!_.isUndefined(val) && this._end !== val) {
7711                                 this.parent._updateSource(val, this._positions.end, this._positions.end + this._end.length);
7712                                 this._end = val;
7713                         }
7714                         
7715                         return this._end;
7716                 },
7717                 
7718                 /**
7719                  * Returns full rule range, with indentation
7720                  * @param {Boolean} isAbsolute Return absolute range (with respect of
7721                  * rule offset)
7722                  * @returns {Range}
7723                  */
7724                 fullRange: function(isAbsolute) {
7725                         var r = this.range(isAbsolute);
7726                         r.start -= this.styleBefore.length;
7727                         return r;
7728                 },
7729                 
7730                 /**
7731                  * Returns item string representation
7732                  * @returns {String}
7733                  */
7734                 valueOf: function() {
7735                         return this.name() + this.styleSeparator + this.value() + this.end();
7736                 }
7737         });
7738         
7739         return {
7740                 /**
7741                  * Parses CSS rule into editable tree
7742                  * @param {String} source
7743                  * @param {Object} options
7744                  * @memberOf emmet.cssEditTree
7745                  * @returns {EditContainer}
7746                  */
7747                 parse: function(source, options) {
7748                         return new CSSEditContainer(source, options);
7749                 },
7750                 
7751                 /**
7752                  * Extract and parse CSS rule from specified position in <code>content</code> 
7753                  * @param {String} content CSS source code
7754                  * @param {Number} pos Character position where to start source code extraction
7755                  * @returns {EditContainer}
7756                  */
7757                 parseFromPosition: function(content, pos, isBackward) {
7758                         var bounds = this.extractRule(content, pos, isBackward);
7759                         if (!bounds || !bounds.inside(pos))
7760                                 // no matching CSS rule or caret outside rule bounds
7761                                 return null;
7762                         
7763                         return this.parse(bounds.substring(content), {
7764                                 offset: bounds.start
7765                         });
7766                 },
7767                 
7768                 /**
7769                  * Extracts single CSS selector definition from source code
7770                  * @param {String} content CSS source code
7771                  * @param {Number} pos Character position where to start source code extraction
7772                  * @returns {Range}
7773                  */
7774                 extractRule: function(content, pos, isBackward) {
7775                         var result = '';
7776                         var len = content.length;
7777                         var offset = pos;
7778                         var stopChars = '{}/\\<>\n\r';
7779                         var bracePos = -1, ch;
7780                         
7781                         // search left until we find rule edge
7782                         while (offset >= 0) {
7783                                 ch = content.charAt(offset);
7784                                 if (ch == '{') {
7785                                         bracePos = offset;
7786                                         break;
7787                                 }
7788                                 else if (ch == '}' && !isBackward) {
7789                                         offset++;
7790                                         break;
7791                                 }
7792                                 
7793                                 offset--;
7794                         }
7795                         
7796                         // search right for full rule set
7797                         while (offset < len) {
7798                                 ch = content.charAt(offset);
7799                                 if (ch == '{') {
7800                                         bracePos = offset;
7801                                 } else if (ch == '}') {
7802                                         if (bracePos != -1)
7803                                                 result = content.substring(bracePos, offset + 1);
7804                                         break;
7805                                 }
7806                                 
7807                                 offset++;
7808                         }
7809                         
7810                         if (result) {
7811                                 // find CSS selector
7812                                 offset = bracePos - 1;
7813                                 var selector = '';
7814                                 while (offset >= 0) {
7815                                         ch = content.charAt(offset);
7816                                         if (stopChars.indexOf(ch) != -1) break;
7817                                         offset--;
7818                                 }
7819                                 
7820                                 // also trim whitespace
7821                                 selector = content.substring(offset + 1, bracePos).replace(/^[\s\n\r]+/m, '');
7822                                 return require('range').create(bracePos - selector.length, result.length + selector.length);
7823                         }
7824                         
7825                         return null;
7826                 },
7827                 
7828                 /**
7829                  * Removes vendor prefix from CSS property
7830                  * @param {String} name CSS property
7831                  * @return {String}
7832                  */
7833                 baseName: function(name) {
7834                         return name.replace(/^\s*\-\w+\-/, '');
7835                 },
7836                 
7837                 /**
7838                  * Finds parts of complex CSS value
7839                  * @param {String} str
7840                  * @returns {Array}
7841                  */
7842                 findParts: findParts
7843         };
7844 });/**
7845  * XML EditTree is a module that can parse an XML/HTML element into a tree with 
7846  * convenient methods for adding, modifying and removing attributes. These 
7847  * changes can be written back to string with respect of code formatting.
7848  * 
7849  * @memberOf __xmlEditTreeDefine
7850  * @constructor
7851  * @param {Function} require
7852  * @param {Underscore} _ 
7853  */
7854 emmet.define('xmlEditTree', function(require, _) {
7855         var defaultOptions = {
7856                 styleBefore: ' ',
7857                 styleSeparator: '=',
7858                 styleQuote: '"',
7859                 offset: 0
7860         };
7861         
7862         var startTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/m;
7863         
7864         var XMLEditContainer = require('editTree').EditContainer.extend({
7865                 initialize: function(source, options) {
7866                         _.defaults(this.options, defaultOptions);
7867                         this._positions.name = 1;
7868                         
7869                         var attrToken = null;
7870                         var tokens = require('xmlParser').parse(source);
7871                         var range = require('range');
7872                         
7873                         _.each(tokens, function(token) {
7874                                 token.value = range.create(token).substring(source);
7875                                 switch (token.type) {
7876                                         case 'tag':
7877                                                 if (/^<[^\/]+/.test(token.value)) {
7878                                                         this._name = token.value.substring(1);
7879                                                 }
7880                                                 break;
7881                                                 
7882                                         case 'attribute':
7883                                                 // add empty attribute
7884                                                 if (attrToken) {
7885                                                         this._children.push(new XMLEditElement(this, attrToken));
7886                                                 }
7887                                                 
7888                                                 attrToken = token;
7889                                                 break;
7890                                                 
7891                                         case 'string':
7892                                                 this._children.push(new XMLEditElement(this, attrToken, token));
7893                                                 attrToken = null;
7894                                                 break;
7895                                 }
7896                         }, this);
7897                         
7898                         if (attrToken) {
7899                                 this._children.push(new XMLEditElement(this, attrToken));
7900                         }
7901                         
7902                         this._saveStyle();
7903                 },
7904                 
7905                 /**
7906                  * Remembers all styles of properties
7907                  * @private
7908                  */
7909                 _saveStyle: function() {
7910                         var start = this.nameRange().end;
7911                         var source = this.source;
7912                         
7913                         _.each(this.list(), /** @param {EditElement} p */ function(p) {
7914                                 p.styleBefore = source.substring(start, p.namePosition());
7915                                 
7916                                 if (p.valuePosition() !== -1) {
7917                                         p.styleSeparator = source.substring(p.namePosition() + p.name().length, p.valuePosition() - p.styleQuote.length);
7918                                 }
7919                                 
7920                                 start = p.range().end;
7921                         });
7922                 },
7923                 
7924                 /**
7925                  * Adds new attribute 
7926                  * @param {String} name Property name
7927                  * @param {String} value Property value
7928                  * @param {Number} pos Position at which to insert new property. By 
7929                  * default the property is inserted at the end of rule 
7930                  */
7931                 add: function(name, value, pos) {
7932                         var list = this.list();
7933                         var start = this.nameRange().end;
7934                         var editTree = require('editTree');
7935                         var styles = _.pick(this.options, 'styleBefore', 'styleSeparator', 'styleQuote');
7936                         
7937                         if (_.isUndefined(pos))
7938                                 pos = list.length;
7939                         
7940                         
7941                         /** @type XMLEditAttribute */
7942                         var donor = list[pos];
7943                         if (donor) {
7944                                 start = donor.fullRange().start;
7945                         } else if ((donor = list[pos - 1])) {
7946                                 start = donor.range().end;
7947                         }
7948                         
7949                         if (donor) {
7950                                 styles = _.pick(donor, 'styleBefore', 'styleSeparator', 'styleQuote');
7951                         }
7952                         
7953                         value = styles.styleQuote + value + styles.styleQuote;
7954                         
7955                         var attribute = new XMLEditElement(this, 
7956                                         editTree.createToken(start + styles.styleBefore.length, name),
7957                                         editTree.createToken(start + styles.styleBefore.length + name.length 
7958                                                         + styles.styleSeparator.length, value)
7959                                         );
7960                         
7961                         _.extend(attribute, styles);
7962                         
7963                         // write new attribute into the source
7964                         this._updateSource(attribute.styleBefore + attribute.toString(), start);
7965                         
7966                         // insert new attribute
7967                         this._children.splice(pos, 0, attribute);
7968                         return attribute;
7969                 }
7970         });
7971         
7972         var XMLEditElement = require('editTree').EditElement.extend({
7973                 initialize: function(parent, nameToken, valueToken) {
7974                         this.styleBefore = parent.options.styleBefore;
7975                         this.styleSeparator = parent.options.styleSeparator;
7976                         
7977                         var value = '', quote = parent.options.styleQuote;
7978                         if (valueToken) {
7979                                 value = valueToken.value;
7980                                 quote = value.charAt(0);
7981                                 if (quote == '"' || quote == "'") {
7982                                         value = value.substring(1);
7983                                 } else {
7984                                         quote = '';
7985                                 }
7986                                 
7987                                 if (quote && value.charAt(value.length - 1) == quote) {
7988                                         value = value.substring(0, value.length - 1);
7989                                 }
7990                         }
7991                         
7992                         this.styleQuote = quote;
7993                         
7994                         this._value = value;
7995                         this._positions.value = valueToken ? valueToken.start + quote.length : -1;
7996                 },
7997                 
7998                 /**
7999                  * Returns full rule range, with indentation
8000                  * @param {Boolean} isAbsolute Return absolute range (with respect of
8001                  * rule offset)
8002                  * @returns {Range}
8003                  */
8004                 fullRange: function(isAbsolute) {
8005                         var r = this.range(isAbsolute);
8006                         r.start -= this.styleBefore.length;
8007                         return r;
8008                 },
8009                 
8010                 valueOf: function() {
8011                         return this.name() + this.styleSeparator
8012                                 + this.styleQuote + this.value() + this.styleQuote;
8013                 }
8014         });
8015         
8016         return {
8017                 /**
8018                  * Parses HTML element into editable tree
8019                  * @param {String} source
8020                  * @param {Object} options
8021                  * @memberOf emmet.htmlEditTree
8022                  * @returns {EditContainer}
8023                  */
8024                 parse: function(source, options) {
8025                         return new XMLEditContainer(source, options);
8026                 },
8027                 
8028                 /**
8029                  * Extract and parse HTML from specified position in <code>content</code> 
8030                  * @param {String} content CSS source code
8031                  * @param {Number} pos Character position where to start source code extraction
8032                  * @returns {XMLEditElement}
8033                  */
8034                 parseFromPosition: function(content, pos, isBackward) {
8035                         var bounds = this.extractTag(content, pos, isBackward);
8036                         if (!bounds || !bounds.inside(pos))
8037                                 // no matching HTML tag or caret outside tag bounds
8038                                 return null;
8039                         
8040                         return this.parse(bounds.substring(content), {
8041                                 offset: bounds.start
8042                         });
8043                 },
8044                 
8045                 /**
8046                  * Extracts nearest HTML tag range from <code>content</code>, starting at 
8047                  * <code>pos</code> position
8048                  * @param {String} content
8049                  * @param {Number} pos
8050                  * @param {Boolean} isBackward
8051                  * @returns {Range}
8052                  */
8053                 extractTag: function(content, pos, isBackward) {
8054                         var len = content.length, i;
8055                         var range = require('range');
8056                         
8057                         // max extraction length. I don't think there may be tags larger 
8058                         // than 2000 characters length
8059                         var maxLen = Math.min(2000, len);
8060                         
8061                         /** @type Range */
8062                         var r = null;
8063                         
8064                         var match = function(pos) {
8065                                 var m;
8066                                 if (content.charAt(pos) == '<' && (m = content.substr(pos, maxLen).match(startTag)))
8067                                         return range.create(pos, m[0]);
8068                         };
8069                         
8070                         // lookup backward, in case we are inside tag already
8071                         for (i = pos; i >= 0; i--) {
8072                                 if ((r = match(i))) break;
8073                         }
8074                         
8075                         if (r && (r.inside(pos) || isBackward))
8076                                 return r;
8077                         
8078                         if (!r && isBackward)
8079                                 return null;
8080                         
8081                         // search forward
8082                         for (i = pos; i < len; i++) {
8083                                 if ((r = match(i)))
8084                                         return r;
8085                         }
8086                 }
8087         };
8088 });/**
8089  * 'Expand abbreviation' editor action: extracts abbreviation from current caret 
8090  * position and replaces it with formatted output. 
8091  * <br><br>
8092  * This behavior can be overridden with custom handlers which can perform 
8093  * different actions when 'Expand Abbreviation' action is called.
8094  * For example, a CSS gradient handler that produces vendor-prefixed gradient
8095  * definitions registers its own expand abbreviation handler.  
8096  *  
8097  * @constructor
8098  * @memberOf __expandAbbreviationActionDefine
8099  * @param {Function} require
8100  * @param {Underscore} _
8101  */
8102 emmet.define('expandAbbreviation', function(require, _) {
8103         /**
8104          * @type HandlerList List of registered handlers
8105          */
8106         var handlers = require('handlerList').create();
8107         
8108         /** Back-reference to module */
8109         var module = null;
8110         
8111         var actions = require('actions');
8112         /**
8113          * 'Expand abbreviation' editor action 
8114          * @param {IEmmetEditor} editor Editor instance
8115          * @param {String} syntax Syntax type (html, css, etc.)
8116          * @param {String} profile Output profile name (html, xml, xhtml)
8117          * @return {Boolean} Returns <code>true</code> if abbreviation was expanded 
8118          * successfully
8119          */
8120         actions.add('expand_abbreviation', function(editor, syntax, profile) {
8121                 var args = _.toArray(arguments);
8122                 
8123                 // normalize incoming arguments
8124                 var info = require('editorUtils').outputInfo(editor, syntax, profile);
8125                 args[1] = info.syntax;
8126                 args[2] = info.profile;
8127                 
8128                 return handlers.exec(false, args);
8129         });
8130         
8131         /**
8132          * A special version of <code>expandAbbreviation</code> function: if it can't
8133          * find abbreviation, it will place Tab character at caret position
8134          * @param {IEmmetEditor} editor Editor instance
8135          * @param {String} syntax Syntax type (html, css, etc.)
8136          * @param {String} profile Output profile name (html, xml, xhtml)
8137          */
8138         actions.add('expand_abbreviation_with_tab', function(editor, syntax, profile) {
8139                 var sel = editor.getSelection();
8140                 var indent = require('resources').getVariable('indentation');
8141                 if (sel) {
8142                         // indent selection
8143                         var utils = require('utils');
8144                         var selRange = require('range').create(editor.getSelectionRange());
8145                         var content = utils.padString(sel, indent);
8146                         
8147                         editor.replaceContent(indent + '${0}', editor.getCaretPos());
8148                         var replaceRange = require('range').create(editor.getCaretPos(), selRange.length());
8149                         editor.replaceContent(content, replaceRange.start, replaceRange.end, true);
8150                         editor.createSelection(replaceRange.start, replaceRange.start + content.length);
8151                         return true;
8152                 }
8153                 
8154                 if (!actions.run('expand_abbreviation', editor, syntax, profile)) {
8155                         editor.replaceContent(indent, editor.getCaretPos());
8156                 }
8157                 
8158                 return true;
8159         }, {hidden: true});
8160         
8161         // XXX setup default handler
8162         /**
8163          * Extracts abbreviation from current caret 
8164          * position and replaces it with formatted output 
8165          * @param {IEmmetEditor} editor Editor instance
8166          * @param {String} syntax Syntax type (html, css, etc.)
8167          * @param {String} profile Output profile name (html, xml, xhtml)
8168          * @return {Boolean} Returns <code>true</code> if abbreviation was expanded 
8169          * successfully
8170          */
8171         handlers.add(function(editor, syntax, profile) {
8172                 var caretPos = editor.getSelectionRange().end;
8173                 var abbr = module.findAbbreviation(editor);
8174                         
8175                 if (abbr) {
8176                         var content = emmet.expandAbbreviation(abbr, syntax, profile, 
8177                                         require('actionUtils').captureContext(editor));
8178                         if (content) {
8179                                 editor.replaceContent(content, caretPos - abbr.length, caretPos);
8180                                 return true;
8181                         }
8182                 }
8183                 
8184                 return false;
8185         }, {order: -1});
8186         
8187         return module = {
8188                 /**
8189                  * Adds custom expand abbreviation handler. The passed function should 
8190                  * return <code>true</code> if it was performed successfully, 
8191                  * <code>false</code> otherwise.
8192                  * 
8193                  * Added handlers will be called when 'Expand Abbreviation' is called
8194                  * in order they were added
8195                  * @memberOf expandAbbreviation
8196                  * @param {Function} fn
8197                  * @param {Object} options
8198                  */
8199                 addHandler: function(fn, options) {
8200                         handlers.add(fn, options);
8201                 },
8202                 
8203                 /**
8204                  * Removes registered handler
8205                  * @returns
8206                  */
8207                 removeHandler: function(fn) {
8208                         handlers.remove(fn);
8209                 },
8210                 
8211                 /**
8212                  * Search for abbreviation in editor from current caret position
8213                  * @param {IEmmetEditor} editor Editor instance
8214                  * @return {String}
8215                  */
8216                 findAbbreviation: function(editor) {
8217                         /** @type Range */
8218                         var range = require('range').create(editor.getSelectionRange());
8219                         var content = String(editor.getContent());
8220                         if (range.length()) {
8221                                 // abbreviation is selected by user
8222                                 return range.substring(content);
8223                         }
8224                         
8225                         // search for new abbreviation from current caret position
8226                         var curLine = editor.getCurrentLineRange();
8227                         return require('actionUtils').extractAbbreviation(content.substring(curLine.start, range.start));
8228                 }
8229         };
8230 });/**
8231  * Action that wraps content with abbreviation. For convenience, action is 
8232  * defined as reusable module
8233  * @constructor
8234  * @memberOf __wrapWithAbbreviationDefine
8235  */
8236 emmet.define('wrapWithAbbreviation', function(require, _) {
8237         /** Back-references to current module */
8238         var module = null;
8239         
8240         /**
8241          * Wraps content with abbreviation
8242          * @param {IEmmetEditor} Editor instance
8243          * @param {String} abbr Abbreviation to wrap with
8244          * @param {String} syntax Syntax type (html, css, etc.)
8245          * @param {String} profile Output profile name (html, xml, xhtml)
8246          */
8247         require('actions').add('wrap_with_abbreviation', function (editor, abbr, syntax, profile) {
8248                 var info = require('editorUtils').outputInfo(editor, syntax, profile);
8249                 var utils = require('utils');
8250                 /** @type emmet.editorUtils */
8251                 var editorUtils = require('editorUtils');
8252                 abbr = abbr || editor.prompt("Enter abbreviation");
8253                 
8254                 if (!abbr) 
8255                         return null;
8256                 
8257                 abbr = String(abbr);
8258                 
8259                 var range = require('range').create(editor.getSelectionRange());
8260                 
8261                 if (!range.length()) {
8262                         // no selection, find tag pair
8263                         var match = require('htmlMatcher').tag(info.content, range.start);
8264                         if (!match) {  // nothing to wrap
8265                                 return false;
8266                         }
8267                         
8268                         range = utils.narrowToNonSpace(info.content, match.range);
8269                 }
8270                 
8271                 var newContent = utils.escapeText(range.substring(info.content));
8272                 var result = module
8273                         .wrap(abbr, editorUtils.unindent(editor, newContent), info.syntax, 
8274                                         info.profile, require('actionUtils').captureContext(editor));
8275                 
8276                 if (result) {
8277                         editor.replaceContent(result, range.start, range.end);
8278                         return true;
8279                 }
8280                 
8281                 return false;
8282         });
8283         
8284         return module = {
8285                 /**
8286                  * Wraps passed text with abbreviation. Text will be placed inside last
8287                  * expanded element
8288                  * @memberOf wrapWithAbbreviation
8289                  * @param {String} abbr Abbreviation
8290                  * @param {String} text Text to wrap
8291                  * @param {String} syntax Document type (html, xml, etc.). Default is 'html'
8292                  * @param {String} profile Output profile's name. Default is 'plain'
8293                  * @param {Object} contextNode Context node inside which abbreviation
8294                  * is wrapped. It will be used as a reference for node name resolvers
8295                  * @return {String}
8296                  */
8297                 wrap: function(abbr, text, syntax, profile, contextNode) {
8298                         /** @type emmet.filters */
8299                         var filters = require('filters');
8300                         /** @type emmet.utils */
8301                         var utils = require('utils');
8302                         
8303                         syntax = syntax || emmet.defaultSyntax();
8304                         profile = require('profile').get(profile, syntax);
8305                         
8306                         require('tabStops').resetTabstopIndex();
8307                         
8308                         var data = filters.extractFromAbbreviation(abbr);
8309                         var parsedTree = require('abbreviationParser').parse(data[0], {
8310                                 syntax: syntax,
8311                                 pastedContent: text,
8312                                 contextNode: contextNode
8313                         });
8314                         if (parsedTree) {
8315                                 var filtersList = filters.composeList(syntax, profile, data[1]);
8316                                 filters.apply(parsedTree, filtersList, profile);
8317                                 return utils.replaceVariables(parsedTree.valueOf());
8318                         }
8319                         
8320                         return null;
8321                 }
8322         };
8323 });/**
8324  * Toggles HTML and CSS comments depending on current caret context. Unlike
8325  * the same action in most editors, this action toggles comment on currently
8326  * matched item—HTML tag or CSS selector—when nothing is selected.
8327  * 
8328  * @param {Function} require
8329  * @param {Underscore} _
8330  * @memberOf __toggleCommentAction
8331  * @constructor
8332  */
8333 emmet.exec(function(require, _) {
8334         var prefs = require('preferences');
8335 
8336         /**
8337          * Toggle HTML comment on current selection or tag
8338          * @param {IEmmetEditor} editor
8339          * @return {Boolean} Returns <code>true</code> if comment was toggled
8340          */
8341         function toggleHTMLComment(editor) {
8342                 /** @type Range */
8343                 var range = require('range').create(editor.getSelectionRange());
8344                 var info = require('editorUtils').outputInfo(editor);
8345                         
8346                 if (!range.length()) {
8347                         // no selection, find matching tag
8348                         var tag = require('htmlMatcher').tag(info.content, editor.getCaretPos());
8349                         if (tag) { // found pair
8350                                 range = tag.outerRange;
8351                         }
8352                 }
8353                 
8354                 return genericCommentToggle(editor, '<!--', '-->', range);
8355         }
8356 
8357         /**
8358          * Simple CSS commenting
8359          * @param {IEmmetEditor} editor
8360          * @return {Boolean} Returns <code>true</code> if comment was toggled
8361          */
8362         function toggleCSSComment(editor) {
8363                 /** @type Range */
8364                 var range = require('range').create(editor.getSelectionRange());
8365                 var info = require('editorUtils').outputInfo(editor);
8366                         
8367                 if (!range.length()) {
8368                         // no selection, try to get current rule
8369                         /** @type CSSRule */
8370                         var rule = require('cssEditTree').parseFromPosition(info.content, editor.getCaretPos());
8371                         if (rule) {
8372                                 var property = cssItemFromPosition(rule, editor.getCaretPos());
8373                                 range = property 
8374                                         ? property.range(true) 
8375                                         : require('range').create(rule.nameRange(true).start, rule.source);
8376                         }
8377                 }
8378                 
8379                 if (!range.length()) {
8380                         // still no selection, get current line
8381                         range = require('range').create(editor.getCurrentLineRange());
8382                         require('utils').narrowToNonSpace(info.content, range);
8383                 }
8384                 
8385                 return genericCommentToggle(editor, '/*', '*/', range);
8386         }
8387         
8388         /**
8389          * Returns CSS property from <code>rule</code> that matches passed position
8390          * @param {EditContainer} rule
8391          * @param {Number} absPos
8392          * @returns {EditElement}
8393          */
8394         function cssItemFromPosition(rule, absPos) {
8395                 // do not use default EditContainer.itemFromPosition() here, because
8396                 // we need to make a few assumptions to make CSS commenting more reliable
8397                 var relPos = absPos - (rule.options.offset || 0);
8398                 var reSafeChar = /^[\s\n\r]/;
8399                 return _.find(rule.list(), function(item) {
8400                         if (item.range().end === relPos) {
8401                                 // at the end of property, but outside of it
8402                                 // if there’s a space character at current position,
8403                                 // use current property
8404                                 return reSafeChar.test(rule.source.charAt(relPos));
8405                         }
8406                         
8407                         return item.range().inside(relPos);
8408                 });
8409         }
8410 
8411         /**
8412          * Search for nearest comment in <code>str</code>, starting from index <code>from</code>
8413          * @param {String} text Where to search
8414          * @param {Number} from Search start index
8415          * @param {String} start_token Comment start string
8416          * @param {String} end_token Comment end string
8417          * @return {Range} Returns null if comment wasn't found
8418          */
8419         function searchComment(text, from, startToken, endToken) {
8420                 var commentStart = -1;
8421                 var commentEnd = -1;
8422                 
8423                 var hasMatch = function(str, start) {
8424                         return text.substr(start, str.length) == str;
8425                 };
8426                         
8427                 // search for comment start
8428                 while (from--) {
8429                         if (hasMatch(startToken, from)) {
8430                                 commentStart = from;
8431                                 break;
8432                         }
8433                 }
8434                 
8435                 if (commentStart != -1) {
8436                         // search for comment end
8437                         from = commentStart;
8438                         var contentLen = text.length;
8439                         while (contentLen >= from++) {
8440                                 if (hasMatch(endToken, from)) {
8441                                         commentEnd = from + endToken.length;
8442                                         break;
8443                                 }
8444                         }
8445                 }
8446                 
8447                 return (commentStart != -1 && commentEnd != -1) 
8448                         ? require('range').create(commentStart, commentEnd - commentStart) 
8449                         : null;
8450         }
8451 
8452         /**
8453          * Generic comment toggling routine
8454          * @param {IEmmetEditor} editor
8455          * @param {String} commentStart Comment start token
8456          * @param {String} commentEnd Comment end token
8457          * @param {Range} range Selection range
8458          * @return {Boolean}
8459          */
8460         function genericCommentToggle(editor, commentStart, commentEnd, range) {
8461                 var editorUtils = require('editorUtils');
8462                 var content = editorUtils.outputInfo(editor).content;
8463                 var caretPos = editor.getCaretPos();
8464                 var newContent = null;
8465                 
8466                 var utils = require('utils');
8467                         
8468                 /**
8469                  * Remove comment markers from string
8470                  * @param {Sting} str
8471                  * @return {String}
8472                  */
8473                 function removeComment(str) {
8474                         return str
8475                                 .replace(new RegExp('^' + utils.escapeForRegexp(commentStart) + '\\s*'), function(str){
8476                                         caretPos -= str.length;
8477                                         return '';
8478                                 }).replace(new RegExp('\\s*' + utils.escapeForRegexp(commentEnd) + '$'), '');
8479                 }
8480                 
8481                 // first, we need to make sure that this substring is not inside 
8482                 // comment
8483                 var commentRange = searchComment(content, caretPos, commentStart, commentEnd);
8484                 if (commentRange && commentRange.overlap(range)) {
8485                         // we're inside comment, remove it
8486                         range = commentRange;
8487                         newContent = removeComment(range.substring(content));
8488                 } else {
8489                         // should add comment
8490                         // make sure that there's no comment inside selection
8491                         newContent = commentStart + ' ' +
8492                                 range.substring(content)
8493                                         .replace(new RegExp(utils.escapeForRegexp(commentStart) + '\\s*|\\s*' + utils.escapeForRegexp(commentEnd), 'g'), '') +
8494                                 ' ' + commentEnd;
8495                                 
8496                         // adjust caret position
8497                         caretPos += commentStart.length + 1;
8498                 }
8499 
8500                 // replace editor content
8501                 if (newContent !== null) {
8502                         newContent = utils.escapeText(newContent);
8503                         editor.setCaretPos(range.start);
8504                         editor.replaceContent(editorUtils.unindent(editor, newContent), range.start, range.end);
8505                         editor.setCaretPos(caretPos);
8506                         return true;
8507                 }
8508                 
8509                 return false;
8510         }
8511         
8512         /**
8513          * Toggle comment on current editor's selection or HTML tag/CSS rule
8514          * @param {IEmmetEditor} editor
8515          */
8516         require('actions').add('toggle_comment', function(editor) {
8517                 var info = require('editorUtils').outputInfo(editor);
8518                 if (info.syntax == 'css') {
8519                         // in case our editor is good enough and can recognize syntax from 
8520                         // current token, we have to make sure that cursor is not inside
8521                         // 'style' attribute of html element
8522                         var caretPos = editor.getCaretPos();
8523                         var tag = require('htmlMatcher').tag(info.content, caretPos);
8524                         if (tag && tag.open.range.inside(caretPos)) {
8525                                 info.syntax = 'html';
8526                         }
8527                 }
8528                 
8529                 var cssSyntaxes = prefs.getArray('css.syntaxes');
8530                 if (_.include(cssSyntaxes, info.syntax)) {
8531                         return toggleCSSComment(editor);
8532                 }
8533                 
8534                 return toggleHTMLComment(editor);
8535         });
8536 });/**
8537  * Move between next/prev edit points. 'Edit points' are places between tags 
8538  * and quotes of empty attributes in html
8539  * @constructor
8540  * 
8541  * @memberOf __editPointActionDefine
8542  * @param {Function} require
8543  * @param {Underscore} _
8544  */
8545 emmet.exec(function(require, _) {
8546         /**
8547          * Search for new caret insertion point
8548          * @param {IEmmetEditor} editor Editor instance
8549          * @param {Number} inc Search increment: -1 — search left, 1 — search right
8550          * @param {Number} offset Initial offset relative to current caret position
8551          * @return {Number} Returns -1 if insertion point wasn't found
8552          */
8553         function findNewEditPoint(editor, inc, offset) {
8554                 inc = inc || 1;
8555                 offset = offset || 0;
8556                 
8557                 var curPoint = editor.getCaretPos() + offset;
8558                 var content = String(editor.getContent());
8559                 var maxLen = content.length;
8560                 var nextPoint = -1;
8561                 var reEmptyLine = /^\s+$/;
8562                 
8563                 function getLine(ix) {
8564                         var start = ix;
8565                         while (start >= 0) {
8566                                 var c = content.charAt(start);
8567                                 if (c == '\n' || c == '\r')
8568                                         break;
8569                                 start--;
8570                         }
8571                         
8572                         return content.substring(start, ix);
8573                 }
8574                         
8575                 while (curPoint <= maxLen && curPoint >= 0) {
8576                         curPoint += inc;
8577                         var curChar = content.charAt(curPoint);
8578                         var nextChar = content.charAt(curPoint + 1);
8579                         var prevChar = content.charAt(curPoint - 1);
8580                                 
8581                         switch (curChar) {
8582                                 case '"':
8583                                 case '\'':
8584                                         if (nextChar == curChar && prevChar == '=') {
8585                                                 // empty attribute
8586                                                 nextPoint = curPoint + 1;
8587                                         }
8588                                         break;
8589                                 case '>':
8590                                         if (nextChar == '<') {
8591                                                 // between tags
8592                                                 nextPoint = curPoint + 1;
8593                                         }
8594                                         break;
8595                                 case '\n':
8596                                 case '\r':
8597                                         // empty line
8598                                         if (reEmptyLine.test(getLine(curPoint - 1))) {
8599                                                 nextPoint = curPoint;
8600                                         }
8601                                         break;
8602                         }
8603                         
8604                         if (nextPoint != -1)
8605                                 break;
8606                 }
8607                 
8608                 return nextPoint;
8609         }
8610         
8611         /** @type emmet.actions */
8612         var actions = require('actions');
8613         
8614         /**
8615          * Move caret to previous edit point
8616          * @param {IEmmetEditor} editor Editor instance
8617          */
8618         actions.add('prev_edit_point', function(editor) {
8619                 var curPos = editor.getCaretPos();
8620                 var newPoint = findNewEditPoint(editor, -1);
8621                         
8622                 if (newPoint == curPos)
8623                         // we're still in the same point, try searching from the other place
8624                         newPoint = findNewEditPoint(editor, -1, -2);
8625                 
8626                 if (newPoint != -1) {
8627                         editor.setCaretPos(newPoint);
8628                         return true;
8629                 }
8630                 
8631                 return false;
8632         }, {label: 'Previous Edit Point'});
8633         
8634         /**
8635          * Move caret to next edit point
8636          * @param {IEmmetEditor} editor Editor instance
8637          */
8638         actions.add('next_edit_point', function(editor) {
8639                 var newPoint = findNewEditPoint(editor, 1);
8640                 if (newPoint != -1) {
8641                         editor.setCaretPos(newPoint);
8642                         return true;
8643                 }
8644                 
8645                 return false;
8646         });
8647 });/**
8648  * Actions that use stream parsers and tokenizers for traversing:
8649  * -- Search for next/previous items in HTML
8650  * -- Search for next/previous items in CSS
8651  * @constructor
8652  * @memberOf __selectItemActionDefine
8653  * @param {Function} require
8654  * @param {Underscore} _
8655  */
8656 emmet.exec(function(require, _) {
8657         var startTag = /^<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/;
8658         
8659         /**
8660          * Generic function for searching for items to select
8661          * @param {IEmmetEditor} editor
8662          * @param {Boolean} isBackward Search backward (search forward otherwise)
8663          * @param {Function} extractFn Function that extracts item content
8664          * @param {Function} rangeFn Function that search for next token range
8665          */
8666         function findItem(editor, isBackward, extractFn, rangeFn) {
8667                 var range = require('range');
8668                 var content = require('editorUtils').outputInfo(editor).content;
8669                 
8670                 var contentLength = content.length;
8671                 var itemRange, rng;
8672                 /** @type Range */
8673                 var prevRange = range.create(-1, 0);
8674                 /** @type Range */
8675                 var sel = range.create(editor.getSelectionRange());
8676                 
8677                 var searchPos = sel.start, loop = 100000; // endless loop protection
8678                 while (searchPos >= 0 && searchPos < contentLength && --loop > 0) {
8679                         if ( (itemRange = extractFn(content, searchPos, isBackward)) ) {
8680                                 if (prevRange.equal(itemRange)) {
8681                                         break;
8682                                 }
8683                                 
8684                                 prevRange = itemRange.clone();
8685                                 rng = rangeFn(itemRange.substring(content), itemRange.start, sel.clone());
8686                                 
8687                                 if (rng) {
8688                                         editor.createSelection(rng.start, rng.end);
8689                                         return true;
8690                                 } else {
8691                                         searchPos = isBackward ? itemRange.start : itemRange.end - 1;
8692                                 }
8693                         }
8694                         
8695                         searchPos += isBackward ? -1 : 1;
8696                 }
8697                 
8698                 return false;
8699         }
8700         
8701         // XXX HTML section
8702         
8703         /**
8704          * Find next HTML item
8705          * @param {IEmmetEditor} editor
8706          */
8707         function findNextHTMLItem(editor) {
8708                 var isFirst = true;
8709                 return findItem(editor, false, function(content, searchPos){
8710                         if (isFirst) {
8711                                 isFirst = false;
8712                                 return findOpeningTagFromPosition(content, searchPos);
8713                         } else {
8714                                 return getOpeningTagFromPosition(content, searchPos);
8715                         }
8716                 }, function(tag, offset, selRange) {
8717                         return getRangeForHTMLItem(tag, offset, selRange, false);
8718                 });
8719         }
8720         
8721         /**
8722          * Find previous HTML item
8723          * @param {IEmmetEditor} editor
8724          */
8725         function findPrevHTMLItem(editor) {
8726                 return findItem(editor, true, getOpeningTagFromPosition, function (tag, offset, selRange) {
8727                         return getRangeForHTMLItem(tag, offset, selRange, true);
8728                 });
8729         }
8730         
8731         /**
8732          * Creates possible selection ranges for HTML tag
8733          * @param {String} source Original HTML source for tokens
8734          * @param {Array} tokens List of HTML tokens
8735          * @returns {Array}
8736          */
8737         function makePossibleRangesHTML(source, tokens, offset) {
8738                 offset = offset || 0;
8739                 var range = require('range');
8740                 var result = [];
8741                 var attrStart = -1, attrName = '', attrValue = '', attrValueRange, tagName;
8742                 _.each(tokens, function(tok) {
8743                         switch (tok.type) {
8744                                 case 'tag':
8745                                         tagName = source.substring(tok.start, tok.end);
8746                                         if (/^<[\w\:\-]/.test(tagName)) {
8747                                                 // add tag name
8748                                                 result.push(range.create({
8749                                                         start: tok.start + 1, 
8750                                                         end: tok.end
8751                                                 }));
8752                                         }
8753                                         break;
8754                                 case 'attribute':
8755                                         attrStart = tok.start;
8756                                         attrName = source.substring(tok.start, tok.end);
8757                                         break;
8758                                         
8759                                 case 'string':
8760                                         // attribute value
8761                                         // push full attribute first
8762                                         result.push(range.create(attrStart, tok.end - attrStart));
8763                                         
8764                                         attrValueRange = range.create(tok);
8765                                         attrValue = attrValueRange.substring(source);
8766                                         
8767                                         // is this a quoted attribute?
8768                                         if (isQuote(attrValue.charAt(0)))
8769                                                 attrValueRange.start++;
8770                                         
8771                                         if (isQuote(attrValue.charAt(attrValue.length - 1)))
8772                                                 attrValueRange.end--;
8773                                         
8774                                         result.push(attrValueRange);
8775                                         
8776                                         if (attrName == 'class') {
8777                                                 result = result.concat(classNameRanges(attrValueRange.substring(source), attrValueRange.start));
8778                                         }
8779                                         
8780                                         break;
8781                         }
8782                 });
8783                 
8784                 // offset ranges
8785                 _.each(result, function(r) {
8786                         r.shift(offset);
8787                 });
8788                 
8789                 return _.chain(result)
8790                         .filter(function(item) {        // remove empty
8791                                 return !!item.length();
8792                         })
8793                         .uniq(false, function(item) {   // remove duplicates
8794                                 return item.toString();
8795                         })
8796                         .value();
8797         }
8798         
8799         /**
8800          * Returns ranges of class names in "class" attribute value
8801          * @param {String} className
8802          * @returns {Array}
8803          */
8804         function classNameRanges(className, offset) {
8805                 offset = offset || 0;
8806                 var result = [];
8807                 /** @type StringStream */
8808                 var stream = require('stringStream').create(className);
8809                 var range = require('range');
8810                 
8811                 // skip whitespace
8812                 stream.eatSpace();
8813                 stream.start = stream.pos;
8814                 
8815                 var ch;
8816                 while ((ch = stream.next())) {
8817                         if (/[\s\u00a0]/.test(ch)) {
8818                                 result.push(range.create(stream.start + offset, stream.pos - stream.start - 1));
8819                                 stream.eatSpace();
8820                                 stream.start = stream.pos;
8821                         }
8822                 }
8823                 
8824                 result.push(range.create(stream.start + offset, stream.pos - stream.start));
8825                 return result;
8826         }
8827         
8828         /**
8829          * Returns best HTML tag range match for current selection
8830          * @param {String} tag Tag declaration
8831          * @param {Number} offset Tag's position index inside content
8832          * @param {Range} selRange Selection range
8833          * @return {Range} Returns range if next item was found, <code>null</code> otherwise
8834          */
8835         function getRangeForHTMLItem(tag, offset, selRange, isBackward) {
8836                 var ranges = makePossibleRangesHTML(tag, require('xmlParser').parse(tag), offset);
8837                 
8838                 if (isBackward)
8839                         ranges.reverse();
8840                 
8841                 // try to find selected range
8842                 var curRange = _.find(ranges, function(r) {
8843                         return r.equal(selRange);
8844                 });
8845                 
8846                 if (curRange) {
8847                         var ix = _.indexOf(ranges, curRange);
8848                         if (ix < ranges.length - 1)
8849                                 return ranges[ix + 1];
8850                         
8851                         return null;
8852                 }
8853                 
8854                 // no selected range, find nearest one
8855                 if (isBackward)
8856                         // search backward
8857                         return _.find(ranges, function(r) {
8858                                 return r.start < selRange.start;
8859                         });
8860                 
8861                 // search forward
8862                 // to deal with overlapping ranges (like full attribute definition
8863                 // and attribute value) let's find range under caret first
8864                 if (!curRange) {
8865                         var matchedRanges = _.filter(ranges, function(r) {
8866                                 return r.inside(selRange.end);
8867                         });
8868                         
8869                         if (matchedRanges.length > 1)
8870                                 return matchedRanges[1];
8871                 }
8872                 
8873                 
8874                 return _.find(ranges, function(r) {
8875                         return r.end > selRange.end;
8876                 });
8877         }
8878         
8879         /**
8880          * Search for opening tag in content, starting at specified position
8881          * @param {String} html Where to search tag
8882          * @param {Number} pos Character index where to start searching
8883          * @return {Range} Returns range if valid opening tag was found,
8884          * <code>null</code> otherwise
8885          */
8886         function findOpeningTagFromPosition(html, pos) {
8887                 var tag;
8888                 while (pos >= 0) {
8889                         if ((tag = getOpeningTagFromPosition(html, pos)))
8890                                 return tag;
8891                         pos--;
8892                 }
8893                 
8894                 return null;
8895         }
8896         
8897         /**
8898          * @param {String} html Where to search tag
8899          * @param {Number} pos Character index where to start searching
8900          * @return {Range} Returns range if valid opening tag was found,
8901          * <code>null</code> otherwise
8902          */
8903         function getOpeningTagFromPosition(html, pos) {
8904                 var m;
8905                 if (html.charAt(pos) == '<' && (m = html.substring(pos, html.length).match(startTag))) {
8906                         return require('range').create(pos, m[0]);
8907                 }
8908         }
8909         
8910         function isQuote(ch) {
8911                 return ch == '"' || ch == "'";
8912         }
8913         
8914         /**
8915          * Makes all possible selection ranges for specified CSS property
8916          * @param {CSSProperty} property
8917          * @returns {Array}
8918          */
8919         function makePossibleRangesCSS(property) {
8920                 // find all possible ranges, sorted by position and size
8921                 var valueRange = property.valueRange(true);
8922                 var result = [property.range(true), valueRange];
8923                 var stringStream = require('stringStream');
8924                 var cssEditTree = require('cssEditTree');
8925                 var range = require('range');
8926                 
8927                 // locate parts of complex values.
8928                 // some examples:
8929                 // – 1px solid red: 3 parts
8930                 // – arial, sans-serif: enumeration, 2 parts
8931                 // – url(image.png): function value part
8932                 var value = property.value();
8933                 _.each(property.valueParts(), function(r) {
8934                         // add absolute range
8935                         var clone = r.clone();
8936                         result.push(clone.shift(valueRange.start));
8937                         
8938                         /** @type StringStream */
8939                         var stream = stringStream.create(r.substring(value));
8940                         if (stream.match(/^[\w\-]+\(/, true)) {
8941                                 // we have a function, find values in it.
8942                                 // but first add function contents
8943                                 stream.start = stream.pos;
8944                                 stream.skipToPair('(', ')');
8945                                 var fnBody = stream.current();
8946                                 result.push(range.create(clone.start + stream.start, fnBody));
8947                                 
8948                                 // find parts
8949                                 _.each(cssEditTree.findParts(fnBody), function(part) {
8950                                         result.push(range.create(clone.start + stream.start + part.start, part.substring(fnBody)));
8951                                 });
8952                         }
8953                 });
8954                 
8955                 // optimize result: remove empty ranges and duplicates
8956                 return _.chain(result)
8957                         .filter(function(item) {
8958                                 return !!item.length();
8959                         })
8960                         .uniq(false, function(item) {
8961                                 return item.toString();
8962                         })
8963                         .value();
8964         }
8965         
8966         /**
8967          * Tries to find matched CSS property and nearest range for selection
8968          * @param {CSSRule} rule
8969          * @param {Range} selRange
8970          * @param {Boolean} isBackward
8971          * @returns {Range}
8972          */
8973         function matchedRangeForCSSProperty(rule, selRange, isBackward) {
8974                 /** @type CSSProperty */
8975                 var property = null;
8976                 var possibleRanges, curRange = null, ix;
8977                 var list = rule.list();
8978                 var searchFn, nearestItemFn;
8979                 
8980                 if (isBackward) {
8981                         list.reverse();
8982                         searchFn = function(p) {
8983                                 return p.range(true).start <= selRange.start;
8984                         };
8985                         nearestItemFn = function(r) {
8986                                 return r.start < selRange.start;
8987                         };
8988                 } else {
8989                         searchFn = function(p) {
8990                                 return p.range(true).end >= selRange.end;
8991                         };
8992                         nearestItemFn = function(r) {
8993                                 return r.end > selRange.start;
8994                         };
8995                 }
8996                 
8997                 // search for nearest to selection CSS property
8998                 var eqSel = function(r) {
8999                         return r.equal(selRange);
9000                 };
9001                 var inSel = function(r) {
9002                         return r.inside(selRange.end);
9003                 };
9004                 while ((property = _.find(list, searchFn))) {
9005                         possibleRanges = makePossibleRangesCSS(property);
9006                         if (isBackward)
9007                                 possibleRanges.reverse();
9008                         
9009                         // check if any possible range is already selected
9010                         curRange = _.find(possibleRanges, eqSel);
9011                         
9012                         if (!curRange) {
9013                                 // no selection, select nearest item
9014                                 var matchedRanges = _.filter(possibleRanges, inSel);
9015                                 
9016                                 if (matchedRanges.length > 1) {
9017                                         curRange = matchedRanges[1];
9018                                         break;
9019                                 }
9020                                 
9021                                 if ((curRange = _.find(possibleRanges, nearestItemFn)))
9022                                         break;
9023                         } else {
9024                                 ix = _.indexOf(possibleRanges, curRange);
9025                                 if (ix != possibleRanges.length - 1) {
9026                                         curRange = possibleRanges[ix + 1];
9027                                         break;
9028                                 }
9029                         }
9030                         
9031                         curRange = null;
9032                         selRange.start = selRange.end = isBackward 
9033                                 ? property.range(true).start - 1
9034                                 : property.range(true).end + 1;
9035                 }
9036                 
9037                 return curRange;
9038         }
9039         
9040         function findNextCSSItem(editor) {
9041                 return findItem(editor, false, require('cssEditTree').extractRule, getRangeForNextItemInCSS);
9042         }
9043         
9044         function findPrevCSSItem(editor) {
9045                 return findItem(editor, true, require('cssEditTree').extractRule, getRangeForPrevItemInCSS);
9046         }
9047         
9048         /**
9049          * Returns range for item to be selected in CSS after current caret 
9050          * (selection) position
9051          * @param {String} rule CSS rule declaration
9052          * @param {Number} offset Rule's position index inside content
9053          * @param {Range} selRange Selection range
9054          * @return {Range} Returns range if next item was found, <code>null</code> otherwise
9055          */
9056         function getRangeForNextItemInCSS(rule, offset, selRange) {
9057                 var tree = require('cssEditTree').parse(rule, {
9058                         offset: offset
9059                 });
9060                 
9061                 // check if selector is matched
9062                 var range = tree.nameRange(true);
9063                 if (selRange.end < range.end) {
9064                         return range;
9065                 }
9066                 
9067                 return matchedRangeForCSSProperty(tree, selRange, false);
9068         }
9069         
9070         /**
9071          * Returns range for item to be selected in CSS before current caret 
9072          * (selection) position
9073          * @param {String} rule CSS rule declaration
9074          * @param {Number} offset Rule's position index inside content
9075          * @param {Range} selRange Selection range
9076          * @return {Range} Returns range if previous item was found, <code>null</code> otherwise
9077          */
9078         function getRangeForPrevItemInCSS(rule, offset, selRange) {
9079                 var tree = require('cssEditTree').parse(rule, {
9080                         offset: offset
9081                 });
9082                 
9083                 var curRange = matchedRangeForCSSProperty(tree, selRange, true);
9084                 
9085                 if (!curRange) {
9086                         // no matched property, try to match selector
9087                         var range = tree.nameRange(true);
9088                         if (selRange.start > range.start) {
9089                                 return range;
9090                         }
9091                 }
9092                 
9093                 return curRange;
9094         }
9095         
9096         // XXX register actions
9097         var actions = require('actions');
9098         actions.add('select_next_item', function(editor){
9099                 if (editor.getSyntax() == 'css')
9100                         return findNextCSSItem(editor);
9101                 else
9102                         return findNextHTMLItem(editor);
9103         });
9104         
9105         actions.add('select_previous_item', function(editor){
9106                 if (editor.getSyntax() == 'css')
9107                         return findPrevCSSItem(editor);
9108                 else
9109                         return findPrevHTMLItem(editor);
9110         });
9111 });/**
9112  * HTML pair matching (balancing) actions
9113  * @constructor
9114  * @memberOf __matchPairActionDefine
9115  * @param {Function} require
9116  * @param {Underscore} _
9117  */
9118 emmet.exec(function(require, _) {
9119         /** @type emmet.actions */
9120         var actions = require('actions');
9121         var matcher = require('htmlMatcher');
9122         var lastMatch = null;
9123         
9124         /**
9125          * Find and select HTML tag pair
9126          * @param {IEmmetEditor} editor Editor instance
9127          * @param {String} direction Direction of pair matching: 'in' or 'out'. 
9128          * Default is 'out'
9129          */
9130         function matchPair(editor, direction) {
9131                 direction = String((direction || 'out').toLowerCase());
9132                 var info = require('editorUtils').outputInfo(editor);
9133                 
9134                 var range = require('range');
9135                 /** @type Range */
9136                 var sel = range.create(editor.getSelectionRange());
9137                 var content = info.content;
9138                 
9139                 // validate previous match
9140                 if (lastMatch && !lastMatch.range.equal(sel)) {
9141                         lastMatch = null;
9142                 }
9143                 
9144                 if (lastMatch && sel.length()) {
9145                         if (direction == 'in') {
9146                                 // user has previously selected tag and wants to move inward
9147                                 if (lastMatch.type == 'tag' && !lastMatch.close) {
9148                                         // unary tag was selected, can't move inward
9149                                         return false;
9150                                 } else {
9151                                         if (lastMatch.range.equal(lastMatch.outerRange)) {
9152                                                 lastMatch.range = lastMatch.innerRange;
9153                                         } else {
9154                                                 var narrowed = require('utils').narrowToNonSpace(content, lastMatch.innerRange);
9155                                                 lastMatch = matcher.find(content, narrowed.start + 1);
9156                                                 if (lastMatch && lastMatch.range.equal(sel) && lastMatch.outerRange.equal(sel)) {
9157                                                         lastMatch.range = lastMatch.innerRange;
9158                                                 }
9159                                         }
9160                                 }
9161                         } else {
9162                                 if (
9163                                                 !lastMatch.innerRange.equal(lastMatch.outerRange) 
9164                                                 && lastMatch.range.equal(lastMatch.innerRange) 
9165                                                 && sel.equal(lastMatch.range)) {
9166                                         lastMatch.range = lastMatch.outerRange;
9167                                 } else {
9168                                         lastMatch = matcher.find(content, sel.start);
9169                                         if (lastMatch && lastMatch.range.equal(sel) && lastMatch.innerRange.equal(sel)) {
9170                                                 lastMatch.range = lastMatch.outerRange;
9171                                         }
9172                                 }
9173                         }
9174                 } else {
9175                         lastMatch = matcher.find(content, sel.start);
9176                 }
9177                 
9178                 if (lastMatch && !lastMatch.range.equal(sel)) {
9179                         editor.createSelection(lastMatch.range.start, lastMatch.range.end);
9180                         return true;
9181                 }
9182                 
9183                 lastMatch = null;
9184                 return false;
9185         }
9186         
9187         actions.add('match_pair', matchPair, {hidden: true});
9188         actions.add('match_pair_inward', function(editor){
9189                 return matchPair(editor, 'in');
9190         }, {label: 'HTML/Match Pair Tag (inward)'});
9191 
9192         actions.add('match_pair_outward', function(editor){
9193                 return matchPair(editor, 'out');
9194         }, {label: 'HTML/Match Pair Tag (outward)'});
9195         
9196         /**
9197          * Moves caret to matching opening or closing tag
9198          * @param {IEmmetEditor} editor
9199          */
9200         actions.add('matching_pair', function(editor) {
9201                 var content = String(editor.getContent());
9202                 var caretPos = editor.getCaretPos();
9203                 
9204                 if (content.charAt(caretPos) == '<') 
9205                         // looks like caret is outside of tag pair  
9206                         caretPos++;
9207                         
9208                 var tag = matcher.tag(content, caretPos);
9209                 if (tag && tag.close) { // exclude unary tags
9210                         if (tag.open.range.inside(caretPos)) {
9211                                 editor.setCaretPos(tag.close.range.start);
9212                         } else {
9213                                 editor.setCaretPos(tag.open.range.start);
9214                         }
9215                         
9216                         return true;
9217                 }
9218                 
9219                 return false;
9220         }, {label: 'HTML/Go To Matching Tag Pair'});
9221 });/**
9222  * Gracefully removes tag under cursor
9223  * 
9224  * @param {Function} require
9225  * @param {Underscore} _ 
9226  */
9227 emmet.exec(function(require, _) {
9228         require('actions').add('remove_tag', function(editor) {
9229                 var utils = require('utils');
9230                 var info = require('editorUtils').outputInfo(editor);
9231                 
9232                 // search for tag
9233                 var tag = require('htmlMatcher').tag(info.content, editor.getCaretPos());
9234                 if (tag) {
9235                         if (!tag.close) {
9236                                 // simply remove unary tag
9237                                 editor.replaceContent(utils.getCaretPlaceholder(), tag.range.start, tag.range.end);
9238                         } else {
9239                                 // remove tag and its newlines
9240                                 /** @type Range */
9241                                 var tagContentRange = utils.narrowToNonSpace(info.content, tag.innerRange);
9242                                 /** @type Range */
9243                                 var startLineBounds = utils.findNewlineBounds(info.content, tagContentRange.start);
9244                                 var startLinePad = utils.getLinePadding(startLineBounds.substring(info.content));
9245                                 var tagContent = tagContentRange.substring(info.content);
9246                                 
9247                                 tagContent = utils.unindentString(tagContent, startLinePad);
9248                                 editor.replaceContent(utils.getCaretPlaceholder() + utils.escapeText(tagContent), tag.outerRange.start, tag.outerRange.end);
9249                         }
9250                         
9251                         return true;
9252                 }
9253                 
9254                 return false;
9255         }, {label: 'HTML/Remove Tag'});
9256 });
9257 /**
9258  * Splits or joins tag, e.g. transforms it into a short notation and vice versa:<br>
9259  * &lt;div&gt;&lt;/div&gt; → &lt;div /&gt; : join<br>
9260  * &lt;div /&gt; → &lt;div&gt;&lt;/div&gt; : split
9261  * @param {Function} require
9262  * @param {Underscore} _
9263  * @memberOf __splitJoinTagAction
9264  * @constructor
9265  */
9266 emmet.exec(function(require, _) {
9267         /**
9268          * @param {IEmmetEditor} editor
9269          * @param {Object} profile
9270          * @param {Object} tag
9271          */
9272         function joinTag(editor, profile, tag) {
9273                 /** @type emmet.utils */
9274                 var utils = require('utils');
9275                 
9276                 // empty closing slash is a nonsense for this action
9277                 var slash = profile.selfClosing() || ' /';
9278                 var content = tag.open.range.substring(tag.source).replace(/\s*>$/, slash + '>');
9279                 
9280                 var caretPos = editor.getCaretPos();
9281                 
9282                 // update caret position
9283                 if (content.length + tag.outerRange.start < caretPos) {
9284                         caretPos = content.length + tag.outerRange.start;
9285                 }
9286                 
9287                 content = utils.escapeText(content);
9288                 editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end);
9289                 editor.setCaretPos(caretPos);
9290                 return true;
9291         }
9292         
9293         function splitTag(editor, profile, tag) {
9294                 /** @type emmet.utils */
9295                 var utils = require('utils');
9296                 
9297                 var nl = utils.getNewline();
9298                 var pad = require('resources').getVariable('indentation');
9299                 var caretPos = editor.getCaretPos();
9300                 
9301                 // define tag content depending on profile
9302                 var tagContent = (profile.tag_nl === true) ? nl + pad + nl : '';
9303                 var content = tag.outerContent().replace(/\s*\/>$/, '>');
9304                 caretPos = tag.outerRange.start + content.length;
9305                 content += tagContent + '</' + tag.open.name + '>';
9306                 
9307                 content = utils.escapeText(content);
9308                 editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end);
9309                 editor.setCaretPos(caretPos);
9310                 return true;
9311         }
9312         
9313         require('actions').add('split_join_tag', function(editor, profileName) {
9314                 var matcher = require('htmlMatcher');
9315                 
9316                 var info = require('editorUtils').outputInfo(editor, null, profileName);
9317                 var profile = require('profile').get(info.profile);
9318                 
9319                 // find tag at current position
9320                 var tag = matcher.tag(info.content, editor.getCaretPos());
9321                 if (tag) {
9322                         return tag.close 
9323                                 ? joinTag(editor, profile, tag) 
9324                                 : splitTag(editor, profile, tag);
9325                 }
9326                 
9327                 return false;
9328         }, {label: 'HTML/Split\\Join Tag Declaration'});
9329 });/**
9330  * Reflect CSS value: takes rule's value under caret and pastes it for the same 
9331  * rules with vendor prefixes
9332  * @constructor
9333  * @memberOf __reflectCSSActionDefine
9334  * @param {Function} require
9335  * @param {Underscore} _
9336  */
9337 emmet.define('reflectCSSValue', function(require, _) {
9338         /**
9339          * @type HandlerList List of registered handlers
9340          */
9341         var handlers = require('handlerList').create();
9342         
9343         require('actions').add('reflect_css_value', function(editor) {
9344                 if (editor.getSyntax() != 'css') return false;
9345                 
9346                 return require('actionUtils').compoundUpdate(editor, doCSSReflection(editor));
9347         }, {label: 'CSS/Reflect Value'});
9348         
9349         function doCSSReflection(editor) {
9350                 /** @type emmet.cssEditTree */
9351                 var cssEditTree = require('cssEditTree');
9352                 var outputInfo = require('editorUtils').outputInfo(editor);
9353                 var caretPos = editor.getCaretPos();
9354                 
9355                 var cssRule = cssEditTree.parseFromPosition(outputInfo.content, caretPos);
9356                 if (!cssRule) return;
9357                 
9358                 var property = cssRule.itemFromPosition(caretPos, true);
9359                 // no property under cursor, nothing to reflect
9360                 if (!property) return;
9361                 
9362                 var oldRule = cssRule.source;
9363                 var offset = cssRule.options.offset;
9364                 var caretDelta = caretPos - offset - property.range().start;
9365                 
9366                 handlers.exec(false, [property]);
9367                 
9368                 if (oldRule !== cssRule.source) {
9369                         return {
9370                                 data:  cssRule.source,
9371                                 start: offset,
9372                                 end:   offset + oldRule.length,
9373                                 caret: offset + property.range().start + caretDelta
9374                         };
9375                 }
9376         }
9377         
9378         /**
9379          * Returns regexp that should match reflected CSS property names
9380          * @param {String} name Current CSS property name
9381          * @return {RegExp}
9382          */
9383         function getReflectedCSSName(name) {
9384                 name = require('cssEditTree').baseName(name);
9385                 var vendorPrefix = '^(?:\\-\\w+\\-)?', m;
9386                 
9387                 if (name == 'opacity' || name == 'filter') {
9388                         return new RegExp(vendorPrefix + '(?:opacity|filter)$');
9389                 } else if ((m = name.match(/^border-radius-(top|bottom)(left|right)/))) {
9390                         // Mozilla-style border radius
9391                         return new RegExp(vendorPrefix + '(?:' + name + '|border-' + m[1] + '-' + m[2] + '-radius)$');
9392                 } else if ((m = name.match(/^border-(top|bottom)-(left|right)-radius/))) { 
9393                         return new RegExp(vendorPrefix + '(?:' + name + '|border-radius-' + m[1] + m[2] + ')$');
9394                 }
9395                 
9396                 return new RegExp(vendorPrefix + name + '$');
9397         }
9398         
9399         /**
9400          * Reflects value from <code>donor</code> into <code>receiver</code>
9401          * @param {CSSProperty} donor Donor CSS property from which value should
9402          * be reflected
9403          * @param {CSSProperty} receiver Property that should receive reflected 
9404          * value from donor
9405          */
9406         function reflectValue(donor, receiver) {
9407                 var value = getReflectedValue(donor.name(), donor.value(), 
9408                                 receiver.name(), receiver.value());
9409                 
9410                 receiver.value(value);
9411         }
9412         
9413         /**
9414          * Returns value that should be reflected for <code>refName</code> CSS property
9415          * from <code>curName</code> property. This function is used for special cases,
9416          * when the same result must be achieved with different properties for different
9417          * browsers. For example: opаcity:0.5; → filter:alpha(opacity=50);<br><br>
9418          * 
9419          * This function does value conversion between different CSS properties
9420          * 
9421          * @param {String} curName Current CSS property name
9422          * @param {String} curValue Current CSS property value
9423          * @param {String} refName Receiver CSS property's name 
9424          * @param {String} refValue Receiver CSS property's value
9425          * @return {String} New value for receiver property
9426          */
9427         function getReflectedValue(curName, curValue, refName, refValue) {
9428                 var cssEditTree = require('cssEditTree');
9429                 var utils = require('utils');
9430                 curName = cssEditTree.baseName(curName);
9431                 refName = cssEditTree.baseName(refName);
9432                 
9433                 if (curName == 'opacity' && refName == 'filter') {
9434                         return refValue.replace(/opacity=[^)]*/i, 'opacity=' + Math.floor(parseFloat(curValue) * 100));
9435                 } else if (curName == 'filter' && refName == 'opacity') {
9436                         var m = curValue.match(/opacity=([^)]*)/i);
9437                         return m ? utils.prettifyNumber(parseInt(m[1], 10) / 100) : refValue;
9438                 }
9439                 
9440                 return curValue;
9441         }
9442         
9443         // XXX add default handler
9444         handlers.add(function(property) {
9445                 var reName = getReflectedCSSName(property.name());
9446                 _.each(property.parent.list(), function(p) {
9447                         if (reName.test(p.name())) {
9448                                 reflectValue(property, p);
9449                         }
9450                 });
9451         }, {order: -1});
9452         
9453         return {
9454                 /**
9455                  * Adds custom reflect handler. The passed function will receive matched
9456                  * CSS property (as <code>CSSEditElement</code> object) and should
9457                  * return <code>true</code> if it was performed successfully (handled 
9458                  * reflection), <code>false</code> otherwise.
9459                  * @param {Function} fn
9460                  * @param {Object} options
9461                  */
9462                 addHandler: function(fn, options) {
9463                         handlers.add(fn, options);
9464                 },
9465                 
9466                 /**
9467                  * Removes registered handler
9468                  * @returns
9469                  */
9470                 removeHandler: function(fn) {
9471                         handlers.remove(fn);
9472                 }
9473         };
9474 });/**
9475  * Evaluates simple math expression under caret
9476  * @param {Function} require
9477  * @param {Underscore} _ 
9478  */
9479 emmet.exec(function(require, _) {
9480         require('actions').add('evaluate_math_expression', function(editor) {
9481                 var actionUtils = require('actionUtils');
9482                 var utils = require('utils');
9483                 
9484                 var content = String(editor.getContent());
9485                 var chars = '.+-*/\\';
9486                 
9487                 /** @type Range */
9488                 var sel = require('range').create(editor.getSelectionRange());
9489                 if (!sel.length()) {
9490                         sel = actionUtils.findExpressionBounds(editor, function(ch) {
9491                                 return utils.isNumeric(ch) || chars.indexOf(ch) != -1;
9492                         });
9493                 }
9494                 
9495                 if (sel && sel.length()) {
9496                         var expr = sel.substring(content);
9497                         
9498                         // replace integral division: 11\2 => Math.round(11/2) 
9499                         expr = expr.replace(/([\d\.\-]+)\\([\d\.\-]+)/g, 'Math.round($1/$2)');
9500                         
9501                         try {
9502                                 var result = utils.prettifyNumber(new Function('return ' + expr)());
9503                                 editor.replaceContent(result, sel.start, sel.end);
9504                                 editor.setCaretPos(sel.start + result.length);
9505                                 return true;
9506                         } catch (e) {}
9507                 }
9508                 
9509                 return false;
9510         }, {label: 'Numbers/Evaluate Math Expression'});
9511 });
9512 /**
9513  * Increment/decrement number under cursor
9514  * @param {Function} require
9515  * @param {Underscore} _
9516  */
9517 emmet.exec(function(require, _) {
9518         /**
9519          * Extract number from current caret position of the <code>editor</code> and
9520          * increment it by <code>step</code>
9521          * @param {IEmmetEditor} editor
9522          * @param {Number} step Increment step (may be negative)
9523          */
9524         function incrementNumber(editor, step) {
9525                 var utils = require('utils');
9526                 var actionUtils = require('actionUtils');
9527                 
9528                 var hasSign = false;
9529                 var hasDecimal = false;
9530                         
9531                 var r = actionUtils.findExpressionBounds(editor, function(ch, pos, content) {
9532                         if (utils.isNumeric(ch))
9533                                 return true;
9534                         if (ch == '.') {
9535                                 // make sure that next character is numeric too
9536                                 if (!utils.isNumeric(content.charAt(pos + 1)))
9537                                         return false;
9538                                 
9539                                 return hasDecimal ? false : hasDecimal = true;
9540                         }
9541                         if (ch == '-')
9542                                 return hasSign ? false : hasSign = true;
9543                                 
9544                         return false;
9545                 });
9546                         
9547                 if (r && r.length()) {
9548                         var strNum = r.substring(String(editor.getContent()));
9549                         var num = parseFloat(strNum);
9550                         if (!_.isNaN(num)) {
9551                                 num = utils.prettifyNumber(num + step);
9552                                 
9553                                 // do we have zero-padded number?
9554                                 if (/^(\-?)0+[1-9]/.test(strNum)) {
9555                                         var minus = '';
9556                                         if (RegExp.$1) {
9557                                                 minus = '-';
9558                                                 num = num.substring(1);
9559                                         }
9560                                                 
9561                                         var parts = num.split('.');
9562                                         parts[0] = utils.zeroPadString(parts[0], intLength(strNum));
9563                                         num = minus + parts.join('.');
9564                                 }
9565                                 
9566                                 editor.replaceContent(num, r.start, r.end);
9567                                 editor.createSelection(r.start, r.start + num.length);
9568                                 return true;
9569                         }
9570                 }
9571                 
9572                 return false;
9573         }
9574         
9575         /**
9576          * Returns length of integer part of number
9577          * @param {String} num
9578          */
9579         function intLength(num) {
9580                 num = num.replace(/^\-/, '');
9581                 if (~num.indexOf('.')) {
9582                         return num.split('.')[0].length;
9583                 }
9584                 
9585                 return num.length;
9586         }
9587         
9588         var actions = require('actions');
9589         _.each([1, -1, 10, -10, 0.1, -0.1], function(num) {
9590                 var prefix = num > 0 ? 'increment' : 'decrement';
9591                 
9592                 actions.add(prefix + '_number_by_' + String(Math.abs(num)).replace('.', '').substring(0, 2), function(editor) {
9593                         return incrementNumber(editor, num);
9594                 }, {label: 'Numbers/' + prefix.charAt(0).toUpperCase() + prefix.substring(1) + ' number by ' + Math.abs(num)});
9595         });
9596 });/**
9597  * Actions to insert line breaks. Some simple editors (like browser's 
9598  * &lt;textarea&gt;, for example) do not provide such simple things
9599  * @param {Function} require
9600  * @param {Underscore} _
9601  */
9602 emmet.exec(function(require, _) {
9603         var actions = require('actions');
9604         /** @type emmet.preferences */
9605         var prefs = require('preferences');
9606         
9607         // setup default preferences
9608         prefs.define('css.closeBraceIndentation', '\n',
9609                         'Indentation before closing brace of CSS rule. Some users prefere ' 
9610                         + 'indented closing brace of CSS rule for better readability. '
9611                         + 'This preference’s value will be automatically inserted before '
9612                         + 'closing brace when user adds newline in newly created CSS rule '
9613                         + '(e.g. when “Insert formatted linebreak” action will be performed ' 
9614                         + 'in CSS file). If you’re such user, you may want to write put a value ' 
9615                         + 'like <code>\\n\\t</code> in this preference.');
9616         
9617         /**
9618          * Inserts newline character with proper indentation in specific positions only.
9619          * @param {IEmmetEditor} editor
9620          * @return {Boolean} Returns <code>true</code> if line break was inserted 
9621          */
9622         actions.add('insert_formatted_line_break_only', function(editor) {
9623                 var utils = require('utils');
9624                 /** @type emmet.resources */
9625                 var res = require('resources');
9626                 
9627                 var info = require('editorUtils').outputInfo(editor);
9628                 var caretPos = editor.getCaretPos();
9629                 var nl = utils.getNewline();
9630                 var pad;
9631                 
9632                 if (_.include(['html', 'xml', 'xsl'], info.syntax)) {
9633                         pad = res.getVariable('indentation');
9634                         // let's see if we're breaking newly created tag
9635                         var tag = require('htmlMatcher').tag(info.content, caretPos);
9636                         if (tag && !tag.innerRange.length()) {
9637                                 editor.replaceContent(nl + pad + utils.getCaretPlaceholder() + nl, caretPos);
9638                                 return true;
9639                         }
9640                 } else if (info.syntax == 'css') {
9641                         /** @type String */
9642                         var content = info.content;
9643                         if (caretPos && content.charAt(caretPos - 1) == '{') {
9644                                 var append = prefs.get('css.closeBraceIndentation');
9645                                 pad = res.getVariable('indentation');
9646                                 
9647                                 var hasCloseBrace = content.charAt(caretPos) == '}';
9648                                 if (!hasCloseBrace) {
9649                                         // do we really need special formatting here?
9650                                         // check if this is really a newly created rule,
9651                                         // look ahead for a closing brace
9652                                         for (var i = caretPos, il = content.length, ch; i < il; i++) {
9653                                                 ch = content.charAt(i);
9654                                                 if (ch == '{') {
9655                                                         // ok, this is a new rule without closing brace
9656                                                         break;
9657                                                 }
9658                                                 
9659                                                 if (ch == '}') {
9660                                                         // not a new rule, just add indentation
9661                                                         append = '';
9662                                                         hasCloseBrace = true;
9663                                                         break;
9664                                                 }
9665                                         }
9666                                 }
9667                                 
9668                                 if (!hasCloseBrace) {
9669                                         append += '}';
9670                                 }
9671                                 
9672                                 // defining rule set
9673                                 var insValue = nl + pad + utils.getCaretPlaceholder() + append;
9674                                 editor.replaceContent(insValue, caretPos);
9675                                 return true;
9676                         }
9677                 }
9678                         
9679                 return false;
9680         }, {hidden: true});
9681         
9682         /**
9683          * Inserts newline character with proper indentation. This action is used in
9684          * editors that doesn't have indentation control (like textarea element) to 
9685          * provide proper indentation
9686          * @param {IEmmetEditor} editor Editor instance
9687          */
9688         actions.add('insert_formatted_line_break', function(editor) {
9689                 if (!actions.run('insert_formatted_line_break_only', editor)) {
9690                         var utils = require('utils');
9691                         
9692                         var curPadding = require('editorUtils').getCurrentLinePadding(editor);
9693                         var content = String(editor.getContent());
9694                         var caretPos = editor.getCaretPos();
9695                         var len = content.length;
9696                         var nl = utils.getNewline();
9697                                 
9698                         // check out next line padding
9699                         var lineRange = editor.getCurrentLineRange();
9700                         var nextPadding = '';
9701                                 
9702                         for (var i = lineRange.end + 1, ch; i < len; i++) {
9703                                 ch = content.charAt(i);
9704                                 if (ch == ' ' || ch == '\t')
9705                                         nextPadding += ch;
9706                                 else
9707                                         break;
9708                         }
9709                         
9710                         if (nextPadding.length > curPadding.length)
9711                                 editor.replaceContent(nl + nextPadding, caretPos, caretPos, true);
9712                         else
9713                                 editor.replaceContent(nl, caretPos);
9714                 }
9715                 
9716                 return true;
9717         }, {hidden: true});
9718 });/**
9719  * Merges selected lines or lines between XHTML tag pairs
9720  * @param {Function} require
9721  * @param {Underscore} _
9722  */
9723 emmet.exec(function(require, _) {
9724         require('actions').add('merge_lines', function(editor) {
9725                 var matcher = require('htmlMatcher');
9726                 var utils = require('utils');
9727                 var editorUtils = require('editorUtils');
9728                 var info = editorUtils.outputInfo(editor);
9729                 
9730                 /** @type Range */
9731                 var selection = require('range').create(editor.getSelectionRange());
9732                 if (!selection.length()) {
9733                         // find matching tag
9734                         var pair = matcher.find(info.content, editor.getCaretPos());
9735                         if (pair) {
9736                                 selection = pair.outerRange;
9737                         }
9738                 }
9739                 
9740                 if (selection.length()) {
9741                         // got range, merge lines
9742                         var text =  selection.substring(info.content);
9743                         var lines = utils.splitByLines(text);
9744                         
9745                         for (var i = 1; i < lines.length; i++) {
9746                                 lines[i] = lines[i].replace(/^\s+/, '');
9747                         }
9748                         
9749                         text = lines.join('').replace(/\s{2,}/, ' ');
9750                         var textLen = text.length;
9751                         text = utils.escapeText(text);
9752                         editor.replaceContent(text, selection.start, selection.end);
9753                         editor.createSelection(selection.start, selection.start + textLen);
9754                         
9755                         return true;
9756                 }
9757                 
9758                 return false;
9759         });
9760 });/**
9761  * Encodes/decodes image under cursor to/from base64
9762  * @param {IEmmetEditor} editor
9763  * @since 0.65
9764  * 
9765  * @memberOf __base64ActionDefine
9766  * @constructor
9767  * @param {Function} require
9768  * @param {Underscore} _
9769  */
9770 emmet.exec(function(require, _) {
9771         require('actions').add('encode_decode_data_url', function(editor) {
9772                 var data = String(editor.getSelection());
9773                 var caretPos = editor.getCaretPos();
9774                         
9775                 if (!data) {
9776                         // no selection, try to find image bounds from current caret position
9777                         var text = String(editor.getContent()),  m;
9778                         while (caretPos-- >= 0) {
9779                                 if (startsWith('src=', text, caretPos)) { // found <img src="">
9780                                         if ((m = text.substr(caretPos).match(/^(src=(["'])?)([^'"<>\s]+)\1?/))) {
9781                                                 data = m[3];
9782                                                 caretPos += m[1].length;
9783                                         }
9784                                         break;
9785                                 } else if (startsWith('url(', text, caretPos)) { // found CSS url() pattern
9786                                         if ((m = text.substr(caretPos).match(/^(url\((['"])?)([^'"\)\s]+)\1?/))) {
9787                                                 data = m[3];
9788                                                 caretPos += m[1].length;
9789                                         }
9790                                         break;
9791                                 }
9792                         }
9793                 }
9794                 
9795                 if (data) {
9796                         if (startsWith('data:', data))
9797                                 return decodeFromBase64(editor, data, caretPos);
9798                         else
9799                                 return encodeToBase64(editor, data, caretPos);
9800                 }
9801                 
9802                 return false;
9803         }, {label: 'Encode\\Decode data:URL image'});
9804         
9805         /**
9806          * Test if <code>text</code> starts with <code>token</code> at <code>pos</code>
9807          * position. If <code>pos</code> is omitted, search from beginning of text 
9808          * @param {String} token Token to test
9809          * @param {String} text Where to search
9810          * @param {Number} pos Position where to start search
9811          * @return {Boolean}
9812          * @since 0.65
9813          */
9814         function startsWith(token, text, pos) {
9815                 pos = pos || 0;
9816                 return text.charAt(pos) == token.charAt(0) && text.substr(pos, token.length) == token;
9817         }
9818         
9819         /**
9820          * Encodes image to base64
9821          * 
9822          * @param {IEmmetEditor} editor
9823          * @param {String} imgPath Path to image
9824          * @param {Number} pos Caret position where image is located in the editor
9825          * @return {Boolean}
9826          */
9827         function encodeToBase64(editor, imgPath, pos) {
9828                 var file = require('file');
9829                 var actionUtils = require('actionUtils');
9830                 
9831                 var editorFile = editor.getFilePath();
9832                 var defaultMimeType = 'application/octet-stream';
9833                         
9834                 if (editorFile === null) {
9835                         throw "You should save your file before using this action";
9836                 }
9837                 
9838                 // locate real image path
9839                 var realImgPath = file.locateFile(editorFile, imgPath);
9840                 if (realImgPath === null) {
9841                         throw "Can't find " + imgPath + ' file';
9842                 }
9843                 
9844                 file.read(realImgPath, function(err, content) {
9845                         if (err) {
9846                                 throw 'Unable to read ' + realImgPath + ': ' + err;
9847                         }
9848                         
9849                         var b64 = require('base64').encode(String(content));
9850                         if (!b64) {
9851                                 throw "Can't encode file content to base64";
9852                         }
9853                         
9854                         b64 = 'data:' + (actionUtils.mimeTypes[String(file.getExt(realImgPath))] || defaultMimeType) +
9855                                 ';base64,' + b64;
9856                                 
9857                         editor.replaceContent('$0' + b64, pos, pos + imgPath.length);
9858                 });
9859                 
9860                 
9861                 return true;
9862         }
9863 
9864         /**
9865          * Decodes base64 string back to file.
9866          * @param {IEmmetEditor} editor
9867          * @param {String} data Base64-encoded file content
9868          * @param {Number} pos Caret position where image is located in the editor
9869          */
9870         function decodeFromBase64(editor, data, pos) {
9871                 // ask user to enter path to file
9872                 var filePath = String(editor.prompt('Enter path to file (absolute or relative)'));
9873                 if (!filePath)
9874                         return false;
9875                         
9876                 var file = require('file');
9877                 var absPath = file.createPath(editor.getFilePath(), filePath);
9878                 if (!absPath) {
9879                         throw "Can't save file";
9880                 }
9881                 
9882                 file.save(absPath, require('base64').decode( data.replace(/^data\:.+?;.+?,/, '') ));
9883                 editor.replaceContent('$0' + filePath, pos, pos + data.length);
9884                 return true;
9885         }
9886 });
9887 /**
9888  * Automatically updates image size attributes in HTML's &lt;img&gt; element or
9889  * CSS rule
9890  * @param {Function} require
9891  * @param {Underscore} _
9892  * @constructor
9893  * @memberOf __updateImageSizeAction
9894  */
9895 emmet.exec(function(require, _) {
9896         /**
9897          * Updates image size of &lt;img src=""&gt; tag
9898          * @param {IEmmetEditor} editor
9899          */
9900         function updateImageSizeHTML(editor) {
9901                 var offset = editor.getCaretPos();
9902                 
9903                 // find tag from current caret position
9904                 var info = require('editorUtils').outputInfo(editor);
9905                 var xmlElem = require('xmlEditTree').parseFromPosition(info.content, offset, true);
9906                 if (xmlElem && (xmlElem.name() || '').toLowerCase() == 'img') {
9907                         getImageSizeForSource(editor, xmlElem.value('src'), function(size) {
9908                                 if (size) {
9909                                         var compoundData = xmlElem.range(true);
9910                                         xmlElem.value('width', size.width);
9911                                         xmlElem.value('height', size.height, xmlElem.indexOf('width') + 1);
9912                                         
9913                                         require('actionUtils').compoundUpdate(editor, _.extend(compoundData, {
9914                                                 data: xmlElem.toString(),
9915                                                 caret: offset
9916                                         }));
9917                                 }
9918                         });
9919                 }
9920         }
9921         
9922         /**
9923          * Updates image size of CSS property
9924          * @param {IEmmetEditor} editor
9925          */
9926         function updateImageSizeCSS(editor) {
9927                 var offset = editor.getCaretPos();
9928                 
9929                 // find tag from current caret position
9930                 var info = require('editorUtils').outputInfo(editor);
9931                 var cssRule = require('cssEditTree').parseFromPosition(info.content, offset, true);
9932                 if (cssRule) {
9933                         // check if there is property with image under caret
9934                         var prop = cssRule.itemFromPosition(offset, true), m;
9935                         if (prop && (m = /url\((["']?)(.+?)\1\)/i.exec(prop.value() || ''))) {
9936                                 getImageSizeForSource(editor, m[2], function(size) {
9937                                         if (size) {
9938                                                 var compoundData = cssRule.range(true);
9939                                                 cssRule.value('width', size.width + 'px');
9940                                                 cssRule.value('height', size.height + 'px', cssRule.indexOf('width') + 1);
9941                                                 
9942                                                 require('actionUtils').compoundUpdate(editor, _.extend(compoundData, {
9943                                                         data: cssRule.toString(),
9944                                                         caret: offset
9945                                                 }));
9946                                         }
9947                                 });
9948                         }
9949                 }
9950         }
9951         
9952         /**
9953          * Returns image dimensions for source
9954          * @param {IEmmetEditor} editor
9955          * @param {String} src Image source (path or data:url)
9956          */
9957         function getImageSizeForSource(editor, src, callback) {
9958                 var fileContent;
9959                 var au = require('actionUtils');
9960                 if (src) {
9961                         // check if it is data:url
9962                         if (/^data:/.test(src)) {
9963                                 fileContent = require('base64').decode( src.replace(/^data\:.+?;.+?,/, '') );
9964                                 return callback(au.getImageSize(fileContent));
9965                         }
9966                         
9967                         var file = require('file');
9968                         var absPath = file.locateFile(editor.getFilePath(), src);
9969                         if (absPath === null) {
9970                                 throw "Can't find " + src + ' file';
9971                         }
9972                         
9973                         file.read(absPath, function(err, content) {
9974                                 if (err) {
9975                                         throw 'Unable to read ' + absPath + ': ' + err;
9976                                 }
9977                                 
9978                                 content = String(content);
9979                                 callback(au.getImageSize(content));
9980                         });
9981                 }
9982         }
9983         
9984         require('actions').add('update_image_size', function(editor) {
9985                 // this action will definitely won’t work in SASS dialect,
9986                 // but may work in SCSS or LESS
9987                 if (_.include(['css', 'less', 'scss'], String(editor.getSyntax()))) {
9988                         updateImageSizeCSS(editor);
9989                 } else {
9990                         updateImageSizeHTML(editor);
9991                 }
9992                 
9993                 return true;
9994         });
9995 });/**
9996  * Resolver for fast CSS typing. Handles abbreviations with the following 
9997  * notation:<br>
9998  * 
9999  * <code>(-vendor prefix)?property(value)*(!)?</code>
10000  * 
10001  * <br><br>
10002  * <b>Abbreviation handling</b><br>
10003  * 
10004  * By default, Emmet searches for matching snippet definition for provided abbreviation.
10005  * If snippet wasn't found, Emmet automatically generates element with 
10006  * abbreviation's name. For example, <code>foo</code> abbreviation will generate
10007  * <code>&lt;foo&gt;&lt;/foo&gt;</code> output.
10008  * <br><br>
10009  * This module will capture all expanded properties and upgrade them with values, 
10010  * vendor prefixes and !important declarations. All unmatched abbreviations will 
10011  * be automatically transformed into <code>property-name: ${1}</code> snippets. 
10012  * 
10013  * <b>Vendor prefixes<b><br>
10014  * 
10015  * If CSS-property is preceded with dash, resolver should output property with
10016  * all <i>known</i> vendor prefixes. For example, if <code>brad</code> 
10017  * abbreviation generates <code>border-radius: ${value};</code> snippet,
10018  * the <code>-brad</code> abbreviation should generate:
10019  * <pre><code>
10020  * -webkit-border-radius: ${value};
10021  * -moz-border-radius: ${value};
10022  * border-radius: ${value};
10023  * </code></pre>
10024  * Note that <i>o</i> and <i>ms</i> prefixes are omitted since Opera and IE 
10025  * supports unprefixed property.<br><br>
10026  * 
10027  * Users can also provide an explicit list of one-character prefixes for any
10028  * CSS property. For example, <code>-wm-float</code> will produce
10029  * 
10030  * <pre><code>
10031  * -webkit-float: ${1};
10032  * -moz-float: ${1};
10033  * float: ${1};
10034  * </code></pre>
10035  * 
10036  * Although this example looks pointless, users can use this feature to write
10037  * cutting-edge properties implemented by browser vendors recently.  
10038  * 
10039  * @constructor
10040  * @memberOf __cssResolverDefine
10041  * @param {Function} require
10042  * @param {Underscore} _
10043  */
10044 emmet.define('cssResolver', function(require, _) {
10045         /** Back-reference to module */
10046         var module = null;
10047         
10048         var prefixObj = {
10049                 /** Real vendor prefix name */
10050                 prefix: 'emmet',
10051                 
10052                 /** 
10053                  * Indicates this prefix is obsolete and should't be used when user 
10054                  * wants to generate all-prefixed properties
10055                  */
10056                 obsolete: false,
10057                 
10058                 /**
10059                  * Returns prefixed CSS property name
10060                  * @param {String} name Unprefixed CSS property
10061                  */
10062                 transformName: function(name) {
10063                         return '-' + this.prefix + '-' + name;
10064                 },
10065                 
10066                 /**
10067                  * List of unprefixed CSS properties that supported by 
10068                  * current prefix. This list is used to generate all-prefixed property
10069                  * @returns {Array} 
10070                  */
10071                 properties: function() {
10072                         return getProperties('css.' + this.prefix + 'Properties') || [];
10073                 },
10074                 
10075                 /**
10076                  * Check if given property is supported by current prefix
10077                  * @param name
10078                  */
10079                 supports: function(name) {
10080                         return _.include(this.properties(), name);
10081                 }
10082         };
10083         
10084         
10085         /** 
10086          * List of registered one-character prefixes. Key is a one-character prefix, 
10087          * value is an <code>prefixObj</code> object
10088          */
10089         var vendorPrefixes = {};
10090         
10091         var defaultValue = '${1};';
10092         
10093         // XXX module preferences
10094         var prefs = require('preferences');
10095         prefs.define('css.valueSeparator', ': ',
10096                         'Defines a symbol that should be placed between CSS property and ' 
10097                         + 'value when expanding CSS abbreviations.');
10098         prefs.define('css.propertyEnd', ';',
10099                         'Defines a symbol that should be placed at the end of CSS property  ' 
10100                         + 'when expanding CSS abbreviations.');
10101         
10102         prefs.define('stylus.valueSeparator', ' ',
10103                         'Defines a symbol that should be placed between CSS property and ' 
10104                         + 'value when expanding CSS abbreviations in Stylus dialect.');
10105         prefs.define('stylus.propertyEnd', '',
10106                         'Defines a symbol that should be placed at the end of CSS property  ' 
10107                         + 'when expanding CSS abbreviations in Stylus dialect.');
10108         
10109         prefs.define('sass.propertyEnd', '',
10110                         'Defines a symbol that should be placed at the end of CSS property  ' 
10111                         + 'when expanding CSS abbreviations in SASS dialect.');
10112 
10113         prefs.define('css.syntaxes', 'css, less, sass, scss, stylus, styl',
10114                         'List of syntaxes that should be treated as CSS dialects.');
10115         
10116         prefs.define('css.autoInsertVendorPrefixes', true,
10117                         'Automatically generate vendor-prefixed copies of expanded CSS ' 
10118                         + 'property. By default, Emmet will generate vendor-prefixed '
10119                         + 'properties only when you put dash before abbreviation ' 
10120                         + '(e.g. <code>-bxsh</code>). With this option enabled, you don’t ' 
10121                         + 'need dashes before abbreviations: Emmet will produce ' 
10122                         + 'vendor-prefixed properties for you.');
10123         
10124         var descTemplate = _.template('A comma-separated list of CSS properties that may have ' 
10125                 + '<code><%= vendor %></code> vendor prefix. This list is used to generate '
10126                 + 'a list of prefixed properties when expanding <code>-property</code> '
10127                 + 'abbreviations. Empty list means that all possible CSS values may ' 
10128                 + 'have <code><%= vendor %></code> prefix.');
10129         
10130         var descAddonTemplate = _.template('A comma-separated list of <em>additional</em> CSS properties ' 
10131                         + 'for <code>css.<%= vendor %>Preperties</code> preference. ' 
10132                         + 'You should use this list if you want to add or remove a few CSS ' 
10133                         + 'properties to original set. To add a new property, simply write its name, '
10134                         + 'to remove it, precede property with hyphen.<br>'
10135                         + 'For example, to add <em>foo</em> property and remove <em>border-radius</em> one, '
10136                         + 'the preference value will look like this: <code>foo, -border-radius</code>.');
10137         
10138         // properties list is created from cssFeatures.html file
10139         var props = {
10140                 'webkit': 'animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-clip, background-composite, background-origin, background-size, border-fit, border-horizontal-spacing, border-image, border-vertical-spacing, box-align, box-direction, box-flex, box-flex-group, box-lines, box-ordinal-group, box-orient, box-pack, box-reflect, box-shadow, color-correction, column-break-after, column-break-before, column-break-inside, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-span, column-width, dashboard-region, font-smoothing, highlight, hyphenate-character, hyphenate-limit-after, hyphenate-limit-before, hyphens, line-box-contain, line-break, line-clamp, locale, margin-before-collapse, margin-after-collapse, marquee-direction, marquee-increment, marquee-repetition, marquee-style, mask-attachment, mask-box-image, mask-box-image-outset, mask-box-image-repeat, mask-box-image-slice, mask-box-image-source, mask-box-image-width, mask-clip, mask-composite, mask-image, mask-origin, mask-position, mask-repeat, mask-size, nbsp-mode, perspective, perspective-origin, rtl-ordering, text-combine, text-decorations-in-effect, text-emphasis-color, text-emphasis-position, text-emphasis-style, text-fill-color, text-orientation, text-security, text-stroke-color, text-stroke-width, transform, transition, transform-origin, transform-style, transition-delay, transition-duration, transition-property, transition-timing-function, user-drag, user-modify, user-select, writing-mode, svg-shadow, box-sizing, border-radius',
10141                 'moz': 'animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, appearance, backface-visibility, background-inline-policy, binding, border-bottom-colors, border-image, border-left-colors, border-right-colors, border-top-colors, box-align, box-direction, box-flex, box-ordinal-group, box-orient, box-pack, box-shadow, box-sizing, column-count, column-gap, column-rule-color, column-rule-style, column-rule-width, column-width, float-edge, font-feature-settings, font-language-override, force-broken-image-icon, hyphens, image-region, orient, outline-radius-bottomleft, outline-radius-bottomright, outline-radius-topleft, outline-radius-topright, perspective, perspective-origin, stack-sizing, tab-size, text-blink, text-decoration-color, text-decoration-line, text-decoration-style, text-size-adjust, transform, transform-origin, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-focus, user-input, user-modify, user-select, window-shadow, background-clip, border-radius',
10142                 'ms': 'accelerator, backface-visibility, background-position-x, background-position-y, behavior, block-progression, box-align, box-direction, box-flex, box-line-progression, box-lines, box-ordinal-group, box-orient, box-pack, content-zoom-boundary, content-zoom-boundary-max, content-zoom-boundary-min, content-zoom-chaining, content-zoom-snap, content-zoom-snap-points, content-zoom-snap-type, content-zooming, filter, flow-from, flow-into, font-feature-settings, grid-column, grid-column-align, grid-column-span, grid-columns, grid-layer, grid-row, grid-row-align, grid-row-span, grid-rows, high-contrast-adjust, hyphenate-limit-chars, hyphenate-limit-lines, hyphenate-limit-zone, hyphens, ime-mode, interpolation-mode, layout-flow, layout-grid, layout-grid-char, layout-grid-line, layout-grid-mode, layout-grid-type, line-break, overflow-style, perspective, perspective-origin, perspective-origin-x, perspective-origin-y, scroll-boundary, scroll-boundary-bottom, scroll-boundary-left, scroll-boundary-right, scroll-boundary-top, scroll-chaining, scroll-rails, scroll-snap-points-x, scroll-snap-points-y, scroll-snap-type, scroll-snap-x, scroll-snap-y, scrollbar-arrow-color, scrollbar-base-color, scrollbar-darkshadow-color, scrollbar-face-color, scrollbar-highlight-color, scrollbar-shadow-color, scrollbar-track-color, text-align-last, text-autospace, text-justify, text-kashida-space, text-overflow, text-size-adjust, text-underline-position, touch-action, transform, transform-origin, transform-origin-x, transform-origin-y, transform-origin-z, transform-style, transition, transition-delay, transition-duration, transition-property, transition-timing-function, user-select, word-break, word-wrap, wrap-flow, wrap-margin, wrap-through, writing-mode',
10143                 'o': 'dashboard-region, animation, animation-delay, animation-direction, animation-duration, animation-fill-mode, animation-iteration-count, animation-name, animation-play-state, animation-timing-function, border-image, link, link-source, object-fit, object-position, tab-size, table-baseline, transform, transform-origin, transition, transition-delay, transition-duration, transition-property, transition-timing-function, accesskey, input-format, input-required, marquee-dir, marquee-loop, marquee-speed, marquee-style'
10144         };
10145         
10146         _.each(props, function(v, k) {
10147                 prefs.define('css.' + k + 'Properties', v, descTemplate({vendor: k}));
10148                 prefs.define('css.' + k + 'PropertiesAddon', '', descAddonTemplate({vendor: k}));
10149         });
10150         
10151         prefs.define('css.unitlessProperties', 'z-index, line-height, opacity, font-weight, zoom', 
10152                         'The list of properties whose values ​​must not contain units.');
10153         
10154         prefs.define('css.intUnit', 'px', 'Default unit for integer values');
10155         prefs.define('css.floatUnit', 'em', 'Default unit for float values');
10156         
10157         prefs.define('css.keywords', 'auto, inherit', 
10158                         'A comma-separated list of valid keywords that can be used in CSS abbreviations.');
10159         
10160         prefs.define('css.keywordAliases', 'a:auto, i:inherit, s:solid, da:dashed, do:dotted, t:transparent', 
10161                         'A comma-separated list of keyword aliases, used in CSS abbreviation. '
10162                         + 'Each alias should be defined as <code>alias:keyword_name</code>.');
10163         
10164         prefs.define('css.unitAliases', 'e:em, p:%, x:ex, r:rem', 
10165                         'A comma-separated list of unit aliases, used in CSS abbreviation. '
10166                         + 'Each alias should be defined as <code>alias:unit_value</code>.');
10167         
10168         prefs.define('css.color.short', true, 
10169                         'Should color values like <code>#ffffff</code> be shortened to '
10170                         + '<code>#fff</code> after abbreviation with color was expanded.');
10171         
10172         prefs.define('css.color.case', 'keep', 
10173                         'Letter case of color values generated by abbreviations with color '
10174                         + '(like <code>c#0</code>). Possible values are <code>upper</code>, '
10175                         + '<code>lower</code> and <code>keep</code>.');
10176         
10177         prefs.define('css.fuzzySearch', true, 
10178                         'Enable fuzzy search among CSS snippet names. When enabled, every ' 
10179                         + '<em>unknown</em> snippet will be scored against available snippet '
10180                         + 'names (not values or CSS properties!). The match with best score '
10181                         + 'will be used to resolve snippet value. For example, with this ' 
10182                         + 'preference enabled, the following abbreviations are equal: '
10183                         + '<code>ov:h</code> == <code>ov-h</code> == <code>o-h</code> == '
10184                         + '<code>oh</code>');
10185         
10186         prefs.define('css.fuzzySearchMinScore', 0.3, 
10187                         'The minium score (from 0 to 1) that fuzzy-matched abbreviation should ' 
10188                         + 'achive. Lower values may produce many false-positive matches, '
10189                         + 'higher values may reduce possible matches.');
10190         
10191         prefs.define('css.alignVendor', false, 
10192                         'If set to <code>true</code>, all generated vendor-prefixed properties ' 
10193                         + 'will be aligned by real property name.');
10194         
10195         
10196         function isNumeric(ch) {
10197                 var code = ch && ch.charCodeAt(0);
10198                 return (ch && ch == '.' || (code > 47 && code < 58));
10199         }
10200         
10201         /**
10202          * Check if provided snippet contains only one CSS property and value.
10203          * @param {String} snippet
10204          * @returns {Boolean}
10205          */
10206         function isSingleProperty(snippet) {
10207                 var utils = require('utils');
10208                 snippet = utils.trim(snippet);
10209                 
10210                 // check if it doesn't contain a comment and a newline
10211                 if (~snippet.indexOf('/*') || /[\n\r]/.test(snippet)) {
10212                         return false;
10213                 }
10214                 
10215                 // check if it's a valid snippet definition
10216                 if (!/^[a-z0-9\-]+\s*\:/i.test(snippet)) {
10217                         return false;
10218                 }
10219                 
10220                 snippet = require('tabStops').processText(snippet, {
10221                         replaceCarets: true,
10222                         tabstop: function() {
10223                                 return 'value';
10224                         }
10225                 });
10226                 
10227                 return snippet.split(':').length == 2;
10228         }
10229         
10230         /**
10231          * Normalizes abbreviated value to final CSS one
10232          * @param {String} value
10233          * @returns {String}
10234          */
10235         function normalizeValue(value) {
10236                 if (value.charAt(0) == '-' && !/^\-[\.\d]/.test(value)) {
10237                         value = value.replace(/^\-+/, '');
10238                 }
10239                 
10240                 if (value.charAt(0) == '#') {
10241                         return normalizeHexColor(value);
10242                 }
10243                 
10244                 return getKeyword(value);
10245         }
10246         
10247         function normalizeHexColor(value) {
10248                 var hex = value.replace(/^#+/, '') || '0';
10249                 if (hex.toLowerCase() == 't') {
10250                         return 'transparent';
10251                 }
10252                 
10253                 var repeat = require('utils').repeatString;
10254                 var color = null;
10255                 switch (hex.length) {
10256                         case 1:
10257                                 color = repeat(hex, 6);
10258                                 break;
10259                         case 2:
10260                                 color = repeat(hex, 3);
10261                                 break;
10262                         case 3:
10263                                 color = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
10264                                 break;
10265                         case 4:
10266                                 color = hex + hex.substr(0, 2);
10267                                 break;
10268                         case 5:
10269                                 color = hex + hex.charAt(0);
10270                                 break;
10271                         default:
10272                                 color = hex.substr(0, 6);
10273                 }
10274                 
10275                 // color must be shortened?
10276                 if (prefs.get('css.color.short')) {
10277                         var p = color.split('');
10278                         if (p[0] == p[1] && p[2] == p[3] && p[4] == p[5]) {
10279                                 color = p[0] + p[2] + p[4];
10280                         }
10281                 }
10282                 
10283                 // should transform case?
10284                 switch (prefs.get('css.color.case')) {
10285                         case 'upper':
10286                                 color = color.toUpperCase();
10287                                 break;
10288                         case 'lower':
10289                                 color = color.toLowerCase();
10290                                 break;
10291                 }
10292                 
10293                 return '#' + color;
10294         }
10295         
10296         function getKeyword(name) {
10297                 var aliases = prefs.getDict('css.keywordAliases');
10298                 return name in aliases ? aliases[name] : name;
10299         }
10300         
10301         function getUnit(name) {
10302                 var aliases = prefs.getDict('css.unitAliases');
10303                 return name in aliases ? aliases[name] : name;
10304         }
10305         
10306         function isValidKeyword(keyword) {
10307                 return _.include(prefs.getArray('css.keywords'), getKeyword(keyword));
10308         }
10309         
10310         /**
10311          * Check if passed CSS property support specified vendor prefix 
10312          * @param {String} property
10313          * @param {String} prefix
10314          */
10315         function hasPrefix(property, prefix) {
10316                 var info = vendorPrefixes[prefix];
10317                 
10318                 if (!info)
10319                         info = _.find(vendorPrefixes, function(data) {
10320                                 return data.prefix == prefix;
10321                         });
10322                 
10323                 return info && info.supports(property);
10324         }
10325         
10326         /**
10327          * Search for a list of supported prefixes for CSS property. This list
10328          * is used to generate all-prefixed snippet
10329          * @param {String} property CSS property name
10330          * @returns {Array}
10331          */
10332         function findPrefixes(property, noAutofill) {
10333                 var result = [];
10334                 _.each(vendorPrefixes, function(obj, prefix) {
10335                         if (hasPrefix(property, prefix)) {
10336                                 result.push(prefix);
10337                         }
10338                 });
10339                 
10340                 if (!result.length && !noAutofill) {
10341                         // add all non-obsolete prefixes
10342                         _.each(vendorPrefixes, function(obj, prefix) {
10343                                 if (!obj.obsolete)
10344                                         result.push(prefix);
10345                         });
10346                 }
10347                 
10348                 return result;
10349         }
10350         
10351         function addPrefix(name, obj) {
10352                 if (_.isString(obj))
10353                         obj = {prefix: obj};
10354                 
10355                 vendorPrefixes[name] = _.extend({}, prefixObj, obj);
10356         }
10357         
10358         function getSyntaxPreference(name, syntax) {
10359                 if (syntax) {
10360                         // hacky alias for Stylus dialect
10361                         if (syntax == 'styl') {
10362                                 syntax = 'stylus';
10363                         }
10364 
10365                         var val = prefs.get(syntax + '.' + name);
10366                         if (!_.isUndefined(val))
10367                                 return val;
10368                 }
10369                 
10370                 return prefs.get('css.' + name);
10371         }
10372         
10373         /**
10374          * Format CSS property according to current syntax dialect
10375          * @param {String} property
10376          * @param {String} syntax
10377          * @returns {String}
10378          */
10379         function formatProperty(property, syntax) {
10380                 var ix = property.indexOf(':');
10381                 property = property.substring(0, ix).replace(/\s+$/, '') 
10382                         + getSyntaxPreference('valueSeparator', syntax)
10383                         + require('utils').trim(property.substring(ix + 1));
10384                 
10385                 return property.replace(/\s*;\s*$/, getSyntaxPreference('propertyEnd', syntax));
10386         }
10387         
10388         /**
10389          * Transforms snippet value if required. For example, this transformation
10390          * may add <i>!important</i> declaration to CSS property
10391          * @param {String} snippet
10392          * @param {Boolean} isImportant
10393          * @returns {String}
10394          */
10395         function transformSnippet(snippet, isImportant, syntax) {
10396                 if (!_.isString(snippet))
10397                         snippet = snippet.data;
10398                 
10399                 if (!isSingleProperty(snippet))
10400                         return snippet;
10401                 
10402                 if (isImportant) {
10403                         if (~snippet.indexOf(';')) {
10404                                 snippet = snippet.split(';').join(' !important;');
10405                         } else {
10406                                 snippet += ' !important';
10407                         }
10408                 }
10409                 
10410                 return formatProperty(snippet, syntax);
10411         }
10412         
10413         function getProperties(key) {
10414                 var list = prefs.getArray(key);
10415                 _.each(prefs.getArray(key + 'Addon'), function(prop) {
10416                         if (prop.charAt(0) == '-') {
10417                                 list = _.without(list, prop.substr(1));
10418                         } else {
10419                                 if (prop.charAt(0) == '+')
10420                                         prop = prop.substr(1);
10421                                 
10422                                 list.push(prop);
10423                         }
10424                 });
10425                 
10426                 return list;
10427         }
10428         
10429         
10430         // TODO refactor, this looks awkward now
10431         addPrefix('w', {
10432                 prefix: 'webkit'
10433         });
10434         addPrefix('m', {
10435                 prefix: 'moz'
10436         });
10437         addPrefix('s', {
10438                 prefix: 'ms'
10439         });
10440         addPrefix('o', {
10441                 prefix: 'o'
10442         });
10443         
10444         /**
10445          * XXX register resolver
10446          * @param {TreeNode} node
10447          * @param {String} syntax
10448          */
10449         require('resources').addResolver(function(node, syntax) {
10450                 var cssSyntaxes = prefs.getArray('css.syntaxes');
10451                 if (_.include(cssSyntaxes, syntax) && node.isElement()) {
10452                         return module.expandToSnippet(node.abbreviation, syntax);
10453                 }
10454                 
10455                 return null;
10456         });
10457         
10458         var ea = require('expandAbbreviation');
10459         /**
10460          * For CSS-like syntaxes, we need to handle a special use case. Some editors
10461          * (like Sublime Text 2) may insert semicolons automatically when user types
10462          * abbreviation. After expansion, user receives a double semicolon. This
10463          * handler automatically removes semicolon from generated content in such cases.
10464          * @param {IEmmetEditor} editor
10465          * @param {String} syntax
10466          * @param {String} profile
10467          */
10468         ea.addHandler(function(editor, syntax, profile) {
10469                 var cssSyntaxes = prefs.getArray('css.syntaxes');
10470                 if (!_.include(cssSyntaxes, syntax)) {
10471                         return false;
10472                 }
10473                 
10474                 var caretPos = editor.getSelectionRange().end;
10475                 var abbr = ea.findAbbreviation(editor);
10476                         
10477                 if (abbr) {
10478                         var content = emmet.expandAbbreviation(abbr, syntax, profile);
10479                         if (content) {
10480                                 var replaceFrom = caretPos - abbr.length;
10481                                 var replaceTo = caretPos;
10482                                 if (editor.getContent().charAt(caretPos) == ';' && content.charAt(content.length - 1) == ';') {
10483                                         replaceTo++;
10484                                 }
10485                                 
10486                                 editor.replaceContent(content, replaceFrom, replaceTo);
10487                                 return true;
10488                         }
10489                 }
10490                 
10491                 return false;
10492         });
10493         
10494         return module = {
10495                 /**
10496                  * Adds vendor prefix
10497                  * @param {String} name One-character prefix name
10498                  * @param {Object} obj Object describing vendor prefix
10499                  * @memberOf cssResolver
10500                  */
10501                 addPrefix: addPrefix,
10502                 
10503                 /**
10504                  * Check if passed CSS property supports specified vendor prefix
10505                  * @param {String} property
10506                  * @param {String} prefix
10507                  */
10508                 supportsPrefix: hasPrefix,
10509                 
10510                 /**
10511                  * Returns prefixed version of passed CSS property, only if this
10512                  * property supports such prefix
10513                  * @param {String} property
10514                  * @param {String} prefix
10515                  * @returns
10516                  */
10517                 prefixed: function(property, prefix) {
10518                         return hasPrefix(property, prefix) 
10519                                 ? '-' + prefix + '-' + property 
10520                                 : property;
10521                 },
10522                 
10523                 /**
10524                  * Returns list of all registered vendor prefixes
10525                  * @returns {Array}
10526                  */
10527                 listPrefixes: function() {
10528                         return _.map(vendorPrefixes, function(obj) {
10529                                 return obj.prefix;
10530                         });
10531                 },
10532                 
10533                 /**
10534                  * Returns object describing vendor prefix
10535                  * @param {String} name
10536                  * @returns {Object}
10537                  */
10538                 getPrefix: function(name) {
10539                         return vendorPrefixes[name];
10540                 },
10541                 
10542                 /**
10543                  * Removes prefix object
10544                  * @param {String} name
10545                  */
10546                 removePrefix: function(name) {
10547                         if (name in vendorPrefixes)
10548                                 delete vendorPrefixes[name];
10549                 },
10550                 
10551                 /**
10552                  * Extract vendor prefixes from abbreviation
10553                  * @param {String} abbr
10554                  * @returns {Object} Object containing array of prefixes and clean 
10555                  * abbreviation name
10556                  */
10557                 extractPrefixes: function(abbr) {
10558                         if (abbr.charAt(0) != '-') {
10559                                 return {
10560                                         property: abbr,
10561                                         prefixes: null
10562                                 };
10563                         }
10564                         
10565                         // abbreviation may either contain sequence of one-character prefixes
10566                         // or just dash, meaning that user wants to produce all possible
10567                         // prefixed properties
10568                         var i = 1, il = abbr.length, ch;
10569                         var prefixes = [];
10570                         
10571                         while (i < il) {
10572                                 ch = abbr.charAt(i);
10573                                 if (ch == '-') {
10574                                         // end-sequence character found, stop searching
10575                                         i++;
10576                                         break;
10577                                 }
10578                                 
10579                                 if (ch in vendorPrefixes) {
10580                                         prefixes.push(ch);
10581                                 } else {
10582                                         // no prefix found, meaning user want to produce all
10583                                         // vendor-prefixed properties
10584                                         prefixes.length = 0;
10585                                         i = 1;
10586                                         break;
10587                                 }
10588                                 
10589                                 i++;
10590                         }
10591                         
10592                         // reached end of abbreviation and no property name left
10593                         if (i == il -1) {
10594                                 i = 1;
10595                                 prefixes.length = 1;
10596                         }
10597                         
10598                         return {
10599                                 property: abbr.substring(i),
10600                                 prefixes: prefixes.length ? prefixes : 'all'
10601                         };
10602                 },
10603                 
10604                 /**
10605                  * Search for value substring in abbreviation
10606                  * @param {String} abbr
10607                  * @returns {String} Value substring
10608                  */
10609                 findValuesInAbbreviation: function(abbr, syntax) {
10610                         syntax = syntax || 'css';
10611                         
10612                         var i = 0, il = abbr.length, value = '', ch;
10613                         while (i < il) {
10614                                 ch = abbr.charAt(i);
10615                                 if (isNumeric(ch) || ch == '#' || (ch == '-' && isNumeric(abbr.charAt(i + 1)))) {
10616                                         value = abbr.substring(i);
10617                                         break;
10618                                 }
10619                                 
10620                                 i++;
10621                         }
10622                         
10623                         // try to find keywords in abbreviation
10624                         var property = abbr.substring(0, abbr.length - value.length);
10625                         var res = require('resources');
10626                         var keywords = [];
10627                         // try to extract some commonly-used properties
10628                         while (~property.indexOf('-') && !res.findSnippet(syntax, property)) {
10629                                 var parts = property.split('-');
10630                                 var lastPart = parts.pop();
10631                                 if (!isValidKeyword(lastPart)) {
10632                                         break;
10633                                 }
10634                                 
10635                                 keywords.unshift(lastPart);
10636                                 property = parts.join('-');
10637                         }
10638                         
10639                         return keywords.join('-') + value;
10640                 },
10641                 
10642                 parseValues: function(str) {
10643                         /** @type StringStream */
10644                         var stream = require('stringStream').create(str);
10645                         var values = [];
10646                         var ch = null;
10647                         
10648                         while ((ch = stream.next())) {
10649                                 if (ch == '#') {
10650                                         stream.match(/^t|[0-9a-f]+/i, true);
10651                                         values.push(stream.current());
10652                                 } else if (ch == '-') {
10653                                         if (isValidKeyword(_.last(values)) || 
10654                                                         ( stream.start && isNumeric(str.charAt(stream.start - 1)) )
10655                                                 ) {
10656                                                 stream.start = stream.pos;
10657                                         }
10658                                         
10659                                         stream.match(/^\-?[0-9]*(\.[0-9]+)?[a-z%\.]*/, true);
10660                                         values.push(stream.current());
10661                                 } else {
10662                                         stream.match(/^[0-9]*(\.[0-9]*)?[a-z%]*/, true);
10663                                         values.push(stream.current());
10664                                 }
10665                                 
10666                                 stream.start = stream.pos;
10667                         }
10668                         
10669                         return _.map(_.compact(values), normalizeValue);
10670                 },
10671                 
10672                 /**
10673                  * Extracts values from abbreviation
10674                  * @param {String} abbr
10675                  * @returns {Object} Object containing array of values and clean 
10676                  * abbreviation name
10677                  */
10678                 extractValues: function(abbr) {
10679                         // search for value start
10680                         var abbrValues = this.findValuesInAbbreviation(abbr);
10681                         if (!abbrValues) {
10682                                 return {
10683                                         property: abbr,
10684                                         values: null
10685                                 };
10686                         }
10687                         
10688                         return {
10689                                 property: abbr.substring(0, abbr.length - abbrValues.length).replace(/-$/, ''),
10690                                 values: this.parseValues(abbrValues)
10691                         };
10692                 },
10693                 
10694                 /**
10695                  * Normalizes value, defined in abbreviation.
10696                  * @param {String} value
10697                  * @param {String} property
10698                  * @returns {String}
10699                  */
10700                 normalizeValue: function(value, property) {
10701                         property = (property || '').toLowerCase();
10702                         var unitlessProps = prefs.getArray('css.unitlessProperties');
10703                         return value.replace(/^(\-?[0-9\.]+)([a-z]*)$/, function(str, val, unit) {
10704                                 if (!unit && (val == '0' || _.include(unitlessProps, property)))
10705                                         return val;
10706                                 
10707                                 if (!unit)
10708                                         return val.replace(/\.$/, '') + prefs.get(~val.indexOf('.') ? 'css.floatUnit' : 'css.intUnit');
10709                                 
10710                                 return val + getUnit(unit);
10711                         });
10712                 },
10713                 
10714                 /**
10715                  * Expands abbreviation into a snippet
10716                  * @param {String} abbr Abbreviation name to expand
10717                  * @param {String} value Abbreviation value
10718                  * @param {String} syntax Currect syntax or dialect. Default is 'css'
10719                  * @returns {Object} Array of CSS properties and values or predefined
10720                  * snippet (string or element)
10721                  */
10722                 expand: function(abbr, value, syntax) {
10723                         syntax = syntax || 'css';
10724                         var resources = require('resources');
10725                         var autoInsertPrefixes = prefs.get('css.autoInsertVendorPrefixes');
10726                         
10727                         // check if snippet should be transformed to !important
10728                         var isImportant = /^(.+)\!$/.test(abbr);
10729                         if (isImportant) {
10730                                 abbr = RegExp.$1;
10731                         }
10732                         
10733                         // check if we have abbreviated resource
10734                         var snippet = resources.findSnippet(syntax, abbr);
10735                         if (snippet && !autoInsertPrefixes) {
10736                                 return transformSnippet(snippet, isImportant, syntax);
10737                         }
10738                         
10739                         // no abbreviated resource, parse abbreviation
10740                         var prefixData = this.extractPrefixes(abbr);
10741                         var valuesData = this.extractValues(prefixData.property);
10742                         var abbrData = _.extend(prefixData, valuesData);
10743                         
10744                         if (!snippet) {
10745                                 snippet = resources.findSnippet(syntax, abbrData.property);
10746                         } else {
10747                                 abbrData.values = null;
10748                         }
10749                         
10750                         if (!snippet && prefs.get('css.fuzzySearch')) {
10751                                 // let’s try fuzzy search
10752                                 snippet = resources.fuzzyFindSnippet(syntax, abbrData.property, parseFloat(prefs.get('css.fuzzySearchMinScore')));
10753                         }
10754                         
10755                         if (!snippet) {
10756                                 snippet = abbrData.property + ':' + defaultValue;
10757                         } else if (!_.isString(snippet)) {
10758                                 snippet = snippet.data;
10759                         }
10760                         
10761                         if (!isSingleProperty(snippet)) {
10762                                 return snippet;
10763                         }
10764                         
10765                         var snippetObj = this.splitSnippet(snippet);
10766                         var result = [];
10767                         if (!value && abbrData.values) {
10768                                 value = _.map(abbrData.values, function(val) {
10769                                         return this.normalizeValue(val, snippetObj.name);
10770                                 }, this).join(' ') + ';';
10771                         }
10772                         
10773                         snippetObj.value = value || snippetObj.value;
10774                         
10775                         var prefixes = abbrData.prefixes == 'all' || (!abbrData.prefixes && autoInsertPrefixes) 
10776                                 ? findPrefixes(snippetObj.name, autoInsertPrefixes && abbrData.prefixes != 'all')
10777                                 : abbrData.prefixes;
10778                                 
10779                                 
10780                         var names = [], propName;
10781                         _.each(prefixes, function(p) {
10782                                 if (p in vendorPrefixes) {
10783                                         propName = vendorPrefixes[p].transformName(snippetObj.name);
10784                                         names.push(propName);
10785                                         result.push(transformSnippet(propName + ':' + snippetObj.value,
10786                                                         isImportant, syntax));
10787                                 }
10788                         });
10789                         
10790                         // put the original property
10791                         result.push(transformSnippet(snippetObj.name + ':' + snippetObj.value, isImportant, syntax));
10792                         names.push(snippetObj.name);
10793                         
10794                         if (prefs.get('css.alignVendor')) {
10795                                 var pads = require('utils').getStringsPads(names);
10796                                 result = _.map(result, function(prop, i) {
10797                                         return pads[i] + prop;
10798                                 });
10799                         }
10800                         
10801                         return result;
10802                 },
10803                 
10804                 /**
10805                  * Same as <code>expand</code> method but transforms output into 
10806                  * Emmet snippet
10807                  * @param {String} abbr
10808                  * @param {String} syntax
10809                  * @returns {String}
10810                  */
10811                 expandToSnippet: function(abbr, syntax) {
10812                         var snippet = this.expand(abbr, null, syntax);
10813                         if (_.isArray(snippet)) {
10814                                 return snippet.join('\n');
10815                         }
10816                         
10817                         if (!_.isString(snippet))
10818                                 return snippet.data;
10819                         
10820                         return String(snippet);
10821                 },
10822                 
10823                 /**
10824                  * Split snippet into a CSS property-value pair
10825                  * @param {String} snippet
10826                  */
10827                 splitSnippet: function(snippet) {
10828                         var utils = require('utils');
10829                         snippet = utils.trim(snippet);
10830                         if (snippet.indexOf(':') == -1) {
10831                                 return {
10832                                         name: snippet,
10833                                         value: defaultValue
10834                                 };
10835                         }
10836                         
10837                         var pair = snippet.split(':');
10838                         
10839                         return {
10840                                 name: utils.trim(pair.shift()),
10841                                 // replace ${0} tabstop to produce valid vendor-prefixed values
10842                                 // where possible
10843                                 value: utils.trim(pair.join(':')).replace(/^(\$\{0\}|\$0)(\s*;?)$/, '${1}$2')
10844                         };
10845                 },
10846                 
10847                 getSyntaxPreference: getSyntaxPreference,
10848                 transformSnippet: transformSnippet
10849         };
10850 });
10851 /**
10852  * 'Expand Abbreviation' handler that parses gradient definition from under 
10853  * cursor and updates CSS rule with vendor-prefixed values.
10854  * 
10855  * @memberOf __cssGradientHandlerDefine
10856  * @param {Function} require
10857  * @param {Underscore} _
10858  */
10859 emmet.define('cssGradient', function(require, _) {
10860         var defaultLinearDirections = ['top', 'to bottom', '0deg'];
10861         /** Back-reference to current module */
10862         var module = null;
10863         
10864         var cssSyntaxes = ['css', 'less', 'sass', 'scss', 'stylus', 'styl'];
10865         
10866         var reDeg = /\d+deg/i;
10867         var reKeyword = /top|bottom|left|right/i;
10868         
10869         // XXX define preferences
10870         /** @type preferences */
10871         var prefs = require('preferences');
10872         prefs.define('css.gradient.prefixes', 'webkit, moz, o',
10873                         'A comma-separated list of vendor-prefixes for which values should ' 
10874                         + 'be generated.');
10875         
10876         prefs.define('css.gradient.oldWebkit', true,
10877                         'Generate gradient definition for old Webkit implementations');
10878         
10879         prefs.define('css.gradient.omitDefaultDirection', true,
10880                 'Do not output default direction definition in generated gradients.');
10881         
10882         prefs.define('css.gradient.defaultProperty', 'background-image',
10883                 'When gradient expanded outside CSS value context, it will produce '
10884                         + 'properties with this name.');
10885         
10886         prefs.define('css.gradient.fallback', false,
10887                         'With this option enabled, CSS gradient generator will produce '
10888                         + '<code>background-color</code> property with gradient first color '
10889                         + 'as fallback for old browsers.');
10890         
10891         function normalizeSpace(str) {
10892                 return require('utils').trim(str).replace(/\s+/g, ' ');
10893         }
10894         
10895         /**
10896          * Parses linear gradient definition
10897          * @param {String}
10898          */
10899         function parseLinearGradient(gradient) {
10900                 var direction = defaultLinearDirections[0];
10901                 
10902                 // extract tokens
10903                 /** @type StringStream */
10904                 var stream = require('stringStream').create(require('utils').trim(gradient));
10905                 var colorStops = [], ch;
10906                 while ((ch = stream.next())) {
10907                         if (stream.peek() == ',') {
10908                                 colorStops.push(stream.current());
10909                                 stream.next();
10910                                 stream.eatSpace();
10911                                 stream.start = stream.pos;
10912                         } else if (ch == '(') { // color definition, like 'rgb(0,0,0)'
10913                                 stream.skipTo(')');
10914                         }
10915                 }
10916                 
10917                 // add last token
10918                 colorStops.push(stream.current());
10919                 colorStops = _.compact(_.map(colorStops, normalizeSpace));
10920                 
10921                 if (!colorStops.length)
10922                         return null;
10923                 
10924                 // let's see if the first color stop is actually a direction
10925                 if (reDeg.test(colorStops[0]) || reKeyword.test(colorStops[0])) {
10926                         direction = colorStops.shift();
10927                 }
10928                 
10929                 return {
10930                         type: 'linear',
10931                         direction: direction,
10932                         colorStops: _.map(colorStops, parseColorStop)
10933                 };
10934         }
10935         
10936         /**
10937          * Parses color stop definition
10938          * @param {String} colorStop
10939          * @returns {Object}
10940          */
10941         function parseColorStop(colorStop) {
10942                 colorStop = normalizeSpace(colorStop);
10943                 
10944                 // find color declaration
10945                 // first, try complex color declaration, like rgb(0,0,0)
10946                 var color = null;
10947                 colorStop = colorStop.replace(/^(\w+\(.+?\))\s*/, function(str, c) {
10948                         color = c;
10949                         return '';
10950                 });
10951                 
10952                 if (!color) {
10953                         // try simple declaration, like yellow, #fco, #ffffff, etc.
10954                         var parts = colorStop.split(' ');
10955                         color = parts[0];
10956                         colorStop = parts[1] || '';
10957                 }
10958                 
10959                 var result = {
10960                         color: color
10961                 };
10962                 
10963                 if (colorStop) {
10964                         // there's position in color stop definition
10965                         colorStop.replace(/^(\-?[\d\.]+)([a-z%]+)?$/, function(str, pos, unit) {
10966                                 result.position = pos;
10967                                 if (~pos.indexOf('.')) {
10968                                         unit = '';
10969                                 } else if (!unit) {
10970                                         unit = '%';
10971                                 }
10972                                 
10973                                 if (unit)
10974                                         result.unit = unit;
10975                         });
10976                 }
10977                 
10978                 return result;
10979         }
10980         
10981         /**
10982          * Resolves property name (abbreviation): searches for snippet definition in 
10983          * 'resources' and returns new name of matched property
10984          */
10985         function resolvePropertyName(name, syntax) {
10986                 var res = require('resources');
10987                 var prefs = require('preferences');
10988                 var snippet = res.findSnippet(syntax, name);
10989                 
10990                 if (!snippet && prefs.get('css.fuzzySearch')) {
10991                         snippet = res.fuzzyFindSnippet(syntax, name, 
10992                                         parseFloat(prefs.get('css.fuzzySearchMinScore')));
10993                 }
10994                 
10995                 if (snippet) {
10996                         if (!_.isString(snippet)) {
10997                                 snippet = snippet.data;
10998                         }
10999                         
11000                         return require('cssResolver').splitSnippet(snippet).name;
11001                 }
11002         }
11003         
11004         /**
11005          * Fills-out implied positions in color-stops. This function is useful for
11006          * old Webkit gradient definitions
11007          */
11008         function fillImpliedPositions(colorStops) {
11009                 var from = 0;
11010                 
11011                 _.each(colorStops, function(cs, i) {
11012                         // make sure that first and last positions are defined
11013                         if (!i)
11014                                 return cs.position = cs.position || 0;
11015                         
11016                         if (i == colorStops.length - 1 && !('position' in cs))
11017                                 cs.position = 1;
11018                         
11019                         if ('position' in cs) {
11020                                 var start = colorStops[from].position || 0;
11021                                 var step = (cs.position - start) / (i - from);
11022                                 _.each(colorStops.slice(from, i), function(cs2, j) {
11023                                         cs2.position = start + step * j;
11024                                 });
11025                                 
11026                                 from = i;
11027                         }
11028                 });
11029         }
11030         
11031         /**
11032          * Returns textual version of direction expressed in degrees
11033          * @param {String} direction
11034          * @returns {String}
11035          */
11036         function textualDirection(direction) {
11037                 var angle = parseFloat(direction);
11038                 
11039                 if(!_.isNaN(angle)) {
11040                         switch(angle % 360) {
11041                                 case 0:   return 'left';
11042                                 case 90:  return 'bottom';
11043                                 case 180: return 'right';
11044                                 case 240: return 'top';
11045                         }
11046                 }
11047                 
11048                 return direction;
11049         }
11050         
11051         /**
11052          * Creates direction definition for old Webkit gradients
11053          * @param {String} direction
11054          * @returns {String}
11055          */
11056         function oldWebkitDirection(direction) {
11057                 direction = textualDirection(direction);
11058                 
11059                 if(reDeg.test(direction))
11060                         throw "The direction is an angle that cant be converted.";
11061                 
11062                 var v = function(pos) {
11063                         return ~direction.indexOf(pos) ? '100%' : '0';
11064                 };
11065                 
11066                 return v('right') + ' ' + v('bottom') + ', ' + v('left') + ' ' + v('top');
11067         }
11068         
11069         function getPrefixedNames(name) {
11070                 var prefixes = prefs.getArray('css.gradient.prefixes');
11071                 var names = prefixes 
11072                         ? _.map(prefixes, function(p) {
11073                                 return '-' + p + '-' + name;
11074                         }) 
11075                         : [];
11076                 
11077                 names.push(name);
11078                 
11079                 return names;
11080         }
11081         
11082         /**
11083          * Returns list of CSS properties with gradient
11084          * @param {Object} gradient
11085          * @param {String} propertyName Original CSS property name
11086          * @returns {Array}
11087          */
11088         function getPropertiesForGradient(gradient, propertyName) {
11089                 var props = [];
11090                 var css = require('cssResolver');
11091                 
11092                 if (prefs.get('css.gradient.fallback') && ~propertyName.toLowerCase().indexOf('background')) {
11093                         props.push({
11094                                 name: 'background-color',
11095                                 value: '${1:' + gradient.colorStops[0].color + '}'
11096                         });
11097                 }
11098                 
11099                 _.each(prefs.getArray('css.gradient.prefixes'), function(prefix) {
11100                         var name = css.prefixed(propertyName, prefix);
11101                         if (prefix == 'webkit' && prefs.get('css.gradient.oldWebkit')) {
11102                                 try {
11103                                         props.push({
11104                                                 name: name,
11105                                                 value: module.oldWebkitLinearGradient(gradient)
11106                                         });
11107                                 } catch(e) {}
11108                         }
11109                         
11110                         props.push({
11111                                 name: name,
11112                                 value: module.toString(gradient, prefix)
11113                         });
11114                 });
11115                 
11116                 return props.sort(function(a, b) {
11117                         return b.name.length - a.name.length;
11118                 });
11119         }
11120         
11121         /**
11122          * Pastes gradient definition into CSS rule with correct vendor-prefixes
11123          * @param {EditElement} property Matched CSS property
11124          * @param {Object} gradient Parsed gradient
11125          * @param {Range} valueRange If passed, only this range within property 
11126          * value will be replaced with gradient. Otherwise, full value will be 
11127          * replaced
11128          */
11129         function pasteGradient(property, gradient, valueRange) {
11130                 var rule = property.parent;
11131                 var utils = require('utils');
11132                 var alignVendor = require('preferences').get('css.alignVendor');
11133                 
11134                 // we may have aligned gradient definitions: find the smallest value
11135                 // separator
11136                 var sep = property.styleSeparator;
11137                 var before = property.styleBefore;
11138                 
11139                 // first, remove all properties within CSS rule with the same name and
11140                 // gradient definition
11141                 _.each(rule.getAll(getPrefixedNames(property.name())), function(item) {
11142                         if (item != property && /gradient/i.test(item.value())) {
11143                                 if (item.styleSeparator.length < sep.length) {
11144                                         sep = item.styleSeparator;
11145                                 }
11146                                 if (item.styleBefore.length < before.length) {
11147                                         before = item.styleBefore;
11148                                 }
11149                                 rule.remove(item);
11150                         }
11151                 });
11152                 
11153                 if (alignVendor) {
11154                         // update prefix
11155                         if (before != property.styleBefore) {
11156                                 var fullRange = property.fullRange();
11157                                 rule._updateSource(before, fullRange.start, fullRange.start + property.styleBefore.length);
11158                                 property.styleBefore = before;
11159                         }
11160                         
11161                         // update separator value
11162                         if (sep != property.styleSeparator) {
11163                                 rule._updateSource(sep, property.nameRange().end, property.valueRange().start);
11164                                 property.styleSeparator = sep;
11165                         }
11166                 }
11167                 
11168                 var value = property.value();
11169                 if (!valueRange)
11170                         valueRange = require('range').create(0, property.value());
11171                 
11172                 var val = function(v) {
11173                         return utils.replaceSubstring(value, v, valueRange);
11174                 };
11175                 
11176                 // put vanilla-clean gradient definition into current rule
11177                 property.value(val(module.toString(gradient)) + '${2}');
11178                 
11179                 // create list of properties to insert
11180                 var propsToInsert = getPropertiesForGradient(gradient, property.name());
11181                 
11182                 // align prefixed values
11183                 if (alignVendor) {
11184                         var values = _.pluck(propsToInsert, 'value');
11185                         var names = _.pluck(propsToInsert, 'name');
11186                         values.push(property.value());
11187                         names.push(property.name());
11188                         
11189                         var valuePads = utils.getStringsPads(_.map(values, function(v) {
11190                                 return v.substring(0, v.indexOf('('));
11191                         }));
11192                         
11193                         var namePads = utils.getStringsPads(names);
11194                         property.name(_.last(namePads) + property.name());
11195                         
11196                         _.each(propsToInsert, function(prop, i) {
11197                                 prop.name = namePads[i] + prop.name;
11198                                 prop.value = valuePads[i] + prop.value;
11199                         });
11200                         
11201                         property.value(_.last(valuePads) + property.value());
11202                 }
11203                 
11204                 // put vendor-prefixed definitions before current rule
11205                 _.each(propsToInsert, function(prop) {
11206                         rule.add(prop.name, prop.value, rule.indexOf(property));
11207                 });
11208         }
11209         
11210         /**
11211          * Search for gradient definition inside CSS property value
11212          */
11213         function findGradient(cssProp) {
11214                 var value = cssProp.value();
11215                 var gradient = null;
11216                 var matchedPart = _.find(cssProp.valueParts(), function(part) {
11217                         return gradient = module.parse(part.substring(value));
11218                 });
11219                 
11220                 if (matchedPart && gradient) {
11221                         return {
11222                                 gradient: gradient,
11223                                 valueRange: matchedPart
11224                         };
11225                 }
11226                 
11227                 return null;
11228         }
11229         
11230         /**
11231          * Tries to expand gradient outside CSS value 
11232          * @param {IEmmetEditor} editor
11233          * @param {String} syntax
11234          */
11235         function expandGradientOutsideValue(editor, syntax) {
11236                 var propertyName = prefs.get('css.gradient.defaultProperty');
11237                 
11238                 if (!propertyName)
11239                         return false;
11240                 
11241                 // assuming that gradient definition is written on new line,
11242                 // do a simplified parsing
11243                 var content = String(editor.getContent());
11244                 /** @type Range */
11245                 var lineRange = require('range').create(editor.getCurrentLineRange());
11246                 
11247                 // get line content and adjust range with padding
11248                 var line = lineRange.substring(content)
11249                         .replace(/^\s+/, function(pad) {
11250                                 lineRange.start += pad.length;
11251                                 return '';
11252                         })
11253                         .replace(/\s+$/, function(pad) {
11254                                 lineRange.end -= pad.length;
11255                                 return '';
11256                         });
11257                 
11258                 var css = require('cssResolver');
11259                 var gradient = module.parse(line);
11260                 if (gradient) {
11261                         var props = getPropertiesForGradient(gradient, propertyName);
11262                         props.push({
11263                                 name: propertyName,
11264                                 value: module.toString(gradient) + '${2}'
11265                         });
11266                         
11267                         var sep = css.getSyntaxPreference('valueSeparator', syntax);
11268                         var end = css.getSyntaxPreference('propertyEnd', syntax);
11269                         
11270                         if (require('preferences').get('css.alignVendor')) {
11271                                 var pads = require('utils').getStringsPads(_.map(props, function(prop) {
11272                                         return prop.value.substring(0, prop.value.indexOf('('));
11273                                 }));
11274                                 _.each(props, function(prop, i) {
11275                                         prop.value = pads[i] + prop.value;
11276                                 });
11277                         }
11278                         
11279                         props = _.map(props, function(item) {
11280                                 return item.name + sep + item.value + end;
11281                         });
11282                         
11283                         editor.replaceContent(props.join('\n'), lineRange.start, lineRange.end);
11284                         return true;
11285                 }
11286                 
11287                 return false;
11288         }
11289         
11290         /**
11291          * Search for gradient definition inside CSS value under cursor
11292          * @param {String} content
11293          * @param {Number} pos
11294          * @returns {Object}
11295          */
11296         function findGradientFromPosition(content, pos) {
11297                 var cssProp = null;
11298                 /** @type EditContainer */
11299                 var cssRule = require('cssEditTree').parseFromPosition(content, pos, true);
11300                 
11301                 if (cssRule) {
11302                         cssProp = cssRule.itemFromPosition(pos, true);
11303                         if (!cssProp) {
11304                                 // in case user just started writing CSS property
11305                                 // and didn't include semicolon–try another approach
11306                                 cssProp = _.find(cssRule.list(), function(elem) {
11307                                         return elem.range(true).end == pos;
11308                                 });
11309                         }
11310                 }
11311                 
11312                 return {
11313                         rule: cssRule,
11314                         property: cssProp
11315                 };
11316         }
11317         
11318         // XXX register expand abbreviation handler
11319         /**
11320          * @param {IEmmetEditor} editor
11321          * @param {String} syntax
11322          * @param {String} profile
11323          */
11324         require('expandAbbreviation').addHandler(function(editor, syntax, profile) {
11325                 var info = require('editorUtils').outputInfo(editor, syntax, profile);
11326                 if (!_.include(cssSyntaxes, info.syntax))
11327                         return false;
11328                 
11329                 // let's see if we are expanding gradient definition
11330                 var caret = editor.getCaretPos();
11331                 var content = info.content;
11332                 var css = findGradientFromPosition(content, caret);
11333                 
11334                 if (css.property) {
11335                         // make sure that caret is inside property value with gradient 
11336                         // definition
11337                         var g = findGradient(css.property);
11338                         if (g) {
11339                                 var ruleStart = css.rule.options.offset || 0;
11340                                 var ruleEnd = ruleStart + css.rule.toString().length;
11341                                 
11342                                 // Handle special case:
11343                                 // user wrote gradient definition between existing CSS 
11344                                 // properties and did not finished it with semicolon.
11345                                 // In this case, we have semicolon right after gradient 
11346                                 // definition and re-parse rule again
11347                                 if (/[\n\r]/.test(css.property.value())) {
11348                                         // insert semicolon at the end of gradient definition
11349                                         var insertPos = css.property.valueRange(true).start + g.valueRange.end;
11350                                         content = require('utils').replaceSubstring(content, ';', insertPos);
11351                                         var newCss = findGradientFromPosition(content, caret);
11352                                         if (newCss.property) {
11353                                                 g = findGradient(newCss.property);
11354                                                 css = newCss;
11355                                         }
11356                                 }
11357                                 
11358                                 // make sure current property has terminating semicolon
11359                                 css.property.end(';');
11360                                 
11361                                 // resolve CSS property name
11362                                 var resolvedName = resolvePropertyName(css.property.name(), syntax);
11363                                 if (resolvedName) {
11364                                         css.property.name(resolvedName);
11365                                 }
11366                                 
11367                                 pasteGradient(css.property, g.gradient, g.valueRange);
11368                                 editor.replaceContent(css.rule.toString(), ruleStart, ruleEnd, true);
11369                                 return true;
11370                         }
11371                 }
11372                 
11373                 return expandGradientOutsideValue(editor, syntax);
11374         });
11375         
11376         // XXX register "Reflect CSS Value" action delegate
11377         /**
11378          * @param {EditElement} property
11379          */
11380         require('reflectCSSValue').addHandler(function(property) {
11381                 var utils = require('utils');
11382                 
11383                 var g = findGradient(property);
11384                 if (!g)
11385                         return false;
11386                 
11387                 var value = property.value();
11388                 var val = function(v) {
11389                         return utils.replaceSubstring(value, v, g.valueRange);
11390                 };
11391                 
11392                 // reflect value for properties with the same name
11393                 _.each(property.parent.getAll(getPrefixedNames(property.name())), function(prop) {
11394                         if (prop === property)
11395                                 return;
11396                         
11397                         // check if property value starts with gradient definition
11398                         var m = prop.value().match(/^\s*(\-([a-z]+)\-)?linear\-gradient/);
11399                         if (m) {
11400                                 prop.value(val(module.toString(g.gradient, m[2] || '')));
11401                         } else if ((m = prop.value().match(/\s*\-webkit\-gradient/))) {
11402                                 // old webkit gradient definition
11403                                 prop.value(val(module.oldWebkitLinearGradient(g.gradient)));
11404                         }
11405                 });
11406                 
11407                 return true;
11408         });
11409         
11410         return module = {
11411                 /**
11412                  * Parses gradient definition
11413                  * @param {String} gradient
11414                  * @returns {Object}
11415                  */
11416                 parse: function(gradient) {
11417                         var result = null;
11418                         require('utils').trim(gradient).replace(/^([\w\-]+)\((.+?)\)$/, function(str, type, definition) {
11419                                 // remove vendor prefix
11420                                 type = type.toLowerCase().replace(/^\-[a-z]+\-/, '');
11421                                 if (type == 'linear-gradient' || type == 'lg') {
11422                                         result = parseLinearGradient(definition);
11423                                         return '';
11424                                 }
11425                                 
11426                                 return str;
11427                         });
11428                         
11429                         return result;
11430                 },
11431                 
11432                 /**
11433                  * Produces linear gradient definition used in early Webkit 
11434                  * implementations
11435                  * @param {Object} gradient Parsed gradient
11436                  * @returns {String}
11437                  */
11438                 oldWebkitLinearGradient: function(gradient) {
11439                         if (_.isString(gradient))
11440                                 gradient = this.parse(gradient);
11441                         
11442                         if (!gradient)
11443                                 return null;
11444                         
11445                         var colorStops = _.map(gradient.colorStops, _.clone);
11446                         
11447                         // normalize color-stops position
11448                         _.each(colorStops, function(cs) {
11449                                 if (!('position' in cs)) // implied position
11450                                         return;
11451                                 
11452                                 if (~cs.position.indexOf('.') || cs.unit == '%') {
11453                                         cs.position = parseFloat(cs.position) / (cs.unit == '%' ? 100 : 1);
11454                                 } else {
11455                                         throw "Can't convert color stop '" + (cs.position + (cs.unit || '')) + "'";
11456                                 }
11457                         });
11458                         
11459                         fillImpliedPositions(colorStops);
11460                         
11461                         // transform color-stops into string representation
11462                         colorStops = _.map(colorStops, function(cs, i) {
11463                                 if (!cs.position && !i)
11464                                         return 'from(' + cs.color + ')';
11465                                 
11466                                 if (cs.position == 1 && i == colorStops.length - 1)
11467                                         return 'to(' + cs.color + ')';
11468                                 
11469                                 return 'color-stop(' + (cs.position.toFixed(2).replace(/\.?0+$/, '')) + ', ' + cs.color + ')';
11470                         });
11471                         
11472                         return '-webkit-gradient(linear, ' 
11473                                 + oldWebkitDirection(gradient.direction)
11474                                 + ', '
11475                                 + colorStops.join(', ')
11476                                 + ')';
11477                 },
11478                 
11479                 /**
11480                  * Returns string representation of parsed gradient
11481                  * @param {Object} gradient Parsed gradient
11482                  * @param {String} prefix Vendor prefix
11483                  * @returns {String}
11484                  */
11485                 toString: function(gradient, prefix) {
11486                         if (gradient.type == 'linear') {
11487                                 var fn = (prefix ? '-' + prefix + '-' : '') + 'linear-gradient';
11488                                 
11489                                 // transform color-stops
11490                                 var colorStops = _.map(gradient.colorStops, function(cs) {
11491                                         return cs.color + ('position' in cs 
11492                                                         ? ' ' + cs.position + (cs.unit || '')
11493                                                         : '');
11494                                 });
11495                                 
11496                                 if (gradient.direction 
11497                                                 && (!prefs.get('css.gradient.omitDefaultDirection') 
11498                                                 || !_.include(defaultLinearDirections, gradient.direction))) {
11499                                         colorStops.unshift(gradient.direction);
11500                                 }
11501                                 
11502                                 return fn + '(' + colorStops.join(', ') + ')';
11503                         }
11504                 }
11505         };
11506 });/**
11507  * Module adds support for generators: a regexp-based abbreviation resolver 
11508  * that can produce custom output.
11509  * @param {Function} require
11510  * @param {Underscore} _
11511  */
11512 emmet.exec(function(require, _) {
11513         /** @type HandlerList */
11514         var generators = require('handlerList').create();
11515         var resources = require('resources');
11516         
11517         _.extend(resources, {
11518                 /**
11519                  * Add generator. A generator function <code>fn</code> will be called 
11520                  * only if current abbreviation matches <code>regexp</code> regular 
11521                  * expression and this function should return <code>null</code> if
11522                  * abbreviation cannot be resolved
11523                  * @param {RegExp} regexp Regular expression for abbreviation element name
11524                  * @param {Function} fn Resolver function
11525                  * @param {Object} options Options list as described in 
11526                  * {@link HandlerList#add()} method
11527                  */
11528                 addGenerator: function(regexp, fn, options) {
11529                         if (_.isString(regexp))
11530                                 regexp = new RegExp(regexp);
11531                         
11532                         generators.add(function(node, syntax) {
11533                                 var m;
11534                                 if ((m = regexp.exec(node.name()))) {
11535                                         return fn(m, node, syntax);
11536                                 }
11537                                 
11538                                 return null;
11539                         }, options);
11540                 }
11541         });
11542         
11543         resources.addResolver(function() {
11544                 return generators.exec(null, _.toArray(arguments));
11545         });
11546 });/**
11547  * Module for resolving tag names: returns best matched tag name for child
11548  * element based on passed parent's tag name. Also provides utility function
11549  * for element type detection (inline, block-level, empty)
11550  * @param {Function} require
11551  * @param {Underscore} _
11552  */
11553 emmet.define('tagName', function(require, _) {
11554         var elementTypes = {
11555 //              empty: 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,keygen,command'.split(','),
11556                 empty: [],
11557                 blockLevel: 'address,applet,blockquote,button,center,dd,del,dir,div,dl,dt,fieldset,form,frameset,hr,iframe,ins,isindex,li,link,map,menu,noframes,noscript,object,ol,p,pre,script,table,tbody,td,tfoot,th,thead,tr,ul,h1,h2,h3,h4,h5,h6'.split(','),
11558                 inlineLevel: 'a,abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'.split(',')
11559         };
11560         
11561         var elementMap = {
11562                 'p': 'span',
11563                 'ul': 'li',
11564                 'ol': 'li',
11565                 'table': 'tr',
11566                 'tr': 'td',
11567                 'tbody': 'tr',
11568                 'thead': 'tr',
11569                 'tfoot': 'tr',
11570                 'colgroup': 'col',
11571                 'select': 'option',
11572                 'optgroup': 'option',
11573                 'audio': 'source',
11574                 'video': 'source',
11575                 'object': 'param',
11576                 'map': 'area'
11577         };
11578         
11579         return {
11580                 /**
11581                  * Returns best matched child element name for passed parent's
11582                  * tag name
11583                  * @param {String} name
11584                  * @returns {String}
11585                  * @memberOf tagName
11586                  */
11587                 resolve: function(name) {
11588                         name = (name || '').toLowerCase();
11589                         
11590                         if (name in elementMap)
11591                                 return this.getMapping(name);
11592                         
11593                         if (this.isInlineLevel(name))
11594                                 return 'span';
11595                         
11596                         return 'div';
11597                 },
11598                 
11599                 /**
11600                  * Returns mapped child element name for passed parent's name 
11601                  * @param {String} name
11602                  * @returns {String}
11603                  */
11604                 getMapping: function(name) {
11605                         return elementMap[name.toLowerCase()];
11606                 },
11607                 
11608                 /**
11609                  * Check if passed element name belongs to inline-level element
11610                  * @param {String} name
11611                  * @returns {Boolean}
11612                  */
11613                 isInlineLevel: function(name) {
11614                         return this.isTypeOf(name, 'inlineLevel');
11615                 },
11616                 
11617                 /**
11618                  * Check if passed element belongs to block-level element.
11619                  * For better matching of unknown elements (for XML, for example), 
11620                  * you should use <code>!this.isInlineLevel(name)</code>
11621                  * @returns {Boolean}
11622                  */
11623                 isBlockLevel: function(name) {
11624                         return this.isTypeOf(name, 'blockLevel');
11625                 },
11626                 
11627                 /**
11628                  * Check if passed element is void (i.e. should not have closing tag).
11629                  * @returns {Boolean}
11630                  */
11631                 isEmptyElement: function(name) {
11632                         return this.isTypeOf(name, 'empty');
11633                 },
11634                 
11635                 /**
11636                  * Generic function for testing if element name belongs to specified
11637                  * elements collection
11638                  * @param {String} name Element name
11639                  * @param {String} type Collection name
11640                  * @returns {Boolean}
11641                  */
11642                 isTypeOf: function(name, type) {
11643                         return _.include(elementTypes[type], name);
11644                 },
11645                 
11646                 /**
11647                  * Adds new parent–child mapping
11648                  * @param {String} parent
11649                  * @param {String} child
11650                  */
11651                 addMapping: function(parent, child) {
11652                         elementMap[parent] = child;
11653                 },
11654                 
11655                 /**
11656                  * Removes parent-child mapping
11657                  */
11658                 removeMapping: function(parent) {
11659                         if (parent in elementMap)
11660                                 delete elementMap[parent];
11661                 },
11662                 
11663                 /**
11664                  * Adds new element into collection
11665                  * @param {String} name Element name
11666                  * @param {String} collection Collection name
11667                  */
11668                 addElementToCollection: function(name, collection) {
11669                         if (!elementTypes[collection])
11670                                 elementTypes[collection] = [];
11671                         
11672                         var col = this.getCollection(collection);
11673                         if (!_.include(col, name))
11674                                 col.push(name);
11675                 },
11676                 
11677                 /**
11678                  * Removes element name from specified collection
11679                  * @param {String} name Element name
11680                  * @param {String} collection Collection name
11681                  * @returns
11682                  */
11683                 removeElementFromCollection: function(name, collection) {
11684                         if (collection in elementTypes) {
11685                                 elementTypes[collection] = _.without(this.getCollection(collection), name);
11686                         }
11687                 },
11688                 
11689                 /**
11690                  * Returns elements name collection
11691                  * @param {String} name Collection name
11692                  * @returns {Array}
11693                  */
11694                 getCollection: function(name) {
11695                         return elementTypes[name];
11696                 }
11697         };
11698 });/**
11699  * Filter for aiding of writing elements with complex class names as described
11700  * in Yandex's BEM (Block, Element, Modifier) methodology. This filter will
11701  * automatically inherit block and element names from parent elements and insert
11702  * them into child element classes
11703  * @memberOf __bemFilterDefine
11704  * @constructor
11705  * @param {Function} require
11706  * @param {Underscore} _
11707  */
11708 emmet.exec(function(require, _) {
11709         var prefs = require('preferences');
11710         prefs.define('bem.elementSeparator', '__', 'Class name’s element separator.');
11711         prefs.define('bem.modifierSeparator', '_', 'Class name’s modifier separator.');
11712         prefs.define('bem.shortElementPrefix', '-', 
11713                         'Symbol for describing short “block-element” notation. Class names '
11714                         + 'prefixed with this symbol will be treated as element name for parent‘s '
11715                         + 'block name. Each symbol instance traverses one level up in parsed ' 
11716                         + 'tree for block name lookup. Empty value will disable short notation.');
11717         
11718         var shouldRunHtmlFilter = false;
11719         
11720         function getSeparators() {
11721                 return {
11722                         element: prefs.get('bem.elementSeparator'),
11723                         modifier: prefs.get('bem.modifierSeparator')
11724                 };
11725         }
11726         
11727         /**
11728          * @param {AbbreviationNode} item
11729          */
11730         function bemParse(item) {
11731                 if (require('abbreviationUtils').isSnippet(item))
11732                         return item;
11733                 
11734                 // save BEM stuff in cache for faster lookups
11735                 item.__bem = {
11736                         block: '',
11737                         element: '',
11738                         modifier: ''
11739                 };
11740                 
11741                 var classNames = normalizeClassName(item.attribute('class')).split(' ');
11742                 
11743                 // guess best match for block name
11744                 var reBlockName = /^[a-z]\-/i;
11745                 item.__bem.block = _.find(classNames, function(name) {
11746                         return reBlockName.test(name);
11747                 });
11748                 
11749                 // guessing doesn't worked, pick first class name as block name
11750                 if (!item.__bem.block) {
11751                         reBlockName = /^[a-z]/i;
11752                         item.__bem.block = _.find(classNames, function(name) {
11753                                 return reBlockName.test(name);
11754                         }) || '';
11755                 }
11756                 
11757                 classNames = _.chain(classNames)
11758                         .map(function(name) {return processClassName(name, item);})
11759                         .flatten()
11760                         .uniq()
11761                         .value()
11762                         .join(' ');
11763                 
11764                 if (classNames)
11765                         item.attribute('class', classNames);
11766                 
11767                 return item;
11768         }
11769         
11770         /**
11771          * @param {String} className
11772          * @returns {String}
11773          */
11774         function normalizeClassName(className) {
11775                 var utils = require('utils');
11776                 className = (' ' + (className || '') + ' ').replace(/\s+/g, ' ');
11777                 
11778                 var shortSymbol = prefs.get('bem.shortElementPrefix');
11779                 if (shortSymbol) {
11780                         var re = new RegExp('\\s(' + utils.escapeForRegexp(shortSymbol) + '+)', 'g');
11781                         className = className.replace(re, function(str, p1) {
11782                                 return ' ' + utils.repeatString(getSeparators().element, p1.length);
11783                         });
11784                 }
11785                 
11786                 return utils.trim(className);
11787         }
11788         
11789         /**
11790          * Processes class name
11791          * @param {String} name Class name item to process
11792          * @param {AbbreviationNode} item Host node for provided class name
11793          * @returns Processed class name. May return <code>Array</code> of
11794          * class names 
11795          */
11796         function processClassName(name, item) {
11797                 name = transformClassName(name, item, 'element');
11798                 name = transformClassName(name, item, 'modifier');
11799                 
11800                 // expand class name
11801                 // possible values:
11802                 // * block__element
11803                 // * block__element_modifier
11804                 // * block__element_modifier1_modifier2
11805                 // * block_modifier
11806                 var block = '', element = '', modifier = '';
11807                 var separators = getSeparators();
11808                 if (~name.indexOf(separators.element)) {
11809                         var blockElem = name.split(separators.element);
11810                         var elemModifiers = blockElem[1].split(separators.modifier);
11811                         
11812                         block = blockElem[0];
11813                         element = elemModifiers.shift();
11814                         modifier = elemModifiers.join(separators.modifier);
11815                 } else if (~name.indexOf(separators.modifier)) {
11816                         var blockModifiers = name.split(separators.modifier);
11817                         
11818                         block = blockModifiers.shift();
11819                         modifier = blockModifiers.join(separators.modifier);
11820                 }
11821                 
11822                 if (block || element || modifier) {
11823                         if (!block) {
11824                                 block = item.__bem.block;
11825                         }
11826                         
11827                         // inherit parent bem element, if exists
11828 //                      if (item.parent && item.parent.__bem && item.parent.__bem.element)
11829 //                              element = item.parent.__bem.element + separators.element + element;
11830                         
11831                         // produce multiple classes
11832                         var prefix = block;
11833                         var result = [];
11834                         
11835                         if (element) {
11836                                 prefix += separators.element + element;
11837                                 result.push(prefix);
11838                         } else {
11839                                 result.push(prefix);
11840                         }
11841                         
11842                         if (modifier) {
11843                                 result.push(prefix + separators.modifier + modifier);
11844                         }
11845                         
11846                         item.__bem.block = block;
11847                         item.__bem.element = element;
11848                         item.__bem.modifier = modifier;
11849                         
11850                         return result;
11851                 }
11852                 
11853                 // ...otherwise, return processed or original class name
11854                 return name;
11855         }
11856         
11857         /**
11858          * Low-level function to transform user-typed class name into full BEM class
11859          * @param {String} name Class name item to process
11860          * @param {AbbreviationNode} item Host node for provided class name
11861          * @param {String} entityType Type of entity to be tried to transform 
11862          * ('element' or 'modifier')
11863          * @returns {String} Processed class name or original one if it can't be
11864          * transformed
11865          */
11866         function transformClassName(name, item, entityType) {
11867                 var separators = getSeparators();
11868                 var reSep = new RegExp('^(' + separators[entityType] + ')+', 'g');
11869                 if (reSep.test(name)) {
11870                         var depth = 0; // parent lookup depth
11871                         var cleanName = name.replace(reSep, function(str) {
11872                                 depth = str.length / separators[entityType].length;
11873                                 return '';
11874                         });
11875                         
11876                         // find donor element
11877                         var donor = item;
11878                         while (donor.parent && depth--) {
11879                                 donor = donor.parent;
11880                         }
11881                         
11882                         if (!donor || !donor.__bem)
11883                                 donor = item;
11884                         
11885                         if (donor && donor.__bem) {
11886                                 var prefix = donor.__bem.block;
11887                                 
11888                                 // decide if we should inherit element name
11889 //                              if (entityType == 'element') {
11890 //                                      var curElem = cleanName.split(separators.modifier, 1)[0];
11891 //                                      if (donor.__bem.element && donor.__bem.element != curElem)
11892 //                                              prefix += separators.element + donor.__bem.element;
11893 //                              }
11894                                 
11895                                 if (entityType == 'modifier' &&  donor.__bem.element)
11896                                         prefix += separators.element + donor.__bem.element;
11897                                 
11898                                 return prefix + separators[entityType] + cleanName;
11899                         }
11900                 }
11901                 
11902                 return name;
11903         }
11904         
11905         /**
11906          * Recursive function for processing tags, which extends class names 
11907          * according to BEM specs: http://bem.github.com/bem-method/pages/beginning/beginning.ru.html
11908          * <br><br>
11909          * It does several things:<br>
11910          * <ul>
11911          * <li>Expands complex class name (according to BEM symbol semantics):
11912          * .block__elem_modifier → .block.block__elem.block__elem_modifier
11913          * </li>
11914          * <li>Inherits block name on child elements: 
11915          * .b-block > .__el > .__el → .b-block > .b-block__el > .b-block__el__el
11916          * </li>
11917          * <li>Treats first dash symbol as '__'</li>
11918          * <li>Double underscore (or typographic '–') is also treated as an element 
11919          * level lookup, e.g. ____el will search for element definition in parent’s 
11920          * parent element:
11921          * .b-block > .__el1 > .____el2 → .b-block > .b-block__el1 > .b-block__el2
11922          * </li>
11923          * </ul>
11924          * 
11925          * @param {AbbreviationNode} tree
11926          * @param {Object} profile
11927          */
11928         function process(tree, profile) {
11929                 if (tree.name)
11930                         bemParse(tree, profile);
11931                 
11932                 var abbrUtils = require('abbreviationUtils');
11933                 _.each(tree.children, function(item) {
11934                         process(item, profile);
11935                         if (!abbrUtils.isSnippet(item) && item.start)
11936                                 shouldRunHtmlFilter = true;
11937                 });
11938                 
11939                 return tree;
11940         }
11941         
11942         require('filters').add('bem', function(tree, profile) {
11943                 shouldRunHtmlFilter = false;
11944                 tree = process(tree, profile);
11945                 // in case 'bem' filter is applied after 'html' filter: run it again
11946                 // to update output
11947                 if (shouldRunHtmlFilter) {
11948                         tree = require('filters').apply(tree, 'html', profile);
11949                 }
11950                 
11951                 return tree;
11952         });
11953 });
11954 
11955 /**
11956  * Comment important tags (with 'id' and 'class' attributes)
11957  * @author Sergey Chikuyonok (serge.che@gmail.com)
11958  * @link http://chikuyonok.ru
11959  * @constructor
11960  * @memberOf __commentFilterDefine
11961  * @param {Function} require
11962  * @param {Underscore} _
11963  */
11964 emmet.exec(function(require, _) {
11965         // define some preferences
11966         /** @type emmet.preferences */
11967         var prefs = require('preferences');
11968         
11969         prefs.define('filter.commentAfter', 
11970                         '\n<!-- /<%= attr("id", "#") %><%= attr("class", ".") %> -->',
11971                         'A definition of comment that should be placed <i>after</i> matched '
11972                         + 'element when <code>comment</code> filter is applied. This definition '
11973                         + 'is an ERB-style template passed to <code>_.template()</code> '
11974                         + 'function (see Underscore.js docs for details). In template context, '
11975                         + 'the following properties and functions are availabe:\n'
11976                         + '<ul>'
11977                         
11978                         + '<li><code>attr(name, before, after)</code> – a function that outputs' 
11979                         + 'specified attribute value concatenated with <code>before</code> ' 
11980                         + 'and <code>after</code> strings. If attribute doesn\'t exists, the ' 
11981                         + 'empty string will be returned.</li>'
11982                         
11983                         + '<li><code>node</code> – current node (instance of <code>AbbreviationNode</code>)</li>'
11984                         
11985                         + '<li><code>name</code> – name of current tag</li>'
11986                         
11987                         + '<li><code>padding</code> – current string padding, can be used ' 
11988                         + 'for formatting</li>'
11989                         
11990                         +'</ul>');
11991         
11992         prefs.define('filter.commentBefore', 
11993                         '',
11994                         'A definition of comment that should be placed <i>before</i> matched '
11995                         + 'element when <code>comment</code> filter is applied. '
11996                         + 'For more info, read description of <code>filter.commentAfter</code> '
11997                         + 'property');
11998         
11999         prefs.define('filter.commentTrigger', 'id, class',
12000                         'A comma-separated list of attribute names that should exist in abbreviatoin '
12001                         + 'where comment should be added. If you wish to add comment for '
12002                         + 'every element, set this option to <code>*</code>');
12003         
12004         /**
12005          * Add comments to tag
12006          * @param {AbbreviationNode} node
12007          */
12008         function addComments(node, templateBefore, templateAfter) {
12009                 var utils = require('utils');
12010                 
12011                 // check if comments should be added
12012                 var trigger = prefs.get('filter.commentTrigger');
12013                 if (trigger != '*') {
12014                         var shouldAdd = _.find(trigger.split(','), function(name) {
12015                                 return !!node.attribute(utils.trim(name));
12016                         });
12017                         if (!shouldAdd) return;
12018                 }
12019                 
12020                 var ctx = {
12021                         node: node,
12022                         name: node.name(),
12023                         padding: node.parent ? node.parent.padding : '',
12024                         attr: function(name, before, after) {
12025                                 var attr = node.attribute(name);
12026                                 if (attr) {
12027                                         return (before || '') + attr + (after || '');
12028                                 }
12029                                 
12030                                 return '';
12031                         }
12032                 };
12033                 
12034                 var nodeBefore = utils.normalizeNewline(templateBefore ? templateBefore(ctx) : '');
12035                 var nodeAfter = utils.normalizeNewline(templateAfter ? templateAfter(ctx) : '');
12036                 
12037                 node.start = node.start.replace(/</, nodeBefore + '<');
12038                 node.end = node.end.replace(/>/, '>' + nodeAfter);
12039         }
12040         
12041         function process(tree, before, after) {
12042                 var abbrUtils = require('abbreviationUtils');
12043                 _.each(tree.children, function(item) {
12044                         if (abbrUtils.isBlock(item))
12045                                 addComments(item, before, after);
12046                         
12047                         process(item, before, after);
12048                 });
12049                         
12050                 return tree;
12051         }
12052         
12053         require('filters').add('c', function(tree) {
12054                 var templateBefore = _.template(prefs.get('filter.commentBefore'));
12055                 var templateAfter = _.template(prefs.get('filter.commentAfter'));
12056                 
12057                 return process(tree, templateBefore, templateAfter);
12058         });
12059 });
12060 /**
12061  * Filter for escaping unsafe XML characters: <, >, &
12062  * @author Sergey Chikuyonok (serge.che@gmail.com)
12063  * @link http://chikuyonok.ru
12064  */
12065 emmet.exec(function(require, _) {
12066         var charMap = {
12067                 '<': '&lt;',
12068                 '>': '&gt;',
12069                 '&': '&amp;'
12070         };
12071         
12072         function escapeChars(str) {
12073                 return str.replace(/([<>&])/g, function(str, p1){
12074                         return charMap[p1];
12075                 });
12076         }
12077         
12078         require('filters').add('e', function process(tree) {
12079                 _.each(tree.children, function(item) {
12080                         item.start = escapeChars(item.start);
12081                         item.end = escapeChars(item.end);
12082                         item.content = escapeChars(item.content);
12083                         process(item);
12084                 });
12085                 
12086                 return tree;
12087         });
12088 });/**
12089  * Generic formatting filter: creates proper indentation for each tree node,
12090  * placing "%s" placeholder where the actual output should be. You can use
12091  * this filter to preformat tree and then replace %s placeholder to whatever you
12092  * need. This filter should't be called directly from editor as a part 
12093  * of abbreviation.
12094  * @author Sergey Chikuyonok (serge.che@gmail.com)
12095  * @link http://chikuyonok.ru
12096  * @constructor
12097  * @memberOf __formatFilterDefine
12098  * @param {Function} require
12099  * @param {Underscore} _
12100  */
12101 emmet.exec(function(require, _){
12102         var placeholder = '%s';
12103         
12104         /** @type preferences */
12105         var prefs = require('preferences');
12106         prefs.define('format.noIndentTags', 'html', 
12107                         'A comma-separated list of tag names that should not get inner indentation.');
12108         
12109         prefs.define('format.forceIndentationForTags', 'body', 
12110                 'A comma-separated list of tag names that should <em>always</em> get inner indentation.');
12111         
12112         /**
12113          * Get indentation for given node
12114          * @param {AbbreviationNode} node
12115          * @returns {String}
12116          */
12117         function getIndentation(node) {
12118                 if (_.include(prefs.getArray('format.noIndentTags') || [], node.name())) {
12119                         return '';
12120                 }
12121                 
12122                 return require('resources').getVariable('indentation');
12123         }
12124         
12125         /**
12126          * Test if passed node has block-level sibling element
12127          * @param {AbbreviationNode} item
12128          * @return {Boolean}
12129          */
12130         function hasBlockSibling(item) {
12131                 return item.parent && require('abbreviationUtils').hasBlockChildren(item.parent);
12132         }
12133         
12134         /**
12135          * Test if passed item is very first child in parsed tree
12136          * @param {AbbreviationNode} item
12137          */
12138         function isVeryFirstChild(item) {
12139                 return item.parent && !item.parent.parent && !item.index();
12140         }
12141         
12142         /**
12143          * Check if a newline should be added before element
12144          * @param {AbbreviationNode} node
12145          * @param {OutputProfile} profile
12146          * @return {Boolean}
12147          */
12148         function shouldAddLineBreak(node, profile) {
12149                 var abbrUtils = require('abbreviationUtils');
12150                 if (profile.tag_nl === true || abbrUtils.isBlock(node))
12151                         return true;
12152                 
12153                 if (!node.parent || !profile.inline_break)
12154                         return false;
12155                 
12156                 // check if there are required amount of adjacent inline element
12157                 return shouldFormatInline(node.parent, profile);
12158 }
12159         
12160         /**
12161          * Need to add newline because <code>item</code> has too many inline children
12162          * @param {AbbreviationNode} node
12163          * @param {OutputProfile} profile
12164          */
12165         function shouldBreakChild(node, profile) {
12166                 // we need to test only one child element, because 
12167                 // hasBlockChildren() method will do the rest
12168                 return node.children.length && shouldAddLineBreak(node.children[0], profile);
12169         }
12170         
12171         function shouldFormatInline(node, profile) {
12172                 var nodeCount = 0;
12173                 var abbrUtils = require('abbreviationUtils');
12174                 return !!_.find(node.children, function(child) {
12175                         if (child.isTextNode() || !abbrUtils.isInline(child))
12176                                 nodeCount = 0;
12177                         else if (abbrUtils.isInline(child))
12178                                 nodeCount++;
12179                         
12180                         if (nodeCount >= profile.inline_break)
12181                                 return true;
12182                 });
12183         }
12184         
12185         function isRoot(item) {
12186                 return !item.parent;
12187         }
12188         
12189         /**
12190          * Processes element with matched resource of type <code>snippet</code>
12191          * @param {AbbreviationNode} item
12192          * @param {OutputProfile} profile
12193          */
12194         function processSnippet(item, profile) {
12195                 item.start = item.end = '';
12196                 if (!isVeryFirstChild(item) && profile.tag_nl !== false && shouldAddLineBreak(item, profile)) {
12197                         // check if we’re not inside inline element
12198                         if (isRoot(item.parent) || !require('abbreviationUtils').isInline(item.parent)) {
12199                                 item.start = require('utils').getNewline() + item.start;
12200                         }
12201                 }
12202                 
12203                 return item;
12204         }
12205         
12206         /**
12207          * Check if we should add line breaks inside inline element
12208          * @param {AbbreviationNode} node
12209          * @param {OutputProfile} profile
12210          * @return {Boolean}
12211          */
12212         function shouldBreakInsideInline(node, profile) {
12213                 var abbrUtils = require('abbreviationUtils');
12214                 var hasBlockElems = _.any(node.children, function(child) {
12215                         if (abbrUtils.isSnippet(child))
12216                                 return false;
12217                         
12218                         return !abbrUtils.isInline(child);
12219                 });
12220                 
12221                 if (!hasBlockElems) {
12222                         return shouldFormatInline(node, profile);
12223                 }
12224                 
12225                 return true;
12226         }
12227         
12228         /**
12229          * Processes element with <code>tag</code> type
12230          * @param {AbbreviationNode} item
12231          * @param {OutputProfile} profile
12232          */
12233         function processTag(item, profile) {
12234                 item.start = item.end = placeholder;
12235                 var utils = require('utils');
12236                 var abbrUtils = require('abbreviationUtils');
12237                 var isUnary = abbrUtils.isUnary(item);
12238                 var nl = utils.getNewline();
12239                 var indent = getIndentation(item);
12240                         
12241                 // formatting output
12242                 if (profile.tag_nl !== false) {
12243                         var forceNl = profile.tag_nl === true && (profile.tag_nl_leaf || item.children.length);
12244                         if (!forceNl) {
12245                                 forceNl = _.include(prefs.getArray('format.forceIndentationForTags') || [], item.name());
12246                         }
12247                         
12248                         // formatting block-level elements
12249                         if (!item.isTextNode()) {
12250                                 if (shouldAddLineBreak(item, profile)) {
12251                                         // - do not indent the very first element
12252                                         // - do not indent first child of a snippet
12253                                         if (!isVeryFirstChild(item) && (!abbrUtils.isSnippet(item.parent) || item.index()))
12254                                                 item.start = nl + item.start;
12255                                                 
12256                                         if (abbrUtils.hasBlockChildren(item) || shouldBreakChild(item, profile) || (forceNl && !isUnary))
12257                                                 item.end = nl + item.end;
12258                                                 
12259                                         if (abbrUtils.hasTagsInContent(item) || (forceNl && !item.children.length && !isUnary))
12260                                                 item.start += nl + indent;
12261                                 } else if (abbrUtils.isInline(item) && hasBlockSibling(item) && !isVeryFirstChild(item)) {
12262                                         item.start = nl + item.start;
12263                                 } else if (abbrUtils.isInline(item) && shouldBreakInsideInline(item, profile)) {
12264                                         item.end = nl + item.end;
12265                                 }
12266                                 
12267                                 item.padding = indent;
12268                         }
12269                 }
12270                 
12271                 return item;
12272         }
12273         
12274         /**
12275          * Processes simplified tree, making it suitable for output as HTML structure
12276          * @param {AbbreviationNode} tree
12277          * @param {OutputProfile} profile
12278          * @param {Number} level Depth level
12279          */
12280         require('filters').add('_format', function process(tree, profile, level) {
12281                 level = level || 0;
12282                 var abbrUtils = require('abbreviationUtils');
12283                 
12284                 _.each(tree.children, function(item) {
12285                         if (abbrUtils.isSnippet(item))
12286                                 processSnippet(item, profile, level);
12287                         else
12288                                 processTag(item, profile, level);
12289                         
12290                         process(item, profile, level + 1);
12291                 });
12292                 
12293                 return tree;
12294         });
12295 });/**
12296  * Filter for producing HAML code from abbreviation.
12297  * @author Sergey Chikuyonok (serge.che@gmail.com)
12298  * @link http://chikuyonok.ru
12299  * @constructor
12300  * @memberOf __hamlFilterDefine
12301  * @param {Function} require
12302  * @param {Underscore} _
12303  */
12304 emmet.exec(function(require, _) {
12305         function transformClassName(className) {
12306                 return require('utils').trim(className).replace(/\s+/g, '.');
12307         }
12308         
12309         /**
12310          * Creates HAML attributes string from tag according to profile settings
12311          * @param {AbbreviationNode} tag
12312          * @param {Object} profile
12313          */
12314         function makeAttributesString(tag, profile) {
12315                 var attrs = '';
12316                 var otherAttrs = [];
12317                 var attrQuote = profile.attributeQuote();
12318                 var cursor = profile.cursor();
12319                 
12320                 _.each(tag.attributeList(), function(a) {
12321                         var attrName = profile.attributeName(a.name);
12322                         switch (attrName.toLowerCase()) {
12323                                 // use short notation for ID and CLASS attributes
12324                                 case 'id':
12325                                         attrs += '#' + (a.value || cursor);
12326                                         break;
12327                                 case 'class':
12328                                         attrs += '.' + transformClassName(a.value || cursor);
12329                                         break;
12330                                 // process other attributes
12331                                 default:
12332                                         otherAttrs.push(':' +attrName + ' => ' + attrQuote + (a.value || cursor) + attrQuote);
12333                         }
12334                 });
12335                 
12336                 if (otherAttrs.length)
12337                         attrs += '{' + otherAttrs.join(', ') + '}';
12338                 
12339                 return attrs;
12340         }
12341         
12342         /**
12343          * Processes element with <code>tag</code> type
12344          * @param {AbbreviationNode} item
12345          * @param {OutputProfile} profile
12346          */
12347         function processTag(item, profile) {
12348                 if (!item.parent)
12349                         // looks like it's root element
12350                         return item;
12351                 
12352                 var abbrUtils = require('abbreviationUtils');
12353                 var utils = require('utils');
12354                 
12355                 var attrs = makeAttributesString(item, profile);
12356                 var cursor = profile.cursor();
12357                 var isUnary = abbrUtils.isUnary(item);
12358                 var selfClosing = profile.self_closing_tag && isUnary ? '/' : '';
12359                 var start= '';
12360                         
12361                 // define tag name
12362                 var tagName = '%' + profile.tagName(item.name());
12363                 if (tagName.toLowerCase() == '%div' && attrs && attrs.indexOf('{') == -1)
12364                         // omit div tag
12365                         tagName = '';
12366                         
12367                 item.end = '';
12368                 start = tagName + attrs + selfClosing + ' ';
12369                 
12370                 var placeholder = '%s';
12371                 // We can't just replace placeholder with new value because
12372                 // JavaScript will treat double $ character as a single one, assuming
12373                 // we're using RegExp literal.
12374                 item.start = utils.replaceSubstring(item.start, start, item.start.indexOf(placeholder), placeholder);
12375                 
12376                 if (!item.children.length && !isUnary)
12377                         item.start += cursor;
12378                 
12379                 return item;
12380         }
12381         
12382         /**
12383          * Processes simplified tree, making it suitable for output as HTML structure
12384          * @param {AbbreviationNode} tree
12385          * @param {Object} profile
12386          * @param {Number} level Depth level
12387          */
12388         require('filters').add('haml', function process(tree, profile, level) {
12389                 level = level || 0;
12390                 var abbrUtils = require('abbreviationUtils');
12391                 
12392                 if (!level) {
12393                         tree = require('filters').apply(tree, '_format', profile);
12394                 }
12395                 
12396                 _.each(tree.children, function(item) {
12397                         if (!abbrUtils.isSnippet(item))
12398                                 processTag(item, profile, level);
12399                         
12400                         process(item, profile, level + 1);
12401                 });
12402                 
12403                 return tree;
12404         });
12405 });/**
12406  * Filter that produces HTML tree
12407  * @author Sergey Chikuyonok (serge.che@gmail.com)
12408  * @link http://chikuyonok.ru
12409  * @constructor
12410  * @memberOf __htmlFilterDefine
12411  * @param {Function} require
12412  * @param {Underscore} _
12413  */
12414 emmet.exec(function(require, _) {
12415         /**
12416          * Creates HTML attributes string from tag according to profile settings
12417          * @param {AbbreviationNode} node
12418          * @param {OutputProfile} profile
12419          */
12420         function makeAttributesString(node, profile) {
12421                 var attrQuote = profile.attributeQuote();
12422                 var cursor = profile.cursor();
12423                 
12424                 return _.map(node.attributeList(), function(a) {
12425                         var attrName = profile.attributeName(a.name);
12426                         return ' ' + attrName + '=' + attrQuote + (a.value || cursor) + attrQuote;
12427                 }).join('');
12428         }
12429         
12430         /**
12431          * Processes element with <code>tag</code> type
12432          * @param {AbbreviationNode} item
12433          * @param {OutputProfile} profile
12434          */
12435         function processTag(item, profile) {
12436                 if (!item.parent) // looks like it's root element
12437                         return item;
12438                 
12439                 var abbrUtils = require('abbreviationUtils');
12440                 var utils = require('utils');
12441                 
12442                 var attrs = makeAttributesString(item, profile); 
12443                 var cursor = profile.cursor();
12444                 var isUnary = abbrUtils.isUnary(item);
12445                 var start= '';
12446                 var end = '';
12447                         
12448                 // define opening and closing tags
12449                 if (!item.isTextNode()) {
12450                         var tagName = profile.tagName(item.name());
12451                         if (isUnary) {
12452                                 start = '<' + tagName + attrs + profile.selfClosing() + '>';
12453                                 item.end = '';
12454                         } else {
12455                                 start = '<' + tagName + attrs + '>';
12456                                 end = '</' + tagName + '>';
12457                         }
12458                 }
12459                 
12460                 var placeholder = '%s';
12461                 // We can't just replace placeholder with new value because
12462                 // JavaScript will treat double $ character as a single one, assuming
12463                 // we're using RegExp literal.
12464                 item.start = utils.replaceSubstring(item.start, start, item.start.indexOf(placeholder), placeholder);
12465                 item.end = utils.replaceSubstring(item.end, end, item.end.indexOf(placeholder), placeholder);
12466                 
12467                 // should we put caret placeholder after opening tag?
12468                 if (
12469                                 !item.children.length 
12470                                 && !isUnary 
12471                                 && !~item.content.indexOf(cursor)
12472                                 && !require('tabStops').extract(item.content).tabstops.length
12473                         ) {
12474                         item.start += cursor;
12475                 }
12476                 
12477                 return item;
12478         }
12479         
12480         /**
12481          * Processes simplified tree, making it suitable for output as HTML structure
12482          * @param {AbbreviationNode} tree
12483          * @param {Object} profile
12484          * @param {Number} level Depth level
12485          */
12486         require('filters').add('html', function process(tree, profile, level) {
12487                 level = level || 0;
12488                 var abbrUtils = require('abbreviationUtils');
12489                 
12490                 if (!level) {
12491                         tree = require('filters').apply(tree, '_format', profile);
12492                 }
12493                 
12494                 _.each(tree.children, function(item) {
12495                         if (!abbrUtils.isSnippet(item))
12496                                 processTag(item, profile, level);
12497                         
12498                         process(item, profile, level + 1);
12499                 });
12500                 
12501                 return tree;
12502         });
12503 });/**
12504  * Output abbreviation on a single line (i.e. no line breaks)
12505  * @author Sergey Chikuyonok (serge.che@gmail.com)
12506  * @link http://chikuyonok.ru
12507  * @constructor
12508  * @memberOf __singleLineFilterDefine
12509  * @param {Function} require
12510  * @param {Underscore} _
12511  */
12512 emmet.exec(function(require, _) {
12513         var rePad = /^\s+/;
12514         var reNl = /[\n\r]/g;
12515         
12516         require('filters').add('s', function process(tree) {
12517                 var abbrUtils = require('abbreviationUtils');
12518                 
12519                 _.each(tree.children, function(item) {
12520                         if (!abbrUtils.isSnippet(item)) {
12521                                 // remove padding from item 
12522                                 item.start = item.start.replace(rePad, '');
12523                                 item.end = item.end.replace(rePad, '');
12524                         }
12525                         
12526                         // remove newlines 
12527                         item.start = item.start.replace(reNl, '');
12528                         item.end = item.end.replace(reNl, '');
12529                         item.content = item.content.replace(reNl, '');
12530                         
12531                         process(item);
12532                 });
12533                 
12534                 return tree;
12535         });
12536 });
12537 /**
12538  * Trim filter: removes characters at the beginning of the text
12539  * content that indicates lists: numbers, #, *, -, etc.
12540  * 
12541  * Useful for wrapping lists with abbreviation.
12542  * 
12543  * @author Sergey Chikuyonok (serge.che@gmail.com)
12544  * @link http://chikuyonok.ru
12545  * 
12546  * @constructor
12547  * @memberOf __trimFilterDefine
12548  * @param {Function} require
12549  * @param {Underscore} _
12550  */
12551 emmet.exec(function(require, _) {
12552         require('preferences').define('filter.trimRegexp', '[\\s|\\u00a0]*[\\d|#|\\-|\*|\\u2022]+\\.?\\s*',
12553                         'Regular expression used to remove list markers (numbers, dashes, ' 
12554                         + 'bullets, etc.) in <code>t</code> (trim) filter. The trim filter '
12555                         + 'is useful for wrapping with abbreviation lists, pased from other ' 
12556                         + 'documents (for example, Word documents).');
12557         
12558         function process(tree, re) {
12559                 _.each(tree.children, function(item) {
12560                         if (item.content)
12561                                 item.content = item.content.replace(re, '');
12562                         
12563                         process(item, re);
12564                 });
12565                 
12566                 return tree;
12567         }
12568         
12569         require('filters').add('t', function(tree) {
12570                 var re = new RegExp(require('preferences').get('filter.trimRegexp'));
12571                 return process(tree, re);
12572         });
12573 });
12574 /**
12575  * Filter for trimming "select" attributes from some tags that contains
12576  * child elements
12577  * @author Sergey Chikuyonok (serge.che@gmail.com)
12578  * @link http://chikuyonok.ru
12579  * 
12580  * @constructor
12581  * @memberOf __xslFilterDefine
12582  * @param {Function} require
12583  * @param {Underscore} _
12584  */
12585 emmet.exec(function(require, _) {
12586         var tags = {
12587                 'xsl:variable': 1,
12588                 'xsl:with-param': 1
12589         };
12590         
12591         /**
12592          * Removes "select" attribute from node
12593          * @param {AbbreviationNode} node
12594          */
12595         function trimAttribute(node) {
12596                 node.start = node.start.replace(/\s+select\s*=\s*(['"]).*?\1/, '');
12597         }
12598         
12599         require('filters').add('xsl', function process(tree) {
12600                 var abbrUtils = require('abbreviationUtils');
12601                 _.each(tree.children, function(item) {
12602                         if (!abbrUtils.isSnippet(item)
12603                                         && (item.name() || '').toLowerCase() in tags 
12604                                         && item.children.length)
12605                                 trimAttribute(item);
12606                         process(item);
12607                 });
12608                 
12609                 return tree;
12610         });
12611 });/**
12612  * "Lorem ipsum" text generator. Matches <code>lipsum(num)?</code> or 
12613  * <code>lorem(num)?</code> abbreviation.
12614  * This code is based on Django's contribution: 
12615  * https://code.djangoproject.com/browser/django/trunk/django/contrib/webdesign/lorem_ipsum.py
12616  * <br><br>
12617  * Examples to test:<br>
12618  * <code>lipsum</code> – generates 30 words text.<br>
12619  * <code>lipsum*6</code> – generates 6 paragraphs (autowrapped with &lt;p&gt; element) of text.<br>
12620  * <code>ol>lipsum10*5</code> — generates ordered list with 5 list items (autowrapped with &lt;li&gt; tag)
12621  * with text of 10 words on each line<br>
12622  * <code>span*3>lipsum20</code> – generates 3 paragraphs of 20-words text, each wrapped with &lt;span&gt; element .
12623  * Each paragraph phrase is unique   
12624  * @param {Function} require
12625  * @param {Underscore} _ 
12626  * @constructor
12627  * @memberOf __loremIpsumGeneratorDefine
12628  */
12629 emmet.define('lorem', function(require, _) {
12630         var langs = {
12631                 en: {
12632                         common: ['lorem', 'ipsum', 'dolor', 'sit', 'amet', 'consectetur', 'adipisicing', 'elit'],
12633                         words: ['exercitationem', 'perferendis', 'perspiciatis', 'laborum', 'eveniet',
12634                                 'sunt', 'iure', 'nam', 'nobis', 'eum', 'cum', 'officiis', 'excepturi',
12635                                 'odio', 'consectetur', 'quasi', 'aut', 'quisquam', 'vel', 'eligendi',
12636                                 'itaque', 'non', 'odit', 'tempore', 'quaerat', 'dignissimos',
12637                                 'facilis', 'neque', 'nihil', 'expedita', 'vitae', 'vero', 'ipsum',
12638                                 'nisi', 'animi', 'cumque', 'pariatur', 'velit', 'modi', 'natus',
12639                                 'iusto', 'eaque', 'sequi', 'illo', 'sed', 'ex', 'et', 'voluptatibus',
12640                                 'tempora', 'veritatis', 'ratione', 'assumenda', 'incidunt', 'nostrum',
12641                                 'placeat', 'aliquid', 'fuga', 'provident', 'praesentium', 'rem',
12642                                 'necessitatibus', 'suscipit', 'adipisci', 'quidem', 'possimus',
12643                                 'voluptas', 'debitis', 'sint', 'accusantium', 'unde', 'sapiente',
12644                                 'voluptate', 'qui', 'aspernatur', 'laudantium', 'soluta', 'amet',
12645                                 'quo', 'aliquam', 'saepe', 'culpa', 'libero', 'ipsa', 'dicta',
12646                                 'reiciendis', 'nesciunt', 'doloribus', 'autem', 'impedit', 'minima',
12647                                 'maiores', 'repudiandae', 'ipsam', 'obcaecati', 'ullam', 'enim',
12648                                 'totam', 'delectus', 'ducimus', 'quis', 'voluptates', 'dolores',
12649                                 'molestiae', 'harum', 'dolorem', 'quia', 'voluptatem', 'molestias',
12650                                 'magni', 'distinctio', 'omnis', 'illum', 'dolorum', 'voluptatum', 'ea',
12651                                 'quas', 'quam', 'corporis', 'quae', 'blanditiis', 'atque', 'deserunt',
12652                                 'laboriosam', 'earum', 'consequuntur', 'hic', 'cupiditate',
12653                                 'quibusdam', 'accusamus', 'ut', 'rerum', 'error', 'minus', 'eius',
12654                                 'ab', 'ad', 'nemo', 'fugit', 'officia', 'at', 'in', 'id', 'quos',
12655                                 'reprehenderit', 'numquam', 'iste', 'fugiat', 'sit', 'inventore',
12656                                 'beatae', 'repellendus', 'magnam', 'recusandae', 'quod', 'explicabo',
12657                                 'doloremque', 'aperiam', 'consequatur', 'asperiores', 'commodi',
12658                                 'optio', 'dolor', 'labore', 'temporibus', 'repellat', 'veniam',
12659                                 'architecto', 'est', 'esse', 'mollitia', 'nulla', 'a', 'similique',
12660                                 'eos', 'alias', 'dolore', 'tenetur', 'deleniti', 'porro', 'facere',
12661                                 'maxime', 'corrupti']
12662                 },
12663                 ru: {
12664                         common: ['далеко-далеко', 'за', 'словесными', 'горами', 'в стране', 'гласных', 'и согласных', 'живут', 'рыбные', 'тексты'],
12665                         words: ['вдали', 'от всех', 'они', 'буквенных', 'домах', 'на берегу', 'семантика', 
12666                                 'большого', 'языкового', 'океана', 'маленький', 'ручеек', 'даль', 
12667                                 'журчит', 'по всей', 'обеспечивает', 'ее','всеми', 'необходимыми', 
12668                                 'правилами', 'эта', 'парадигматическая', 'страна', 'которой', 'жаренные', 
12669                                 'предложения', 'залетают', 'прямо', 'рот', 'даже', 'всемогущая', 
12670                                 'пунктуация', 'не', 'имеет', 'власти', 'над', 'рыбными', 'текстами', 
12671                                 'ведущими', 'безорфографичный', 'образ', 'жизни', 'однажды', 'одна', 
12672                                 'маленькая', 'строчка','рыбного', 'текста', 'имени', 'lorem', 'ipsum', 
12673                                 'решила', 'выйти', 'большой', 'мир', 'грамматики', 'великий', 'оксмокс', 
12674                                 'предупреждал', 'о', 'злых', 'запятых', 'диких', 'знаках', 'вопроса', 
12675                                 'коварных', 'точках', 'запятой', 'но', 'текст', 'дал', 'сбить', 
12676                                 'себя', 'толку', 'он', 'собрал', 'семь', 'своих', 'заглавных', 'букв', 
12677                                 'подпоясал', 'инициал', 'за', 'пояс', 'пустился', 'дорогу', 
12678                                 'взобравшись', 'первую', 'вершину', 'курсивных', 'гор', 'бросил', 
12679                                 'последний', 'взгляд', 'назад', 'силуэт', 'своего', 'родного', 'города', 
12680                                 'буквоград', 'заголовок', 'деревни', 'алфавит', 'подзаголовок', 'своего', 
12681                                 'переулка', 'грустный', 'реторический', 'вопрос', 'скатился', 'его', 
12682                                 'щеке', 'продолжил', 'свой', 'путь', 'дороге', 'встретил', 'рукопись', 
12683                                 'она', 'предупредила',  'моей', 'все', 'переписывается', 'несколько', 
12684                                 'раз', 'единственное', 'что', 'меня', 'осталось', 'это', 'приставка', 
12685                                 'возвращайся', 'ты', 'лучше', 'свою', 'безопасную', 'страну', 'послушавшись', 
12686                                 'рукописи', 'наш', 'продолжил', 'свой', 'путь', 'вскоре', 'ему', 
12687                                 'повстречался', 'коварный', 'составитель', 'рекламных', 'текстов', 
12688                                 'напоивший', 'языком', 'речью', 'заманивший', 'свое', 'агенство', 
12689                                 'которое', 'использовало', 'снова', 'снова', 'своих', 'проектах', 
12690                                 'если', 'переписали', 'то', 'живет', 'там', 'до', 'сих', 'пор']
12691                 }
12692         };
12693 
12694         var prefs = require('preferences');
12695         prefs.define('lorem.defaultLang', 'en');
12696 
12697         /**
12698          * @param {AbbreviationNode} tree
12699          */
12700         require('abbreviationParser').addPreprocessor(function(tree) {
12701                 var re = /^(?:lorem|lipsum)([a-z]{2})?(\d*)$/i, match;
12702                 
12703                 /** @param {AbbreviationNode} node */
12704                 tree.findAll(function(node) {
12705                         if (node._name && (match = node._name.match(re))) {
12706                                 var wordCound = match[2] || 30;
12707                                 var lang = match[1] || prefs.get('lorem.defaultLang') || 'en';
12708                                 
12709                                 // force node name resolving if node should be repeated
12710                                 // or contains attributes. In this case, node should be outputed
12711                                 // as tag, otherwise as text-only node
12712                                 node._name = '';
12713                                 node.data('forceNameResolving', node.isRepeating() || node.attributeList().length);
12714                                 node.data('pasteOverwrites', true);
12715                                 node.data('paste', function(i) {
12716                                         return paragraph(lang, wordCound, !i);
12717                                 });
12718                         }
12719                 });
12720         });
12721         
12722         /**
12723          * Returns random integer between <code>from</code> and <code>to</code> values
12724          * @param {Number} from
12725          * @param {Number} to
12726          * @returns {Number}
12727          */
12728         function randint(from, to) {
12729                 return Math.round(Math.random() * (to - from) + from);
12730         }
12731         
12732         /**
12733          * @param {Array} arr
12734          * @param {Number} count
12735          * @returns {Array}
12736          */
12737         function sample(arr, count) {
12738                 var len = arr.length;
12739                 var iterations = Math.min(len, count);
12740                 var result = [];
12741                 while (result.length < iterations) {
12742                         var randIx = randint(0, len - 1);
12743                         if (!_.include(result, randIx))
12744                                 result.push(randIx);
12745                 }
12746                 
12747                 return _.map(result, function(ix) {
12748                         return arr[ix];
12749                 });
12750         }
12751         
12752         function choice(val) {
12753                 if (_.isString(val))
12754                         return val.charAt(randint(0, val.length - 1));
12755                 
12756                 return val[randint(0, val.length - 1)];
12757         }
12758         
12759         function sentence(words, end) {
12760                 if (words.length) {
12761                         words[0] = words[0].charAt(0).toUpperCase() + words[0].substring(1);
12762                 }
12763                 
12764                 return words.join(' ') + (end || choice('?!...')); // more dots that question marks
12765         }
12766         
12767         /**
12768          * Insert commas at randomly selected words. This function modifies values
12769          * inside <code>words</code> array 
12770          * @param {Array} words
12771          */
12772         function insertCommas(words) {
12773                 var len = words.length;
12774                 var totalCommas = 0;
12775                 
12776                 if (len > 3 && len <= 6) {
12777                         totalCommas = randint(0, 1);
12778                 } else if (len > 6 && len <= 12) {
12779                         totalCommas = randint(0, 2);
12780                 } else {
12781                         totalCommas = randint(1, 4);
12782                 }
12783 
12784                 _.each(_.range(totalCommas), function(ix) {
12785                         if (ix < words.length - 1) {
12786                                 words[ix] += ',';
12787                         }
12788                 });
12789         }
12790         
12791         /**
12792          * Generate a paragraph of "Lorem ipsum" text
12793          * @param {Number} wordCount Words count in paragraph
12794          * @param {Boolean} startWithCommon Should paragraph start with common 
12795          * "lorem ipsum" sentence.
12796          * @returns {String}
12797          */
12798         function paragraph(lang, wordCount, startWithCommon) {
12799                 var data = langs[lang];
12800                 if (!data) {
12801                         return '';
12802                 }
12803 
12804                 var result = [];
12805                 var totalWords = 0;
12806                 var words;
12807                 
12808                 wordCount = parseInt(wordCount, 10);
12809                 
12810                 if (startWithCommon && data.common) {
12811                         words = data.common.slice(0, wordCount);
12812                         if (words.length > 5)
12813                                 words[4] += ',';
12814                         totalWords += words.length;
12815                         result.push(sentence(words, '.'));
12816                 }
12817                 
12818                 while (totalWords < wordCount) {
12819                         words = sample(data.words, Math.min(randint(3, 12) * randint(1, 5), wordCount - totalWords));
12820                         totalWords += words.length;
12821                         insertCommas(words);
12822                         result.push(sentence(words));
12823                 }
12824                 
12825                 return result.join(' ');
12826         }
12827 
12828         return {
12829                 /**
12830                  * Adds new language words for Lorem Ipsum generator
12831                  * @param {String} lang Two-letter lang definition
12832                  * @param {Object} data Words for language. Maight be either a space-separated 
12833                  * list of words (String), Array of words or object with <code>text</code> and
12834                  * <code>common</code> properties
12835                  */
12836                 addLang: function(lang, data) {
12837                         if (_.isString(data)) {
12838                                 data = {words: _.compact(data.split(' '))};
12839                         } else if (_.isArray(data)) {
12840                                 data = {words: data};
12841                         }
12842 
12843                         langs[lang] = data;
12844                 }
12845         };
12846 });/**
12847  * Select current line (for simple editors like browser's &lt;textarea&gt;)
12848  */
12849 emmet.exec(function(require, _) {
12850         require('actions').add('select_line', function(editor) {
12851                 var range = editor.getCurrentLineRange();
12852                 editor.createSelection(range.start, range.end);
12853                 return true;
12854         });
12855 });emmet.exec(function(require, _){require('resources').setVocabulary({
12856         "variables": {
12857                 "lang": "en",
12858                 "locale": "en-US",
12859                 "charset": "UTF-8",
12860                 "indentation": "\t",
12861                 "newline": "\n"
12862         },
12863         
12864         "css": {
12865                 "filters": "html",
12866                 "snippets": {
12867                         "@i": "@import url(|);",
12868                         "@import": "@import url(|);",
12869                         "@m": "@media ${1:screen} {\n\t|\n}",
12870                         "@media": "@media ${1:screen} {\n\t|\n}",
12871                         "@f": "@font-face {\n\tfont-family:|;\n\tsrc:url(|);\n}",
12872                         "@f+": "@font-face {\n\tfont-family: '${1:FontName}';\n\tsrc: url('${2:FileName}.eot');\n\tsrc: url('${2:FileName}.eot?#iefix') format('embedded-opentype'),\n\t\t url('${2:FileName}.woff') format('woff'),\n\t\t url('${2:FileName}.ttf') format('truetype'),\n\t\t url('${2:FileName}.svg#${1:FontName}') format('svg');\n\tfont-style: ${3:normal};\n\tfont-weight: ${4:normal};\n}",
12873 
12874                         "@kf": "@-webkit-keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}\n@-o-keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}\n@-moz-keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}\n@keyframes ${1:identifier} {\n\t${2:from} { ${3} }${6}\n\t${4:to} { ${5} }\n}",
12875 
12876 
12877                         "anim": "animation:|;",
12878                         "anim-": "animation:${1:name} ${2:duration} ${3:timing-function} ${4:delay} ${5:iteration-count} ${6:direction} ${7:fill-mode};",
12879                         "animdel": "animation-delay:${1:time};",
12880                         
12881                         "animdir": "animation-direction:${1:normal};",
12882                         "animdir:n": "animation-direction:normal;",
12883                         "animdir:r": "animation-direction:reverse;",
12884                         "animdir:a": "animation-direction:alternate;",
12885                         "animdir:ar": "animation-direction:alternate-reverse;",
12886                         
12887                         "animdur": "animation-duration:${1:0}s;",
12888                         
12889                         "animfm": "animation-fill-mode:${1:both};",
12890                         "animfm:f": "animation-fill-mode:forwards;",
12891                         "animfm:b": "animation-fill-mode:backwards;",
12892                         "animfm:bt": "animation-fill-mode:both;",
12893                         "animfm:bh": "animation-fill-mode:both;",
12894                         
12895                         "animic": "animation-iteration-count:${1:1};",
12896                         "animic:i": "animation-iteration-count:infinite;",
12897                         
12898                         "animn": "animation-name:${1:none};",
12899 
12900                         "animps": "animation-play-state:${1:running};",
12901                         "animps:p": "animation-play-state:paused;",
12902                         "animps:r": "animation-play-state:running;",
12903 
12904                         "animtf": "animation-timing-function:${1:linear};",
12905                         "animtf:e": "animation-timing-function:ease;",
12906                         "animtf:ei": "animation-timing-function:ease-in;",
12907                         "animtf:eo": "animation-timing-function:ease-out;",
12908                         "animtf:eio": "animation-timing-function:ease-in-out;",
12909                         "animtf:l": "animation-timing-function:linear;",
12910                         "animtf:cb": "animation-timing-function:cubic-bezier(${1:0.1}, ${2:0.7}, ${3:1.0}, ${3:0.1});",
12911                         
12912                         "ap": "appearance:${none};",
12913 
12914                         "!": "!important",
12915                         "pos": "position:${1:relative};",
12916                         "pos:s": "position:static;",
12917                         "pos:a": "position:absolute;",
12918                         "pos:r": "position:relative;",
12919                         "pos:f": "position:fixed;",
12920                         "t": "top:|;",
12921                         "t:a": "top:auto;",
12922                         "r": "right:|;",
12923                         "r:a": "right:auto;",
12924                         "b": "bottom:|;",
12925                         "b:a": "bottom:auto;",
12926                         "l": "left:|;",
12927                         "l:a": "left:auto;",
12928                         "z": "z-index:|;",
12929                         "z:a": "z-index:auto;",
12930                         "fl": "float:${1:left};",
12931                         "fl:n": "float:none;",
12932                         "fl:l": "float:left;",
12933                         "fl:r": "float:right;",
12934                         "cl": "clear:${1:both};",
12935                         "cl:n": "clear:none;",
12936                         "cl:l": "clear:left;",
12937                         "cl:r": "clear:right;",
12938                         "cl:b": "clear:both;",
12939 
12940                         "colm": "columns:|;",
12941                         "colmc": "column-count:|;",
12942                         "colmf": "column-fill:|;",
12943                         "colmg": "column-gap:|;",
12944                         "colmr": "column-rule:|;",
12945                         "colmrc": "column-rule-color:|;",
12946                         "colmrs": "column-rule-style:|;",
12947                         "colmrw": "column-rule-width:|;",
12948                         "colms": "column-span:|;",
12949                         "colmw": "column-width:|;",
12950 
12951                         "d": "display:${1:block};",
12952                         "d:n": "display:none;",
12953                         "d:b": "display:block;",
12954                         "d:i": "display:inline;",
12955                         "d:ib": "display:inline-block;",
12956                         "d:li": "display:list-item;",
12957                         "d:ri": "display:run-in;",
12958                         "d:cp": "display:compact;",
12959                         "d:tb": "display:table;",
12960                         "d:itb": "display:inline-table;",
12961                         "d:tbcp": "display:table-caption;",
12962                         "d:tbcl": "display:table-column;",
12963                         "d:tbclg": "display:table-column-group;",
12964                         "d:tbhg": "display:table-header-group;",
12965                         "d:tbfg": "display:table-footer-group;",
12966                         "d:tbr": "display:table-row;",
12967                         "d:tbrg": "display:table-row-group;",
12968                         "d:tbc": "display:table-cell;",
12969                         "d:rb": "display:ruby;",
12970                         "d:rbb": "display:ruby-base;",
12971                         "d:rbbg": "display:ruby-base-group;",
12972                         "d:rbt": "display:ruby-text;",
12973                         "d:rbtg": "display:ruby-text-group;",
12974                         "v": "visibility:${1:hidden};",
12975                         "v:v": "visibility:visible;",
12976                         "v:h": "visibility:hidden;",
12977                         "v:c": "visibility:collapse;",
12978                         "ov": "overflow:${1:hidden};",
12979                         "ov:v": "overflow:visible;",
12980                         "ov:h": "overflow:hidden;",
12981                         "ov:s": "overflow:scroll;",
12982                         "ov:a": "overflow:auto;",
12983                         "ovx": "overflow-x:${1:hidden};",
12984                         "ovx:v": "overflow-x:visible;",
12985                         "ovx:h": "overflow-x:hidden;",
12986                         "ovx:s": "overflow-x:scroll;",
12987                         "ovx:a": "overflow-x:auto;",
12988                         "ovy": "overflow-y:${1:hidden};",
12989                         "ovy:v": "overflow-y:visible;",
12990                         "ovy:h": "overflow-y:hidden;",
12991                         "ovy:s": "overflow-y:scroll;",
12992                         "ovy:a": "overflow-y:auto;",
12993                         "ovs": "overflow-style:${1:scrollbar};",
12994                         "ovs:a": "overflow-style:auto;",
12995                         "ovs:s": "overflow-style:scrollbar;",
12996                         "ovs:p": "overflow-style:panner;",
12997                         "ovs:m": "overflow-style:move;",
12998                         "ovs:mq": "overflow-style:marquee;",
12999                         "zoo": "zoom:1;",
13000                         "zm": "zoom:1;",
13001                         "cp": "clip:|;",
13002                         "cp:a": "clip:auto;",
13003                         "cp:r": "clip:rect(${1:top} ${2:right} ${3:bottom} ${4:left});",
13004                         "bxz": "box-sizing:${1:border-box};",
13005                         "bxz:cb": "box-sizing:content-box;",
13006                         "bxz:bb": "box-sizing:border-box;",
13007                         "bxsh": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:color};",
13008                         "bxsh:r": "box-shadow:${1:inset }${2:hoff} ${3:voff} ${4:blur} ${5:spread }rgb(${6:0}, ${7:0}, ${8:0});",
13009                         "bxsh:ra": "box-shadow:${1:inset }${2:h} ${3:v} ${4:blur} ${5:spread }rgba(${6:0}, ${7:0}, ${8:0}, .${9:5});",
13010                         "bxsh:n": "box-shadow:none;",
13011                         "m": "margin:|;",
13012                         "m:a": "margin:auto;",
13013                         "mt": "margin-top:|;",
13014                         "mt:a": "margin-top:auto;",
13015                         "mr": "margin-right:|;",
13016                         "mr:a": "margin-right:auto;",
13017                         "mb": "margin-bottom:|;",
13018                         "mb:a": "margin-bottom:auto;",
13019                         "ml": "margin-left:|;",
13020                         "ml:a": "margin-left:auto;",
13021                         "p": "padding:|;",
13022                         "pt": "padding-top:|;",
13023                         "pr": "padding-right:|;",
13024                         "pb": "padding-bottom:|;",
13025                         "pl": "padding-left:|;",
13026                         "w": "width:|;",
13027                         "w:a": "width:auto;",
13028                         "h": "height:|;",
13029                         "h:a": "height:auto;",
13030                         "maw": "max-width:|;",
13031                         "maw:n": "max-width:none;",
13032                         "mah": "max-height:|;",
13033                         "mah:n": "max-height:none;",
13034                         "miw": "min-width:|;",
13035                         "mih": "min-height:|;",
13036                         "mar": "max-resolution:${1:res};",
13037                         "mir": "min-resolution:${1:res};",
13038                         "ori": "orientation:|;",
13039                         "ori:l": "orientation:landscape;",
13040                         "ori:p": "orientation:portrait;",
13041                         "ol": "outline:|;",
13042                         "ol:n": "outline:none;",
13043                         "olo": "outline-offset:|;",
13044                         "olw": "outline-width:|;",
13045                         "olw:tn": "outline-width:thin;",
13046                         "olw:m": "outline-width:medium;",
13047                         "olw:tc": "outline-width:thick;",
13048                         "ols": "outline-style:|;",
13049                         "ols:n": "outline-style:none;",
13050                         "ols:dt": "outline-style:dotted;",
13051                         "ols:ds": "outline-style:dashed;",
13052                         "ols:s": "outline-style:solid;",
13053                         "ols:db": "outline-style:double;",
13054                         "ols:g": "outline-style:groove;",
13055                         "ols:r": "outline-style:ridge;",
13056                         "ols:i": "outline-style:inset;",
13057                         "ols:o": "outline-style:outset;",
13058                         "olc": "outline-color:#${1:000};",
13059                         "olc:i": "outline-color:invert;",
13060                         "bd": "border:|;",
13061                         "bd+": "border:${1:1px} ${2:solid} ${3:#000};",
13062                         "bd:n": "border:none;",
13063                         "bdbk": "border-break:${1:close};",
13064                         "bdbk:c": "border-break:close;",
13065                         "bdcl": "border-collapse:|;",
13066                         "bdcl:c": "border-collapse:collapse;",
13067                         "bdcl:s": "border-collapse:separate;",
13068                         "bdc": "border-color:#${1:000};",
13069                         "bdc:t": "border-color:transparent;",
13070                         "bdi": "border-image:url(|);",
13071                         "bdi:n": "border-image:none;",
13072                         "bdti": "border-top-image:url(|);",
13073                         "bdti:n": "border-top-image:none;",
13074                         "bdri": "border-right-image:url(|);",
13075                         "bdri:n": "border-right-image:none;",
13076                         "bdbi": "border-bottom-image:url(|);",
13077                         "bdbi:n": "border-bottom-image:none;",
13078                         "bdli": "border-left-image:url(|);",
13079                         "bdli:n": "border-left-image:none;",
13080                         "bdci": "border-corner-image:url(|);",
13081                         "bdci:n": "border-corner-image:none;",
13082                         "bdci:c": "border-corner-image:continue;",
13083                         "bdtli": "border-top-left-image:url(|);",
13084                         "bdtli:n": "border-top-left-image:none;",
13085                         "bdtli:c": "border-top-left-image:continue;",
13086                         "bdtri": "border-top-right-image:url(|);",
13087                         "bdtri:n": "border-top-right-image:none;",
13088                         "bdtri:c": "border-top-right-image:continue;",
13089                         "bdbri": "border-bottom-right-image:url(|);",
13090                         "bdbri:n": "border-bottom-right-image:none;",
13091                         "bdbri:c": "border-bottom-right-image:continue;",
13092                         "bdbli": "border-bottom-left-image:url(|);",
13093                         "bdbli:n": "border-bottom-left-image:none;",
13094                         "bdbli:c": "border-bottom-left-image:continue;",
13095                         "bdf": "border-fit:${1:repeat};",
13096                         "bdf:c": "border-fit:clip;",
13097                         "bdf:r": "border-fit:repeat;",
13098                         "bdf:sc": "border-fit:scale;",
13099                         "bdf:st": "border-fit:stretch;",
13100                         "bdf:ow": "border-fit:overwrite;",
13101                         "bdf:of": "border-fit:overflow;",
13102                         "bdf:sp": "border-fit:space;",
13103                         "bdlen": "border-length:|;",
13104                         "bdlen:a": "border-length:auto;",
13105                         "bdsp": "border-spacing:|;",
13106                         "bds": "border-style:|;",
13107                         "bds:n": "border-style:none;",
13108                         "bds:h": "border-style:hidden;",
13109                         "bds:dt": "border-style:dotted;",
13110                         "bds:ds": "border-style:dashed;",
13111                         "bds:s": "border-style:solid;",
13112                         "bds:db": "border-style:double;",
13113                         "bds:dtds": "border-style:dot-dash;",
13114                         "bds:dtdtds": "border-style:dot-dot-dash;",
13115                         "bds:w": "border-style:wave;",
13116                         "bds:g": "border-style:groove;",
13117                         "bds:r": "border-style:ridge;",
13118                         "bds:i": "border-style:inset;",
13119                         "bds:o": "border-style:outset;",
13120                         "bdw": "border-width:|;",
13121                         "bdtw": "border-top-width:|;",
13122                         "bdrw": "border-right-width:|;",
13123                         "bdbw": "border-bottom-width:|;",
13124                         "bdlw": "border-left-width:|;",
13125                         "bdt": "border-top:|;",
13126                         "bt": "border-top:|;",
13127                         "bdt+": "border-top:${1:1px} ${2:solid} ${3:#000};",
13128                         "bdt:n": "border-top:none;",
13129                         "bdts": "border-top-style:|;",
13130                         "bdts:n": "border-top-style:none;",
13131                         "bdtc": "border-top-color:#${1:000};",
13132                         "bdtc:t": "border-top-color:transparent;",
13133                         "bdr": "border-right:|;",
13134                         "br": "border-right:|;",
13135                         "bdr+": "border-right:${1:1px} ${2:solid} ${3:#000};",
13136                         "bdr:n": "border-right:none;",
13137                         "bdrst": "border-right-style:|;",
13138                         "bdrst:n": "border-right-style:none;",
13139                         "bdrc": "border-right-color:#${1:000};",
13140                         "bdrc:t": "border-right-color:transparent;",
13141                         "bdb": "border-bottom:|;",
13142                         "bb": "border-bottom:|;",
13143                         "bdb+": "border-bottom:${1:1px} ${2:solid} ${3:#000};",
13144                         "bdb:n": "border-bottom:none;",
13145                         "bdbs": "border-bottom-style:|;",
13146                         "bdbs:n": "border-bottom-style:none;",
13147                         "bdbc": "border-bottom-color:#${1:000};",
13148                         "bdbc:t": "border-bottom-color:transparent;",
13149                         "bdl": "border-left:|;",
13150                         "bl": "border-left:|;",
13151                         "bdl+": "border-left:${1:1px} ${2:solid} ${3:#000};",
13152                         "bdl:n": "border-left:none;",
13153                         "bdls": "border-left-style:|;",
13154                         "bdls:n": "border-left-style:none;",
13155                         "bdlc": "border-left-color:#${1:000};",
13156                         "bdlc:t": "border-left-color:transparent;",
13157                         "bdrs": "border-radius:|;",
13158                         "bdtrrs": "border-top-right-radius:|;",
13159                         "bdtlrs": "border-top-left-radius:|;",
13160                         "bdbrrs": "border-bottom-right-radius:|;",
13161                         "bdblrs": "border-bottom-left-radius:|;",
13162                         "bg": "background:#${1:000};",
13163                         "bg+": "background:${1:#fff} url(${2}) ${3:0} ${4:0} ${5:no-repeat};",
13164                         "bg:n": "background:none;",
13165                         "bg:ie": "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='${1:x}.png',sizingMethod='${2:crop}');",
13166                         "bgc": "background-color:#${1:fff};",
13167                         "bgc:t": "background-color:transparent;",
13168                         "bgi": "background-image:url(|);",
13169                         "bgi:n": "background-image:none;",
13170                         "bgr": "background-repeat:|;",
13171                         "bgr:n": "background-repeat:no-repeat;",
13172                         "bgr:x": "background-repeat:repeat-x;",
13173                         "bgr:y": "background-repeat:repeat-y;",
13174                         "bgr:sp": "background-repeat:space;",
13175                         "bgr:rd": "background-repeat:round;",
13176                         "bga": "background-attachment:|;",
13177                         "bga:f": "background-attachment:fixed;",
13178                         "bga:s": "background-attachment:scroll;",
13179                         "bgp": "background-position:${1:0} ${2:0};",
13180                         "bgpx": "background-position-x:|;",
13181                         "bgpy": "background-position-y:|;",
13182                         "bgbk": "background-break:|;",
13183                         "bgbk:bb": "background-break:bounding-box;",
13184                         "bgbk:eb": "background-break:each-box;",
13185                         "bgbk:c": "background-break:continuous;",
13186                         "bgcp": "background-clip:${1:padding-box};",
13187                         "bgcp:bb": "background-clip:border-box;",
13188                         "bgcp:pb": "background-clip:padding-box;",
13189                         "bgcp:cb": "background-clip:content-box;",
13190                         "bgcp:nc": "background-clip:no-clip;",
13191                         "bgo": "background-origin:|;",
13192                         "bgo:pb": "background-origin:padding-box;",
13193                         "bgo:bb": "background-origin:border-box;",
13194                         "bgo:cb": "background-origin:content-box;",
13195                         "bgsz": "background-size:|;",
13196                         "bgsz:a": "background-size:auto;",
13197                         "bgsz:ct": "background-size:contain;",
13198                         "bgsz:cv": "background-size:cover;",
13199                         "c": "color:#${1:000};",
13200                         "c:r": "color:rgb(${1:0}, ${2:0}, ${3:0});",
13201                         "c:ra": "color:rgba(${1:0}, ${2:0}, ${3:0}, .${4:5});",
13202                         "cm": "/* |${child} */",
13203                         "cnt": "content:'|';",
13204                         "cnt:n": "content:normal;",
13205                         "cnt:oq": "content:open-quote;",
13206                         "cnt:noq": "content:no-open-quote;",
13207                         "cnt:cq": "content:close-quote;",
13208                         "cnt:ncq": "content:no-close-quote;",
13209                         "cnt:a": "content:attr(|);",
13210                         "cnt:c": "content:counter(|);",
13211                         "cnt:cs": "content:counters(|);",
13212 
13213 
13214                         "tbl": "table-layout:|;",
13215                         "tbl:a": "table-layout:auto;",
13216                         "tbl:f": "table-layout:fixed;",
13217                         "cps": "caption-side:|;",
13218                         "cps:t": "caption-side:top;",
13219                         "cps:b": "caption-side:bottom;",
13220                         "ec": "empty-cells:|;",
13221                         "ec:s": "empty-cells:show;",
13222                         "ec:h": "empty-cells:hide;",
13223                         "lis": "list-style:|;",
13224                         "lis:n": "list-style:none;",
13225                         "lisp": "list-style-position:|;",
13226                         "lisp:i": "list-style-position:inside;",
13227                         "lisp:o": "list-style-position:outside;",
13228                         "list": "list-style-type:|;",
13229                         "list:n": "list-style-type:none;",
13230                         "list:d": "list-style-type:disc;",
13231                         "list:c": "list-style-type:circle;",
13232                         "list:s": "list-style-type:square;",
13233                         "list:dc": "list-style-type:decimal;",
13234                         "list:dclz": "list-style-type:decimal-leading-zero;",
13235                         "list:lr": "list-style-type:lower-roman;",
13236                         "list:ur": "list-style-type:upper-roman;",
13237                         "lisi": "list-style-image:|;",
13238                         "lisi:n": "list-style-image:none;",
13239                         "q": "quotes:|;",
13240                         "q:n": "quotes:none;",
13241                         "q:ru": "quotes:'\\00AB' '\\00BB' '\\201E' '\\201C';",
13242                         "q:en": "quotes:'\\201C' '\\201D' '\\2018' '\\2019';",
13243                         "ct": "content:|;",
13244                         "ct:n": "content:normal;",
13245                         "ct:oq": "content:open-quote;",
13246                         "ct:noq": "content:no-open-quote;",
13247                         "ct:cq": "content:close-quote;",
13248                         "ct:ncq": "content:no-close-quote;",
13249                         "ct:a": "content:attr(|);",
13250                         "ct:c": "content:counter(|);",
13251                         "ct:cs": "content:counters(|);",
13252                         "coi": "counter-increment:|;",
13253                         "cor": "counter-reset:|;",
13254                         "va": "vertical-align:${1:top};",
13255                         "va:sup": "vertical-align:super;",
13256                         "va:t": "vertical-align:top;",
13257                         "va:tt": "vertical-align:text-top;",
13258                         "va:m": "vertical-align:middle;",
13259                         "va:bl": "vertical-align:baseline;",
13260                         "va:b": "vertical-align:bottom;",
13261                         "va:tb": "vertical-align:text-bottom;",
13262                         "va:sub": "vertical-align:sub;",
13263                         "ta": "text-align:${1:left};",
13264                         "ta:l": "text-align:left;",
13265                         "ta:c": "text-align:center;",
13266                         "ta:r": "text-align:right;",
13267                         "ta:j": "text-align:justify;",
13268                         "ta-lst": "text-align-last:|;",
13269                         "tal:a": "text-align-last:auto;",
13270                         "tal:l": "text-align-last:left;",
13271                         "tal:c": "text-align-last:center;",
13272                         "tal:r": "text-align-last:right;",
13273                         "td": "text-decoration:${1:none};",
13274                         "td:n": "text-decoration:none;",
13275                         "td:u": "text-decoration:underline;",
13276                         "td:o": "text-decoration:overline;",
13277                         "td:l": "text-decoration:line-through;",
13278                         "te": "text-emphasis:|;",
13279                         "te:n": "text-emphasis:none;",
13280                         "te:ac": "text-emphasis:accent;",
13281                         "te:dt": "text-emphasis:dot;",
13282                         "te:c": "text-emphasis:circle;",
13283                         "te:ds": "text-emphasis:disc;",
13284                         "te:b": "text-emphasis:before;",
13285                         "te:a": "text-emphasis:after;",
13286                         "th": "text-height:|;",
13287                         "th:a": "text-height:auto;",
13288                         "th:f": "text-height:font-size;",
13289                         "th:t": "text-height:text-size;",
13290                         "th:m": "text-height:max-size;",
13291                         "ti": "text-indent:|;",
13292                         "ti:-": "text-indent:-9999px;",
13293                         "tj": "text-justify:|;",
13294                         "tj:a": "text-justify:auto;",
13295                         "tj:iw": "text-justify:inter-word;",
13296                         "tj:ii": "text-justify:inter-ideograph;",
13297                         "tj:ic": "text-justify:inter-cluster;",
13298                         "tj:d": "text-justify:distribute;",
13299                         "tj:k": "text-justify:kashida;",
13300                         "tj:t": "text-justify:tibetan;",
13301                         "tov": "text-overflow:${ellipsis};",
13302                         "tov:e": "text-overflow:ellipsis;",
13303                         "tov:c": "text-overflow:clip;",
13304                         "to": "text-outline:|;",
13305                         "to+": "text-outline:${1:0} ${2:0} ${3:#000};",
13306                         "to:n": "text-outline:none;",
13307                         "tr": "text-replace:|;",
13308                         "tr:n": "text-replace:none;",
13309                         "tt": "text-transform:${1:uppercase};",
13310                         "tt:n": "text-transform:none;",
13311                         "tt:c": "text-transform:capitalize;",
13312                         "tt:u": "text-transform:uppercase;",
13313                         "tt:l": "text-transform:lowercase;",
13314                         "tw": "text-wrap:|;",
13315                         "tw:n": "text-wrap:normal;",
13316                         "tw:no": "text-wrap:none;",
13317                         "tw:u": "text-wrap:unrestricted;",
13318                         "tw:s": "text-wrap:suppress;",
13319                         "tsh": "text-shadow:${1:hoff} ${2:voff} ${3:blur} ${4:#000};",
13320                         "tsh:r": "text-shadow:${1:h} ${2:v} ${3:blur} rgb(${4:0}, ${5:0}, ${6:0});",
13321                         "tsh:ra": "text-shadow:${1:h} ${2:v} ${3:blur} rgba(${4:0}, ${5:0}, ${6:0}, .${7:5});",
13322                         "tsh+": "text-shadow:${1:0} ${2:0} ${3:0} ${4:#000};",
13323                         "tsh:n": "text-shadow:none;",
13324                         "trf": "transform:|;",
13325                         "trf:skx": "transform: skewX(${1:angle});",
13326                         "trf:sky": "transform: skewY(${1:angle});",
13327                         "trf:sc": "transform: scale(${1:x}, ${2:y});",
13328                         "trf:scx": "transform: scaleX(${1:x});",
13329                         "trf:scy": "transform: scaleY(${1:y});",
13330                         "trf:r": "transform: rotate(${1:angle});",
13331                         "trf:t": "transform: translate(${1:x}, ${2:y});",
13332                         "trf:tx": "transform: translateX(${1:x});",
13333                         "trf:ty": "transform: translateY(${1:y});",
13334                         "trfo": "transform-origin:|;",
13335                         "trfs": "transform-style:${1:preserve-3d};",
13336                         "trs": "transition:${1:prop} ${2:time};",
13337                         "trsde": "transition-delay:${1:time};",
13338                         "trsdu": "transition-duration:${1:time};",
13339                         "trsp": "transition-property:${1:prop};",
13340                         "trstf": "transition-timing-function:${1:tfunc};",
13341                         "lh": "line-height:|;",
13342                         "whs": "white-space:|;",
13343                         "whs:n": "white-space:normal;",
13344                         "whs:p": "white-space:pre;",
13345                         "whs:nw": "white-space:nowrap;",
13346                         "whs:pw": "white-space:pre-wrap;",
13347                         "whs:pl": "white-space:pre-line;",
13348                         "whsc": "white-space-collapse:|;",
13349                         "whsc:n": "white-space-collapse:normal;",
13350                         "whsc:k": "white-space-collapse:keep-all;",
13351                         "whsc:l": "white-space-collapse:loose;",
13352                         "whsc:bs": "white-space-collapse:break-strict;",
13353                         "whsc:ba": "white-space-collapse:break-all;",
13354                         "wob": "word-break:|;",
13355                         "wob:n": "word-break:normal;",
13356                         "wob:k": "word-break:keep-all;",
13357                         "wob:ba": "word-break:break-all;",
13358                         "wos": "word-spacing:|;",
13359                         "wow": "word-wrap:|;",
13360                         "wow:nm": "word-wrap:normal;",
13361                         "wow:n": "word-wrap:none;",
13362                         "wow:u": "word-wrap:unrestricted;",
13363                         "wow:s": "word-wrap:suppress;",
13364                         "wow:b": "word-wrap:break-word;",
13365                         "wm": "writing-mode:${1:lr-tb};",
13366                         "wm:lrt": "writing-mode:lr-tb;",
13367                         "wm:lrb": "writing-mode:lr-bt;",
13368                         "wm:rlt": "writing-mode:rl-tb;",
13369                         "wm:rlb": "writing-mode:rl-bt;",
13370                         "wm:tbr": "writing-mode:tb-rl;",
13371                         "wm:tbl": "writing-mode:tb-lr;",
13372                         "wm:btl": "writing-mode:bt-lr;",
13373                         "wm:btr": "writing-mode:bt-rl;",
13374                         "lts": "letter-spacing:|;",
13375                         "lts-n": "letter-spacing:normal;",
13376                         "f": "font:|;",
13377                         "f+": "font:${1:1em} ${2:Arial,sans-serif};",
13378                         "fw": "font-weight:|;",
13379                         "fw:n": "font-weight:normal;",
13380                         "fw:b": "font-weight:bold;",
13381                         "fw:br": "font-weight:bolder;",
13382                         "fw:lr": "font-weight:lighter;",
13383                         "fs": "font-style:${italic};",
13384                         "fs:n": "font-style:normal;",
13385                         "fs:i": "font-style:italic;",
13386                         "fs:o": "font-style:oblique;",
13387                         "fv": "font-variant:|;",
13388                         "fv:n": "font-variant:normal;",
13389                         "fv:sc": "font-variant:small-caps;",
13390                         "fz": "font-size:|;",
13391                         "fza": "font-size-adjust:|;",
13392                         "fza:n": "font-size-adjust:none;",
13393                         "ff": "font-family:|;",
13394                         "ff:s": "font-family:serif;",
13395                         "ff:ss": "font-family:sans-serif;",
13396                         "ff:c": "font-family:cursive;",
13397                         "ff:f": "font-family:fantasy;",
13398                         "ff:m": "font-family:monospace;",
13399                         "ff:a": "font-family: Arial, \"Helvetica Neue\", Helvetica, sans-serif;",
13400                         "ff:t": "font-family: \"Times New Roman\", Times, Baskerville, Georgia, serif;",
13401                         "ff:v": "font-family: Verdana, Geneva, sans-serif;",
13402                         "fef": "font-effect:|;",
13403                         "fef:n": "font-effect:none;",
13404                         "fef:eg": "font-effect:engrave;",
13405                         "fef:eb": "font-effect:emboss;",
13406                         "fef:o": "font-effect:outline;",
13407                         "fem": "font-emphasize:|;",
13408                         "femp": "font-emphasize-position:|;",
13409                         "femp:b": "font-emphasize-position:before;",
13410                         "femp:a": "font-emphasize-position:after;",
13411                         "fems": "font-emphasize-style:|;",
13412                         "fems:n": "font-emphasize-style:none;",
13413                         "fems:ac": "font-emphasize-style:accent;",
13414                         "fems:dt": "font-emphasize-style:dot;",
13415                         "fems:c": "font-emphasize-style:circle;",
13416                         "fems:ds": "font-emphasize-style:disc;",
13417                         "fsm": "font-smooth:|;",
13418                         "fsm:a": "font-smooth:auto;",
13419                         "fsm:n": "font-smooth:never;",
13420                         "fsm:aw": "font-smooth:always;",
13421                         "fst": "font-stretch:|;",
13422                         "fst:n": "font-stretch:normal;",
13423                         "fst:uc": "font-stretch:ultra-condensed;",
13424                         "fst:ec": "font-stretch:extra-condensed;",
13425                         "fst:c": "font-stretch:condensed;",
13426                         "fst:sc": "font-stretch:semi-condensed;",
13427                         "fst:se": "font-stretch:semi-expanded;",
13428                         "fst:e": "font-stretch:expanded;",
13429                         "fst:ee": "font-stretch:extra-expanded;",
13430                         "fst:ue": "font-stretch:ultra-expanded;",
13431                         "op": "opacity:|;",
13432                         "op:ie": "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity=100);",
13433                         "op:ms": "-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';",
13434                         "rsz": "resize:|;",
13435                         "rsz:n": "resize:none;",
13436                         "rsz:b": "resize:both;",
13437                         "rsz:h": "resize:horizontal;",
13438                         "rsz:v": "resize:vertical;",
13439                         "cur": "cursor:${pointer};",
13440                         "cur:a": "cursor:auto;",
13441                         "cur:d": "cursor:default;",
13442                         "cur:c": "cursor:crosshair;",
13443                         "cur:ha": "cursor:hand;",
13444                         "cur:he": "cursor:help;",
13445                         "cur:m": "cursor:move;",
13446                         "cur:p": "cursor:pointer;",
13447                         "cur:t": "cursor:text;",
13448                         "pgbb": "page-break-before:|;",
13449                         "pgbb:au": "page-break-before:auto;",
13450                         "pgbb:al": "page-break-before:always;",
13451                         "pgbb:l": "page-break-before:left;",
13452                         "pgbb:r": "page-break-before:right;",
13453                         "pgbi": "page-break-inside:|;",
13454                         "pgbi:au": "page-break-inside:auto;",
13455                         "pgbi:av": "page-break-inside:avoid;",
13456                         "pgba": "page-break-after:|;",
13457                         "pgba:au": "page-break-after:auto;",
13458                         "pgba:al": "page-break-after:always;",
13459                         "pgba:l": "page-break-after:left;",
13460                         "pgba:r": "page-break-after:right;",
13461                         "orp": "orphans:|;",
13462                         "us": "user-select:${none};",
13463                         "wid": "widows:|;",
13464                         "wfsm": "-webkit-font-smoothing:${antialiased};",
13465                         "wfsm:a": "-webkit-font-smoothing:antialiased;",
13466                         "wfsm:s": "-webkit-font-smoothing:subpixel-antialiased;",
13467                         "wfsm:sa": "-webkit-font-smoothing:subpixel-antialiased;",
13468                         "wfsm:n": "-webkit-font-smoothing:none;"
13469                 }
13470         },
13471         
13472         "html": {
13473                 "filters": "html",
13474                 "profile": "html",
13475                 "snippets": {
13476                         "!!!":    "<!doctype html>",
13477                         "!!!4t":  "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">",
13478                         "!!!4s":  "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">",
13479                         "!!!xt":  "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">",
13480                         "!!!xs":  "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">",
13481                         "!!!xxs": "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">",
13482 
13483                         "c": "<!-- |${child} -->",
13484                         "cc:ie6": "<!--[if lte IE 6]>\n\t${child}|\n<![endif]-->",
13485                         "cc:ie": "<!--[if IE]>\n\t${child}|\n<![endif]-->",
13486                         "cc:noie": "<!--[if !IE]><!-->\n\t${child}|\n<!--<![endif]-->"
13487                 },
13488                 
13489                 "abbreviations": {
13490                         "!": "html:5",
13491                         "a": "<a href=\"\">",
13492                         "a:link": "<a href=\"http://|\">",
13493                         "a:mail": "<a href=\"mailto:|\">",
13494                         "abbr": "<abbr title=\"\">",
13495                         "acronym": "<acronym title=\"\">",
13496                         "base": "<base href=\"\" />",
13497                         "basefont": "<basefont/>",
13498                         "br": "<br/>",
13499                         "frame": "<frame/>",
13500                         "hr": "<hr/>",
13501                         "bdo": "<bdo dir=\"\">",
13502                         "bdo:r": "<bdo dir=\"rtl\">",
13503                         "bdo:l": "<bdo dir=\"ltr\">",
13504                         "col": "<col/>",
13505                         "link": "<link rel=\"stylesheet\" href=\"\" />",
13506                         "link:css": "<link rel=\"stylesheet\" href=\"${1:style}.css\" />",
13507                         "link:print": "<link rel=\"stylesheet\" href=\"${1:print}.css\" media=\"print\" />",
13508                         "link:favicon": "<link rel=\"shortcut icon\" type=\"image/x-icon\" href=\"${1:favicon.ico}\" />",
13509                         "link:touch": "<link rel=\"apple-touch-icon\" href=\"${1:favicon.png}\" />",
13510                         "link:rss": "<link rel=\"alternate\" type=\"application/rss+xml\" title=\"RSS\" href=\"${1:rss.xml}\" />",
13511                         "link:atom": "<link rel=\"alternate\" type=\"application/atom+xml\" title=\"Atom\" href=\"${1:atom.xml}\" />",
13512                         "meta": "<meta/>",
13513                         "meta:utf": "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\" />",
13514                         "meta:win": "<meta http-equiv=\"Content-Type\" content=\"text/html;charset=windows-1251\" />",
13515                         "meta:vp": "<meta name=\"viewport\" content=\"width=${1:device-width}, user-scalable=${2:no}, initial-scale=${3:1.0}, maximum-scale=${4:1.0}, minimum-scale=${5:1.0}\" />",
13516                         "meta:compat": "<meta http-equiv=\"X-UA-Compatible\" content=\"${1:IE=7}\" />",
13517                         "style": "<style>",
13518                         "script": "<script>",
13519                         "script:src": "<script src=\"\">",
13520                         "img": "<img src=\"\" alt=\"\" />",
13521                         "iframe": "<iframe src=\"\" frameborder=\"0\">",
13522                         "embed": "<embed src=\"\" type=\"\" />",
13523                         "object": "<object data=\"\" type=\"\">",
13524                         "param": "<param name=\"\" value=\"\" />",
13525                         "map": "<map name=\"\">",
13526                         "area": "<area shape=\"\" coords=\"\" href=\"\" alt=\"\" />",
13527                         "area:d": "<area shape=\"default\" href=\"\" alt=\"\" />",
13528                         "area:c": "<area shape=\"circle\" coords=\"\" href=\"\" alt=\"\" />",
13529                         "area:r": "<area shape=\"rect\" coords=\"\" href=\"\" alt=\"\" />",
13530                         "area:p": "<area shape=\"poly\" coords=\"\" href=\"\" alt=\"\" />",
13531                         "form": "<form action=\"\">",
13532                         "form:get": "<form action=\"\" method=\"get\">",
13533                         "form:post": "<form action=\"\" method=\"post\">",
13534                         "label": "<label for=\"\">",
13535                         "input": "<input type=\"${1:text}\" />",
13536                         "inp": "<input type=\"${1:text}\" name=\"\" id=\"\" />",
13537                         "input:hidden": "input[type=hidden name]",
13538                         "input:h": "input:hidden",
13539                         "input:text": "inp",
13540                         "input:t": "inp",
13541                         "input:search": "inp[type=search]",
13542                         "input:email": "inp[type=email]",
13543                         "input:url": "inp[type=url]",
13544                         "input:password": "inp[type=password]",
13545                         "input:p": "input:password",
13546                         "input:datetime": "inp[type=datetime]",
13547                         "input:date": "inp[type=date]",
13548                         "input:datetime-local": "inp[type=datetime-local]",
13549                         "input:month": "inp[type=month]",
13550                         "input:week": "inp[type=week]",
13551                         "input:time": "inp[type=time]",
13552                         "input:number": "inp[type=number]",
13553                         "input:color": "inp[type=color]",
13554                         "input:checkbox": "inp[type=checkbox]",
13555                         "input:c": "input:checkbox",
13556                         "input:radio": "inp[type=radio]",
13557                         "input:r": "input:radio",
13558                         "input:range": "inp[type=range]",
13559                         "input:file": "inp[type=file]",
13560                         "input:f": "input:file",
13561                         "input:submit": "<input type=\"submit\" value=\"\" />",
13562                         "input:s": "input:submit",
13563                         "input:image": "<input type=\"image\" src=\"\" alt=\"\" />",
13564                         "input:i": "input:image",
13565                         "input:button": "<input type=\"button\" value=\"\" />",
13566                         "input:b": "input:button",
13567                         "isindex": "<isindex/>",
13568                         "input:reset": "input:button[type=reset]",
13569                         "select": "<select name=\"\" id=\"\">",
13570                         "select:disabled": "select[disabled]",
13571                         "select:d": "select[disabled]",
13572                         "option": "<option value=\"\">",
13573                         "textarea": "<textarea name=\"\" id=\"\" cols=\"${1:30}\" rows=\"${2:10}\">",
13574                         "marquee": "<marquee behavior=\"\" direction=\"\">",
13575                         "menu:context": "menu[type=context]>",
13576                         "menu:c": "menu:context",
13577                         "menu:toolbar": "menu[type=toolbar]>",
13578                         "menu:t": "menu:toolbar",
13579                         "video": "<video src=\"\">",
13580                         "audio": "<audio src=\"\">",
13581                         "html:xml": "<html xmlns=\"http://www.w3.org/1999/xhtml\">",
13582                         "keygen": "<keygen/>",
13583                         "command": "<command/>",
13584                         "button:submit" : "button[type=submit]",
13585                         "button:s" : "button[type=submit]",
13586                         "button:reset" : "button[type=reset]",
13587                         "button:r" : "button[type=reset]",
13588                         "button:disabled" : "button[disabled]",
13589                         "button:d" : "button[disabled]",
13590                         "fieldset:disabled" : "fieldset[disabled]",
13591                         "fieldset:d" : "fieldset[disabled]",
13592                         
13593                         "bq": "blockquote",
13594                         "acr": "acronym",
13595                         "fig": "figure",
13596                         "figc": "figcaption",
13597                         "ifr": "iframe",
13598                         "emb": "embed",
13599                         "obj": "object",
13600                         "src": "source",
13601                         "cap": "caption",
13602                         "colg": "colgroup",
13603                         "fst": "fieldset",
13604                         "fst:d": "fieldset[disabled]",
13605                         "btn": "button",
13606                         "btn:b": "button[type=button]",
13607                         "btn:r": "button[type=reset]",
13608                         "btn:s": "button[type=submit]",
13609                         "btn:d": "button[disabled]",
13610                         "optg": "optgroup",
13611                         "opt": "option",
13612                         "tarea": "textarea",
13613                         "leg": "legend",
13614                         "sect": "section",
13615                         "art": "article",
13616                         "hdr": "header",
13617                         "ftr": "footer",
13618                         "adr": "address",
13619                         "dlg": "dialog",
13620                         "str": "strong",
13621                         "prog": "progress",
13622                         "fset": "fieldset",
13623                         "fset:d": "fieldset[disabled]",
13624                         "datag": "datagrid",
13625                         "datal": "datalist",
13626                         "kg": "keygen",
13627                         "out": "output",
13628                         "det": "details",
13629                         "cmd": "command",
13630                         "doc": "html>(head>meta[charset=UTF-8]+title{${1:Document}})+body",
13631                         "doc4": "html>(head>meta[http-equiv=\"Content-Type\" content=\"text/html;charset=${charset}\"]+title{${1:Document}})+body",
13632 
13633                         "html:4t":  "!!!4t+doc4[lang=${lang}]",
13634                         "html:4s":  "!!!4s+doc4[lang=${lang}]",
13635                         "html:xt":  "!!!xt+doc4[xmlns=http://www.w3.org/1999/xhtml xml:lang=${lang}]",
13636                         "html:xs":  "!!!xs+doc4[xmlns=http://www.w3.org/1999/xhtml xml:lang=${lang}]",
13637                         "html:xxs": "!!!xxs+doc4[xmlns=http://www.w3.org/1999/xhtml xml:lang=${lang}]",
13638                         "html:5":   "!!!+doc[lang=${lang}]",
13639                         
13640                         "ol+": "ol>li",
13641                         "ul+": "ul>li",
13642                         "dl+": "dl>dt+dd",
13643                         "map+": "map>area",
13644                         "table+": "table>tr>td",
13645                         "colgroup+": "colgroup>col",
13646                         "colg+": "colgroup>col",
13647                         "tr+": "tr>td",
13648                         "select+": "select>option",
13649                         "optgroup+": "optgroup>option",
13650                         "optg+": "optgroup>option"
13651                 }
13652         },
13653         
13654         "xml": {
13655                 "extends": "html",
13656                 "profile": "xml",
13657                 "filters": "html"
13658         },
13659         
13660         "xsl": {
13661                 "extends": "html",
13662                 "profile": "xml",
13663                 "filters": "html, xsl",
13664                 "abbreviations": {
13665                         "tm": "<xsl:template match=\"\" mode=\"\">",
13666                         "tmatch": "tm",
13667                         "tn": "<xsl:template name=\"\">",
13668                         "tname": "tn",
13669                         "call": "<xsl:call-template name=\"\"/>",
13670                         "ap": "<xsl:apply-templates select=\"\" mode=\"\"/>",
13671                         "api": "<xsl:apply-imports/>",
13672                         "imp": "<xsl:import href=\"\"/>",
13673                         "inc": "<xsl:include href=\"\"/>",
13674 
13675                         "ch": "<xsl:choose>",
13676                         "xsl:when": "<xsl:when test=\"\">",
13677                         "wh": "xsl:when",
13678                         "ot": "<xsl:otherwise>",
13679                         "if": "<xsl:if test=\"\">",
13680 
13681                         "par": "<xsl:param name=\"\">",
13682                         "pare": "<xsl:param name=\"\" select=\"\"/>",
13683                         "var": "<xsl:variable name=\"\">",
13684                         "vare": "<xsl:variable name=\"\" select=\"\"/>",
13685                         "wp": "<xsl:with-param name=\"\" select=\"\"/>",
13686                         "key": "<xsl:key name=\"\" match=\"\" use=\"\"/>",
13687 
13688                         "elem": "<xsl:element name=\"\">",
13689                         "attr": "<xsl:attribute name=\"\">",
13690                         "attrs": "<xsl:attribute-set name=\"\">",
13691 
13692                         "cp": "<xsl:copy select=\"\"/>",
13693                         "co": "<xsl:copy-of select=\"\"/>",
13694                         "val": "<xsl:value-of select=\"\"/>",
13695                         "each": "<xsl:for-each select=\"\">",
13696                         "for": "each",
13697                         "tex": "<xsl:text></xsl:text>",
13698 
13699                         "com": "<xsl:comment>",
13700                         "msg": "<xsl:message terminate=\"no\">",
13701                         "fall": "<xsl:fallback>",
13702                         "num": "<xsl:number value=\"\"/>",
13703                         "nam": "<namespace-alias stylesheet-prefix=\"\" result-prefix=\"\"/>",
13704                         "pres": "<xsl:preserve-space elements=\"\"/>",
13705                         "strip": "<xsl:strip-space elements=\"\"/>",
13706                         "proc": "<xsl:processing-instruction name=\"\">",
13707                         "sort": "<xsl:sort select=\"\" order=\"\"/>",
13708 
13709                         "choose+": "xsl:choose>xsl:when+xsl:otherwise",
13710                         "xsl": "!!!+xsl:stylesheet[version=1.0 xmlns:xsl=http://www.w3.org/1999/XSL/Transform]>{\n|}"
13711                 }, 
13712                 "snippets": {
13713                         "!!!": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
13714                 }
13715         },
13716         
13717         "haml": {
13718                 "filters": "haml",
13719                 "extends": "html",
13720                 "profile": "xml"
13721         },
13722         
13723         "scss": {
13724                 "extends": "css"
13725         },
13726         
13727         "sass": {
13728                 "extends": "css"
13729         },
13730         
13731         "less": {
13732                 "extends": "css"
13733         },
13734         
13735         "stylus": {
13736                 "extends": "css"
13737         },
13738 
13739         "styl": {
13740                 "extends": "stylus"
13741         }
13742 }
13743 , 'system');});