2011-08-30 05:12:02 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=2 et sw=2 tw=80: */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is the Mozilla Inspector Module.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* The Mozilla Foundation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
2011-09-26 09:46:44 -07:00
|
|
|
* Joe Walker (jwalker@mozilla.com) (Original Author)
|
2011-08-30 05:12:02 -07:00
|
|
|
* Mihai Șucan <mihai.sucan@gmail.com>
|
|
|
|
* Michael Ratcliffe <mratcliffe@mozilla.com>
|
2011-09-26 09:46:44 -07:00
|
|
|
* Rob Campbell <rcampbell@mozilla.com>
|
2011-08-30 05:12:02 -07:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
const Cu = Components.utils;
|
2011-09-20 02:59:13 -07:00
|
|
|
const FILTER_CHANGED_TIMEOUT = 300;
|
2011-08-30 05:12:02 -07:00
|
|
|
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
|
|
Cu.import("resource://gre/modules/PluralForm.jsm");
|
|
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
Cu.import("resource:///modules/devtools/CssLogic.jsm");
|
|
|
|
Cu.import("resource:///modules/devtools/Templater.jsm");
|
|
|
|
|
2011-09-05 06:14:52 -07:00
|
|
|
var EXPORTED_SYMBOLS = ["CssHtmlTree", "PropertyView"];
|
2011-08-30 05:12:02 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* CssHtmlTree is a panel that manages the display of a table sorted by style.
|
|
|
|
* There should be one instance of CssHtmlTree per style display (of which there
|
|
|
|
* will generally only be one).
|
|
|
|
*
|
2011-10-21 10:40:22 -07:00
|
|
|
* @params {StyleInspector} aStyleInspector The owner of this CssHtmlTree
|
2011-08-30 05:12:02 -07:00
|
|
|
* @constructor
|
|
|
|
*/
|
2011-10-21 10:40:22 -07:00
|
|
|
function CssHtmlTree(aStyleInspector)
|
2011-08-30 05:12:02 -07:00
|
|
|
{
|
2011-10-21 10:40:22 -07:00
|
|
|
this.styleWin = aStyleInspector.iframe;
|
|
|
|
this.styleInspector = aStyleInspector;
|
|
|
|
this.cssLogic = aStyleInspector.cssLogic;
|
|
|
|
this.doc = aStyleInspector.document;
|
|
|
|
this.win = aStyleInspector.window;
|
|
|
|
this.getRTLAttr = this.win.getComputedStyle(this.win.gBrowser).direction;
|
2011-10-19 03:25:39 -07:00
|
|
|
this.propertyViews = [];
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-11-05 12:31:00 -07:00
|
|
|
// The document in which we display the results (csshtmltree.xul).
|
2011-08-30 05:12:02 -07:00
|
|
|
this.styleDocument = this.styleWin.contentWindow.document;
|
|
|
|
|
|
|
|
// Nodes used in templating
|
|
|
|
this.root = this.styleDocument.getElementById("root");
|
2011-09-20 02:35:01 -07:00
|
|
|
this.path = this.styleDocument.getElementById("path");
|
2011-08-30 05:12:02 -07:00
|
|
|
this.templateRoot = this.styleDocument.getElementById("templateRoot");
|
2011-09-20 02:35:01 -07:00
|
|
|
this.templatePath = this.styleDocument.getElementById("templatePath");
|
2011-08-30 09:40:29 -07:00
|
|
|
this.propertyContainer = this.styleDocument.getElementById("propertyContainer");
|
|
|
|
this.templateProperty = this.styleDocument.getElementById("templateProperty");
|
2011-10-21 10:40:22 -07:00
|
|
|
this.panel = aStyleInspector.panel;
|
2011-08-30 05:12:02 -07:00
|
|
|
|
|
|
|
// The element that we're inspecting, and the document that it comes from.
|
|
|
|
this.viewedElement = null;
|
2011-08-30 09:40:29 -07:00
|
|
|
this.createStyleViews();
|
2011-08-30 05:12:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-10-21 10:40:22 -07:00
|
|
|
* Memoized lookup of a l10n string from a string bundle.
|
2011-08-30 05:12:02 -07:00
|
|
|
* @param {string} aName The key to lookup.
|
|
|
|
* @returns A localized version of the given key.
|
|
|
|
*/
|
|
|
|
CssHtmlTree.l10n = function CssHtmlTree_l10n(aName)
|
|
|
|
{
|
|
|
|
try {
|
|
|
|
return CssHtmlTree._strings.GetStringFromName(aName);
|
|
|
|
} catch (ex) {
|
|
|
|
Services.console.logStringMessage("Error reading '" + aName + "'");
|
|
|
|
throw new Error("l10n error with " + aName);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clone the given template node, and process it by resolving ${} references
|
|
|
|
* in the template.
|
|
|
|
*
|
|
|
|
* @param {nsIDOMElement} aTemplate the template note to use.
|
|
|
|
* @param {nsIDOMElement} aDestination the destination node where the
|
|
|
|
* processed nodes will be displayed.
|
|
|
|
* @param {object} aData the data to pass to the template.
|
2011-08-30 09:40:29 -07:00
|
|
|
* @param {Boolean} aPreserveDestination If true then the template will be
|
|
|
|
* appended to aDestination's content else aDestination.innerHTML will be
|
|
|
|
* cleared before the template is appended.
|
2011-08-30 05:12:02 -07:00
|
|
|
*/
|
2011-08-30 09:40:29 -07:00
|
|
|
CssHtmlTree.processTemplate = function CssHtmlTree_processTemplate(aTemplate,
|
|
|
|
aDestination, aData, aPreserveDestination)
|
2011-08-30 05:12:02 -07:00
|
|
|
{
|
2011-08-30 09:40:29 -07:00
|
|
|
if (!aPreserveDestination) {
|
|
|
|
aDestination.innerHTML = "";
|
|
|
|
}
|
2011-08-30 05:12:02 -07:00
|
|
|
|
|
|
|
// All the templater does is to populate a given DOM tree with the given
|
|
|
|
// values, so we need to clone the template first.
|
|
|
|
let duplicated = aTemplate.cloneNode(true);
|
|
|
|
new Templater().processNode(duplicated, aData);
|
|
|
|
while (duplicated.firstChild) {
|
|
|
|
aDestination.appendChild(duplicated.firstChild);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
XPCOMUtils.defineLazyGetter(CssHtmlTree, "_strings", function() Services.strings
|
2011-10-27 08:35:13 -07:00
|
|
|
.createBundle("chrome://browser/locale/devtools/styleinspector.properties"));
|
2011-08-30 05:12:02 -07:00
|
|
|
|
|
|
|
CssHtmlTree.prototype = {
|
2011-11-05 15:19:02 -07:00
|
|
|
// Cache the list of properties that have matched and unmatched properties.
|
2011-11-02 10:50:15 -07:00
|
|
|
_matchedProperties: null,
|
2011-11-05 15:19:02 -07:00
|
|
|
_unmatchedProperties: null,
|
2011-11-02 10:50:15 -07:00
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
htmlComplete: false,
|
2011-09-20 02:59:13 -07:00
|
|
|
|
|
|
|
// Used for cancelling timeouts in the style filter.
|
2011-11-02 10:50:15 -07:00
|
|
|
_filterChangedTimeout: null,
|
2011-09-20 02:59:13 -07:00
|
|
|
|
|
|
|
// The search filter
|
|
|
|
searchField: null,
|
2011-11-04 10:12:58 -07:00
|
|
|
|
2011-09-20 02:35:01 -07:00
|
|
|
// Reference to the "Only user Styles" checkbox.
|
|
|
|
onlyUserStylesCheckbox: null,
|
|
|
|
|
2011-10-19 03:25:39 -07:00
|
|
|
// Holds the ID of the panelRefresh timeout.
|
|
|
|
_panelRefreshTimeout: null,
|
|
|
|
|
2011-11-04 10:12:58 -07:00
|
|
|
// Toggle for zebra striping
|
|
|
|
_darkStripe: true,
|
|
|
|
|
2011-09-20 02:35:01 -07:00
|
|
|
get showOnlyUserStyles()
|
|
|
|
{
|
|
|
|
return this.onlyUserStylesCheckbox.checked;
|
|
|
|
},
|
2011-09-15 03:30:00 -07:00
|
|
|
|
2011-08-30 05:12:02 -07:00
|
|
|
/**
|
2011-09-15 03:30:00 -07:00
|
|
|
* Update the highlighted element. The CssHtmlTree panel will show the style
|
|
|
|
* information for the given element.
|
2011-08-30 05:12:02 -07:00
|
|
|
* @param {nsIDOMElement} aElement The highlighted node to get styles for.
|
|
|
|
*/
|
|
|
|
highlight: function CssHtmlTree_highlight(aElement)
|
|
|
|
{
|
2011-08-30 09:40:29 -07:00
|
|
|
if (this.viewedElement == aElement) {
|
|
|
|
return;
|
|
|
|
}
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-08-30 09:40:29 -07:00
|
|
|
this.viewedElement = aElement;
|
2011-11-05 15:19:02 -07:00
|
|
|
this._unmatchedProperties = null;
|
2011-11-02 10:50:15 -07:00
|
|
|
this._matchedProperties = null;
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-09-20 02:35:01 -07:00
|
|
|
CssHtmlTree.processTemplate(this.templatePath, this.path, this);
|
2011-08-30 09:40:29 -07:00
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
if (this.htmlComplete) {
|
|
|
|
this.refreshPanel();
|
|
|
|
} else {
|
2011-11-02 10:50:15 -07:00
|
|
|
if (this._panelRefreshTimeout) {
|
|
|
|
this.win.clearTimeout(this._panelRefreshTimeout);
|
|
|
|
}
|
|
|
|
|
2011-09-20 02:35:01 -07:00
|
|
|
CssHtmlTree.processTemplate(this.templateRoot, this.root, this);
|
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
// We use a setTimeout loop to display the properties in batches of 15 at a
|
|
|
|
// time. This results in a perceptibly more responsive UI.
|
|
|
|
let i = 0;
|
|
|
|
let batchSize = 15;
|
|
|
|
let max = CssHtmlTree.propertyNames.length - 1;
|
|
|
|
function displayProperties() {
|
2011-10-21 10:40:22 -07:00
|
|
|
if (this.viewedElement == aElement && this.styleInspector.isOpen()) {
|
2011-09-15 03:30:00 -07:00
|
|
|
// Display the next 15 properties
|
|
|
|
for (let step = i + batchSize; i < step && i <= max; i++) {
|
|
|
|
let name = CssHtmlTree.propertyNames[i];
|
|
|
|
let propView = new PropertyView(this, name);
|
|
|
|
CssHtmlTree.processTemplate(this.templateProperty,
|
|
|
|
this.propertyContainer, propView, true);
|
2011-11-05 15:19:02 -07:00
|
|
|
propView.refreshAllSelectors();
|
2011-10-19 03:25:39 -07:00
|
|
|
this.propertyViews.push(propView);
|
2011-09-15 03:30:00 -07:00
|
|
|
}
|
|
|
|
if (i < max) {
|
|
|
|
// There are still some properties to display. We loop here to display
|
|
|
|
// the next batch of 15.
|
2011-11-02 10:50:15 -07:00
|
|
|
this._panelRefreshTimeout =
|
|
|
|
this.win.setTimeout(displayProperties.bind(this), 15);
|
2011-09-15 03:30:00 -07:00
|
|
|
} else {
|
|
|
|
this.htmlComplete = true;
|
2011-11-02 10:50:15 -07:00
|
|
|
this._panelRefreshTimeout = null;
|
2011-09-20 02:35:01 -07:00
|
|
|
Services.obs.notifyObservers(null, "StyleInspector-populated", null);
|
2011-09-15 03:30:00 -07:00
|
|
|
}
|
2011-08-30 09:40:29 -07:00
|
|
|
}
|
|
|
|
}
|
2011-11-02 10:50:15 -07:00
|
|
|
this._panelRefreshTimeout =
|
|
|
|
this.win.setTimeout(displayProperties.bind(this), 15);
|
2011-09-15 03:30:00 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Refresh the panel content.
|
|
|
|
*/
|
|
|
|
refreshPanel: function CssHtmlTree_refreshPanel()
|
|
|
|
{
|
2011-11-02 10:50:15 -07:00
|
|
|
if (this._panelRefreshTimeout) {
|
|
|
|
this.win.clearTimeout(this._panelRefreshTimeout);
|
|
|
|
}
|
2011-10-19 03:25:39 -07:00
|
|
|
|
2011-11-04 10:12:58 -07:00
|
|
|
// Reset zebra striping.
|
|
|
|
this._darkStripe = true;
|
|
|
|
|
2011-10-19 03:25:39 -07:00
|
|
|
// We use a setTimeout loop to display the properties in batches of 15 at a
|
|
|
|
// time. This results in a perceptibly more responsive UI.
|
|
|
|
let i = 0;
|
|
|
|
let batchSize = 15;
|
|
|
|
let max = this.propertyViews.length - 1;
|
|
|
|
function refreshView() {
|
|
|
|
// Refresh the next 15 property views
|
|
|
|
for (let step = i + batchSize; i < step && i <= max; i++) {
|
|
|
|
this.propertyViews[i].refresh();
|
|
|
|
}
|
|
|
|
if (i < max) {
|
|
|
|
// There are still some property views to refresh. We loop here to
|
|
|
|
// display the next batch of 15.
|
2011-11-02 10:50:15 -07:00
|
|
|
this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 15);
|
2011-10-19 03:25:39 -07:00
|
|
|
} else {
|
2011-11-02 10:50:15 -07:00
|
|
|
this._panelRefreshTimeout = null;
|
2011-10-19 03:25:39 -07:00
|
|
|
Services.obs.notifyObservers(null, "StyleInspector-populated", null);
|
|
|
|
}
|
2011-08-30 09:40:29 -07:00
|
|
|
}
|
2011-11-02 10:50:15 -07:00
|
|
|
this._panelRefreshTimeout = this.win.setTimeout(refreshView.bind(this), 15);
|
2011-08-30 05:12:02 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called when the user clicks on a parent element in the "current element"
|
|
|
|
* path.
|
|
|
|
*
|
|
|
|
* @param {Event} aEvent the DOM Event object.
|
|
|
|
*/
|
|
|
|
pathClick: function CssHtmlTree_pathClick(aEvent)
|
|
|
|
{
|
|
|
|
aEvent.preventDefault();
|
2011-08-30 09:40:29 -07:00
|
|
|
if (aEvent.target && this.viewedElement != aEvent.target.pathElement) {
|
2011-10-21 10:40:22 -07:00
|
|
|
this.styleInspector.selectFromPath(aEvent.target.pathElement);
|
2011-08-30 05:12:02 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-09-20 02:59:13 -07:00
|
|
|
/**
|
|
|
|
* Called when the user enters a search term.
|
|
|
|
*
|
|
|
|
* @param {Event} aEvent the DOM Event object.
|
|
|
|
*/
|
|
|
|
filterChanged: function CssHtmlTree_filterChanged(aEvent)
|
|
|
|
{
|
|
|
|
let win = this.styleWin.contentWindow;
|
2011-11-02 10:50:15 -07:00
|
|
|
|
|
|
|
if (this._filterChangedTimeout) {
|
|
|
|
win.clearTimeout(this._filterChangedTimeout);
|
2011-09-20 02:59:13 -07:00
|
|
|
}
|
|
|
|
|
2011-11-02 10:50:15 -07:00
|
|
|
this._filterChangedTimeout = win.setTimeout(function() {
|
2011-09-20 02:59:13 -07:00
|
|
|
this.refreshPanel();
|
2011-11-02 10:50:15 -07:00
|
|
|
this._filterChangeTimeout = null;
|
2011-09-20 02:59:13 -07:00
|
|
|
}.bind(this), FILTER_CHANGED_TIMEOUT);
|
|
|
|
},
|
|
|
|
|
2011-09-20 02:35:01 -07:00
|
|
|
/**
|
|
|
|
* The change event handler for the onlyUserStyles checkbox. When
|
|
|
|
* onlyUserStyles.checked is true we do not display properties that have no
|
|
|
|
* matched selectors, and we do not display UA styles. If .checked is false we
|
|
|
|
* do display even properties with no matched selectors, and we include the UA
|
|
|
|
* styles.
|
|
|
|
*
|
|
|
|
* @param {Event} aEvent the DOM Event object.
|
|
|
|
*/
|
|
|
|
onlyUserStylesChanged: function CssHtmltree_onlyUserStylesChanged(aEvent)
|
|
|
|
{
|
2011-11-02 10:50:15 -07:00
|
|
|
this._matchedProperties = null;
|
2011-09-20 02:35:01 -07:00
|
|
|
this.cssLogic.sourceFilter = this.showOnlyUserStyles ?
|
|
|
|
CssLogic.FILTER.ALL :
|
|
|
|
CssLogic.FILTER.UA;
|
|
|
|
this.refreshPanel();
|
|
|
|
},
|
|
|
|
|
2011-08-30 05:12:02 -07:00
|
|
|
/**
|
|
|
|
* Provide access to the path to get from document.body to the selected
|
|
|
|
* element.
|
|
|
|
*
|
|
|
|
* @return {array} the array holding the path from document.body to the
|
|
|
|
* selected element.
|
|
|
|
*/
|
|
|
|
get pathElements()
|
|
|
|
{
|
|
|
|
return CssLogic.getShortNamePath(this.viewedElement);
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2011-08-30 09:40:29 -07:00
|
|
|
* The CSS as displayed by the UI.
|
2011-08-30 05:12:02 -07:00
|
|
|
*/
|
2011-08-30 09:40:29 -07:00
|
|
|
createStyleViews: function CssHtmlTree_createStyleViews()
|
2011-08-30 05:12:02 -07:00
|
|
|
{
|
2011-08-30 09:40:29 -07:00
|
|
|
if (CssHtmlTree.propertyNames) {
|
2011-08-30 05:12:02 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2011-08-30 09:40:29 -07:00
|
|
|
CssHtmlTree.propertyNames = [];
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-08-30 09:40:29 -07:00
|
|
|
// Here we build and cache a list of css properties supported by the browser
|
2011-11-05 12:31:00 -07:00
|
|
|
// We could use any element but let's use the main document's root element
|
|
|
|
let styles = this.styleWin.contentWindow.getComputedStyle(this.styleDocument.documentElement);
|
2011-08-30 09:40:29 -07:00
|
|
|
let mozProps = [];
|
|
|
|
for (let i = 0, numStyles = styles.length; i < numStyles; i++) {
|
|
|
|
let prop = styles.item(i);
|
|
|
|
if (prop.charAt(0) == "-") {
|
|
|
|
mozProps.push(prop);
|
2011-08-30 05:12:02 -07:00
|
|
|
} else {
|
2011-08-30 09:40:29 -07:00
|
|
|
CssHtmlTree.propertyNames.push(prop);
|
2011-08-30 05:12:02 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-30 09:40:29 -07:00
|
|
|
CssHtmlTree.propertyNames.sort();
|
|
|
|
CssHtmlTree.propertyNames.push.apply(CssHtmlTree.propertyNames,
|
|
|
|
mozProps.sort());
|
2011-08-30 05:12:02 -07:00
|
|
|
},
|
2011-10-07 08:38:35 -07:00
|
|
|
|
2011-11-02 10:50:15 -07:00
|
|
|
/**
|
|
|
|
* Get a list of properties that have matched selectors.
|
|
|
|
*
|
|
|
|
* @return {object} the object maps property names (keys) to booleans (values)
|
|
|
|
* that tell if the given property has matched selectors or not.
|
|
|
|
*/
|
|
|
|
get matchedProperties()
|
|
|
|
{
|
|
|
|
if (!this._matchedProperties) {
|
|
|
|
this._matchedProperties =
|
|
|
|
this.cssLogic.hasMatchedSelectors(CssHtmlTree.propertyNames);
|
|
|
|
}
|
|
|
|
return this._matchedProperties;
|
|
|
|
},
|
|
|
|
|
2011-11-05 15:19:02 -07:00
|
|
|
/**
|
|
|
|
* Check if a property has unmatched selectors. Result is cached.
|
|
|
|
*
|
|
|
|
* @param {string} aProperty the name of the property you want to check.
|
|
|
|
* @return {boolean} true if the property has unmatched selectors, false
|
|
|
|
* otherwise.
|
|
|
|
*/
|
|
|
|
hasUnmatchedSelectors: function CssHtmlTree_hasUnmatchedSelectors(aProperty)
|
|
|
|
{
|
|
|
|
// Initially check all of the properties that return false for
|
|
|
|
// hasMatchedSelectors(). This speeds-up the UI.
|
|
|
|
if (!this._unmatchedProperties) {
|
|
|
|
let properties = [];
|
|
|
|
CssHtmlTree.propertyNames.forEach(function(aName) {
|
|
|
|
if (!this.matchedProperties[aName]) {
|
|
|
|
properties.push(aName);
|
|
|
|
}
|
|
|
|
}, this);
|
|
|
|
|
|
|
|
if (properties.indexOf(aProperty) == -1) {
|
|
|
|
properties.push(aProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._unmatchedProperties = this.cssLogic.hasUnmatchedSelectors(properties);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lazy-get the result for properties we do not have cached.
|
|
|
|
if (!(aProperty in this._unmatchedProperties)) {
|
|
|
|
let result = this.cssLogic.hasUnmatchedSelectors([aProperty]);
|
|
|
|
this._unmatchedProperties[aProperty] = result[aProperty];
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._unmatchedProperties[aProperty];
|
|
|
|
},
|
|
|
|
|
2011-10-07 08:38:35 -07:00
|
|
|
/**
|
|
|
|
* Destructor for CssHtmlTree.
|
|
|
|
*/
|
|
|
|
destroy: function CssHtmlTree_destroy()
|
|
|
|
{
|
|
|
|
delete this.viewedElement;
|
|
|
|
|
2011-11-04 10:12:58 -07:00
|
|
|
// Remove event listeners
|
|
|
|
this.onlyUserStylesCheckbox.removeEventListener("command",
|
|
|
|
this.onlyUserStylesChanged);
|
|
|
|
this.searchField.removeEventListener("command", this.filterChanged);
|
|
|
|
|
2011-10-07 08:38:35 -07:00
|
|
|
// Nodes used in templating
|
|
|
|
delete this.root;
|
|
|
|
delete this.path;
|
|
|
|
delete this.templatePath;
|
|
|
|
delete this.propertyContainer;
|
|
|
|
delete this.templateProperty;
|
|
|
|
delete this.panel;
|
|
|
|
|
2011-11-05 12:31:00 -07:00
|
|
|
// The document in which we display the results (csshtmltree.xul).
|
2011-10-07 08:38:35 -07:00
|
|
|
delete this.styleDocument;
|
|
|
|
|
|
|
|
// The element that we're inspecting, and the document that it comes from.
|
|
|
|
delete this.propertyViews;
|
|
|
|
delete this.styleWin;
|
|
|
|
delete this.cssLogic;
|
|
|
|
delete this.doc;
|
|
|
|
delete this.win;
|
2011-10-21 10:40:22 -07:00
|
|
|
delete this.styleInspector;
|
2011-10-07 08:38:35 -07:00
|
|
|
},
|
2011-08-30 05:12:02 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A container to give easy access to property data from the template engine.
|
|
|
|
*
|
|
|
|
* @constructor
|
|
|
|
* @param {CssHtmlTree} aTree the CssHtmlTree instance we are working with.
|
|
|
|
* @param {string} aName the CSS property name for which this PropertyView
|
|
|
|
* instance will render the rules.
|
|
|
|
*/
|
2011-08-30 09:40:29 -07:00
|
|
|
function PropertyView(aTree, aName)
|
2011-08-30 05:12:02 -07:00
|
|
|
{
|
|
|
|
this.tree = aTree;
|
|
|
|
this.name = aName;
|
2011-10-21 10:40:22 -07:00
|
|
|
this.getRTLAttr = aTree.getRTLAttr;
|
2011-08-30 05:12:02 -07:00
|
|
|
|
|
|
|
this.link = "https://developer.mozilla.org/en/CSS/" + aName;
|
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
this.templateMatchedSelectors = aTree.styleDocument.getElementById("templateMatchedSelectors");
|
|
|
|
}
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
PropertyView.prototype = {
|
2011-08-30 05:12:02 -07:00
|
|
|
// The parent element which contains the open attribute
|
2011-09-15 03:30:00 -07:00
|
|
|
element: null,
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-11-04 10:12:58 -07:00
|
|
|
// Property header node
|
|
|
|
propertyHeader: null,
|
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
// Destination for property values
|
|
|
|
valueNode: null,
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
// Are matched rules expanded?
|
|
|
|
matchedExpanded: false,
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-11-05 15:19:02 -07:00
|
|
|
// Are unmatched rules expanded?
|
|
|
|
unmatchedExpanded: false,
|
|
|
|
|
|
|
|
// Unmatched selector table
|
|
|
|
unmatchedSelectorTable: null,
|
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
// Matched selector container
|
|
|
|
matchedSelectorsContainer: null,
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
// Matched selector expando
|
|
|
|
matchedExpander: null,
|
|
|
|
|
2011-11-05 15:19:02 -07:00
|
|
|
// Unmatched selector expando
|
|
|
|
unmatchedExpander: null,
|
|
|
|
|
|
|
|
// Unmatched selector container
|
|
|
|
unmatchedSelectorsContainer: null,
|
|
|
|
|
|
|
|
// Unmatched title block
|
|
|
|
unmatchedTitleBlock: null,
|
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
// Cache for matched selector views
|
|
|
|
_matchedSelectorViews: null,
|
|
|
|
|
2011-11-05 15:19:02 -07:00
|
|
|
// Cache for unmatched selector views
|
|
|
|
_unmatchedSelectorViews: null,
|
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
// The previously selected element used for the selector view caches
|
|
|
|
prevViewedElement: null,
|
2011-08-30 05:12:02 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the computed style for the current property.
|
|
|
|
*
|
|
|
|
* @return {string} the computed style for the current property of the
|
|
|
|
* currently highlighted element.
|
|
|
|
*/
|
|
|
|
get value()
|
|
|
|
{
|
|
|
|
return this.propertyInfo.value;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2011-09-15 03:30:00 -07:00
|
|
|
* An easy way to access the CssPropertyInfo behind this PropertyView.
|
2011-08-30 05:12:02 -07:00
|
|
|
*/
|
|
|
|
get propertyInfo()
|
|
|
|
{
|
|
|
|
return this.tree.cssLogic.getPropertyInfo(this.name);
|
|
|
|
},
|
|
|
|
|
2011-10-13 02:28:52 -07:00
|
|
|
/**
|
|
|
|
* Does the property have any matched selectors?
|
|
|
|
*/
|
|
|
|
get hasMatchedSelectors()
|
|
|
|
{
|
2011-11-02 10:50:15 -07:00
|
|
|
return this.tree.matchedProperties[this.name];
|
2011-10-13 02:28:52 -07:00
|
|
|
},
|
|
|
|
|
2011-11-05 15:19:02 -07:00
|
|
|
/**
|
|
|
|
* Does the property have any unmatched selectors?
|
|
|
|
*/
|
|
|
|
get hasUnmatchedSelectors()
|
|
|
|
{
|
|
|
|
return this.tree.hasUnmatchedSelectors(this.name);
|
|
|
|
},
|
|
|
|
|
2011-09-20 02:35:01 -07:00
|
|
|
/**
|
|
|
|
* Should this property be visible?
|
|
|
|
*/
|
|
|
|
get visible()
|
|
|
|
{
|
2011-10-13 02:28:52 -07:00
|
|
|
if (this.tree.showOnlyUserStyles && !this.hasMatchedSelectors) {
|
2011-09-20 02:35:01 -07:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-09-20 02:59:13 -07:00
|
|
|
let searchTerm = this.tree.searchField.value.toLowerCase();
|
|
|
|
if (searchTerm && this.name.toLowerCase().indexOf(searchTerm) == -1 &&
|
|
|
|
this.value.toLowerCase().indexOf(searchTerm) == -1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-09-20 02:35:01 -07:00
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the className that should be assigned to the propertyView.
|
2011-11-04 10:12:58 -07:00
|
|
|
*
|
|
|
|
* @return string
|
2011-09-20 02:35:01 -07:00
|
|
|
*/
|
|
|
|
get className()
|
|
|
|
{
|
2011-11-04 10:12:58 -07:00
|
|
|
if (this.visible) {
|
|
|
|
this.tree._darkStripe = !this.tree._darkStripe;
|
|
|
|
let darkValue = this.tree._darkStripe ?
|
|
|
|
"property-view darkrow" : "property-view";
|
|
|
|
return darkValue;
|
|
|
|
}
|
|
|
|
return "property-view-hidden";
|
2011-09-20 02:35:01 -07:00
|
|
|
},
|
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
/**
|
|
|
|
* Refresh the panel's CSS property value.
|
|
|
|
*/
|
|
|
|
refresh: function PropertyView_refresh()
|
|
|
|
{
|
2011-09-20 02:35:01 -07:00
|
|
|
this.element.className = this.className;
|
|
|
|
|
|
|
|
if (this.prevViewedElement != this.tree.viewedElement) {
|
|
|
|
this._matchedSelectorViews = null;
|
2011-11-05 15:19:02 -07:00
|
|
|
this._unmatchedSelectorViews = null;
|
2011-09-20 02:35:01 -07:00
|
|
|
this.prevViewedElement = this.tree.viewedElement;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!this.tree.viewedElement || !this.visible) {
|
2011-09-15 03:30:00 -07:00
|
|
|
this.valueNode.innerHTML = "";
|
|
|
|
this.matchedSelectorsContainer.hidden = true;
|
2011-11-04 10:12:58 -07:00
|
|
|
this.matchedSelectorsContainer.innerHTML = "";
|
2011-09-15 03:30:00 -07:00
|
|
|
this.matchedExpander.removeAttribute("open");
|
|
|
|
return;
|
|
|
|
}
|
2011-08-30 05:12:02 -07:00
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
this.valueNode.innerHTML = this.propertyInfo.value;
|
2011-11-05 15:19:02 -07:00
|
|
|
this.refreshAllSelectors();
|
2011-08-30 05:12:02 -07:00
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
2011-09-15 03:30:00 -07:00
|
|
|
* Refresh the panel matched rules.
|
2011-08-30 05:12:02 -07:00
|
|
|
*/
|
2011-09-15 03:30:00 -07:00
|
|
|
refreshMatchedSelectors: function PropertyView_refreshMatchedSelectors()
|
2011-08-30 05:12:02 -07:00
|
|
|
{
|
2011-10-13 02:28:52 -07:00
|
|
|
let hasMatchedSelectors = this.hasMatchedSelectors;
|
|
|
|
this.matchedSelectorsContainer.hidden = !hasMatchedSelectors;
|
2011-09-15 03:30:00 -07:00
|
|
|
|
2011-11-06 02:26:09 -08:00
|
|
|
if (hasMatchedSelectors) {
|
2011-11-04 10:12:58 -07:00
|
|
|
this.propertyHeader.classList.add("expandable");
|
|
|
|
} else {
|
|
|
|
this.propertyHeader.classList.remove("expandable");
|
|
|
|
}
|
|
|
|
|
2011-10-13 02:28:52 -07:00
|
|
|
if (this.matchedExpanded && hasMatchedSelectors) {
|
2011-09-15 03:30:00 -07:00
|
|
|
CssHtmlTree.processTemplate(this.templateMatchedSelectors,
|
2011-11-04 10:12:58 -07:00
|
|
|
this.matchedSelectorsContainer, this);
|
2011-09-15 03:30:00 -07:00
|
|
|
this.matchedExpander.setAttribute("open", "");
|
|
|
|
} else {
|
2011-11-04 10:12:58 -07:00
|
|
|
this.matchedSelectorsContainer.innerHTML = "";
|
2011-09-15 03:30:00 -07:00
|
|
|
this.matchedExpander.removeAttribute("open");
|
2011-08-30 05:12:02 -07:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-11-05 15:19:02 -07:00
|
|
|
/**
|
|
|
|
* Refresh the panel unmatched rules.
|
|
|
|
*/
|
|
|
|
refreshUnmatchedSelectors: function PropertyView_refreshUnmatchedSelectors()
|
|
|
|
{
|
|
|
|
let hasMatchedSelectors = this.hasMatchedSelectors;
|
|
|
|
|
|
|
|
this.unmatchedSelectorTable.hidden = !this.unmatchedExpanded;
|
|
|
|
|
|
|
|
if (hasMatchedSelectors) {
|
|
|
|
this.unmatchedSelectorsContainer.hidden = !this.matchedExpanded ||
|
|
|
|
!this.hasUnmatchedSelectors;
|
|
|
|
this.unmatchedTitleBlock.hidden = false;
|
|
|
|
} else {
|
|
|
|
this.unmatchedSelectorsContainer.hidden = !this.unmatchedExpanded;
|
|
|
|
this.unmatchedTitleBlock.hidden = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this.unmatchedExpanded && this.hasUnmatchedSelectors) {
|
|
|
|
CssHtmlTree.processTemplate(this.templateUnmatchedSelectors,
|
|
|
|
this.unmatchedSelectorTable, this);
|
|
|
|
if (!hasMatchedSelectors) {
|
|
|
|
this.matchedExpander.setAttribute("open", "");
|
|
|
|
this.unmatchedSelectorTable.classList.add("only-unmatched");
|
|
|
|
} else {
|
|
|
|
this.unmatchedExpander.setAttribute("open", "");
|
|
|
|
this.unmatchedSelectorTable.classList.remove("only-unmatched");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!hasMatchedSelectors) {
|
|
|
|
this.matchedExpander.removeAttribute("open");
|
|
|
|
}
|
|
|
|
this.unmatchedExpander.removeAttribute("open");
|
|
|
|
this.unmatchedSelectorTable.innerHTML = "";
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Refresh the panel matched and unmatched rules
|
|
|
|
*/
|
|
|
|
refreshAllSelectors: function PropertyView_refreshAllSelectors()
|
|
|
|
{
|
|
|
|
this.refreshMatchedSelectors();
|
|
|
|
},
|
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
/**
|
|
|
|
* Provide access to the matched SelectorViews that we are currently
|
|
|
|
* displaying.
|
|
|
|
*/
|
|
|
|
get matchedSelectorViews()
|
|
|
|
{
|
|
|
|
if (!this._matchedSelectorViews) {
|
|
|
|
this._matchedSelectorViews = [];
|
|
|
|
this.propertyInfo.matchedSelectors.forEach(
|
|
|
|
function matchedSelectorViews_convert(aSelectorInfo) {
|
2011-10-21 10:40:22 -07:00
|
|
|
this._matchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
|
2011-09-15 03:30:00 -07:00
|
|
|
}, this);
|
2011-08-30 05:12:02 -07:00
|
|
|
}
|
2011-11-05 15:19:02 -07:00
|
|
|
|
2011-09-15 03:30:00 -07:00
|
|
|
return this._matchedSelectorViews;
|
2011-08-30 05:12:02 -07:00
|
|
|
},
|
|
|
|
|
2011-11-05 15:19:02 -07:00
|
|
|
/**
|
|
|
|
* Provide access to the unmatched SelectorViews that we are currently
|
|
|
|
* displaying.
|
|
|
|
*/
|
|
|
|
get unmatchedSelectorViews()
|
|
|
|
{
|
|
|
|
if (!this._unmatchedSelectorViews) {
|
|
|
|
this._unmatchedSelectorViews = [];
|
|
|
|
this.propertyInfo.unmatchedSelectors.forEach(
|
|
|
|
function unmatchedSelectorViews_convert(aSelectorInfo) {
|
|
|
|
this._unmatchedSelectorViews.push(new SelectorView(this.tree, aSelectorInfo));
|
|
|
|
}, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._unmatchedSelectorViews;
|
|
|
|
},
|
|
|
|
|
2011-08-30 05:12:02 -07:00
|
|
|
/**
|
2011-09-15 03:30:00 -07:00
|
|
|
* The action when a user expands matched selectors.
|
2011-11-04 10:12:58 -07:00
|
|
|
*
|
|
|
|
* @param {Event} aEvent Used to determine the class name of the targets click
|
|
|
|
* event. If the class name is "helplink" then the event is allowed to bubble
|
|
|
|
* to the mdn link icon.
|
2011-08-30 05:12:02 -07:00
|
|
|
*/
|
2011-11-04 10:12:58 -07:00
|
|
|
propertyHeaderClick: function PropertyView_propertyHeaderClick(aEvent)
|
2011-08-30 05:12:02 -07:00
|
|
|
{
|
2011-11-04 10:12:58 -07:00
|
|
|
if (aEvent.target.className != "helplink") {
|
|
|
|
this.matchedExpanded = !this.matchedExpanded;
|
2011-11-05 15:19:02 -07:00
|
|
|
this.refreshAllSelectors();
|
2011-11-04 10:12:58 -07:00
|
|
|
aEvent.preventDefault();
|
|
|
|
}
|
2011-08-30 05:12:02 -07:00
|
|
|
},
|
|
|
|
|
2011-11-05 15:19:02 -07:00
|
|
|
/**
|
|
|
|
* The action when a user expands unmatched selectors.
|
|
|
|
*/
|
|
|
|
unmatchedSelectorsClick: function PropertyView_unmatchedSelectorsClick(aEvent)
|
|
|
|
{
|
|
|
|
this.unmatchedExpanded = !this.unmatchedExpanded;
|
|
|
|
this.refreshUnmatchedSelectors();
|
|
|
|
aEvent.preventDefault();
|
|
|
|
},
|
|
|
|
|
2011-11-04 10:12:58 -07:00
|
|
|
/**
|
|
|
|
* The action when a user clicks on the MDN help link for a property.
|
|
|
|
*/
|
|
|
|
mdnLinkClick: function PropertyView_mdnLinkClick(aEvent)
|
|
|
|
{
|
|
|
|
this.tree.win.openUILinkIn(this.link, "tab");
|
|
|
|
aEvent.preventDefault();
|
|
|
|
},
|
2011-08-30 05:12:02 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A container to view us easy access to display data from a CssRule
|
2011-10-21 10:40:22 -07:00
|
|
|
* @param CssHtmlTree aTree, the owning CssHtmlTree
|
|
|
|
* @param aSelectorInfo
|
2011-08-30 05:12:02 -07:00
|
|
|
*/
|
2011-10-21 10:40:22 -07:00
|
|
|
function SelectorView(aTree, aSelectorInfo)
|
2011-08-30 05:12:02 -07:00
|
|
|
{
|
2011-10-21 10:40:22 -07:00
|
|
|
this.tree = aTree;
|
2011-08-30 05:12:02 -07:00
|
|
|
this.selectorInfo = aSelectorInfo;
|
|
|
|
this._cacheStatusNames();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Decode for cssInfo.rule.status
|
|
|
|
* @see SelectorView.prototype._cacheStatusNames
|
|
|
|
* @see CssLogic.STATUS
|
|
|
|
*/
|
|
|
|
SelectorView.STATUS_NAMES = [
|
2011-11-05 15:19:02 -07:00
|
|
|
// "Unmatched", "Parent Match", "Matched", "Best Match"
|
2011-08-30 05:12:02 -07:00
|
|
|
];
|
|
|
|
|
|
|
|
SelectorView.CLASS_NAMES = [
|
2011-11-05 15:19:02 -07:00
|
|
|
"unmatched", "parentmatch", "matched", "bestmatch"
|
2011-08-30 05:12:02 -07:00
|
|
|
];
|
|
|
|
|
|
|
|
SelectorView.prototype = {
|
|
|
|
/**
|
|
|
|
* Cache localized status names.
|
|
|
|
*
|
2011-08-30 09:40:29 -07:00
|
|
|
* These statuses are localized inside the styleinspector.properties string
|
|
|
|
* bundle.
|
2011-08-30 05:12:02 -07:00
|
|
|
* @see CssLogic.jsm - the CssLogic.STATUS array.
|
|
|
|
*
|
|
|
|
* @return {void}
|
|
|
|
*/
|
|
|
|
_cacheStatusNames: function SelectorView_cacheStatusNames()
|
|
|
|
{
|
|
|
|
if (SelectorView.STATUS_NAMES.length) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let status in CssLogic.STATUS) {
|
|
|
|
let i = CssLogic.STATUS[status];
|
2011-11-05 15:19:02 -07:00
|
|
|
if (i > -1) {
|
2011-08-30 05:12:02 -07:00
|
|
|
let value = CssHtmlTree.l10n("rule.status." + status);
|
|
|
|
// Replace normal spaces with non-breaking spaces
|
|
|
|
SelectorView.STATUS_NAMES[i] = value.replace(/ /g, '\u00A0');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A localized version of cssRule.status
|
|
|
|
*/
|
|
|
|
get statusText()
|
|
|
|
{
|
|
|
|
return SelectorView.STATUS_NAMES[this.selectorInfo.status];
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get class name for selector depending on status
|
|
|
|
*/
|
|
|
|
get statusClass()
|
|
|
|
{
|
|
|
|
return SelectorView.CLASS_NAMES[this.selectorInfo.status];
|
|
|
|
},
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A localized Get localized human readable info
|
|
|
|
*/
|
|
|
|
humanReadableText: function SelectorView_humanReadableText(aElement)
|
|
|
|
{
|
2011-10-21 10:40:22 -07:00
|
|
|
if (this.tree.getRTLAttr == "rtl") {
|
2011-08-30 05:12:02 -07:00
|
|
|
return this.selectorInfo.value + " \u2190 " + this.text(aElement);
|
|
|
|
} else {
|
|
|
|
return this.text(aElement) + " \u2192 " + this.selectorInfo.value;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
text: function SelectorView_text(aElement) {
|
|
|
|
let result = this.selectorInfo.selector.text;
|
|
|
|
if (this.selectorInfo.elementStyle) {
|
2011-10-26 11:52:34 -07:00
|
|
|
let source = this.selectorInfo.sourceElement;
|
|
|
|
let IUI = this.tree.styleInspector.IUI;
|
|
|
|
if (IUI && IUI.selection == source) {
|
|
|
|
result = "this";
|
|
|
|
} else {
|
|
|
|
result = CssLogic.getShortName(source);
|
2011-08-30 05:12:02 -07:00
|
|
|
}
|
2011-10-26 11:52:34 -07:00
|
|
|
|
2011-10-21 10:40:22 -07:00
|
|
|
aElement.parentNode.querySelector(".rule-link > a").
|
|
|
|
addEventListener("click", function(aEvent) {
|
2011-10-26 11:52:34 -07:00
|
|
|
this.tree.styleInspector.selectFromPath(source);
|
2011-10-21 10:40:22 -07:00
|
|
|
aEvent.preventDefault();
|
|
|
|
}.bind(this), false);
|
2011-08-30 05:12:02 -07:00
|
|
|
result += ".style";
|
|
|
|
}
|
2011-10-21 10:40:22 -07:00
|
|
|
|
2011-08-30 05:12:02 -07:00
|
|
|
return result;
|
|
|
|
},
|
|
|
|
};
|