src/pyams_skin/resources/js/ext/tinymce/dev/classes/ui/Selector.js
changeset 566 a1707c607eec
parent 565 318533413200
child 567 bca1726b1d85
equal deleted inserted replaced
565:318533413200 566:a1707c607eec
     1 /**
       
     2  * Selector.js
       
     3  *
       
     4  * Copyright, Moxiecode Systems AB
       
     5  * Released under LGPL License.
       
     6  *
       
     7  * License: http://www.tinymce.com/license
       
     8  * Contributing: http://www.tinymce.com/contributing
       
     9  */
       
    10 
       
    11 /*eslint no-nested-ternary:0 */
       
    12 
       
    13 /**
       
    14  * Selector engine, enables you to select controls by using CSS like expressions.
       
    15  * We currently only support basic CSS expressions to reduce the size of the core
       
    16  * and the ones we support should be enough for most cases.
       
    17  *
       
    18  * @example
       
    19  * Supported expressions:
       
    20  *  element
       
    21  *  element#name
       
    22  *  element.class
       
    23  *  element[attr]
       
    24  *  element[attr*=value]
       
    25  *  element[attr~=value]
       
    26  *  element[attr!=value]
       
    27  *  element[attr^=value]
       
    28  *  element[attr$=value]
       
    29  *  element:<state>
       
    30  *  element:not(<expression>)
       
    31  *  element:first
       
    32  *  element:last
       
    33  *  element:odd
       
    34  *  element:even
       
    35  *  element element
       
    36  *  element > element
       
    37  *
       
    38  * @class tinymce.ui.Selector
       
    39  */
       
    40 define("tinymce/ui/Selector", [
       
    41 	"tinymce/util/Class"
       
    42 ], function(Class) {
       
    43 	"use strict";
       
    44 
       
    45 	/**
       
    46 	 * Produces an array with a unique set of objects. It will not compare the values
       
    47 	 * but the references of the objects.
       
    48 	 *
       
    49 	 * @private
       
    50 	 * @method unqiue
       
    51 	 * @param {Array} array Array to make into an array with unique items.
       
    52 	 * @return {Array} Array with unique items.
       
    53 	 */
       
    54 	function unique(array) {
       
    55 		var uniqueItems = [], i = array.length, item;
       
    56 
       
    57 		while (i--) {
       
    58 			item = array[i];
       
    59 
       
    60 			if (!item.__checked) {
       
    61 				uniqueItems.push(item);
       
    62 				item.__checked = 1;
       
    63 			}
       
    64 		}
       
    65 
       
    66 		i = uniqueItems.length;
       
    67 		while (i--) {
       
    68 			delete uniqueItems[i].__checked;
       
    69 		}
       
    70 
       
    71 		return uniqueItems;
       
    72 	}
       
    73 
       
    74 	var expression = /^([\w\\*]+)?(?:#([\w\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i;
       
    75 
       
    76 	/*jshint maxlen:255 */
       
    77 	/*eslint max-len:0 */
       
    78 	var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
       
    79 		whiteSpace = /^\s*|\s*$/g,
       
    80 		Collection;
       
    81 
       
    82 	var Selector = Class.extend({
       
    83 		/**
       
    84 		 * Constructs a new Selector instance.
       
    85 		 *
       
    86 		 * @constructor
       
    87 		 * @method init
       
    88 		 * @param {String} selector CSS like selector expression.
       
    89 		 */
       
    90 		init: function(selector) {
       
    91 			var match = this.match;
       
    92 
       
    93 			function compileNameFilter(name) {
       
    94 				if (name) {
       
    95 					name = name.toLowerCase();
       
    96 
       
    97 					return function(item) {
       
    98 						return name === '*' || item.type === name;
       
    99 					};
       
   100 				}
       
   101 			}
       
   102 
       
   103 			function compileIdFilter(id) {
       
   104 				if (id) {
       
   105 					return function(item) {
       
   106 						return item._name === id;
       
   107 					};
       
   108 				}
       
   109 			}
       
   110 
       
   111 			function compileClassesFilter(classes) {
       
   112 				if (classes) {
       
   113 					classes = classes.split('.');
       
   114 
       
   115 					return function(item) {
       
   116 						var i = classes.length;
       
   117 
       
   118 						while (i--) {
       
   119 							if (!item.hasClass(classes[i])) {
       
   120 								return false;
       
   121 							}
       
   122 						}
       
   123 
       
   124 						return true;
       
   125 					};
       
   126 				}
       
   127 			}
       
   128 
       
   129 			function compileAttrFilter(name, cmp, check) {
       
   130 				if (name) {
       
   131 					return function(item) {
       
   132 						var value = item[name] ? item[name]() : '';
       
   133 
       
   134 						return !cmp ? !!check :
       
   135 							cmp === "=" ? value === check :
       
   136 							cmp === "*=" ? value.indexOf(check) >= 0 :
       
   137 							cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 :
       
   138 							cmp === "!=" ? value != check :
       
   139 							cmp === "^=" ? value.indexOf(check) === 0 :
       
   140 							cmp === "$=" ? value.substr(value.length - check.length) === check :
       
   141 							false;
       
   142 					};
       
   143 				}
       
   144 			}
       
   145 
       
   146 			function compilePsuedoFilter(name) {
       
   147 				var notSelectors;
       
   148 
       
   149 				if (name) {
       
   150 					name = /(?:not\((.+)\))|(.+)/i.exec(name);
       
   151 
       
   152 					if (!name[1]) {
       
   153 						name = name[2];
       
   154 
       
   155 						return function(item, index, length) {
       
   156 							return name === 'first' ? index === 0 :
       
   157 								name === 'last' ? index === length - 1 :
       
   158 								name === 'even' ? index % 2 === 0 :
       
   159 								name === 'odd' ? index % 2 === 1 :
       
   160 								item[name] ? item[name]() :
       
   161 								false;
       
   162 						};
       
   163 					} else {
       
   164 						// Compile not expression
       
   165 						notSelectors = parseChunks(name[1], []);
       
   166 
       
   167 						return function(item) {
       
   168 							return !match(item, notSelectors);
       
   169 						};
       
   170 					}
       
   171 				}
       
   172 			}
       
   173 
       
   174 			function compile(selector, filters, direct) {
       
   175 				var parts;
       
   176 
       
   177 				function add(filter) {
       
   178 					if (filter) {
       
   179 						filters.push(filter);
       
   180 					}
       
   181 				}
       
   182 
       
   183 				// Parse expression into parts
       
   184 				parts = expression.exec(selector.replace(whiteSpace, ''));
       
   185 
       
   186 				add(compileNameFilter(parts[1]));
       
   187 				add(compileIdFilter(parts[2]));
       
   188 				add(compileClassesFilter(parts[3]));
       
   189 				add(compileAttrFilter(parts[4], parts[5], parts[6]));
       
   190 				add(compilePsuedoFilter(parts[7]));
       
   191 
       
   192 				// Mark the filter with psuedo for performance
       
   193 				filters.psuedo = !!parts[7];
       
   194 				filters.direct = direct;
       
   195 
       
   196 				return filters;
       
   197 			}
       
   198 
       
   199 			// Parser logic based on Sizzle by John Resig
       
   200 			function parseChunks(selector, selectors) {
       
   201 				var parts = [], extra, matches, i;
       
   202 
       
   203 				do {
       
   204 					chunker.exec("");
       
   205 					matches = chunker.exec(selector);
       
   206 
       
   207 					if (matches) {
       
   208 						selector = matches[3];
       
   209 						parts.push(matches[1]);
       
   210 
       
   211 						if (matches[2]) {
       
   212 							extra = matches[3];
       
   213 							break;
       
   214 						}
       
   215 					}
       
   216 				} while (matches);
       
   217 
       
   218 				if (extra) {
       
   219 					parseChunks(extra, selectors);
       
   220 				}
       
   221 
       
   222 				selector = [];
       
   223 				for (i = 0; i < parts.length; i++) {
       
   224 					if (parts[i] != '>') {
       
   225 						selector.push(compile(parts[i], [], parts[i - 1] === '>'));
       
   226 					}
       
   227 				}
       
   228 
       
   229 				selectors.push(selector);
       
   230 
       
   231 				return selectors;
       
   232 			}
       
   233 
       
   234 			this._selectors = parseChunks(selector, []);
       
   235 		},
       
   236 
       
   237 		/**
       
   238 		 * Returns true/false if the selector matches the specified control.
       
   239 		 *
       
   240 		 * @method match
       
   241 		 * @param {tinymce.ui.Control} control Control to match agains the selector.
       
   242 		 * @param {Array} selectors Optional array of selectors, mostly used internally.
       
   243 		 * @return {Boolean} true/false state if the control matches or not.
       
   244 		 */
       
   245 		match: function(control, selectors) {
       
   246 			var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item;
       
   247 
       
   248 			selectors = selectors || this._selectors;
       
   249 			for (i = 0, l = selectors.length; i < l; i++) {
       
   250 				selector = selectors[i];
       
   251 				sl = selector.length;
       
   252 				item = control;
       
   253 				count = 0;
       
   254 
       
   255 				for (si = sl - 1; si >= 0; si--) {
       
   256 					filters = selector[si];
       
   257 
       
   258 					while (item) {
       
   259 						// Find the index and length since a psuedo filter like :first needs it
       
   260 						if (filters.psuedo) {
       
   261 							siblings = item.parent().items();
       
   262 							index = length = siblings.length;
       
   263 							while (index--) {
       
   264 								if (siblings[index] === item) {
       
   265 									break;
       
   266 								}
       
   267 							}
       
   268 						}
       
   269 
       
   270 						for (fi = 0, fl = filters.length; fi < fl; fi++) {
       
   271 							if (!filters[fi](item, index, length)) {
       
   272 								fi = fl + 1;
       
   273 								break;
       
   274 							}
       
   275 						}
       
   276 
       
   277 						if (fi === fl) {
       
   278 							count++;
       
   279 							break;
       
   280 						} else {
       
   281 							// If it didn't match the right most expression then
       
   282 							// break since it's no point looking at the parents
       
   283 							if (si === sl - 1) {
       
   284 								break;
       
   285 							}
       
   286 						}
       
   287 
       
   288 						item = item.parent();
       
   289 					}
       
   290 				}
       
   291 
       
   292 				// If we found all selectors then return true otherwise continue looking
       
   293 				if (count === sl) {
       
   294 					return true;
       
   295 				}
       
   296 			}
       
   297 
       
   298 			return false;
       
   299 		},
       
   300 
       
   301 		/**
       
   302 		 * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container.
       
   303 		 *
       
   304 		 * @method find
       
   305 		 * @param {tinymce.ui.Control} container Container to look for items in.
       
   306 		 * @return {tinymce.ui.Collection} Collection with matched elements.
       
   307 		 */
       
   308 		find: function(container) {
       
   309 			var matches = [], i, l, selectors = this._selectors;
       
   310 
       
   311 			function collect(items, selector, index) {
       
   312 				var i, l, fi, fl, item, filters = selector[index];
       
   313 
       
   314 				for (i = 0, l = items.length; i < l; i++) {
       
   315 					item = items[i];
       
   316 
       
   317 					// Run each filter agains the item
       
   318 					for (fi = 0, fl = filters.length; fi < fl; fi++) {
       
   319 						if (!filters[fi](item, i, l)) {
       
   320 							fi = fl + 1;
       
   321 							break;
       
   322 						}
       
   323 					}
       
   324 
       
   325 					// All filters matched the item
       
   326 					if (fi === fl) {
       
   327 						// Matched item is on the last expression like: panel toolbar [button]
       
   328 						if (index == selector.length - 1) {
       
   329 							matches.push(item);
       
   330 						} else {
       
   331 							// Collect next expression type
       
   332 							if (item.items) {
       
   333 								collect(item.items(), selector, index + 1);
       
   334 							}
       
   335 						}
       
   336 					} else if (filters.direct) {
       
   337 						return;
       
   338 					}
       
   339 
       
   340 					// Collect child items
       
   341 					if (item.items) {
       
   342 						collect(item.items(), selector, index);
       
   343 					}
       
   344 				}
       
   345 			}
       
   346 
       
   347 			if (container.items) {
       
   348 				for (i = 0, l = selectors.length; i < l; i++) {
       
   349 					collect(container.items(), selectors[i], 0);
       
   350 				}
       
   351 
       
   352 				// Unique the matches if needed
       
   353 				if (l > 1) {
       
   354 					matches = unique(matches);
       
   355 				}
       
   356 			}
       
   357 
       
   358 			// Fix for circular reference
       
   359 			if (!Collection) {
       
   360 				// TODO: Fix me!
       
   361 				Collection = Selector.Collection;
       
   362 			}
       
   363 
       
   364 			return new Collection(matches);
       
   365 		}
       
   366 	});
       
   367 
       
   368 	return Selector;
       
   369 });