home *** CD-ROM | disk | FTP | other *** search
/ Freelog 112 / FreelogNo112-NovembreDecembre2012.iso / Multimedia / Songbird / Songbird_2.0.0-2311_windows-i686-msvc8.exe / components / sbDeviceServicePaneService.js < prev    next >
Text File  |  2012-06-08  |  25KB  |  757 lines

  1. /* vim: set ts=2 sw=2 expandtab : */
  2. /*
  3.  *=BEGIN SONGBIRD GPL
  4.  *
  5.  * This file is part of the Songbird web player.
  6.  *
  7.  * Copyright(c) 2005-2010 POTI, Inc.
  8.  * http://www.songbirdnest.com
  9.  *
  10.  * This file may be licensed under the terms of of the
  11.  * GNU General Public License Version 2 (the ``GPL'').
  12.  *
  13.  * Software distributed under the License is distributed
  14.  * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either
  15.  * express or implied. See the GPL for the specific language
  16.  * governing rights and limitations.
  17.  *
  18.  * You should have received a copy of the GPL along with this
  19.  * program. If not, go to http://www.gnu.org/licenses/gpl.html
  20.  * or write to the Free Software Foundation, Inc.,
  21.  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  22.  *
  23.  *=END SONGBIRD GPL
  24.  */
  25.  
  26. /**
  27.  * \file sbDeviceServicePane.js
  28.  */
  29.  
  30. const Cc = Components.classes;
  31. const Ci = Components.interfaces;
  32. const Cr = Components.results;
  33. const Cu = Components.utils;
  34.  
  35. const CONTRACTID = "@songbirdnest.com/servicepane/device;1";
  36.  
  37. const SP = "http://songbirdnest.com/rdf/servicepane#";
  38. const LSP = 'http://songbirdnest.com/rdf/library-servicepane#';
  39. const DEVICESP_NS = "http://songbirdnest.com/rdf/device-servicepane#";
  40.  
  41. const URN_PREFIX_DEVICE = "urn:device:";
  42. const DEVICE_NODE_WEIGHT = -2;
  43.  
  44. Cu.import("resource://gre/modules/XPCOMUtils.jsm");
  45. Cu.import("resource://app/jsmodules/ArrayConverter.jsm");
  46. Cu.import("resource://app/jsmodules/DOMUtils.jsm");
  47. Cu.import("resource://app/jsmodules/sbProperties.jsm");
  48. Cu.import("resource://app/jsmodules/sbLibraryUtils.jsm");
  49. Cu.import("resource://app/jsmodules/DebugUtils.jsm");
  50.  
  51. /**
  52.  * Given the arguments var of a function, dump the
  53.  * name of the function and the parameters provided
  54.  */
  55. function logcall(parentArgs) {
  56.   dump("\n");
  57.   dump(parentArgs.callee.name + "(");
  58.   for (var i = 0; i < parentArgs.length; i++) {
  59.     dump(parentArgs[i]);
  60.     if (i < parentArgs.length - 1) {
  61.       dump(', ');
  62.     }
  63.   }
  64.   dump(")\n");
  65. }
  66.  
  67.  
  68.  
  69. /**
  70.  * /class sbDeviceServicePane
  71.  * /brief Provides device related nodes for the service pane
  72.  * \sa sbIServicePaneService sbIDeviceServicePaneService
  73.  */
  74. function sbDeviceServicePane() {
  75.   this._servicePane = null;
  76.   this._libraryServicePane = null;
  77.  
  78.   // use the default stringbundle to translate tree nodes
  79.   this.stringbundle = null;
  80.  
  81.   this.log = DebugUtils.generateLogFunction("DeviceServicePaneService");
  82. }
  83.  
  84. //////////////////////////
  85. // XPCOM Support        //
  86. //////////////////////////
  87. sbDeviceServicePane.prototype.QueryInterface =
  88.   XPCOMUtils.generateQI([Ci.sbIServicePaneModule,
  89.                          Ci.sbIServicePaneMutationListener,
  90.                          Ci.sbIDeviceServicePaneService]);
  91. sbDeviceServicePane.prototype.classDescription =
  92.   "Songbird Device Service Pane Service";
  93. sbDeviceServicePane.prototype.classID =
  94.   Components.ID("{845c31ee-c30e-4fb6-9667-0b10e58c7069}");
  95. sbDeviceServicePane.prototype.contractID =
  96.   CONTRACTID;
  97. sbDeviceServicePane.prototype._xpcom_categories = [
  98.   {
  99.     category: 'service-pane',
  100.     entry: 'device', // we want this to load first
  101.     value: CONTRACTID
  102.   }];
  103.  
  104. //////////////////////////
  105. // sbIServicePaneModule //
  106. //////////////////////////
  107.  
  108. sbDeviceServicePane.prototype.servicePaneInit =
  109. function sbDeviceServicePane_servicePaneInit(sps) {
  110.   //logcall(arguments);
  111.  
  112.   // keep track of the service pane services
  113.   this._servicePane = sps;
  114.   this._libraryServicePane = Cc["@songbirdnest.com/servicepane/library;1"]
  115.                                .getService(Ci.sbILibraryServicePaneService);
  116.  
  117.   // load the device context menu document
  118.   this._deviceContextMenuDoc =
  119.         DOMUtils.loadDocument
  120.           ("chrome://songbird/content/xul/device/deviceContextMenu.xul");
  121.  
  122.   // cache the device manager
  123.   this._deviceMgr = Cc["@songbirdnest.com/Songbird/DeviceManager;2"]
  124.                       .getService(Ci.sbIDeviceManager2);
  125.  
  126.   this._mainLibraryListeners = [];
  127.   this._deviceLibraryAndListenerObjs = [];
  128. }
  129.  
  130. sbDeviceServicePane.prototype.shutdown =
  131. function sbDeviceServicePane_shutdown() {
  132.   // release object references
  133.   for (var i = 0; i < this._deviceLibraryAndListenerObjs.length; i++) {
  134.     var currObj = this._deviceLibraryAndListenerObjs[i];
  135.     currObj["library"].removeListener(currObj["listener"]);
  136.   }
  137.  
  138.   for (var i = 0; i < this._mainLibraryListeners.length; i++) {
  139.     LibraryUtils.mainLibrary.removeListener(this._mainLibraryListeners[i]);
  140.   }
  141.  
  142.   this._servicePane.root.removeMutationListener(this);
  143.   this._servicePane = null;
  144.   if (this._deviceGroupNode)
  145.     this._deviceGroupNode.removeMutationListener(this);
  146.   this._deviceGroupNode = null;
  147.   this._libraryServicePane = null;
  148.   this._deviceContextMenuDoc = null;
  149.   this._deviceMgr = null;
  150. }
  151.  
  152. sbDeviceServicePane.prototype.fillContextMenu =
  153. function sbDeviceServicePane_fillContextMenu(aNode, aContextMenu, aParentWindow) {
  154.   // Get the node device ID.  Do nothing if not a device node.
  155.   var deviceID = aNode.getAttributeNS(DEVICESP_NS, "device-id");
  156.   if (!deviceID)
  157.     return;
  158.  
  159.   // Do nothing if not set to fill with the default device context menu items.
  160.   var fillDefaultContextMenu = aNode.getAttributeNS(DEVICESP_NS,
  161.                                                     "fillDefaultContextMenu");
  162.   if (fillDefaultContextMenu != "true")
  163.     return;
  164.  
  165.   // Get the device node type.
  166.   var deviceNodeType = aNode.getAttributeNS(DEVICESP_NS, "deviceNodeType");
  167.  
  168.   // Import device context menu items into the context menu.
  169.   if (deviceNodeType == "device") {
  170.     DOMUtils.importChildElements(aContextMenu,
  171.                                  this._deviceContextMenuDoc,
  172.                                  "device_context_menu_items",
  173.                                  { "device-id": deviceID,
  174.                                    "service_pane_node_id": aNode.id });
  175.   } else if (deviceNodeType == "library") {
  176.     DOMUtils.importChildElements(aContextMenu,
  177.                                  this._deviceContextMenuDoc,
  178.                                  "device_library_context_menu_items",
  179.                                  { "device-id": deviceID,
  180.                                    "service_pane_node_id": aNode.id });
  181.   }
  182. }
  183.  
  184. sbDeviceServicePane.prototype.fillNewItemMenu =
  185. function sbDeviceServicePane_fillNewItemMenu(aNode, aContextMenu, aParentWindow) {
  186. }
  187.  
  188. sbDeviceServicePane.prototype.onSelectionChanged =
  189. function sbDeviceServicePane_onSelectionChanged(aNode, aContainer, aParentWindow) {
  190. }
  191.  
  192. sbDeviceServicePane.prototype.canDrop =
  193. function sbDeviceServicePane_canDrop(aNode, aDragSession, aOrientation, aWindow) {
  194.   return false;
  195. }
  196.  
  197. sbDeviceServicePane.prototype.onDrop =
  198. function sbDeviceServicePane_onDrop(aNode, aDragSession, aOrientation, aWindow) {
  199. }
  200.  
  201. sbDeviceServicePane.prototype.onDragGesture =
  202. function sbDeviceServicePane_onDragGesture(aNode, aDataTransfer) {
  203.   return false;
  204. }
  205.  
  206.  
  207. /**
  208.  * Called when the user is about to attempt to rename a device node
  209.  */
  210. sbDeviceServicePane.prototype.onBeforeRename =
  211. function sbDeviceServicePane_onBeforeRename(aNode) {
  212. }
  213.  
  214. /**
  215.  * Called when the user has attempted to rename a device node
  216.  */
  217. sbDeviceServicePane.prototype.onRename =
  218. function sbDeviceServicePane_onRename(aNode, aNewName) {
  219. }
  220.  
  221.  
  222. //////////////////////////////////
  223. // sbIDeviceServicePaneService //
  224. //////////////////////////////////
  225.  
  226. sbDeviceServicePane.prototype.createNodeForDevice =
  227. function sbDeviceServicePane_createNodeForDevice(aDevice, aDeviceIdentifier) {
  228.   //logcall(arguments);
  229.   this.log("createNodeForDevice");
  230.  
  231.   // Make sure the devices group exists first
  232.   let devicesNode = this._ensureDevicesGroupExists();
  233.  
  234.   // Get the Node.
  235.   var id = this._deviceURN(aDevice, aDeviceIdentifier);
  236.   var node = this._servicePane.getNode(id);
  237.   if (!node) {
  238.     // Create the node
  239.     node = this._servicePane.createNode();
  240.     node.id = id;
  241.   }
  242.  
  243.   // Refresh the information just in case it is supposed to change
  244.   node.contractid = CONTRACTID;
  245.   node.setAttributeNS(SP, "Weight", DEVICE_NODE_WEIGHT);
  246.   node.editable = false;
  247.  
  248.   // Add the node
  249.   if (!node.parentNode) {
  250.     this.log("\tNo parentNode, appending to devices group node");
  251.     devicesNode.appendChild(node);
  252.   }
  253.  
  254.   return node;
  255. }
  256.  
  257. sbDeviceServicePane.prototype.createNodeForDevice2 =
  258. function sbDeviceServicePane_createNodeForDevice2(aDevice, aEjectable) {
  259.   this.log("createNodeForDevice2");
  260.  
  261.   // Make sure the devices group exists first
  262.   let devicesNode = this._ensureDevicesGroupExists();
  263.  
  264.   // Get the Node.
  265.   var id = this._deviceURN2(aDevice);
  266.  
  267.   var node = this._servicePane.getNode(id);
  268.   if (!node) {
  269.     // Create the node
  270.     node = this._servicePane.createNode();
  271.     node.id = id;
  272.   }
  273.  
  274.   // Refresh the information just in case it is supposed to change
  275.   node.contractid = CONTRACTID;
  276.   node.setAttributeNS(DEVICESP_NS, "device-id", aDevice.id);
  277.   node.setAttributeNS(DEVICESP_NS, "deviceNodeType", "device");
  278.   node.setAttributeNS(SP, "Weight", DEVICE_NODE_WEIGHT);
  279.   if (aEjectable) {
  280.     node.setAttribute("ejectable", "true");
  281.     let listener = new _deviceNodeEventListener(aDevice);
  282.     node.addEventListener(listener);
  283.   }
  284.   node.editable = false;
  285.   node.className = "device";
  286.  
  287.   try {
  288.     var iconUri = aDevice.properties.iconUri;
  289.     if (iconUri) {
  290.       var spec = iconUri.spec;
  291.       if (iconUri.schemeIs("file") && /\.ico$/i(spec)) {
  292.         // for *.ico, try to get the small icon
  293.         spec = "moz-icon://" + spec + "?size=16";
  294.       }
  295.       node.image = spec;
  296.     }
  297.   } catch(ex) {
  298.     /* we do not care if setting the icon fails */
  299.   }
  300.  
  301.   // Add the node
  302.   if (!node.parentNode) {
  303.     this.log("\tNo parentNode, appending to devices group node");
  304.     devicesNode.appendChild(node);
  305.   }
  306.  
  307.   this._device = aDevice;
  308.   return node;
  309. }
  310.  
  311. sbDeviceServicePane.prototype.createLibraryNodeForDevice =
  312. function sbDeviceServicePane_createLibraryNodeForDevice(aDevice, aLibrary) {
  313.   this.log("Creating library nodes for device " + aDevice.id);
  314.   var deviceNode = this.getNodeForDevice(aDevice);
  315.  
  316.   // Create the library nodes and move them underneath the device node
  317.   this._libraryServicePane.createNodeForLibrary(aLibrary);
  318.   this._moveLibraryNodes(aLibrary);
  319.  
  320.   /* Add a listener to detect when a device library item's originItemGuid
  321.    * property is updated.
  322.    * We use this listener to detect when a medialist has it's originItemGuid
  323.    * changed which will occur when the medialist is imporpied into the main
  324.    * library and will likely mean we should remove its 'deviceonly' icon */
  325.   var deviceUpdateListener = new DeviceLibraryItemUpdateListener();
  326.   var listenToPropsArray =
  327.     Cc["@songbirdnest.com/Songbird/Properties/MutablePropertyArray;1"]
  328.       .createInstance(Ci.sbIMutablePropertyArray);
  329.   listenToPropsArray.appendProperty(SBProperties.originItemGuid, "");
  330.   aLibrary.addListener(deviceUpdateListener,
  331.                        false,
  332.                        Ci.sbIMediaList.LISTENER_FLAGS_ITEMUPDATED,
  333.                        listenToPropsArray);
  334.   var deviceLibListenerObj = {
  335.     library: aLibrary,
  336.     listener: deviceUpdateListener
  337.   };
  338.  
  339.   this._deviceLibraryAndListenerObjs.push(deviceLibListenerObj);
  340.  
  341.   /* Add a listener to the main library to detect when a medialist is removed.
  342.    * This listener, when a medialist is removed, checks if there is an item
  343.    * on the device whose originItemGuid pointed to the removed medialist and, if
  344.    * so, labels the device medialist with the 'deviceonly' icon. */
  345.   var mainLibraryRemovedListener = new MainLibraryItemRemovedListener(aLibrary);
  346.   LibraryUtils.mainLibrary.addListener
  347.                           (mainLibraryRemovedListener,
  348.                            false,
  349.                            Ci.sbIMediaList.LISTENER_FLAGS_AFTERITEMREMOVED,
  350.                            null);
  351.   this._mainLibraryListeners.push(mainLibraryRemovedListener);
  352.  
  353.   return this._libraryServicePane.getNodeForLibraryResource(aLibrary);
  354. }
  355.  
  356. sbDeviceServicePane.prototype.getNodeForDevice =
  357. function sbDeviceServicePane_getNodeForDevice(aDevice) {
  358.   // Get the Node.
  359.   var id = this._deviceURN2(aDevice);
  360.   return this._servicePane.getNode(id);
  361. }
  362.  
  363. sbDeviceServicePane.prototype.setFillDefaultContextMenu =
  364. function sbDeviceServicePane_setFillDefaultContxtMenu(aNode,
  365.                                                       aEnabled) {
  366.   if (aEnabled) {
  367.     aNode.setAttributeNS(DEVICESP_NS, "fillDefaultContextMenu", "true");
  368.   } else {
  369.     aNode.setAttributeNS(DEVICESP_NS, "fillDefaultContextMenu", "false");
  370.   }
  371. }
  372.  
  373. sbDeviceServicePane.prototype.insertChildByName =
  374. function sbDeviceServicePane_insertChildByName(aDevice, aChild) {
  375.   var lastNode = null;
  376.   var deviceNode = this.getNodeForDevice(aDevice);
  377.   for (let node = deviceNode.firstChild; node; node = node.nextSibling) {
  378.     var listType = node.getAttributeNS(LSP, "ListType");
  379.     // Ignore library node.
  380.     if (listType == "library") {
  381.       continue;
  382.     }
  383.  
  384.     // Find the node to insert before. Ignore case.
  385.     var childname = aChild.name ? aChild.name.toLowerCase() : aChild.name;
  386.     var nodename = node.name ? node.name.toLowerCase() : node.name;
  387.     if (childname < nodename) {
  388.       lastNode = node;
  389.       break;
  390.     }
  391.   }
  392.  
  393.   // Insert before the node found, or insert at the end if lastNode is null.
  394.   deviceNode.insertBefore(aChild, lastNode);
  395.  
  396.   // If we are inserting a medialist node, check if it is deviceonly
  397.   if (aChild.className.match(/medialist/) != null) {
  398.  
  399.     /* The node looks like a medialist.  We'll get it's resource, check that
  400.      * it's a medialist, and look for an item in the mainLibrary that has a
  401.      * guid matching it's originGUID.  If we can't find one, we'll label the
  402.      * node as representing a deviceonly medialist */
  403.     var resource = this._libraryServicePane.getLibraryResourceForNode(aChild);
  404.     if (resource instanceof Ci.sbIMediaList &&
  405.         !LibraryUtils.getMainLibraryOriginItem(resource)) {
  406.       var classes = aChild.className.split(/\s/);
  407.       classes.push("medialisttype-deviceonly");
  408.       aChild.className = classes.join(' ');
  409.     }
  410.   }
  411. }
  412.  
  413. ////////////////////////////////////
  414. // sbIServicePaneMutationListener //
  415. ////////////////////////////////////
  416. function _deviceNodeEventListener(aDevice) {
  417.   this._device = aDevice;
  418. }
  419. _deviceNodeEventListener.prototype = {
  420.   handleEvent: function sbDeviceServicePane_handleEvent(aEventName) {
  421.     switch (aEventName) {
  422.       case "eject":
  423.         try {
  424.           this._device.eject();
  425.         } catch (e) {
  426.           dump("Exception in sbDeviceServicePane event listener: " + e + "\n");
  427.         }
  428.         break;
  429.     }
  430.   }
  431. }
  432.  
  433. ////////////////////////////////////
  434. // sbIServicePaneMutationListener //
  435. ////////////////////////////////////
  436. sbDeviceServicePane.prototype.attrModified =
  437. function sbDeviceServicePane_attrModified(aNode, aAttrName, aNamespace,
  438.                                           aOldVal, aNewVal) {
  439.   // Ensure that the parent device's node reflects any changes.
  440.   this._updateParentDeviceNode();
  441. };
  442.  
  443. sbDeviceServicePane.prototype.nodeInserted =
  444. function sbDeviceServicePane_nodeInserted(aNode, aParent, aInsertBefore) {
  445.   // Ensure that the parent device's node reflects any changes.
  446.   this._updateParentDeviceNode();
  447. };
  448.  
  449. sbDeviceServicePane.prototype.nodeRemoved =
  450. function sbDeviceServicePane_nodeRemoved(aNode, aParent) {
  451.   // Ensure that the parent device's node reflects any changes.
  452.   this._updateParentDeviceNode();
  453. };
  454.  
  455. ////////////////////////////////////
  456. // DeviceLibraryItemUpdateListener //
  457. ////////////////////////////////////
  458. /* This listener detects when the originItemGuid of an item in the mainlibrary
  459.  * is updated.  If the item is a medialist, we check if there is an item in the
  460.  * main library with a guid matching that originItemGuid.  If there is not, we
  461.  * label the medialist with a 'deviceonly' icon in the servicepane.
  462.  * We are unconcerned with mediaitems as they are labeled 'deviceonly'
  463.  * separately */
  464. function DeviceLibraryItemUpdateListener() {
  465. }
  466.  
  467. DeviceLibraryItemUpdateListener.prototype = {
  468.   onItemUpdated: function DeviceLibraryItemUpdateListener_onItemUpdated
  469.                  (aMediaList, aMediaItem, aProperties) {
  470.  
  471.     // only deal with medialists
  472.     if (!(aMediaItem instanceof Ci.sbIMediaList)) {
  473.       return true;
  474.     }
  475.     var libSPS = Cc["@songbirdnest.com/servicepane/library;1"]
  476.                    .getService(Ci.sbILibraryServicePaneService);
  477.     var node = libSPS.getNodeForLibraryResource(aMediaItem);
  478.     var classes = node.className.split(/\s/);
  479.  
  480.     /* Check if there is an item in the main library with a guid corresponding
  481.      * to this medialist's originItemGuid. */
  482.     if (!LibraryUtils.getMainLibraryOriginItem(aMediaItem)) {
  483.       // no corresponding item in the main library, label as deviceonly
  484.       classes.push("medialisttype-deviceonly");
  485.     }
  486.     else {
  487.       /* Corresponding item in the main library, this is not deviceonly
  488.        * so remove the 'medialisttype-deviceonly' class if it exists */
  489.       classes = classes.filter(function(aClass) {
  490.         return aClass != "medialisttype-deviceonly";
  491.       });
  492.     }
  493.     node.className = classes.join(' ');
  494.   }
  495. }
  496.  
  497. ////////////////////////////////////
  498. // MainLibraryItemRemovedListener  //
  499. ////////////////////////////////////
  500. /* This listener detects when a medialist is removed from the mainlibrary and
  501.  * checks if there is a medialist on this device whose originItemGuid pointed to
  502.  * the removed item's guid.  If there was, that device medialist should now be
  503.  * marked as deviceonly. */
  504. function MainLibraryItemRemovedListener(devLibrary) {
  505.   this.deviceLibrary = devLibrary;
  506. }
  507.  
  508. MainLibraryItemRemovedListener.prototype = {
  509.   onAfterItemRemoved: function MainLibraryItemRemovedListener_onAfterItemRemoved
  510.                  (aMediaList, aMediaItem, aIndex) {
  511.  
  512.     // only concerned with medialists that are removed
  513.     if (!(aMediaItem instanceof Ci.sbIMediaList)) {
  514.       return true;
  515.     }
  516.  
  517.     /* Check for items on the device whose originItemGuid pointed to the removed
  518.      * medialist's guid */
  519.     var foundLists = this.deviceLibrary.getItemsByProperty
  520.                                        (SBProperties.originItemGuid,
  521.                                         aMediaItem.guid);
  522.     var libSPS = Cc["@songbirdnest.com/servicepane/library;1"]
  523.                    .getService(Ci.sbILibraryServicePaneService);
  524.  
  525.     for (let i = 0; i < foundLists.length; i++) {
  526.  
  527.       // We found an item of interest, check if its node was already 'deviceonly'
  528.       var foundList = foundLists.queryElementAt(i, Ci.sbIMediaItem);
  529.  
  530.       var node = libSPS.getNodeForLibraryResource(foundList);
  531.  
  532.       if (node && node.className.match(/medialisttype-deviceonly/) == null) {
  533.  
  534.         /* The device medialist whose originItemGuid corresponded to the removed
  535.          * medialist's guid was not labeled 'deviceonly' and should be. */
  536.         var classes = node.className.split(/\s/);
  537.         classes.push("medialisttype-deviceonly");
  538.         node.className = classes.join(" ");
  539.         return true;
  540.       }
  541.     }
  542.   }
  543. }
  544.  
  545. /////////////////////
  546. // Private Methods //
  547. /////////////////////
  548.  
  549. /**
  550.  *
  551.  */
  552. sbDeviceServicePane.prototype._updateParentDeviceNode =
  553. function sbDeviceServicePane__updateParentDeviceNode() {
  554.   try {
  555.     // Check to see if the devices group should be hidden or not
  556.     let hidden = true;
  557.     let devicesNode = this._servicePane.getNode("SB:Devices");
  558.     if (!devicesNode) {
  559.       // there's no devices to look at, we don't care
  560.       return;
  561.     }
  562.     for (let node = devicesNode.firstChild; node; node = node.nextSibling) {
  563.       if (node && !node.hidden) {
  564.         hidden = false;
  565.         break;
  566.       }
  567.     }
  568.  
  569.     if (devicesNode.hidden != hidden) {
  570.       this.log("Hiding devices node since all children are gone or hidden");
  571.       devicesNode.hidden = hidden;
  572.     }
  573.   }
  574.   catch (e) {
  575.     this.log("Execption updating device node: " + e);
  576.   }
  577. };
  578.  
  579. /**
  580.  * Get a service pane identifier for the given device
  581.  */
  582. sbDeviceServicePane.prototype._deviceURN =
  583. function sbDeviceServicePane__deviceURN(aDevice, aDeviceIdentifier) {
  584.   return URN_PREFIX_DEVICE + aDevice.deviceCategory + ":" + aDeviceIdentifier;
  585. }
  586.  
  587. /**
  588.  * Ensure the "Devices" service pane group exists (where all devices live)
  589.  */
  590. sbDeviceServicePane.prototype._ensureDevicesGroupExists =
  591. function sbDeviceServicePane__ensureDevicesGroupExists() {
  592.   this.log("ensureDevicesGroupExists");
  593.   let fnode = this._servicePane.getNode("SB:Devices");
  594.   if (!fnode) {
  595.     // make sure it exists
  596.     fnode = this._servicePane.createNode();
  597.     fnode.id = "SB:Devices";
  598.     fnode.name = "&servicesource.devices";
  599.     this._addClassNames(fnode, ["folder", this._makeCSSProperty(fnode.name)]);
  600.     fnode.contractid = CONTRACTID;
  601.     // XXXstevel need to set fnode.dndAcceptIn?
  602.     fnode.editable = false;
  603.     fnode.setAttributeNS(SP, 'Weight', -2);
  604.     this._servicePane.root.appendChild(fnode);
  605.     fnode.addMutationListener(this);
  606.     this._deviceGroupNode = fnode;
  607.     this.log("\tDevices group created");
  608.   }
  609.  
  610.   return fnode;
  611. }
  612.  
  613. sbDeviceServicePane.prototype._addClassNames =
  614. function sbDeviceServicePane__addClassNames(aNode, aList) {
  615.   let className = aNode.className || "";
  616.   let existing = {};
  617.   for each (let name in className.split(" "))
  618.     existing[name] = true;
  619.  
  620.   for each (let name in aList)
  621.     if (!existing.hasOwnProperty(name))
  622.       className += (className ? " " : "") + name;
  623.  
  624.   aNode.className = className;
  625. }
  626.  
  627.  
  628. sbDeviceServicePane.prototype._makeCSSProperty =
  629. function sbDeviceServicePane__makeCSSProperty(aString) {
  630.   if ( aString[0] == "&" ) {
  631.     aString = aString.substr(1, aString.length);
  632.     aString = aString.replace(/\./g, "-");
  633.   }
  634.   return aString;
  635. }
  636.  
  637.  
  638. sbDeviceServicePane.prototype._deviceURN2 =
  639. function sbDeviceServicePane__deviceURN2(aDevice) {
  640.   var id = "" + aDevice.id;
  641.  
  642.   if(id.charAt(0) == "{" &&
  643.      id.charAt(-1) == "}") {
  644.     id = id.substring(1, -1);
  645.   }
  646.  
  647.  
  648.   return URN_PREFIX_DEVICE + id;
  649. }
  650.  
  651.  
  652. /**
  653.  * Move all library nodes for the library specified by aLibrary to the device
  654.  * node.
  655.  *
  656.  * \param aLibrary              Library for which to move nodes.
  657.  */
  658. sbDeviceServicePane.prototype._moveLibraryNodes =
  659. function sbDeviceServicePane__moveLibraryNodes(aLibrary) {
  660.   var nodeList = this._libraryServicePane.getNodesForLibraryResource(aLibrary);
  661.   nodeList = ArrayConverter.JSArray(nodeList);
  662.   for each (var node in nodeList) {
  663.     node.QueryInterface(Ci.sbIServicePaneNode);
  664.     this._moveLibraryResourceNode(node);
  665.   }
  666. }
  667.  
  668.  
  669. /**
  670.  * Get the index out of the type array based on the node class name.
  671.  *
  672.  * \param aNode              Library resource node.
  673.  */
  674. sbDeviceServicePane.prototype._getNodeIndexFromClassName =
  675. function sbDeviceServicePane__getNodeIndexFromClassName(aNode) {
  676.   const K_TYPES = ["audio", "video", "podcast"];
  677.   var types = aNode.className.split(/\s/);
  678.   var index = -1;
  679.   for (let i = 0; i < types.length; ++i) {
  680.     if ((index = K_TYPES.indexOf(types[i])) > -1)
  681.       break;
  682.   }
  683.  
  684.   return index;
  685. }
  686.  
  687.  
  688. /**
  689.  * Move the library resource node specified by aNode to the device node.  Do
  690.  * nothing if node is not a device library resource node.
  691.  *
  692.  * \param aNode                 Library resource node to move.
  693.  */
  694. sbDeviceServicePane.prototype._moveLibraryResourceNode =
  695. function sbDeviceServicePane__moveLibraryResourceNode(aNode) {
  696.   var resource = this._libraryServicePane.getLibraryResourceForNode(aNode);
  697.   if (!resource) {
  698.     return;
  699.   }
  700.   if (!(resource instanceof Ci.sbIMediaList)) {
  701.     // not a media list, we don't care
  702.     return;
  703.   }
  704.   var device = this._deviceMgr.getDeviceForItem(resource);
  705.   if (!device) {
  706.     // not a device playlist
  707.     return;
  708.   }
  709.  
  710.   // Only move nodes that the device supports
  711.   var functions = device.capabilities.getSupportedFunctionTypes({});
  712.   const CAPS_MAP = {
  713.     "audio": Ci.sbIDeviceCapabilities.FUNCTION_AUDIO_PLAYBACK,
  714.     "video": Ci.sbIDeviceCapabilities.FUNCTION_VIDEO_PLAYBACK
  715.   };
  716.  
  717.   var props = aNode.className.split(/\s/);
  718.   props = props.filter(function(val) val in CAPS_MAP);
  719.   // there should only be one anyway...
  720.   // assert(props.length < 2);
  721.   if (functions.indexOf(CAPS_MAP[props[0]]) < 0) {
  722.     this.log("Not moving library node " + aNode.id +
  723.              " to device node, capability not supported");
  724.     return;
  725.   }
  726.  
  727.   // Set up the device library node info.
  728.   aNode.setAttributeNS(DEVICESP_NS, "device-id", device.id);
  729.   aNode.setAttributeNS(DEVICESP_NS, "deviceNodeType", "library");
  730.  
  731.   var deviceNode = this.getNodeForDevice(device);
  732.   if (deviceNode && aNode.parentNode != deviceNode) {
  733.     var index1 = this._getNodeIndexFromClassName(aNode);
  734.     if (index1 == -1) {
  735.       dump("_moveLibraryResourceNode: Not in the node array??!!\n");
  736.       deviceNode.appendChild(aNode);
  737.       return;
  738.     }
  739.  
  740.     var child = deviceNode.firstChild;
  741.     while (child) {
  742.       var index2 = this._getNodeIndexFromClassName(child);
  743.       if (index2 == -1 || index1 < index2)
  744.         break;
  745.  
  746.       child = child.nextSibling;
  747.     }
  748.     deviceNode.insertBefore(aNode, child);
  749.   }
  750. }
  751.  
  752. ///////////
  753. // XPCOM //
  754. ///////////
  755.  
  756. var NSGetModule = XPCOMUtils.generateNSGetModule([sbDeviceServicePane]);
  757.