StyleEditorUtil.jsm 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /* vim:set ts=2 sw=2 sts=2 et: */
  2. /* This Source Code Form is subject to the terms of the Mozilla Public
  3. * License, v. 2.0. If a copy of the MPL was not distributed with this
  4. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  5. /* All top-level definitions here are exports. */
  6. /* eslint no-unused-vars: [2, {"vars": "local"}] */
  7. "use strict";
  8. this.EXPORTED_SYMBOLS = [
  9. "getString",
  10. "assert",
  11. "log",
  12. "text",
  13. "wire",
  14. "showFilePicker"
  15. ];
  16. const Cc = Components.classes;
  17. const Ci = Components.interfaces;
  18. const Cu = Components.utils;
  19. const PROPERTIES_URL = "chrome://devtools/locale/styleeditor.properties";
  20. const {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
  21. const Services = require("Services");
  22. const console = require("resource://gre/modules/Console.jsm").console;
  23. const gStringBundle = Services.strings.createBundle(PROPERTIES_URL);
  24. /**
  25. * Returns a localized string with the given key name from the string bundle.
  26. *
  27. * @param name
  28. * @param ...rest
  29. * Optional arguments to format in the string.
  30. * @return string
  31. */
  32. function getString(name) {
  33. try {
  34. if (arguments.length == 1) {
  35. return gStringBundle.GetStringFromName(name);
  36. }
  37. let rest = Array.prototype.slice.call(arguments, 1);
  38. return gStringBundle.formatStringFromName(name, rest, rest.length);
  39. } catch (ex) {
  40. console.error(ex);
  41. throw new Error("L10N error. '" + name + "' is missing from " +
  42. PROPERTIES_URL);
  43. }
  44. }
  45. /**
  46. * Assert an expression is true or throw if false.
  47. *
  48. * @param expression
  49. * @param message
  50. * Optional message.
  51. * @return expression
  52. */
  53. function assert(expression, message) {
  54. if (!expression) {
  55. let msg = message ? "ASSERTION FAILURE:" + message : "ASSERTION FAILURE";
  56. log(msg);
  57. throw new Error(msg);
  58. }
  59. return expression;
  60. }
  61. /**
  62. * Retrieve or set the text content of an element.
  63. *
  64. * @param DOMElement root
  65. * The element to use for querySelector.
  66. * @param string selector
  67. * Selector string for the element to get/set the text content.
  68. * @param string textContent
  69. * Optional text to set.
  70. * @return string
  71. * Text content of matching element or null if there were no element
  72. * matching selector.
  73. */
  74. function text(root, selector, textContent) {
  75. let element = root.querySelector(selector);
  76. if (!element) {
  77. return null;
  78. }
  79. if (textContent === undefined) {
  80. return element.textContent;
  81. }
  82. element.textContent = textContent;
  83. return textContent;
  84. }
  85. /**
  86. * Iterates _own_ properties of an object.
  87. *
  88. * @param object
  89. * The object to iterate.
  90. * @param function callback(aKey, aValue)
  91. */
  92. function forEach(object, callback) {
  93. for (let key in object) {
  94. if (object.hasOwnProperty(key)) {
  95. callback(key, object[key]);
  96. }
  97. }
  98. }
  99. /**
  100. * Log a message to the console.
  101. *
  102. * @param ...rest
  103. * One or multiple arguments to log.
  104. * If multiple arguments are given, they will be joined by " "
  105. * in the log.
  106. */
  107. function log() {
  108. console.logStringMessage(Array.prototype.slice.call(arguments).join(" "));
  109. }
  110. /**
  111. * Wire up element(s) matching selector with attributes, event listeners, etc.
  112. *
  113. * @param DOMElement root
  114. * The element to use for querySelectorAll.
  115. * Can be null if selector is a DOMElement.
  116. * @param string|DOMElement selectorOrElement
  117. * Selector string or DOMElement for the element(s) to wire up.
  118. * @param object descriptor
  119. * An object describing how to wire matching selector,
  120. * supported properties are "events" and "attributes" taking
  121. * objects themselves.
  122. * Each key of properties above represents the name of the event or
  123. * attribute, with the value being a function used as an event handler or
  124. * string to use as attribute value.
  125. * If descriptor is a function, the argument is equivalent to :
  126. * {events: {'click': descriptor}}
  127. */
  128. function wire(root, selectorOrElement, descriptor) {
  129. let matches;
  130. if (typeof selectorOrElement == "string") {
  131. // selector
  132. matches = root.querySelectorAll(selectorOrElement);
  133. if (!matches.length) {
  134. return;
  135. }
  136. } else {
  137. // element
  138. matches = [selectorOrElement];
  139. }
  140. if (typeof descriptor == "function") {
  141. descriptor = {events: {click: descriptor}};
  142. }
  143. for (let i = 0; i < matches.length; i++) {
  144. let element = matches[i];
  145. forEach(descriptor.events, function (name, handler) {
  146. element.addEventListener(name, handler, false);
  147. });
  148. forEach(descriptor.attributes, element.setAttribute);
  149. }
  150. }
  151. /**
  152. * Show file picker and return the file user selected.
  153. *
  154. * @param mixed file
  155. * Optional nsIFile or string representing the filename to auto-select.
  156. * @param boolean toSave
  157. * If true, the user is selecting a filename to save.
  158. * @param nsIWindow parentWindow
  159. * Optional parent window. If null the parent window of the file picker
  160. * will be the window of the attached input element.
  161. * @param callback
  162. * The callback method, which will be called passing in the selected
  163. * file or null if the user did not pick one.
  164. * @param AString suggestedFilename
  165. * The suggested filename when toSave is true.
  166. */
  167. function showFilePicker(path, toSave, parentWindow, callback,
  168. suggestedFilename) {
  169. if (typeof path == "string") {
  170. try {
  171. if (Services.io.extractScheme(path) == "file") {
  172. let uri = Services.io.newURI(path, null, null);
  173. let file = uri.QueryInterface(Ci.nsIFileURL).file;
  174. callback(file);
  175. return;
  176. }
  177. } catch (ex) {
  178. callback(null);
  179. return;
  180. }
  181. try {
  182. let file =
  183. Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
  184. file.initWithPath(path);
  185. callback(file);
  186. return;
  187. } catch (ex) {
  188. callback(null);
  189. return;
  190. }
  191. }
  192. if (path) {
  193. // "path" is an nsIFile
  194. callback(path);
  195. return;
  196. }
  197. let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
  198. let mode = toSave ? fp.modeSave : fp.modeOpen;
  199. let key = toSave ? "saveStyleSheet" : "importStyleSheet";
  200. let fpCallback = function (result) {
  201. if (result == Ci.nsIFilePicker.returnCancel) {
  202. callback(null);
  203. } else {
  204. callback(fp.file);
  205. }
  206. };
  207. if (toSave && suggestedFilename) {
  208. fp.defaultString = suggestedFilename;
  209. }
  210. fp.init(parentWindow, getString(key + ".title"), mode);
  211. fp.appendFilter(getString(key + ".filter"), "*.css");
  212. fp.appendFilters(fp.filterAll);
  213. fp.open(fpCallback);
  214. return;
  215. }