PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/share/jscript/iconbox.js

http://iwl.googlecode.com/
JavaScript | 495 lines | 374 code | 18 blank | 103 comment | 116 complexity | 7ea06d3a663f5b965c460b209dc99305 MD5 | raw file
  1. // vim: set autoindent shiftwidth=4 tabstop=8:
  2. /**
  3. * @class IWL.Iconbox is a class for creating a container with icons
  4. * @extends IWL.Widget
  5. * */
  6. IWL.Iconbox = Object.extend(Object.extend({}, IWL.Widget), (function () {
  7. function resizeEvent(event) {
  8. if (!this.loaded) return;
  9. var dims = this.getDimensions();
  10. if (!this.dimensions ||
  11. dims.width != this.dimensions.width ||
  12. dims.height != this.dimensions.height) {
  13. restoreIconsHeight.call(this);
  14. if (this._alignDelay && this.loaded) clearTimeout(this._alignDelay);
  15. this._alignDelay = this._alignIconsVertically.bind(this).delay(0.1);
  16. }
  17. }
  18. function findIconIdVertical(icon, dir) {
  19. var offsetLeftlb;
  20. var offsetLeftub;
  21. var offsetTop = icon.offsetTop;
  22. var offsetLeft = icon.offsetLeft;
  23. var dims = Element.getDimensions(icon);
  24. var width = dims.width;
  25. offsetLeftlb = offsetLeft - width/2;
  26. offsetLeftub = offsetLeft + width/2;
  27. var prevtop = 0;
  28. this.icons.each(function(iter) {
  29. var cur_top = iter.offsetTop;
  30. if (dir == 'up') {
  31. if (cur_top < offsetTop && prevtop < cur_top)
  32. prevtop = cur_top;
  33. } else {
  34. if (cur_top > offsetTop && (!prevtop || prevtop > cur_top))
  35. prevtop = cur_top;
  36. }
  37. }.bind(this));
  38. for (var i = 0; i < this.icons.length; i++) {
  39. var testicon = this.icons[i];
  40. if (prevtop && testicon.offsetTop == prevtop) {
  41. if (testicon.offsetLeft > offsetLeftlb
  42. && testicon.offsetLeft < offsetLeftub) {
  43. var newicon = testicon;
  44. return newicon;
  45. }
  46. }
  47. }
  48. }
  49. function restoreIconsHeight() {
  50. if (!this.icons) return;
  51. this.icons.each(function(icon) {
  52. if (!icon._defaultHeight)
  53. return;
  54. icon.setStyle({height: icon._defaultHeight + 'px'});
  55. }.bind(this));
  56. }
  57. function setMaxHeightOnSameRow(icon) {
  58. var offset_top = icon.offsetTop;
  59. var row_icons = this.icons.select(function(icon) {
  60. if (!icon._defaultHeight) icon._defaultHeight = icon.getStyle('height').parseFloat(); return icon.offsetTop == offset_top;
  61. });
  62. var max_height = {height: (Math.max.apply(Math, row_icons.invoke('getStyle', 'height').invoke('parseFloat'))) + 'px'};
  63. row_icons.invoke('setStyle', max_height);
  64. return row_icons.last().nextIcon();
  65. }
  66. function keyEventsCB(event) {
  67. var keyCode = Event.getKeyCode(event);
  68. var shift = event.shiftKey;
  69. var icon;
  70. if (keyCode == Event.KEY_LEFT) {
  71. if (icon = this.getPrevIcon()) {
  72. icon.setSelected(true, shift);
  73. new Effect.ScrollElement(icon, this.iconsContainer, {duration: 0.3});
  74. Event.stop(event);
  75. }
  76. } else if (keyCode == Event.KEY_RIGHT) {
  77. if (icon = this.getNextIcon()) {
  78. icon.setSelected(true, shift);
  79. new Effect.ScrollElement(icon, this.iconsContainer, {duration: 0.3});
  80. Event.stop(event);
  81. }
  82. } else if (keyCode == Event.KEY_UP) {
  83. if (icon = this.getUpperIcon()) {
  84. icon.setSelected(true, shift);
  85. new Effect.ScrollElement(icon, this.iconsContainer, {duration: 0.3});
  86. Event.stop(event);
  87. }
  88. } else if (keyCode == Event.KEY_DOWN) {
  89. if (icon = this.getLowerIcon()) {
  90. icon.setSelected(true, shift);
  91. new Effect.ScrollElement(icon, this.iconsContainer, {duration: 0.3});
  92. Event.stop(event);
  93. }
  94. } else if (keyCode == Event.KEY_RETURN) {
  95. if (this.currentIcon)
  96. this.currentIcon.activate();
  97. }
  98. }
  99. return {
  100. /**
  101. * Selects the given icon
  102. * @param icon The icon to select. If none is given, the current one is used.
  103. * @returns The object
  104. * */
  105. selectIcon: function(icon) {
  106. var icon = $(icon);
  107. if (!icon) return;
  108. icon.setSelected(true);
  109. return this;
  110. },
  111. /**
  112. * Unselects the given icon
  113. * @param icon The icon to unselect. If none is given, the current one is used.
  114. * @returns The object
  115. * */
  116. unselectIcon: function(icon) {
  117. var icon = $(icon) || this.currentIcon;
  118. if (!icon) return;
  119. icon.setSelected(false);
  120. return this;
  121. },
  122. /**
  123. * Selects all the icons
  124. * @returns The object
  125. * */
  126. selectAllIcons: function() {
  127. this.selectedIcons = [];
  128. if (!this.options.multipleSelect) return;
  129. this.icons.each(function(icon) {
  130. icon.addClassName('icon_selected');
  131. this.selectedIcons.push(icon);
  132. }.bind(this));
  133. this.currentIcon = this.selectedIcons[this.selectedIcons.length - 1];
  134. this.emitSignal('iwl:select_all');
  135. return this;
  136. },
  137. /**
  138. * Unselects all the icons
  139. * @returns The object
  140. * */
  141. unselectAllIcons: function() {
  142. this.selectedIcons.each(function(icon) {
  143. icon.removeClassName("icon_selected");
  144. });
  145. this.selectedIcons = [];
  146. this.currentIcon = null;
  147. this.emitSignal('iwl:unselect_all');
  148. return this;
  149. },
  150. /**
  151. * @returns The currently selected icon
  152. * */
  153. getSelectedIcon: function() {
  154. return this.currentIcon;
  155. },
  156. /**
  157. * @returns An array of all the selected icons
  158. * */
  159. getSelectedIcons: function() {
  160. return this.selectedIcons;
  161. },
  162. /**
  163. * Returns the previous icon
  164. * @param icon The reference icon. If none is given, the current one is used.
  165. * @returns The previous icon
  166. * */
  167. getPrevIcon: function() {
  168. var icon = $(arguments[0]) || this.currentIcon;
  169. if (!icon)
  170. return this.icons[0];
  171. return this.icons[this.icons.indexOf(icon) - 1];
  172. },
  173. /**
  174. * Returns the next icon
  175. * @param icon The reference icon. If none is given, the current one is used.
  176. * @returns The next icon
  177. * */
  178. getNextIcon: function() {
  179. var icon = $(arguments[0]) || this.currentIcon;
  180. if (!icon)
  181. return this.icons[0];
  182. return this.icons[this.icons.indexOf(icon) + 1];
  183. },
  184. /**
  185. * Returns the upper icon
  186. * @param icon The reference icon. If none is given, the current one is used.
  187. * @returns The upper icon
  188. * */
  189. getUpperIcon: function() {
  190. var icon = $(arguments[0]) || this.currentIcon;
  191. if (!icon)
  192. return this.icons[0];
  193. return findIconIdVertical.call(this, icon, "up");
  194. },
  195. /**
  196. * Returns the lower icon
  197. * @param icon The reference icon. If none is given, the current one is used.
  198. * @returns The lower icon
  199. * */
  200. getLowerIcon: function() {
  201. var icon = $(arguments[0]) || this.currentIcon;
  202. if (!icon)
  203. return this.icons[0];
  204. return findIconIdVertical.call(this, icon, "down");
  205. },
  206. /**
  207. * Removes the given icon
  208. * @param icon The icon to remove. If none is given, the current one is used.
  209. * @returns The object
  210. * */
  211. removeIcon: function(icon) {
  212. var icon = $(icon) || this.currentIcon;
  213. if (!icon) return;
  214. icon.remove();
  215. return this;
  216. },
  217. /**
  218. * Appends an icon, or an array of icons
  219. * @param json The icon json object or HTML string to append. It can be an array of such objects
  220. * @param reference A reference icon. If given, the created icon will be inserted before this one
  221. * @returns The object
  222. * */
  223. appendIcon: function(json) {
  224. var reference = $(arguments[1]);
  225. if (!json) return;
  226. if (Object.isString(json) || Object.isElement(json))
  227. json = [json];
  228. else {
  229. if (typeof json !== 'object') return;
  230. if (!json.length) json = [json];
  231. }
  232. for (var i = 0; i < json.length; i++) {
  233. var icon_data = json[i];
  234. if (!icon_data) continue;
  235. var icon = null;
  236. var icon_id = 'iconbox_icon_' + Math.random();
  237. if (Object.isString(icon_data)) {
  238. this.iconsContainer.insert(unescape(icon_data));
  239. icon = this.iconsContainer.childElements().last();
  240. if (reference)
  241. this.iconsContainer.insertBefore(icon, reference);
  242. } else if (Object.isElement(icon_data)) {
  243. if (reference)
  244. icon = this.iconsContainer.insertBefore(icon_data, reference);
  245. else
  246. icon = this.iconsContainer.appendChild(icon_data);
  247. } else {
  248. if (icon_data.src) {
  249. icon = new Element('div').update(new Element('img', {id: icon_id + '_image', src: icon_data.src}));
  250. if (icon_data.text)
  251. icon.appendChild(new Element('p', {id: icon_id + '_label'}).update(icon_data.text));
  252. if (reference)
  253. this.iconsContainer.insertBefore(icon, reference);
  254. else
  255. this.iconsContainer.appendChild(icon);
  256. } else {
  257. icon = this.iconsContainer.createHtmlElement(icon_data, reference);
  258. }
  259. }
  260. if (!icon.id) icon.id = icon_id;
  261. icon.addClassName('icon').addClassName($A(this.classNames()).first() + '_icon');
  262. icon.select('img').first().addClassName('icon_image');
  263. var label = icon.select('p').first();
  264. if (label)
  265. label.addClassName('icon_label');
  266. this.icons.push(IWL.Iconbox.Icon.create(icon, this));
  267. }
  268. return this;
  269. },
  270. /**
  271. * Sets the status bar text
  272. * @param {String} text The text to be shown in the status bar
  273. * @returns The object
  274. * */
  275. statusbarPush: function(text) {
  276. if (!this.statusbar) return;
  277. this.statusbar.update(text);
  278. return this;
  279. },
  280. _init: function(id) {
  281. this.statusbar = $(id + '_status_label');
  282. this.iconsContainer = this.down();
  283. this.icons = new Array;
  284. this.selectedIcons = new Array;
  285. this.currentIcon = null;
  286. this.options = Object.extend({
  287. multipleSelect: false,
  288. clickToSelect: true
  289. }, arguments[1] || {});
  290. this.messages = Object.extend({}, arguments[2]);
  291. var childElements = this.iconsContainer.childElements().select(function(e) {
  292. return e.hasClassName('icon');
  293. });
  294. this._iconCount = childElements.length;
  295. childElements.each(function(e) {
  296. this.icons.push(IWL.Iconbox.Icon.create(e, this));
  297. }.bind(this));
  298. Event.observe(window, 'resize',
  299. resizeEvent.bindAsEventListener(this));
  300. this.registerFocus();
  301. this.keyLogger(keyEventsCB.bindAsEventListener(this));
  302. },
  303. _alignIconsVertically: function() {
  304. if (!this.icons || !this.icons.length) return;
  305. for (var icon = this.icons[0]; icon;)
  306. icon = setMaxHeightOnSameRow.call(this, icon);
  307. this.dimensions = this.getDimensions();
  308. this._alignDelay = false;
  309. return this;
  310. },
  311. _refreshResponse: function(json, params, options) {
  312. if (!json.icons.length) return;
  313. if (this.currentIcon) this.currentIcon.setSelected(false);
  314. this.iconsContainer.update();
  315. this.icons = [];
  316. this._iconCount = json.icons.length;
  317. return this.appendIcon(json.icons);
  318. },
  319. _iconCountdown: function() {
  320. if (--this._iconCount <= 0) {
  321. if (this._alignDelay) clearTimeout(this._alignDelay);
  322. this._alignIconsVertically();
  323. if (!this.loaded) this.emitSignal.bind(this, 'iwl:load').delay(0.15);
  324. this.loaded = true;
  325. this._iconCount = 0;
  326. }
  327. }
  328. }
  329. })());
  330. /**
  331. * @class IWL.Iconbox.Icon is a class for iconbox icons
  332. * @extends IWL.Widget
  333. * */
  334. IWL.Iconbox.Icon = Object.extend(Object.extend({}, IWL.Widget), (function () {
  335. function initEvents() {
  336. Event.observe(this, "mouseover", function(event) {
  337. if (!this.iconbox.options.clickToSelect
  338. && !this.iconbox.options.multipleSelect)
  339. this.setSelected(true);
  340. }.bind(this));
  341. Event.observe(this, "click", function(event) {
  342. if (!this.iconbox.options.clickToSelect
  343. && !this.iconbox.options.multipleSelect)
  344. this.activate();
  345. else {
  346. if (event.ctrlKey) {
  347. if (this.isSelected())
  348. this.setSelected(false, true);
  349. else
  350. this.setSelected(true, true);
  351. } else {
  352. this.iconbox.unselectAllIcons();
  353. this.setSelected(true);
  354. this.iconbox.selectedIcons = [this];
  355. }
  356. }
  357. }.bind(this));
  358. Event.observe(this, "dblclick", function(event) {
  359. this.activate();
  360. }.bind(this));
  361. }
  362. return {
  363. /**
  364. * Sets whether the icon is selected
  365. * @param {Boolean} selected True if the icon should be selected
  366. * @returns The object
  367. * */
  368. setSelected: function(selected, shift) {
  369. if (selected) {
  370. if (this.isSelected()) return;
  371. if (this.iconbox.options.multipleSelect) {
  372. if (!shift) this.iconbox.unselectAllIcons();
  373. } else {
  374. if (this.iconbox.currentIcon)
  375. this.iconbox.currentIcon.setSelected(false);;
  376. }
  377. this.addClassName("icon_selected");
  378. this.iconbox.currentIcon = this;
  379. if (this.iconbox.scrollToSelection)
  380. this.scrollTo();
  381. if (this.iconbox.options.multipleSelect)
  382. this.iconbox.selectedIcons.push(this);
  383. this.emitSignal('iwl:select');
  384. this.iconbox.statusbarPush(this.getLabel());
  385. } else {
  386. if (!this.isSelected()) return;
  387. this.removeClassName("icon_selected");
  388. if (this.iconbox.currentIcon == this)
  389. this.iconbox.currentIcon = null;
  390. if (this.iconbox.options.multipleSelect)
  391. this.iconbox.selectedIcons = this.iconbox.selectedIcons.without(this);
  392. this.emitSignal('iwl:unselect');
  393. }
  394. return this;
  395. },
  396. /**
  397. * @returns True if the icon is selected
  398. * @type Boolean
  399. * */
  400. isSelected: function() {
  401. return this.hasClassName('icon_selected');
  402. },
  403. /**
  404. * @returns The icon label
  405. * @type String
  406. * */
  407. getLabel: function() {
  408. if (!this.label) return '';
  409. return this.label.getText();
  410. },
  411. /**
  412. * Activates the icon
  413. * @returns The object
  414. * */
  415. activate: function() {
  416. this.emitSignal('iwl:activate');
  417. return this;
  418. },
  419. /**
  420. * @returns The previous icon
  421. * */
  422. prevIcon: function() {
  423. return this.iconbox.getPrevIcon(this);
  424. },
  425. /**
  426. * @returns The next icon
  427. * */
  428. nextIcon: function() {
  429. return this.iconbox.getNextIcon(this);
  430. },
  431. /**
  432. * @returns The upper icon
  433. * */
  434. upperIcon: function() {
  435. return this.iconbox.getUpperIcon(this);
  436. },
  437. /**
  438. * @returns The lower icon
  439. * */
  440. lowerIcon: function() {
  441. return this.iconbox.getLowerIcon(this);
  442. },
  443. /**
  444. * Removes the icon
  445. * @returns The object
  446. * */
  447. remove: function() {
  448. var dom_parent = this.parentNode;
  449. var title = this.getLabel();
  450. var prev = this.prevIcon() || this.nextIcon();
  451. this.setSelected(false);
  452. if (prev) prev.setSelected(true);
  453. dom_parent.removeChild(this);
  454. this.iconbox.icons = this.iconbox.icons.without(this);
  455. var message = unescape(this.iconbox.messages['delete']).replace(/{TITLE}/, title);
  456. this.iconbox.statusbarPush(message);
  457. if (this.iconbox._alignDelay && this.iconbox.loaded)
  458. clearTimeout(this.iconbox._alignDelay);
  459. this.iconbox._alignDelay = this.iconbox._alignIconsVertically.bind(this.iconbox).delay(0.1);
  460. this.emitSignal('iwl:remove');
  461. return this;
  462. },
  463. _init: function(id, iconbox) {
  464. this.iconbox = iconbox;
  465. this.label = this.select('.icon_label')[0];
  466. this.image = this.select('img')[0];
  467. initEvents.call(this);
  468. if (this.image.complete)
  469. this.iconbox._iconCountdown();
  470. else
  471. this.image.observe('load', this.iconbox._iconCountdown.bind(this.iconbox));
  472. }
  473. }
  474. })());
  475. /* Deprecated */
  476. var Iconbox = IWL.Iconbox;
  477. var Icon = IWL.Iconbox.Icon;