1 /*
  2  * Module     : injected/jquery-plugins.js
  3  * Copyright  : (c) 2011-2012, Galois, Inc.
  4  *
  5  * Maintainer :
  6  * Stability  : Provisional
  7  * Portability: Portable
  8  *
  9  * Licensed under the Apache License, Version 2.0 (the "License");
 10  * you may not use this file except in compliance with the License.
 11  * You may obtain a copy of the License at
 12  *
 13  *      http://www.apache.org/licenses/LICENSE-2.0
 14  *
 15  * Unless required by applicable law or agreed to in writing, software
 16  * distributed under the License is distributed on an "AS IS" BASIS,
 17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 18  * See the License for the specific language governing permissions and
 19  * limitations under the License.
 20  */
 21 
 22 var fiveui = fiveui || {};
 23 
 24 /**
 25  * <p>This module provides several useful jQuery plugins related to checking and reporting
 26  * UI consistency issues.</p>
 27  *
 28  * @namespace
 29  */
 30 fiveui.jquery = fiveui.jquery || {};
 31 
 32 
 33 /**
 34  * <p>Wrapper for the :contains('text') selector</p>
 35  *
 36  * @example
 37  * $('div').hasText('to remove').remove();
 38  *
 39  * @param {!string} text Text to select for
 40  * @returns {!Object} A modified jQuery object
 41  */
 42 fiveui.jquery.hasText = function (text) {
 43   return this.filter(":contains('" + text + "')");
 44 };
 45 
 46 /**
 47  * <p>Filter for elements which lack of the given attribute. (see also
 48  * fiveui.jquery.attrFilter)</p>
 49  *
 50  * @example
 51  * $('<table></table>').noAttr('summary').each(function(idx, table) {
 52  *   // `table` is a table element that does not have a "summary"
 53  *   // attribute or that has a "summary" attribute that is empty.
 54  * });
 55  *
 56  * @param {!string} attribute name
 57  * @returns {!Object} a filtered jQuery object
 58  */
 59 fiveui.jquery.noAttr = function (name) {
 60   return this.filter(function () {
 61     $attr = $.trim($(this).attr(name));
 62     return $attr == undefined || $attr == '';
 63   });
 64 };
 65 
 66 
 67 /**
 68  * <p>Filter for elements having no sub-elements matching the given selector.</p>
 69  *
 70  * Example: the following should contain no elements
 71  *
 72  * @example
 73  * // Returns an empty result because the <div> contains a <p> element.
 74  * $('<div><p>hello</p></div>').noSubElt('p')
 75  *
 76  * @param {!string} sel a jQuery selector
 77  * @param {!Object} A filtered jQuery object
 78  */
 79 fiveui.jquery.noSubElt = function (sel) {
 80   return this.filter(function () {
 81     return $(this).find(sel).length == 0;
 82   });
 83 };
 84 
 85 /**
 86  * <p>Color checker plugin: filters for elements whose CSS color property is
 87  * not in the given set.</p>
 88  *
 89  * @description
 90  * <p>Note: This is a special case of fiveui.jquery.cssIsNot, i.e.
 91  * $(..).notColorSet(set) == $(..).cssIsNot("color", set, fiveui.color.colorToHex)
 92  * @see {fiveui.color.colorToHex}</p>
 93  *
 94  * @param {string[]} cset An array of allowable color strings
 95  * @returns {!Object} A modified jQuery object
 96  */
 97 fiveui.jquery.notColorSet = function (cset) {
 98   var allowable = {};
 99   // input array -> object
100   for (var i = 0; i < cset.length; i += 1) {
101     allowable[fiveui.color.colorToHex(cset[i])] = true;
102   }
103   return this.filter(function (index) {
104     var color = fiveui.color.colorToHexWithDefault($(this).css("color")); // .css("color") returns rgb(...)
105     return !(color in allowable);
106   });
107 };
108 
109 
110 fiveui.jquery._makeCss = function (pos) {
111   return function (prop, set, fn) {
112     var allowable = {};
113     fn = fn || function (x) { return x; }; // default is Id
114     if (typeof set === "string") {
115       allowable[fn(set)] = true;
116     }
117     else { // assume `set` is an array of strings
118      // array -> object
119      for (var i = 0; i < set.length; i += 1) {
120        allowable[fn(set[i])] = true;
121      }
122     }
123     return this.filter(function (index) {
124       var cssProp = fn($(this).css(prop));
125       return pos ? (cssProp in allowable) : !(cssProp in allowable);
126     });
127   };
128 };
129 
130 /**
131  * <p>General CSS property checker plugin</p>
132  *
133  * @description
134  * <p>This plugin filters elements, keeping only elements whose CSS
135  * property `prop` is a member of the given array `cset`. The names in
136  * `cset` and the CSS values that are checked are transformed using the
137  * optional given function `fn`. This may be used to normalize values
138  * that the browser returns so they can be compared to values in
139  * `cset`.</p>
140  *
141  * @example
142  * var div = $('<div style="visibility:hidden"></div>')
143  * div.cssIs('visibility', ['hidden', 'visible'])  // returns the same div
144  *
145  * @function
146  *
147  * @param {string} prop  CSS property selector
148  * @param {string|string[]} set allowable values (either a string or an array
149  *                          of strings)
150  * @param {function(string):string} [fn] Function to apply to return values
151  *                                       of $(this).css(prop), fn defaults to
152  *                                       the identity function.
153  * @returns {Object} jQuery object
154  */
155 fiveui.jquery.cssIs = fiveui.jquery._makeCss(true);
156 
157 /**
158  * <p>Negated version of fiveui.jquery.cssIs</p>
159  *
160  * @description
161  * <p>Behaves exactly like fiveui.jquery.cssIs - except that this
162  * version excludes elements that have CSS properties and values that
163  * match.</p>
164  *
165  * @example
166  * var div = $('<div style="visibility:hidden"></div>')
167  * div.cssIsNot('visibility', ['hidden', 'visible'])  // returns empty result
168  *
169  * @function
170  *
171  * @param {string} prop  CSS property selector
172  * @param {string|string[]} set allowable values (either a string or an array
173  *                          of strings)
174  * @param {function(string):string} [fn] Function to apply to return values
175  *                                       of $(this).css(prop), fn defaults to
176  *                                       the identity function.
177  * @returns {Object} jQuery object
178  */
179 fiveui.jquery.cssIsNot = fiveui.jquery._makeCss(false);
180 
181 /**
182  * <p>General attribute filter</p>
183  *
184  * @description
185  * <p>This plugin filters for elements whose attribute `a` pass the
186  * predicate `fn`, which should take a string and return true or false.
187  * Elements that don't have the attribute are automatically filtered
188  * out.</p>
189  *
190  * @example
191  * $('input').attrFilter('type', function(t) { return t === 'checkbox'; });
192  *
193  * @param {string} a element attribute name
194  * @param {Function} fn a predicate to run on the element attribute
195  * @returns {Object} jQuery object
196  */
197 fiveui.jquery.attrFilter = function (a, fn) {
198   return this.filter(function () {
199     var x = $(this).attr(a);
200     return x != undefined && fn(x);
201   });
202 }
203 
204 /**
205  * <p>Filter out elements that do not contain the attribute
206  * href=`href`.</p>
207  *
208  * @param {string} href the href to look for
209  * @returns {Object} jQuery object
210  */
211 fiveui.jquery.linksTo = function (href) {
212   return this.filter('[href=' + href + ']');
213 }
214 
215 /**
216  * <p>Visually highlight elements in the jQuery object.</p>
217  *
218  * @description
219  * <p>This plugin is useful mostly in the process of writing
220  * guidelines, for example the guideline developer can load a page,
221  * click the "Break" button on the FiveUI window, enter the browser's
222  * Javascript console, and run:</p>
223  *
224  * @example > $5("p").hasText("foo").highlight();
225  *
226  * @param {string} [hint] Highlighted border color, defaults to "red"
227  * @returns {!Object} A modified jQuery object
228  */
229 fiveui.jquery.highlight = function (hint) {
230   hint = hint || "red"; // Default is "red"
231   return this.css("background-color", "rgba(255, 0, 0, 0.3)")
232              .css("border-style", "solid")
233              .css("border-color", hint);
234 }
235 
236 /**
237  * <p>Returns a list of css properties that element in the jQuery
238  * object have.</p>
239  *
240  * @description
241  * <p>This plugin is useful for analysis of a given page when
242  * writing guielines. For example if the guideline developer wants to
243  * know what font sizes are used on a loaded page, they can run from the
244  * Javascript console:</p>
245  *
246  * @example > $5("*").propDist("font-size", true);
247  *
248  * @param {string} prop CSS property to be inspected
249  * @param {boolean} [log] Boolean which enables console logging of the result; default is `false`.
250  * @returns {Object} A frequence map { "property": frequency }
251  */
252 fiveui.jquery.propDist = function (prop, log) {
253   var res = {};
254   log = log || false;
255   this.each(function (i, elt) {
256     var p = $(elt).css(prop);
257     if (p in res) {
258       res[p] += 1;
259     }
260     else {
261       res[p] = 1;
262     }
263   });
264   if (log) {
265     console.log("Property distribution:");
266     for (var p in res) {
267       console.log("  " + p + ": " + res[p]);
268     }
269   }
270   return res;
271 }
272 
273 /**
274  * Register the plugins. This adds methods to the jQuery.fn namespace.
275  *
276  * @private
277  */
278 fiveui.jquery.init = function () {
279   for (fn in fiveui.jquery) {
280     f = fiveui.jquery[fn];
281     if (jQuery.isFunction(f) && fn != "init") {
282       jQuery.fn[fn] = fiveui.jquery[fn];
283     }
284   }
285 }
286 fiveui.jquery.init();
287