1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/browser/devtools/styleeditor/StyleEditorUtil.jsm Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,228 @@ 1.4 +/* vim:set ts=2 sw=2 sts=2 et: */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +"use strict"; 1.10 + 1.11 +this.EXPORTED_SYMBOLS = [ 1.12 + "_", 1.13 + "assert", 1.14 + "log", 1.15 + "text", 1.16 + "wire", 1.17 + "showFilePicker" 1.18 +]; 1.19 + 1.20 +const Cc = Components.classes; 1.21 +const Ci = Components.interfaces; 1.22 +const Cu = Components.utils; 1.23 + 1.24 +Cu.import("resource://gre/modules/Services.jsm"); 1.25 + 1.26 +const PROPERTIES_URL = "chrome://browser/locale/devtools/styleeditor.properties"; 1.27 + 1.28 +const console = Services.console; 1.29 +const gStringBundle = Services.strings.createBundle(PROPERTIES_URL); 1.30 + 1.31 + 1.32 +/** 1.33 + * Returns a localized string with the given key name from the string bundle. 1.34 + * 1.35 + * @param aName 1.36 + * @param ...rest 1.37 + * Optional arguments to format in the string. 1.38 + * @return string 1.39 + */ 1.40 +this._ = function _(aName) 1.41 +{ 1.42 + 1.43 + if (arguments.length == 1) { 1.44 + return gStringBundle.GetStringFromName(aName); 1.45 + } 1.46 + let rest = Array.prototype.slice.call(arguments, 1); 1.47 + return gStringBundle.formatStringFromName(aName, rest, rest.length); 1.48 +} 1.49 + 1.50 +/** 1.51 + * Assert an expression is true or throw if false. 1.52 + * 1.53 + * @param aExpression 1.54 + * @param aMessage 1.55 + * Optional message. 1.56 + * @return aExpression 1.57 + */ 1.58 +this.assert = function assert(aExpression, aMessage) 1.59 +{ 1.60 + if (!!!(aExpression)) { 1.61 + let msg = aMessage ? "ASSERTION FAILURE:" + aMessage : "ASSERTION FAILURE"; 1.62 + log(msg); 1.63 + throw new Error(msg); 1.64 + } 1.65 + return aExpression; 1.66 +} 1.67 + 1.68 +/** 1.69 + * Retrieve or set the text content of an element. 1.70 + * 1.71 + * @param DOMElement aRoot 1.72 + * The element to use for querySelector. 1.73 + * @param string aSelector 1.74 + * Selector string for the element to get/set the text content. 1.75 + * @param string aText 1.76 + * Optional text to set. 1.77 + * @return string 1.78 + * Text content of matching element or null if there were no element 1.79 + * matching aSelector. 1.80 + */ 1.81 +this.text = function text(aRoot, aSelector, aText) 1.82 +{ 1.83 + let element = aRoot.querySelector(aSelector); 1.84 + if (!element) { 1.85 + return null; 1.86 + } 1.87 + 1.88 + if (aText === undefined) { 1.89 + return element.textContent; 1.90 + } 1.91 + element.textContent = aText; 1.92 + return aText; 1.93 +} 1.94 + 1.95 +/** 1.96 + * Iterates _own_ properties of an object. 1.97 + * 1.98 + * @param aObject 1.99 + * The object to iterate. 1.100 + * @param function aCallback(aKey, aValue) 1.101 + */ 1.102 +function forEach(aObject, aCallback) 1.103 +{ 1.104 + for (let key in aObject) { 1.105 + if (aObject.hasOwnProperty(key)) { 1.106 + aCallback(key, aObject[key]); 1.107 + } 1.108 + } 1.109 +} 1.110 + 1.111 +/** 1.112 + * Log a message to the console. 1.113 + * 1.114 + * @param ...rest 1.115 + * One or multiple arguments to log. 1.116 + * If multiple arguments are given, they will be joined by " " in the log. 1.117 + */ 1.118 +this.log = function log() 1.119 +{ 1.120 + console.logStringMessage(Array.prototype.slice.call(arguments).join(" ")); 1.121 +} 1.122 + 1.123 +/** 1.124 + * Wire up element(s) matching selector with attributes, event listeners, etc. 1.125 + * 1.126 + * @param DOMElement aRoot 1.127 + * The element to use for querySelectorAll. 1.128 + * Can be null if aSelector is a DOMElement. 1.129 + * @param string|DOMElement aSelectorOrElement 1.130 + * Selector string or DOMElement for the element(s) to wire up. 1.131 + * @param object aDescriptor 1.132 + * An object describing how to wire matching selector, supported properties 1.133 + * are "events" and "attributes" taking objects themselves. 1.134 + * Each key of properties above represents the name of the event or 1.135 + * attribute, with the value being a function used as an event handler or 1.136 + * string to use as attribute value. 1.137 + * If aDescriptor is a function, the argument is equivalent to : 1.138 + * {events: {'click': aDescriptor}} 1.139 + */ 1.140 +this.wire = function wire(aRoot, aSelectorOrElement, aDescriptor) 1.141 +{ 1.142 + let matches; 1.143 + if (typeof(aSelectorOrElement) == "string") { // selector 1.144 + matches = aRoot.querySelectorAll(aSelectorOrElement); 1.145 + if (!matches.length) { 1.146 + return; 1.147 + } 1.148 + } else { 1.149 + matches = [aSelectorOrElement]; // element 1.150 + } 1.151 + 1.152 + if (typeof(aDescriptor) == "function") { 1.153 + aDescriptor = {events: {click: aDescriptor}}; 1.154 + } 1.155 + 1.156 + for (let i = 0; i < matches.length; i++) { 1.157 + let element = matches[i]; 1.158 + forEach(aDescriptor.events, function (aName, aHandler) { 1.159 + element.addEventListener(aName, aHandler, false); 1.160 + }); 1.161 + forEach(aDescriptor.attributes, element.setAttribute); 1.162 + } 1.163 +} 1.164 + 1.165 +/** 1.166 + * Show file picker and return the file user selected. 1.167 + * 1.168 + * @param mixed file 1.169 + * Optional nsIFile or string representing the filename to auto-select. 1.170 + * @param boolean toSave 1.171 + * If true, the user is selecting a filename to save. 1.172 + * @param nsIWindow parentWindow 1.173 + * Optional parent window. If null the parent window of the file picker 1.174 + * will be the window of the attached input element. 1.175 + * @param callback 1.176 + * The callback method, which will be called passing in the selected 1.177 + * file or null if the user did not pick one. 1.178 + * @param AString suggestedFilename 1.179 + * The suggested filename when toSave is true. 1.180 + */ 1.181 +this.showFilePicker = function showFilePicker(path, toSave, parentWindow, 1.182 + callback, suggestedFilename) 1.183 +{ 1.184 + if (typeof(path) == "string") { 1.185 + try { 1.186 + if (Services.io.extractScheme(path) == "file") { 1.187 + let uri = Services.io.newURI(path, null, null); 1.188 + let file = uri.QueryInterface(Ci.nsIFileURL).file; 1.189 + callback(file); 1.190 + return; 1.191 + } 1.192 + } catch (ex) { 1.193 + callback(null); 1.194 + return; 1.195 + } 1.196 + try { 1.197 + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); 1.198 + file.initWithPath(path); 1.199 + callback(file); 1.200 + return; 1.201 + } catch (ex) { 1.202 + callback(null); 1.203 + return; 1.204 + } 1.205 + } 1.206 + if (path) { // "path" is an nsIFile 1.207 + callback(path); 1.208 + return; 1.209 + } 1.210 + 1.211 + let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); 1.212 + let mode = toSave ? fp.modeSave : fp.modeOpen; 1.213 + let key = toSave ? "saveStyleSheet" : "importStyleSheet"; 1.214 + let fpCallback = function(result) { 1.215 + if (result == Ci.nsIFilePicker.returnCancel) { 1.216 + callback(null); 1.217 + } else { 1.218 + callback(fp.file); 1.219 + } 1.220 + }; 1.221 + 1.222 + if (toSave && suggestedFilename) { 1.223 + fp.defaultString = suggestedFilename; 1.224 + } 1.225 + 1.226 + fp.init(parentWindow, _(key + ".title"), mode); 1.227 + fp.appendFilters(_(key + ".filter"), "*.css"); 1.228 + fp.appendFilters(fp.filterAll); 1.229 + fp.open(fpCallback); 1.230 + return; 1.231 +}