123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474 |
- /*
- Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
- Available via Academic Free License >= 2.1 OR the modified BSD license.
- see: http://dojotoolkit.org/license for details
- */
- if(!dojo._hasResource["dojox.data.CssRuleStore"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
- dojo._hasResource["dojox.data.CssRuleStore"] = true;
- dojo.provide("dojox.data.CssRuleStore");
- dojo.require("dojo.data.util.sorter");
- dojo.require("dojo.data.util.filter");
- dojo.require("dojox.data.css");
- dojo.declare("dojox.data.CssRuleStore", null, {
- // summary:
- // Basic store to display CSS information.
- // description:
- // The CssRuleStore allows users to get information about active CSS rules in the page running the CssRuleStore.
- // It can also filter out rules from specific stylesheets. The attributes it exposes on rules are as follows:
- // selector: The selector text.
- // classes: An array of classes present in this selector.
- // rule: The actual DOM Rule object.
- // style: The actual DOM CSSStyleDeclaration object.
- // cssText: The cssText string provided on the rule object.
- // styleSheet: The originating DOM Stylesheet object.
- // parentStyleSheet: The parent stylesheet to the sheet this rule originates from.
- // parentStyleSheetHref: The href of the parent stylesheet.
- // AND every style attribute denoted as style.*, such as style.textAlign or style.backgroundColor
- _storeRef: '_S',
- _labelAttribute: 'selector', // text representation of the Item [label and identifier may need to stay due to method names]
- _cache: null,
- _browserMap: null,
- _cName: "dojox.data.CssRuleStore",
- constructor: function(/* Object */ keywordParameters){
- // Initializes this store
- if(keywordParameters){
- dojo.mixin(this, keywordParameters);
- }
- this._cache = {};
- this._allItems = null;
- this._waiting = [];
- this.gatherHandle = null;
- var self = this;
- // CSS files may not be finished loading by the time the store is constructed. We need to
- // give them a little time, so setting the stylesheet loading to retry every 250ms.
- function gatherRules(){
- try{
- // Funkiness here is due to css that may still be loading. This throws an DOM Access
- // error if css isnt completely loaded.
- self.context = dojox.data.css.determineContext(self.context);
- if(self.gatherHandle){
- clearInterval(self.gatherHandle);
- self.gatherHandle = null;
- }
- // Handle any fetches that have been queued while we've been waiting on the CSS files
- // to finish
- while(self._waiting.length){
- var item = self._waiting.pop();
- dojox.data.css.rules.forEach(item.forFunc, null, self.context);
- item.finishFunc();
- }
- }catch(e){}
- }
- this.gatherHandle = setInterval(gatherRules,250);
- },
-
- setContext: function(/* Array */ context){
- // Sets the context in which queries are executed
- // context: Array - Array of CSS string paths to execute queries within
- if(context){
- this.close();
- this.context = dojox.data.css.determineContext(context);
- }
- },
- getFeatures: function(){
- // summary:
- // See dojo.data.api.Read.getFeatures()
- return {
- "dojo.data.api.Read" : true
- };
- },
- isItem: function(item){
- // summary:
- // See dojo.data.api.Read.isItem()
- if(item && item[this._storeRef] == this){
- return true;
- }
- return false;
- },
- hasAttribute: function(item, attribute){
- // summary:
- // See dojo.data.api.Read.hasAttribute()
- this._assertIsItem(item);
- this._assertIsAttribute(attribute);
- var attrs = this.getAttributes(item);
- if(dojo.indexOf(attrs, attribute) != -1){
- return true;
- }
- return false;
- },
- getAttributes: function(item){
- // summary:
- // See dojo.data.api.Read.getAttributes()
- this._assertIsItem(item);
- var attrs = ['selector', 'classes', 'rule', 'style', 'cssText', 'styleSheet', 'parentStyleSheet', 'parentStyleSheetHref'];
- var style = item.rule.style;
- if(style){
- var key;
- for(key in style){
- attrs.push("style." + key);
- }
- }
- return attrs;
- },
- getValue: function(item, attribute, defaultValue){
- // summary:
- // See dojo.data.api.Read.getValue()
- var values = this.getValues(item, attribute);
- var value = defaultValue;
- if(values && values.length > 0){
- return values[0];
- }
- return defaultValue;
- },
- getValues: function(item, attribute){
- // summary:
- // See dojo.data.api.Read.getValues()
- this._assertIsItem(item);
- this._assertIsAttribute(attribute);
- var value = null;
- if(attribute === "selector"){
- value = item.rule["selectorText"];
- if(value && dojo.isString(value)){
- value = value.split(",");
- }
- }else if(attribute === "classes"){
- value = item.classes;
- }else if(attribute === "rule"){
- value = item.rule.rule;
- }else if(attribute === "style"){
- value = item.rule.style;
- }else if(attribute === "cssText"){
- if(dojo.isIE){
- if(item.rule.style){
- value = item.rule.style.cssText;
- if(value){
- value = "{ " + value.toLowerCase() + " }";
- }
- }
- }else{
- value = item.rule.cssText;
- if(value){
- value = value.substring(value.indexOf("{"), value.length);
- }
- }
- }else if(attribute === "styleSheet"){
- value = item.rule.styleSheet;
- }else if(attribute === "parentStyleSheet"){
- value = item.rule.parentStyleSheet;
- }else if(attribute === "parentStyleSheetHref"){
- if(item.href){
- value = item.href;
- }
- }else if(attribute.indexOf("style.") === 0){
- var attr = attribute.substring(attribute.indexOf("."), attribute.length);
- value = item.rule.style[attr];
- }else{
- value = [];
- }
- if(value !== undefined){
- if(!dojo.isArray(value)){
- value = [value];
- }
- }
- return value;
- },
- getLabel: function(item){
- // summary:
- // See dojo.data.api.Read.getLabel()
- this._assertIsItem(item);
- return this.getValue(item, this._labelAttribute);
- },
- getLabelAttributes: function(item){
- // summary:
- // See dojo.data.api.Read.getLabelAttributes()
- return [this._labelAttribute];
- },
- containsValue: function(/* item */ item,
- /* attribute-name-string */ attribute,
- /* anything */ value){
- // summary:
- // See dojo.data.api.Read.containsValue()
- var regexp = undefined;
- if(typeof value === "string"){
- regexp = dojo.data.util.filter.patternToRegExp(value, false);
- }
- return this._containsValue(item, attribute, value, regexp); //boolean.
- },
- isItemLoaded: function(/* anything */ something){
- // summary:
- // See dojo.data.api.Read.isItemLoaded()
- return this.isItem(something); //boolean
- },
- loadItem: function(/* object */ keywordArgs){
- // summary:
- // See dojo.data.api.Read.loadItem()
- this._assertIsItem(keywordArgs.item);
- },
- fetch: function(request){
- // summary:
- // See dojo.data.api.Read.fetch()
- request = request || {};
- if(!request.store){
- request.store = this;
- }
- var scope = request.scope || dojo.global;
- if(this._pending && this._pending.length > 0){
- this._pending.push({request: request, fetch: true});
- }else{
- this._pending = [{request: request, fetch: true}];
- this._fetch(request);
- }
- return request;
- },
- _fetch: function(request){
- // summary:
- // Populates the _allItems object with unique class names
- var scope = request.scope || dojo.global;
- if(this._allItems === null){
- this._allItems = {};
- try{
- if(this.gatherHandle){
- this._waiting.push({'forFunc': dojo.hitch(this, this._handleRule), 'finishFunc': dojo.hitch(this, this._handleReturn)});
- }else{
- dojox.data.css.rules.forEach(dojo.hitch(this, this._handleRule), null, this.context);
- this._handleReturn();
- }
- }catch(e){
- if(request.onError){
- request.onError.call(scope, e, request);
- }
- }
- }else{
- this._handleReturn();
- }
- },
- _handleRule: function(rule, styleSheet, href){
- // summary:
- // Handles the creation of an item based on the passed rule. In this store, this implies
- // parsing out all available class names.
- var selector = rule['selectorText'];
- var s = selector.split(" ");
- var classes = [];
- for(var j=0; j<s.length; j++){
- var tmp = s[j];
- var first = tmp.indexOf('.');
- if(tmp && tmp.length > 0 && first !== -1){
- var last = tmp.indexOf(',') || tmp.indexOf('[');
- tmp = tmp.substring(first, ((last !== -1 && last > first)?last:tmp.length));
- classes.push(tmp);
- }
- }
- var item = {};
- item.rule = rule;
- item.styleSheet = styleSheet;
- item.href = href;
- item.classes = classes;
- item[this._storeRef] = this;
- if(!this._allItems[selector]){
- this._allItems[selector] = [];
- }
- this._allItems[selector].push(item);
- },
- _handleReturn: function(){
- // summary:
- // Handles the return from a fetching action. Delegates requests to act on the resulting
- // item set to eitehr the _handleFetchReturn or _handleFetchByIdentityReturn depending on
- // where the request originated.
- var _inProgress = [];
-
- var items = [];
- var item = null;
- for(var i in this._allItems){
- item = this._allItems[i];
- for(var j in item){
- items.push(item[j]);
- }
- }
- var requestInfo;
- // One-level deep clone (can't use dojo.clone, since we don't want to clone all those store refs!)
- while(this._pending.length){
- requestInfo = this._pending.pop();
- requestInfo.request._items = items;
- _inProgress.push(requestInfo);
- }
- while(_inProgress.length){
- requestInfo = _inProgress.pop();
- this._handleFetchReturn(requestInfo.request);
- }
- },
- _handleFetchReturn: function(/*Request */ request){
- // summary:
- // Handles a fetchByIdentity request by finding the correct items.
- var scope = request.scope || dojo.global;
- var items = [];
- //Check to see if we've looked this query up before
- //If so, just reuse it, much faster. Only regen if query changes.
- var cacheKey = "all";
- var i;
- if(request.query){
- cacheKey = dojo.toJson(request.query);
- }
- if(this._cache[cacheKey]){
- items = this._cache[cacheKey];
- }else if(request.query){
- for(i in request._items){
- var item = request._items[i];
- // Per https://bugs.webkit.org/show_bug.cgi?id=17935 , Safari 3.x always returns the selectorText
- // of a rule in full lowercase.
- var ignoreCase = dojo.isWebKit ? true : (request.queryOptions ? request.queryOptions.ignoreCase : false);
- var regexpList = {};
- var key;
- var value;
- for(key in request.query){
- value = request.query[key];
- if(typeof value === "string"){
- regexpList[key] = dojo.data.util.filter.patternToRegExp(value, ignoreCase);
- }
- }
- var match = true;
- for(key in request.query){
- value = request.query[key];
- if(!this._containsValue(item, key, value, regexpList[key])){
- match = false;
- }
- }
- if(match){
- items.push(item);
- }
- }
- this._cache[cacheKey] = items;
- }else{
- for(i in request._items){
- items.push(request._items[i]);
- }
- }
- var total = items.length;
- //Sort it if we need to.
- if(request.sort){
- items.sort(dojo.data.util.sorter.createSortFunction(request.sort, this));
- }
- var start = 0;
- var count = items.length;
- if(request.start > 0 && request.start < items.length){
- start = request.start;
- }
- if(request.count && request.count){
- count = request.count;
- }
- var endIdx = start + count;
- if(endIdx > items.length){
- endIdx = items.length;
- }
- items = items.slice(start, endIdx);
- if(request.onBegin){
- request.onBegin.call(scope, total, request);
- }
- if(request.onItem){
- if(dojo.isArray(items)){
- for(i = 0; i < items.length; i++){
- request.onItem.call(scope, items[i], request);
- }
- if(request.onComplete){
- request.onComplete.call(scope, null, request);
- }
- }
- }else if(request.onComplete){
- request.onComplete.call(scope, items, request);
- }
- return request;
- },
- close: function(){
- // summary:
- // See dojo.data.api.Read.close()
- // Clears out the cache and allItems objects, meaning all future fetches will requery
- // the stylesheets.
- this._cache = {};
- this._allItems = null;
- },
-
- _assertIsItem: function(/* item */ item){
- // summary:
- // This function tests whether the item passed in is indeed an item in the store.
- // item:
- // The item to test for being contained by the store.
- if(!this.isItem(item)){
- throw new Error(this._cName + ": Invalid item argument.");
- }
- },
- _assertIsAttribute: function(/* attribute-name-string */ attribute){
- // summary:
- // This function tests whether the item passed in is indeed a valid 'attribute' like type for the store.
- // attribute:
- // The attribute to test for being contained by the store.
- if(typeof attribute !== "string"){
- throw new Error(this._cName + ": Invalid attribute argument.");
- }
- },
- _containsValue: function( /* item */ item,
- /* attribute-name-string */ attribute,
- /* anything */ value,
- /* RegExp?*/ regexp){
- // summary:
- // Internal function for looking at the values contained by the item.
- // description:
- // Internal function for looking at the values contained by the item. This
- // function allows for denoting if the comparison should be case sensitive for
- // strings or not (for handling filtering cases where string case should not matter)
- //
- // item:
- // The data item to examine for attribute values.
- // attribute:
- // The attribute to inspect.
- // value:
- // The value to match.
- // regexp:
- // Optional regular expression generated off value if value was of string type to handle wildcarding.
- // If present and attribute values are string, then it can be used for comparison instead of 'value'
- return dojo.some(this.getValues(item, attribute), function(possibleValue){
- if(possibleValue !== null && !dojo.isObject(possibleValue) && regexp){
- if(possibleValue.toString().match(regexp)){
- return true; // Boolean
- }
- }else if(value === possibleValue){
- return true; // Boolean
- }
- return false;
- });
- }
- });
- }
|