DOMUtils.jsm
Go to the documentation of this file.
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set sw=2 :miv */
3 /*
4  *=BEGIN SONGBIRD GPL
5  *
6  * This file is part of the Songbird web player.
7  *
8  * Copyright(c) 2005-2010 POTI, Inc.
9  * http://www.songbirdnest.com
10  *
11  * This file may be licensed under the terms of of the
12  * GNU General Public License Version 2 (the ``GPL'').
13  *
14  * Software distributed under the License is distributed
15  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
16  * express or implied. See the GPL for the specific language
17  * governing rights and limitations.
18  *
19  * You should have received a copy of the GPL along with this
20  * program. If not, go to http://www.gnu.org/licenses/gpl.html
21  * or write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23  *
24  *=END SONGBIRD GPL
25  */
26 
32 //------------------------------------------------------------------------------
33 //
34 // DOM utility JSM configuration.
35 //
36 //------------------------------------------------------------------------------
37 
38 EXPORTED_SYMBOLS = [ "DOMUtils",
39  "sbDOMHighlighter",
40  "DOMEventListenerSet",
41  "sbColorUtils" ];
42 
43 
44 //------------------------------------------------------------------------------
45 //
46 // DOM utility defs.
47 //
48 //------------------------------------------------------------------------------
49 
50 const Cc = Components.classes;
51 const Ci = Components.interfaces;
52 const Cr = Components.results
53 const Cu = Components.utils
54 
55 
56 //------------------------------------------------------------------------------
57 //
58 // DOM utility services.
59 //
60 //------------------------------------------------------------------------------
61 
62 var DOMUtils = {
63  //----------------------------------------------------------------------------
64  //
65  // DOM utility services.
66  //
67  //----------------------------------------------------------------------------
68 
78  loadDocument: function DOMUtils_loadDocument(aDocumentURI) {
79  // Open the document.
80  var ioSvc = Cc["@mozilla.org/network/io-service;1"]
81  .getService(Ci.nsIIOService);
82  var channel = ioSvc.newChannel(aDocumentURI, null, null);
83  var inputStream = channel.open();
84 
85  // Load and return the document.
86  var domParser = Cc["@mozilla.org/xmlextras/domparser;1"]
87  .createInstance(Ci.nsIDOMParser);
88  return domParser.parseFromStream(inputStream,
89  null,
90  channel.contentLength,
91  "text/xml");
92  },
93 
94 
114  importChildElements: function DOMUtils_importChildElements(aDstNode,
115  aSrcDocument,
116  aSrcParentID,
117  aChildAttrList) {
118  // Get the destination document and the list of source children.
119  var dstDoc = aDstNode.ownerDocument;
120  var srcChildList = aSrcDocument.getElementById(aSrcParentID).childNodes;
121 
122  // Import the source elements.
123  for (var i = 0; i < srcChildList.length; i++) {
124  // Get the next source child. Skip if not an element.
125  var srcChild = srcChildList[i];
126  if (srcChild.nodeType != Ci.nsIDOMNode.ELEMENT_NODE)
127  continue;
128 
129  // Import the source child into the destination document.
130  var dstChild = dstDoc.importNode(srcChild, true);
131 
132  // Add the child to the destination node.
133  aDstNode.appendChild(dstChild);
134 
135  // Add the child attributes.
136  for (var attrName in aChildAttrList) {
137  dstChild.setAttribute(attrName, aChildAttrList[attrName]);
138  }
139  }
140  },
141 
142 
157  copyAttributes: function DOMUtils_copyAttributes(aSrcElem,
158  aDstElem,
159  aAttributeList,
160  aRemoveAttributes) {
161  // Copy the attributes.
162  for (var i = 0; i < aAttributeList.length; i++) {
163  // Get attribute name.
164  var attribute = aAttributeList[i];
165 
166  // If source element does not have the attribute, do nothing or remove
167  // the attribute as specified.
168  if (!aSrcElem.hasAttribute(attribute)) {
169  if (aRemoveAttributes)
170  aDstElem.removeAttribute(attribute);
171  continue;
172  }
173 
174  // Copy the attribute from the source element to the destination if the
175  // attribute values differ.
176  var srcAttributeVal = aSrcElem.getAttribute(attribute);
177  var dstAttributeVal = aDstElem.getAttribute(attribute);
178  if (srcAttributeVal != dstAttributeVal)
179  aDstElem.setAttribute(attribute, srcAttributeVal);
180  }
181  },
182 
183 
198  getElementsByAttribute: function DOMUtils_getElementsByAttribute(aRootElem,
199  aAttrName,
200  aAttrValue) {
201  // Start searching for elements from the root.
202  var matchList = [];
203  this._getElementsByAttribute(aRootElem, aAttrName, aAttrValue, matchList);
204 
205  return matchList;
206  },
207 
208  _getElementsByAttribute: function
209  DOMUtils__getElementsByAttribute(aRootElem,
210  aAttrName,
211  aAttrValue,
212  aMatchList) {
213  // Search each of the children.
214  var childList = aRootElem.childNodes;
215  for (var i = 0; i < childList.length; i++) {
216  // Check the child node for a match.
217  var child = childList[i];
218  if (child.hasAttributes() && child.hasAttribute(aAttrName)) {
219  if (!aAttrValue || (child.getAttribute(aAttrName) == aAttrValue))
220  aMatchList.push(child);
221  }
222 
223  // Check each of the child's children.
224  this._getElementsByAttribute(child, aAttrName, aAttrValue, aMatchList);
225  }
226  },
227 
228 
238  encodeTextForXML: function DOMUtils_encodeTextForXML(aText) {
239  // Create an XML text node.
240  var xmlDocument = Cc["@mozilla.org/xml/xml-document;1"]
241  .createInstance(Ci.nsIDOMDocument);
242  var xmlTextNode = xmlDocument.createTextNode(aText);
243 
244  // Produce the encoded XML text by serializing the XML text node.
245  var domSerializer = Cc["@mozilla.org/xmlextras/xmlserializer;1"]
246  .createInstance(Ci.nsIDOMSerializer);
247  var encodedXMLText = domSerializer.serializeToString(xmlTextNode);
248 
249  return encodedXMLText;
250  },
251 
252 
265  rebindXBL: function DOMUtils_rebindXBL(aElem) {
266  // Clone the node to force a rebinding. The clone is not needed.
267  //XXXeps there may be a better way to do this, but this works for now
268  aElem.cloneNode(false);
269  },
270 
271 
272  //----------------------------------------------------------------------------
273  //
274  // DOM class attribute services.
275  //
276  //----------------------------------------------------------------------------
277 
286  setClass: function DOMUtils_setClass(aElem, aClass) {
287  // Get the class attribute. If it's empty, just set the class and return.
288  var classAttr = aElem.getAttribute("class");
289  if (!classAttr) {
290  aElem.setAttribute("class", aClass);
291  return;
292  }
293 
294  // Set class if not already set.
295  var classList = classAttr.split(" ");
296  if (classList.indexOf(aClass) < 0) {
297  classList.push(aClass);
298  aElem.setAttribute("class", classList.join(" "));
299  }
300  },
301 
302 
311  clearClass: function DOMUtils_clearClass(aElem, aClass) {
312  // Get the class attribute. If it's empty, just return.
313  var classAttr = aElem.getAttribute("class");
314  if (!classAttr)
315  return;
316 
317  // Clear specified class from class attribute.
318  var classList = classAttr.split(" ");
319  var classCount = classList.length;
320  for (var i = classCount - 1; i >= 0; i--) {
321  if (classList[i] == aClass)
322  classList.splice(i, 1);
323  }
324  if (classList.length < classCount)
325  aElem.setAttribute("class", classList.join(" "));
326  },
327 
328 
339  isClassSet: function DOMUtils_isClassSet(aElem, aClass) {
340  var classAttr = aElem.getAttribute("class");
341  var classList = classAttr.split(" ");
342  if (classList.indexOf(aClass) < 0)
343  return false;
344 
345  return true;
346  },
347 
348 
349  //----------------------------------------------------------------------------
350  //
351  // DOM node destruction services.
352  //
353  // When a node is removed from a DOM tree, any XBL bindings bound to that
354  // node or its children are not detached. Thus, the binding destructors are
355  // not called. The XBL bindings are not detached until the owner document is
356  // destroyed. This can lead to memory leaks if XBL bound nodes are
357  // dynamically added and removed from a document.
358  // The DOM node destruction services provide support for releasing XBL
359  // binding resources before the XBL binding is detached. An XBL binding may
360  // add a destroy function by calling addNodeDestroyFunc. Multiple destroy
361  // functions may be added for an XBL binding (e.g., for extended bindings).
362  // Before a node is removed from a document, destroyNode should be called.
363  // This function recursively calls the destroy functions for all of the nodes
364  // children, including the anonymous children. Note that destroyNode must be
365  // called before node removal since node removal removes XBL binding content.
366  // If destroyNode is called after node removal, anonymous child nodes will not
367  // be destroyed.
368  //
369  //----------------------------------------------------------------------------
370 
380  addNodeDestroyFunc: function DOMUtils_addNodeDestroyFunc(aNode,
381  aFunc) {
382  // Ensure the destroy function list exists.
383  if (!aNode.destroyFuncList)
384  aNode.destroyFuncList = [];
385 
386  // Push the destroy function on the end of the list.
387  aNode.destroyFuncList.push(aFunc);
388  },
389 
390 
398  destroyNode: function DOMUtils_destroyNode(aNode) {
399  /* Can only destroy element nodes. */
400  if (aNode.nodeType != Ci.nsIDOMNode.ELEMENT_NODE)
401  return;
402 
403  // Destroy all of the node's children, including the anonymous children.
404  var nodeDocument = aNode.ownerDocument;
405  this.destroyNodeList(nodeDocument.getAnonymousNodes(aNode));
406  this.destroyNodeList(aNode.childNodes);
407 
408  // Call the node destroy functions.
409  while (aNode.destroyFuncList) {
410  // Pop the next destroy function from the end of the list and call it.
411  var destroyFunc = aNode.destroyFuncList.pop();
412  try {
413  destroyFunc();
414  } catch (ex) {
415  var msg = "Exception: " + ex +
416  " at " + ex.fileName +
417  ", line " + ex.lineNumber;
418  Cu.reportError(msg);
419  }
420 
421  // If the destroy function list is empty, remove it.
422  if (!aNode.destroyFuncList.length)
423  aNode.destroyFuncList = null;
424  }
425  },
426 
427 
434  destroyNodeList: function DOMUtils_destroyNodeList(aNodeList) {
435  if (!aNodeList)
436  return;
437 
438  for (var i = 0; i < aNodeList.length; i++) {
439  this.destroyNode(aNodeList[i]);
440  }
441  }
442 };
443 
444 
445 //------------------------------------------------------------------------------
446 //
447 // DOM highlighter services.
448 //
449 // These services may be used to apply highlighting effects to DOM elements.
450 // Highlighting is applied by setting a DOM element attribute to a particular
451 // value.
452 // The highlighting effect blinks the highlighting for a specified period of
453 // time and then leaves the highlight applied. When the user clicks anywhere,
454 // the highlighting is removed.
455 //
456 //------------------------------------------------------------------------------
457 
485 function sbDOMHighlighter(aWindow,
486  aElement,
487  aValue,
488  aAttribute,
489  aBlinkDuration) {
490  // Get the attribute to highlight, defaulting to "class".
491  var attribute = aAttribute;
492  if (!attribute)
493  attribute = "class";
494 
495  // Get the attribute value with which to highlight. Choose an appropriate
496  // default if no value was specified.
497  var value = aValue;
498  if (!value) {
499  if (attribute == "class") {
500  value = "highlight";
501  }
502  else {
503  value = "true";
504  }
505  }
506 
507  // Get the blink duration.
508  var blinkDuration = aBlinkDuration;
509  if (!blinkDuration)
510  blinkDuration = this.defaultBlinkDuration;
511 
512  // Save parameters.
513  this._window = aWindow;
514  this._element = aElement;
515  this._value = value;
516  this._attribute = attribute;
517  this._blinkDuration = blinkDuration;
518 }
519 
520 
549 sbDOMHighlighter.highlightElement =
550  function sbDOMHighlighter_highlightElement(aWindow,
551  aElement,
552  aValue,
553  aAttribute,
554  aBlinkDuration) {
555  // Create and start a highlighter.
556  var highlighter = new sbDOMHighlighter(aWindow,
557  aElement,
558  aValue,
559  aAttribute,
560  aBlinkDuration);
561  highlighter.start();
562 
563  return highlighter;
564 }
565 
566 
567 // Define the class.
568 sbDOMHighlighter.prototype = {
569  // Set the constructor.
571 
572  //
573  // DOM highlighter configuration.
574  //
575  // blinkPeriod Blink period in milliseconds.
576  // defaultBlinkDuration Default blink duration in milliseconds.
577  //
578 
579  blinkPeriod: 250,
580  defaultBlinkDuration: 2000,
581 
582 
583  //
584  // DOM highlighter fields.
585  //
586  // _window Window containing element.
587  // _element Element to highlight.
588  // _attribute Attribute to use to highlight.
589  // _value Value to set highlight.
590  // _blinkDuration Duration of blinking.
591  // _domEventListenerSet Set of DOM event listeners.
592  // _blinkTimer Timer used for blinking.
593  // _blinkStart Timestamp of start of blinking.
594  // _isHighlighted If true, element is currently highlighted.
595  //
596 
597  _window: null,
598  _element: null,
599  _attribute: null,
600  _value: null,
601  _blinkDuration: -1,
602  _domEventListenerSet: null,
603  _blinkTimer: null,
604  _blinkStart: 0,
605  _isHighlighted: false,
606 
607 
612  start: function sbDOMHighlighter_start() {
613  var self = this;
614  var func;
615 
616  // Create a DOM event listener set.
617  this._domEventListenerSet = new DOMEventListenerSet();
618 
619  // Listen to document click and window unload events.
620  func = function _onClickWrapper() { self._onClick(); };
621  this._domEventListenerSet.add(this._element.ownerDocument,
622  "click",
623  func,
624  true);
625  if (this._window) {
626  func = function _onUnloadWrapper() { self._onUnload(); };
627  this._domEventListenerSet.add(this._window, "unload", func, false);
628  }
629 
630  // Highlight element.
631  this.setHighlight();
632 
633  // Start blinking.
634  this._blinkStart = Date.now();
635  var self = this;
636  var func = function _blinkWrapper() { self._blink(); };
637  this._blinkTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
638  this._blinkTimer.initWithCallback(func,
639  this.blinkPeriod,
640  Ci.nsITimer.TYPE_REPEATING_SLACK);
641  },
642 
643 
648  cancel: function sbDOMHighlighter_cancel() {
649  // Remove DOM event listeners.
650  if (this._domEventListenerSet)
651  this._domEventListenerSet.removeAll();
652 
653  // Cancel blink timer.
654  if (this._blinkTimer)
655  this._blinkTimer.cancel();
656 
657  // Remove object references.
658  this._element = null;
659  this._domEventListenerSet = null;
660  this._blinkTimer = null;
661  },
662 
663 
668  setHighlight: function sbDOMHighlighter_setHighlight() {
669  // Apply highlight.
670  if (this._attribute == "class") {
671  DOMUtils.setClass(this._element, this._value);
672  }
673  else {
674  this._element.setAttribute(this._attribute, this._value);
675  }
676 
677  // Indicate that the element is now highlighted.
678  this._isHighlighted = true;
679  },
680 
681 
686  clearHighlight: function sbDOMHighlighter_clearHighlight() {
687  // Remove highlight.
688  if (this._attribute == "class") {
689  DOMUtils.clearClass(this._element, this._value);
690  }
691  else {
692  this._element.removeAttribute(this._attribute);
693  }
694 
695  // Indicate that the element is no longer highlighted.
696  this._isHighlighted = false;
697  },
698 
699 
700  //----------------------------------------------------------------------------
701  //
702  // Internal DOM highlighter services.
703  //
704  //----------------------------------------------------------------------------
705 
710  _blink: function sbDOMHighlighter__blink() {
711  // If the blink duration has been reached, set the highlight and stop the
712  // blinking.
713  var blinkTime = Date.now() - this._blinkStart;
714  if (blinkTime >= this._blinkDuration) {
715  this._blinkTimer.cancel();
716  this._blinkTimer = null;
717  this.setHighlight();
718  return;
719  }
720 
721  // Blink the element highlight.
722  if (this._isHighlighted) {
723  this.clearHighlight();
724  }
725  else {
726  this.setHighlight();
727  }
728  },
729 
730 
735  _onClick: function sbDOMHighlighter__onClick() {
736  // Clear highlight and cancel highlighter.
737  this.clearHighlight();
738  this.cancel();
739  },
740 
741 
746  _onUnload: function sbDOMHighlighter__onUnload() {
747  // Cancel highlighter.
748  this.cancel();
749  }
750 };
751 
752 
753 //------------------------------------------------------------------------------
754 //
755 // DOM event listener set services.
756 //
757 // These services may be used to maintain a set of DOM event listeners and to
758 // facilitate the removal of DOM event listeners.
759 //
760 //------------------------------------------------------------------------------
761 
767 {
768  // Initialize some fields.
769  this._eventListenerList = {};
770 }
771 
772 // Set constructor.
773 DOMEventListenerSet.prototype.constructor = DOMEventListenerSet;
774 
775 // Define the class.
776 DOMEventListenerSet.prototype = {
777  //
778  // DOM event listener set fields.
779  //
780  // _eventListenerList List of event listeners.
781  // _nextEventListenerID Next event listener ID.
782  //
783 
784  _eventListenerList: null,
785  _nextEventListenerID: 0,
786 
787 
806  add: function DOMEventListenerSet_add(aElement,
807  aType,
808  aListener,
809  aUseCapture,
810  aOneShot,
811  aWantsUntrusted) {
812  // Create the event listener object.
813  var eventListener = {};
814  eventListener.id = this._nextEventListenerID++;
815  eventListener.element = aElement;
816  eventListener.type = aType;
817  eventListener.listener = aListener;
818  eventListener.useCapture = aUseCapture;
819  eventListener.oneShot = aOneShot;
820  eventListener.wantsUntrusted = aWantsUntrusted == true ? true : false;
821 
822  // Use one-shot function if listener is a one-shot listener.
823  var listenerFunc = eventListener.listener;
824  if (eventListener.oneShot) {
825  var _this = this;
826  listenerFunc =
827  function(aEvent) { return _this._doOneShot(aEvent, eventListener); };
828  }
829  eventListener.addedListener = listenerFunc;
830 
831  // Add the event listener.
832  eventListener.element.addEventListener(eventListener.type,
833  eventListener.addedListener,
834  eventListener.useCapture,
835  eventListener.wantsUntrusted);
836  this._eventListenerList[eventListener.id] = eventListener;
837 
838  return (eventListener.id);
839  },
840 
841 
848  remove: function DOMEventListenerSet_remove(aEventListenerID) {
849  // Get the event listener. Do nothing if not found.
850  var eventListener = this._eventListenerList[aEventListenerID];
851  if (!eventListener)
852  return;
853 
854  // Remove the event listener.
855  eventListener.element.removeEventListener(eventListener.type,
856  eventListener.addedListener,
857  eventListener.useCapture);
858  delete this._eventListenerList[aEventListenerID];
859  },
860 
861 
866  removeAll: function DOMEventListenerSet_removeAll() {
867  // Remove all event listeners.
868  for (var id in this._eventListenerList) {
869  this.remove(id);
870  }
871  this._eventListenerList = {};
872  },
873 
874 
875  //----------------------------------------------------------------------------
876  //
877  // Internal DOM event listener set services.
878  //
879  //----------------------------------------------------------------------------
880 
889  _doOneShot: function DOMEventListenerSet__doOneShot(aEvent, aEventListener) {
890  // Remove event listener if one-shot.
891  if (aEventListener.oneShot)
892  this.remove(aEventListener.id);
893 
894  // Dispatch event to listener.
895  return aEventListener.listener(aEvent);
896  }
897 };
898 
899 
900 //------------------------------------------------------------------------------
901 //
902 // Color utility services.
903 //
904 // These utilities provide services for handling colors specified in DOM
905 // attributes or in CSS.
906 // These utilities make use of color objects with the following fields:
907 //
908 // red Number from 0.0 to 1.0.
909 // green Number from 0.0 to 1.0.
910 // blue Number from 0.0 to 1.0.
911 // alpha Number from 0.0 to 1.0.
912 //
913 //------------------------------------------------------------------------------
914 
926  getColorFromCSSColor: function sbColorUtils_getCSSColor(aCSSColor) {
927  // Parse out the color components substring from the CSS color string.
928  var rgba = aCSSColor.match(/^rgba?\((.*)\)/)
929  if (!rgba || (rgba.length < 2))
930  return null;
931  rgba = rgba[1];
932 
933  // Split the color components substring into an array.
934  rgba = rgba.split(",");
935  if (rgba.length < 3)
936  return null;
937 
938  // Parse the color components into a color object.
939  var color = {
940  red: parseFloat(rgba[0]) / 255.0,
941  green: parseFloat(rgba[1]) / 255.0,
942  blue: parseFloat(rgba[2]) / 255.0
943  };
944  if (rgba.length > 3)
945  color.alpha = parseFloat(rgba[3])
946  else
947  color.alpha = 1.0;
948 
949  return color;
950  },
951 
952 
962  getDOMColorString: function sbColorUtils_getDOMColorString(aColor) {
963  // Convert the colors to integer values from 0 to 255.
964  var red = Math.round(aColor.red * 255.0);
965  var green = Math.round(aColor.green * 255.0);
966  var blue = Math.round(aColor.blue * 255.0);
967 
968  // Combine the individual color values into a single 32-bit rgb value.
969  var colorValue = (red << 16) | (green << 8) | blue;
970 
971  // Convert the 32-bit rgb value to the form "#xxxxxx", adding leading zeros
972  // to ensure there are 6 digits.
973  var colorString = (0x01000000 + colorValue).toString(16);
974  colorString = "#" + colorString.slice(1);
975 
976  return colorString;
977  }
978 };
979 
function start(ch)
const Cu
const Ci
Definition: DOMUtils.jsm:51
function sbDOMHighlighter(aWindow, aElement, aValue, aAttribute, aBlinkDuration)
Definition: DOMUtils.jsm:485
menuItem id
Definition: FeedWriter.js:971
onPageChanged aValue
Definition: FeedWriter.js:1395
var sbColorUtils
Definition: DOMUtils.jsm:915
const Cr
Definition: DOMUtils.jsm:52
function DOMEventListenerSet()
Definition: DOMUtils.jsm:766
EXPORTED_SYMBOLS
Definition: DOMUtils.jsm:38
ui plugin add("draggable","cursor",{start:function(e, ui){var t=$('body');if(t.css("cursor")) ui.options._cursor=t.css("cursor");t.css("cursor", ui.options.cursor);}, stop:function(e, ui){if(ui.options._cursor)$('body').css("cursor", ui.options._cursor);}})
this _window
Definition: FeedWriter.js:1158
var _this
DOMEventListenerSet prototype constructor
Definition: DOMUtils.jsm:773
const Cc
Definition: DOMUtils.jsm:50
return null
Definition: FeedWriter.js:1143
countRef value
Definition: FeedWriter.js:1423
function msg
_getWindowDimension aAttribute
_getSelectedPageStyle s i