michael@0: /* vim:set ts=2 sw=2 sts=2 et: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: this.EXPORTED_SYMBOLS = [ michael@0: "_", michael@0: "assert", michael@0: "log", michael@0: "text", michael@0: "wire", michael@0: "showFilePicker" michael@0: ]; michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: const PROPERTIES_URL = "chrome://browser/locale/devtools/styleeditor.properties"; michael@0: michael@0: const console = Services.console; michael@0: const gStringBundle = Services.strings.createBundle(PROPERTIES_URL); michael@0: michael@0: michael@0: /** michael@0: * Returns a localized string with the given key name from the string bundle. michael@0: * michael@0: * @param aName michael@0: * @param ...rest michael@0: * Optional arguments to format in the string. michael@0: * @return string michael@0: */ michael@0: this._ = function _(aName) michael@0: { michael@0: michael@0: if (arguments.length == 1) { michael@0: return gStringBundle.GetStringFromName(aName); michael@0: } michael@0: let rest = Array.prototype.slice.call(arguments, 1); michael@0: return gStringBundle.formatStringFromName(aName, rest, rest.length); michael@0: } michael@0: michael@0: /** michael@0: * Assert an expression is true or throw if false. michael@0: * michael@0: * @param aExpression michael@0: * @param aMessage michael@0: * Optional message. michael@0: * @return aExpression michael@0: */ michael@0: this.assert = function assert(aExpression, aMessage) michael@0: { michael@0: if (!!!(aExpression)) { michael@0: let msg = aMessage ? "ASSERTION FAILURE:" + aMessage : "ASSERTION FAILURE"; michael@0: log(msg); michael@0: throw new Error(msg); michael@0: } michael@0: return aExpression; michael@0: } michael@0: michael@0: /** michael@0: * Retrieve or set the text content of an element. michael@0: * michael@0: * @param DOMElement aRoot michael@0: * The element to use for querySelector. michael@0: * @param string aSelector michael@0: * Selector string for the element to get/set the text content. michael@0: * @param string aText michael@0: * Optional text to set. michael@0: * @return string michael@0: * Text content of matching element or null if there were no element michael@0: * matching aSelector. michael@0: */ michael@0: this.text = function text(aRoot, aSelector, aText) michael@0: { michael@0: let element = aRoot.querySelector(aSelector); michael@0: if (!element) { michael@0: return null; michael@0: } michael@0: michael@0: if (aText === undefined) { michael@0: return element.textContent; michael@0: } michael@0: element.textContent = aText; michael@0: return aText; michael@0: } michael@0: michael@0: /** michael@0: * Iterates _own_ properties of an object. michael@0: * michael@0: * @param aObject michael@0: * The object to iterate. michael@0: * @param function aCallback(aKey, aValue) michael@0: */ michael@0: function forEach(aObject, aCallback) michael@0: { michael@0: for (let key in aObject) { michael@0: if (aObject.hasOwnProperty(key)) { michael@0: aCallback(key, aObject[key]); michael@0: } michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Log a message to the console. michael@0: * michael@0: * @param ...rest michael@0: * One or multiple arguments to log. michael@0: * If multiple arguments are given, they will be joined by " " in the log. michael@0: */ michael@0: this.log = function log() michael@0: { michael@0: console.logStringMessage(Array.prototype.slice.call(arguments).join(" ")); michael@0: } michael@0: michael@0: /** michael@0: * Wire up element(s) matching selector with attributes, event listeners, etc. michael@0: * michael@0: * @param DOMElement aRoot michael@0: * The element to use for querySelectorAll. michael@0: * Can be null if aSelector is a DOMElement. michael@0: * @param string|DOMElement aSelectorOrElement michael@0: * Selector string or DOMElement for the element(s) to wire up. michael@0: * @param object aDescriptor michael@0: * An object describing how to wire matching selector, supported properties michael@0: * are "events" and "attributes" taking objects themselves. michael@0: * Each key of properties above represents the name of the event or michael@0: * attribute, with the value being a function used as an event handler or michael@0: * string to use as attribute value. michael@0: * If aDescriptor is a function, the argument is equivalent to : michael@0: * {events: {'click': aDescriptor}} michael@0: */ michael@0: this.wire = function wire(aRoot, aSelectorOrElement, aDescriptor) michael@0: { michael@0: let matches; michael@0: if (typeof(aSelectorOrElement) == "string") { // selector michael@0: matches = aRoot.querySelectorAll(aSelectorOrElement); michael@0: if (!matches.length) { michael@0: return; michael@0: } michael@0: } else { michael@0: matches = [aSelectorOrElement]; // element michael@0: } michael@0: michael@0: if (typeof(aDescriptor) == "function") { michael@0: aDescriptor = {events: {click: aDescriptor}}; michael@0: } michael@0: michael@0: for (let i = 0; i < matches.length; i++) { michael@0: let element = matches[i]; michael@0: forEach(aDescriptor.events, function (aName, aHandler) { michael@0: element.addEventListener(aName, aHandler, false); michael@0: }); michael@0: forEach(aDescriptor.attributes, element.setAttribute); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Show file picker and return the file user selected. michael@0: * michael@0: * @param mixed file michael@0: * Optional nsIFile or string representing the filename to auto-select. michael@0: * @param boolean toSave michael@0: * If true, the user is selecting a filename to save. michael@0: * @param nsIWindow parentWindow michael@0: * Optional parent window. If null the parent window of the file picker michael@0: * will be the window of the attached input element. michael@0: * @param callback michael@0: * The callback method, which will be called passing in the selected michael@0: * file or null if the user did not pick one. michael@0: * @param AString suggestedFilename michael@0: * The suggested filename when toSave is true. michael@0: */ michael@0: this.showFilePicker = function showFilePicker(path, toSave, parentWindow, michael@0: callback, suggestedFilename) michael@0: { michael@0: if (typeof(path) == "string") { michael@0: try { michael@0: if (Services.io.extractScheme(path) == "file") { michael@0: let uri = Services.io.newURI(path, null, null); michael@0: let file = uri.QueryInterface(Ci.nsIFileURL).file; michael@0: callback(file); michael@0: return; michael@0: } michael@0: } catch (ex) { michael@0: callback(null); michael@0: return; michael@0: } michael@0: try { michael@0: let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); michael@0: file.initWithPath(path); michael@0: callback(file); michael@0: return; michael@0: } catch (ex) { michael@0: callback(null); michael@0: return; michael@0: } michael@0: } michael@0: if (path) { // "path" is an nsIFile michael@0: callback(path); michael@0: return; michael@0: } michael@0: michael@0: let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); michael@0: let mode = toSave ? fp.modeSave : fp.modeOpen; michael@0: let key = toSave ? "saveStyleSheet" : "importStyleSheet"; michael@0: let fpCallback = function(result) { michael@0: if (result == Ci.nsIFilePicker.returnCancel) { michael@0: callback(null); michael@0: } else { michael@0: callback(fp.file); michael@0: } michael@0: }; michael@0: michael@0: if (toSave && suggestedFilename) { michael@0: fp.defaultString = suggestedFilename; michael@0: } michael@0: michael@0: fp.init(parentWindow, _(key + ".title"), mode); michael@0: fp.appendFilters(_(key + ".filter"), "*.css"); michael@0: fp.appendFilters(fp.filterAll); michael@0: fp.open(fpCallback); michael@0: return; michael@0: }