bookmarkProperties.js
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is the Places Bookmark Properties dialog.
16  *
17  * The Initial Developer of the Original Code is Google Inc.
18  * Portions created by the Initial Developer are Copyright (C) 2006
19  * the Initial Developer. All Rights Reserved.
20  *
21  * Contributor(s):
22  * Joe Hughes <jhughes@google.com>
23  * Dietrich Ayala <dietrich@mozilla.com>
24  * Asaf Romano <mano@mozilla.com>
25  * Marco Bonardo <mak77@bonardo.net>
26  *
27  * Alternatively, the contents of this file may be used under the terms of
28  * either the GNU General Public License Version 2 or later (the "GPL"), or
29  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30  * in which case the provisions of the GPL or the LGPL are applicable instead
31  * of those above. If you wish to allow use of your version of this file only
32  * under the terms of either the GPL or the LGPL, and not to allow others to
33  * use your version of this file under the terms of the MPL, indicate your
34  * decision by deleting the provisions above and replace them with the notice
35  * and other provisions required by the GPL or the LGPL. If you do not delete
36  * the provisions above, a recipient may use your version of this file under
37  * the terms of any one of the MPL, the GPL or the LGPL.
38  *
39  * ***** END LICENSE BLOCK ***** */
40 
96 const BOOKMARK_ITEM = 0;
97 const BOOKMARK_FOLDER = 1;
99 
100 const ACTION_EDIT = 0;
101 const ACTION_ADD = 1;
102 
104 
106  __strings: null,
107  get _strings() {
108  if (!this.__strings) {
109  this.__strings = document.getElementById("stringBundle");
110  }
111  return this.__strings;
112  },
113 
114  _action: null,
115  _itemType: null,
116  _itemId: -1,
117  _uri: null,
118  _loadInSidebar: false,
119  _title: "",
120  _description: "",
121  _URIs: [],
122  _keyword: "",
123  _postData: null,
124  _charSet: "",
125  _feedURI: null,
126  _siteURI: null,
127 
128  _defaultInsertionPoint: null,
129  _hiddenRows: [],
130  _batching: false,
131  _readOnly: false,
132 
137  _getAcceptLabel: function BPP__getAcceptLabel() {
138  if (this._action == ACTION_ADD) {
139  if (this._URIs.length)
140  return this._strings.getString("dialogAcceptLabelAddMulti");
141 
142  if (this._itemType == LIVEMARK_CONTAINER)
143  return this._strings.getString("dialogAcceptLabelAddLivemark");
144 
145  if (this._dummyItem || this._loadInSidebar)
146  return this._strings.getString("dialogAcceptLabelAddItem");
147 
148  return this._strings.getString("dialogAcceptLabelSaveItem");
149  }
150  return this._strings.getString("dialogAcceptLabelEdit");
151  },
152 
157  _getDialogTitle: function BPP__getDialogTitle() {
158  if (this._action == ACTION_ADD) {
159  if (this._itemType == BOOKMARK_ITEM)
160  return this._strings.getString("dialogTitleAddBookmark");
161  if (this._itemType == LIVEMARK_CONTAINER)
162  return this._strings.getString("dialogTitleAddLivemark");
163 
164  // add folder
165  NS_ASSERT(this._itemType == BOOKMARK_FOLDER, "Unknown item type");
166  if (this._URIs.length)
167  return this._strings.getString("dialogTitleAddMulti");
168 
169  return this._strings.getString("dialogTitleAddFolder");
170  }
171  if (this._action == ACTION_EDIT) {
172  return this._strings.getFormattedString("dialogTitleEdit", [this._title]);
173  }
174  return "";
175  },
176 
180  _determineItemInfo: function BPP__determineItemInfo() {
181  var dialogInfo = window.arguments[0];
182  this._action = dialogInfo.action == "add" ? ACTION_ADD : ACTION_EDIT;
183  this._hiddenRows = dialogInfo.hiddenRows ? dialogInfo.hiddenRows : [];
184  if (this._action == ACTION_ADD) {
185  NS_ASSERT("type" in dialogInfo, "missing type property for add action");
186 
187  if ("title" in dialogInfo)
188  this._title = dialogInfo.title;
189 
190  if ("defaultInsertionPoint" in dialogInfo) {
191  this._defaultInsertionPoint = dialogInfo.defaultInsertionPoint;
192  }
193  else
194  this._defaultInsertionPoint =
195  new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
196  PlacesUtils.bookmarks.DEFAULT_INDEX,
197  Ci.nsITreeView.DROP_ON);
198 
199  switch(dialogInfo.type) {
200  case "bookmark":
201  this._itemType = BOOKMARK_ITEM;
202  if ("uri" in dialogInfo) {
203  NS_ASSERT(dialogInfo.uri instanceof Ci.nsIURI,
204  "uri property should be a uri object");
205  this._uri = dialogInfo.uri;
206  if (typeof(this._title) != "string") {
207  this._title = this._getURITitleFromHistory(this._uri) ||
208  this._uri.spec;
209  }
210  }
211  else {
212  this._uri = PlacesUtils._uri("about:blank");
213  this._title = this._strings.getString("newBookmarkDefault");
214  this._dummyItem = true;
215  }
216 
217  if ("loadBookmarkInSidebar" in dialogInfo)
218  this._loadInSidebar = dialogInfo.loadBookmarkInSidebar;
219 
220  if ("keyword" in dialogInfo) {
221  this._keyword = dialogInfo.keyword;
222  this._isAddKeywordDialog = true;
223  if ("postData" in dialogInfo)
224  this._postData = dialogInfo.postData;
225  if ("charSet" in dialogInfo)
226  this._charSet = dialogInfo.charSet;
227  }
228  break;
229 
230  case "folder":
231  this._itemType = BOOKMARK_FOLDER;
232  if (!this._title) {
233  if ("URIList" in dialogInfo) {
234  this._title = this._strings.getString("bookmarkAllTabsDefault");
235  this._URIs = dialogInfo.URIList;
236  }
237  else
238  this._title = this._strings.getString("newFolderDefault");
239  this._dummyItem = true;
240  }
241  break;
242 
243  case "livemark":
244  this._itemType = LIVEMARK_CONTAINER;
245  if ("feedURI" in dialogInfo)
246  this._feedURI = dialogInfo.feedURI;
247  if ("siteURI" in dialogInfo)
248  this._siteURI = dialogInfo.siteURI;
249 
250  if (!this._title) {
251  if (this._feedURI) {
252  this._title = this._getURITitleFromHistory(this._feedURI) ||
253  this._feedURI.spec;
254  }
255  else
256  this._title = this._strings.getString("newLivemarkDefault");
257  }
258  }
259 
260  if ("description" in dialogInfo)
261  this._description = dialogInfo.description;
262  }
263  else { // edit
264  NS_ASSERT("itemId" in dialogInfo);
265  this._itemId = dialogInfo.itemId;
266  this._title = PlacesUtils.bookmarks.getItemTitle(this._itemId);
267  // Don't show folderPicker when editing
268  this._hiddenRows.push("folderPicker");
269  this._readOnly = !!dialogInfo.readOnly;
270 
271  switch (dialogInfo.type) {
272  case "bookmark":
273  this._itemType = BOOKMARK_ITEM;
274 
275  this._uri = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
276  // keyword
277  this._keyword = PlacesUtils.bookmarks
278  .getKeywordForBookmark(this._itemId);
279  // Load In Sidebar
280  this._loadInSidebar = PlacesUtils.annotations
281  .itemHasAnnotation(this._itemId,
283  break;
284 
285  case "folder":
286  if (PlacesUtils.itemIsLivemark(this._itemId)) {
287  this._itemType = LIVEMARK_CONTAINER;
288  this._feedURI = PlacesUtils.livemarks.getFeedURI(this._itemId);
289  this._siteURI = PlacesUtils.livemarks.getSiteURI(this._itemId);
290  }
291  else
292  this._itemType = BOOKMARK_FOLDER;
293  break;
294  }
295 
296  // Description
297  if (PlacesUtils.annotations
298  .itemHasAnnotation(this._itemId, DESCRIPTION_ANNO)) {
299  this._description = PlacesUtils.annotations
300  .getItemAnnotation(this._itemId,
302  }
303  }
304  },
305 
317  _getURITitleFromHistory: function BPP__getURITitleFromHistory(aURI) {
318  NS_ASSERT(aURI instanceof Ci.nsIURI);
319 
320  // get the title from History
321  return PlacesUtils.history.getPageTitle(aURI);
322  },
323 
328  onDialogLoad: function BPP_onDialogLoad() {
329  this._determineItemInfo();
330 
331  document.title = this._getDialogTitle();
332  var acceptButton = document.documentElement.getButton("accept");
333  acceptButton.label = this._getAcceptLabel();
334 
335  this._beginBatch();
336 
337  switch (this._action) {
338  case ACTION_EDIT:
339  this._fillEditProperties();
340  acceptButton.disabled = this._readOnly;
341  break;
342  case ACTION_ADD:
343  this._fillAddProperties();
344  // if this is an uri related dialog disable accept button until
345  // the user fills an uri value.
346  if (this._itemType == BOOKMARK_ITEM ||
347  this._itemType == LIVEMARK_CONTAINER)
348  acceptButton.disabled = !this._inputIsValid();
349  break;
350  }
351 
352  // When collapsible elements change their collapsed attribute we must
353  // resize the dialog.
354  // sizeToContent is not usable due to bug 90276, so we'll use resizeTo
355  // instead and cache the element size. See WSucks in the legacy
356  // UI code (addBookmark2.js).
357  if (!this._element("tagsRow").collapsed) {
358  this._element("tagsSelectorRow")
359  .addEventListener("DOMAttrModified", this, false);
360  }
361  if (!this._element("folderRow").collapsed) {
362  this._element("folderTreeRow")
363  .addEventListener("DOMAttrModified", this, false);
364  }
365 
366  if (!this._readOnly) {
367  // Listen on uri fields to enable accept button if input is valid
368  if (this._itemType == BOOKMARK_ITEM) {
369  this._element("locationField")
370  .addEventListener("input", this, false);
371  if (this._isAddKeywordDialog) {
372  this._element("keywordField")
373  .addEventListener("input", this, false);
374  }
375  }
376  else if (this._itemType == LIVEMARK_CONTAINER) {
377  this._element("feedLocationField")
378  .addEventListener("input", this, false);
379  this._element("siteLocationField")
380  .addEventListener("input", this, false);
381  }
382  }
383 
384  window.sizeToContent();
385  },
386 
387  // nsIDOMEventListener
388  _elementsHeight: [],
389  handleEvent: function BPP_handleEvent(aEvent) {
390  var target = aEvent.target;
391  switch (aEvent.type) {
392  case "input":
393  if (target.id == "editBMPanel_locationField" ||
394  target.id == "editBMPanel_feedLocationField" ||
395  target.id == "editBMPanel_siteLocationField" ||
396  target.id == "editBMPanel_keywordField") {
397  // Check uri fields to enable accept button if input is valid
398  document.documentElement
399  .getButton("accept").disabled = !this._inputIsValid();
400  }
401  break;
402 
403  case "DOMAttrModified":
404  // this is called when collapsing a node, but also its direct children,
405  // we only need to resize when the original node changes.
406  if ((target.id == "editBMPanel_tagsSelectorRow" ||
407  target.id == "editBMPanel_folderTreeRow") &&
408  aEvent.attrName == "collapsed" &&
409  target == aEvent.originalTarget) {
410  var id = target.id;
411  var newHeight = window.outerHeight;
412  if (aEvent.newValue) // is collapsed
413  newHeight -= this._elementsHeight[id];
414  else {
415  this._elementsHeight[id] = target.boxObject.height;
416  newHeight += this._elementsHeight[id];
417  }
418 
419  window.resizeTo(window.outerWidth, newHeight);
420  }
421  break;
422  }
423  },
424 
425  _beginBatch: function BPP__beginBatch() {
426  if (this._batching)
427  return;
428 
429  PlacesUIUtils.ptm.beginBatch();
430  this._batching = true;
431  },
432 
433  _endBatch: function BPP__endBatch() {
434  if (!this._batching)
435  return;
436 
437  PlacesUIUtils.ptm.endBatch();
438  this._batching = false;
439  },
440 
441  _fillEditProperties: function BPP__fillEditProperties() {
442  gEditItemOverlay.initPanel(this._itemId,
443  { hiddenRows: this._hiddenRows,
444  forceReadOnly: this._readOnly });
445  },
446 
447  _fillAddProperties: function BPP__fillAddProperties() {
448  this._createNewItem();
449  // Edit the new item
450  gEditItemOverlay.initPanel(this._itemId,
451  { hiddenRows: this._hiddenRows });
452  // Empty location field if the uri is about:blank, this way inserting a new
453  // url will be easier for the user, Accept button will be automatically
454  // disabled by the input listener until the user fills the field.
455  var locationField = this._element("locationField");
456  if (locationField.value == "about:blank")
457  locationField.value = "";
458  },
459 
460  // nsISupports
461  QueryInterface: function BPP_QueryInterface(aIID) {
462  if (aIID.equals(Ci.nsIDOMEventListener) ||
463  aIID.equals(Ci.nsISupports))
464  return this;
465 
466  throw Cr.NS_NOINTERFACE;
467  },
468 
469  _element: function BPP__element(aID) {
470  return document.getElementById("editBMPanel_" + aID);
471  },
472 
473  onDialogUnload: function BPP_onDialogUnload() {
474  // gEditItemOverlay does not exist anymore here, so don't rely on it.
475  // Calling removeEventListener with arguments which do not identify any
476  // currently registered EventListener on the EventTarget has no effect.
477  this._element("tagsSelectorRow")
478  .removeEventListener("DOMAttrModified", this, false);
479  this._element("folderTreeRow")
480  .removeEventListener("DOMAttrModified", this, false);
481  this._element("locationField")
482  .removeEventListener("input", this, false);
483  this._element("feedLocationField")
484  .removeEventListener("input", this, false);
485  this._element("siteLocationField")
486  .removeEventListener("input", this, false);
487  },
488 
489  onDialogAccept: function BPP_onDialogAccept() {
490  // We must blur current focused element to save its changes correctly
491  document.commandDispatcher.focusedElement.blur();
492  // The order here is important! We have to uninit the panel first, otherwise
493  // late changes could force it to commit more transactions.
494  gEditItemOverlay.uninitPanel(true);
496  this._endBatch();
497  window.arguments[0].performed = true;
498  },
499 
500  onDialogCancel: function BPP_onDialogCancel() {
501  // The order here is important! We have to uninit the panel first, otherwise
502  // changes done as part of Undo may change the panel contents and by
503  // that force it to commit more transactions.
504  gEditItemOverlay.uninitPanel(true);
506  this._endBatch();
507  PlacesUIUtils.ptm.undoTransaction();
508  window.arguments[0].performed = false;
509  },
510 
516  _inputIsValid: function BPP__inputIsValid() {
517  if (this._itemType == BOOKMARK_ITEM &&
518  !this._containsValidURI("locationField"))
519  return false;
520  if (this._isAddKeywordDialog && !this._element("keywordField").value.length)
521  return false;
522 
523  // Feed Location has to be a valid URI;
524  // Site Location has to be a valid URI or empty
525  if (this._itemType == LIVEMARK_CONTAINER) {
526  if (!this._containsValidURI("feedLocationField"))
527  return false;
528  if (!this._containsValidURI("siteLocationField") &&
529  (this._element("siteLocationField").value.length > 0))
530  return false;
531  }
532 
533  return true;
534  },
535 
545  _containsValidURI: function BPP__containsValidURI(aTextboxID) {
546  try {
547  var value = this._element(aTextboxID).value;
548  if (value) {
549  var uri = PlacesUIUtils.createFixedURI(value);
550  return true;
551  }
552  } catch (e) { }
553  return false;
554  },
555 
563  _getInsertionPointDetails: function BPP__getInsertionPointDetails() {
564  var containerId = this._defaultInsertionPoint.itemId;
565  var indexInContainer = this._defaultInsertionPoint.index;
566 
567  return [containerId, indexInContainer];
568  },
569 
574  _getCreateNewBookmarkTransaction:
575  function BPP__getCreateNewBookmarkTransaction(aContainer, aIndex) {
576  var annotations = [];
577  var childTransactions = [];
578 
579  if (this._description) {
580  childTransactions.push(
581  PlacesUIUtils.ptm.editItemDescription(-1, this._description));
582  }
583 
584  if (this._loadInSidebar) {
585  childTransactions.push(
586  PlacesUIUtils.ptm.setLoadInSidebar(-1, this._loadInSidebar));
587  }
588 
589  if (this._postData) {
590  childTransactions.push(
591  PlacesUIUtils.ptm.editBookmarkPostData(-1, this._postData));
592  }
593 
594  //XXX TODO: this should be in a transaction!
595  if (this._charSet)
596  PlacesUtils.history.setCharsetForURI(this._uri, this._charSet);
597 
598  var transactions = [PlacesUIUtils.ptm.createItem(this._uri,
599  aContainer, aIndex,
600  this._title, this._keyword,
601  annotations,
602  childTransactions)];
603 
604  return PlacesUIUtils.ptm.aggregateTransactions(this._getDialogTitle(),
605  transactions);
606  },
607 
612  _getTransactionsForURIList: function BPP__getTransactionsForURIList() {
613  var transactions = [];
614  for (var i = 0; i < this._URIs.length; ++i) {
615  var uri = this._URIs[i];
616  var title = this._getURITitleFromHistory(uri);
617  transactions.push(PlacesUIUtils.ptm.createItem(uri, -1, -1, title));
618  }
619  return transactions;
620  },
621 
626  _getCreateNewFolderTransaction:
627  function BPP__getCreateNewFolderTransaction(aContainer, aIndex) {
628  var annotations = [];
629  var childItemsTransactions;
630  if (this._URIs.length)
631  childItemsTransactions = this._getTransactionsForURIList();
632 
633  if (this._description)
634  annotations.push(this._getDescriptionAnnotation(this._description));
635 
636  return PlacesUIUtils.ptm.createFolder(this._title, aContainer, aIndex,
637  annotations, childItemsTransactions);
638  },
639 
644  _getCreateNewLivemarkTransaction:
645  function BPP__getCreateNewLivemarkTransaction(aContainer, aIndex) {
646  return PlacesUIUtils.ptm.createLivemark(this._feedURI, this._siteURI,
647  this._title,
648  aContainer, aIndex);
649  },
650 
654  _createNewItem: function BPP__getCreateItemTransaction() {
655  var [container, index] = this._getInsertionPointDetails();
656  var txn;
657 
658  switch (this._itemType) {
659  case BOOKMARK_FOLDER:
660  txn = this._getCreateNewFolderTransaction(container, index);
661  break;
662  case LIVEMARK_CONTAINER:
663  txn = this._getCreateNewLivemarkTransaction(container, index);
664  break;
665  default: // BOOKMARK_ITEM
666  txn = this._getCreateNewBookmarkTransaction(container, index);
667  }
668 
669  PlacesUIUtils.ptm.doTransaction(txn);
670  this._itemId = PlacesUtils.bookmarks.getIdForItemAt(container, index);
671  }
672 };
var PlacesUIUtils
Definition: utils.js:85
var gEditItemOverlay
#define DESCRIPTION_ANNO
menuItem id
Definition: FeedWriter.js:971
const BOOKMARK_ITEM
sbOSDControlService prototype QueryInterface
var BookmarkPropertiesPanel
let window
const ACTION_ADD
const ACTION_EDIT
const BOOKMARK_FOLDER
return null
Definition: FeedWriter.js:1143
function NS_ASSERT(cond, msg)
Definition: httpd.js:70
var uri
Definition: FeedWriter.js:1135
countRef value
Definition: FeedWriter.js:1423
const Cr
const Ci
_window _feedURI
Definition: FeedWriter.js:1144
const LIVEMARK_CONTAINER
#define LOAD_IN_SIDEBAR_ANNO
function InsertionPoint(aItemId, aIndex, aOrientation, aIsTag, aDropNearItemId)
Definition: controller.js:84
_getSelectedPageStyle s i