/*
* Copyright (C) 2005 - 2011 Jaspersoft Corporation. All rights reserved.
* http://www.jaspersoft.com.
*
* Unless you have purchased a commercial license agreement from Jaspersoft,
* the following license terms apply:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
var baseList = {
isResponsive: function(item) { return $(item.up(0)).hasClassName(layoutModule.RESPONSIVE_CLASS); },
isCollapsible: function(item) { return $(item.up(0)).hasClassName(layoutModule.COLLAPSIBLE_CLASS); },
selectItem: function(item) {
if (this.isItemDisabled(item)) { return; }
$(item).addClassName(layoutModule.SELECTED_CLASS);
},
deselectItem: function(item) {
$(item).removeClassName(layoutModule.SELECTED_CLASS);
},
isItemSelected: function(item) {
return $(item).hasClassName(layoutModule.SELECTED_CLASS);
},
disableItem: function(item) { buttonManager.disable(item); },
enableItem: function(item) { buttonManager.enable(item); },
isItemDisabled: function(item) { buttonManager.isDisabled(item); },
openItem: function(item) {
$(item).removeClassName(layoutModule.CLOSED_CLASS).addClassName(layoutModule.OPEN_CLASS).isOpen = true;
},
isItemOpen: function(item) {
return ($(item).hasClassName(layoutModule.OPEN_CLASS) || ($(item).isOpen && !$(item).hasClassName(layoutModule.CLOSED_CLASS)));
},
closeItem: function(item) {
$(item).removeClassName(layoutModule.OPEN_CLASS).addClassName(layoutModule.CLOSED_CLASS).isOpen = false;
}
};
///////////////////////////////////////////////////////
// Global module for all list related code
///////////////////////////////////////////////////////
var dynamicList = {
/**
* Map of all created lists on current page
*/
lists: {},
/**
* Id of last active list
*/
activeListId: null,
_templateHash: {},
messages: {
'listNItemsSelected': "#{count} items selected"
}
};
///////////////////////////////////////////////////////
// ListItem
///////////////////////////////////////////////////////
/**
* Respond on events: dblclick, click, mouseover, mousedown, mouseup, key:down and key:up
* Options:
* respondOnItemEvents - when false all event will be ignored
* excludeFromEventHandling - [selectorPattern1, selectorPattern2]
* excludeFromSelectionTriggers - []
*
* @param options
*/
dynamicList.ListItem = function(options) {
this._itemId = undefined;
this._list = undefined;
this.first = false;
this.last = false;
if (options) {
this._value = (options.value) ? options.value : {};
this._label = (options.label) ? options.label : "";
this._subList = options.subList;
this._cssClassName = (options.cssClassName) ? options.cssClassName : undefined;
this._templateDomId = (options.templateDomId) ? options.templateDomId : undefined;
this._respondOnItemEvents = !Object.isUndefined(options.respondOnItemEvents) ? options.respondOnItemEvents : true;
this._excludeFromEventHandling = options.excludeFromEventHandling ? options.excludeFromEventHandling : undefined;
this._excludeFromSelectionTriggers = options.excludeFromSelectionTriggers ? options.excludeFromSelectionTriggers : undefined;
}
};
/**
*
*/
dynamicList.ListItem.addVar('DEFAULT_TEMPLATE_DOM_ID', "dynamicListItemTemplate");
dynamicList.ListItem.addVar('DEFAULT_ITEM_ID_PREFIX', "item");
dynamicList.ListItem.addVar('DEFAULT_SUB_LIST_ID_SUFFIX', "SubList");
///////////////////////////////////////////////////////
// Public ListItem methods
///////////////////////////////////////////////////////
/**
* Gets ID of the item, which is being generated by the list.
* @return {Number} ID of the item
*/
dynamicList.ListItem.addMethod('getId', function() { return this._itemId; });
/**
* Sets the list and generates new id in that list.
* This method is used by the list to set reference on itself.
*
* @param list {{@see dynamicList.List}}
*/
dynamicList.ListItem.addMethod('setList', function(list) {
this._list = list;
if (this.getList()) {
this._itemId = this.getList().getNextItemId();
}
});
/**
* Gets the list where this item is listed.
*
* @return {{@see dynamicList.List}}
*/
dynamicList.ListItem.addMethod('getList', function() { return this._list; });
/**
* Sets value of the item.
*
* @param {String}
*/
dynamicList.ListItem.addMethod('setValue', function(value) { return this._value = value; });
/**
* Gets value of the item.
*
* @return {String}
*/
dynamicList.ListItem.addMethod('getValue', function() { return this._value; });
/**
* Sets label defined by user.
*
* @param {Object}
*/
dynamicList.ListItem.addMethod('setLabel', function(label) { return this._label = label; });
/**
* Gets label of the item defined by user.
*
* @return {Object}
*/
dynamicList.ListItem.addMethod('getLabel', function() { return this._label; });
/**
* Setter and getter for the style of the item.
* Changes of the style will be applied after call of method {@see dynamicList.ListItem#refresh}
*/
dynamicList.ListItem.
addMethod('setCssClassName', function(cssClassName) { this._cssClassName = cssClassName; }).
addMethod('getCssClassName', function() { return this._cssClassName; });
/**
* Sets the ID of DOM element which will be used for rendering of mark up.
* Changes of the style will be applied after call of method {@see dynamicList.ListItem#refresh}
*
* @param templateDomId {String}
*/
dynamicList.ListItem.addMethod('setTemplateDomId', function(templateDomId) { this._templateDomId = templateDomId; });
/**
* Gets the ID of the current template of the item.
* Attr
* @return {String}
*/
dynamicList.ListItem.addMethod('getTemplateDomId', function() { return this._templateDomId; });
/**
* Render the item specified container.
* Use this method when you need re-render item.
*
* @param container {DOMElement}
*/
dynamicList.ListItem.addMethod('show', function(container) {
if (!container) { return; }
this._element = this.processTemplate(this._getTemplate());
this._getElement().setAttribute('id', this._generateId());
this._getElement().setAttribute('tabindex', -1);
this.first && this.getList().tabindex && this._getElement().writeAttribute("tabindex", this.getList().tabindex);
this._getElement().listItem = this;
this.refreshStyle();
var siblings = container.childElements();
var itemIndex = this.index();
var afterIndex = itemIndex - 1;
(afterIndex > -1 && afterIndex < siblings.length)
? this._getElement().insert({after: siblings[afterIndex]})
: $(container).insert(this._getElement());
});
/**
*
*/
dynamicList.ListItem.addMethod('refresh', function() {
if(!isNotNullORUndefined(this._getElement())) {
return;
}
if(this.getList()) {
this._element = this.processTemplate(this._getElement());
this.refreshStyle();
} else {
this._getElement().remove();
this._element = null;
}
});
/**
*
*/
dynamicList.ListItem.addMethod('refreshStyle', function() {
var element = this._getElement();
if(element.templateClassName) { element.className = element.templateClassName; }
if (this.first) { element.addClassName(layoutModule.FIRST_CLASS); }
if (this.last) { element.addClassName(layoutModule.LAST_CLASS); }
if (this.isSelected()) { element.addClassName(layoutModule.SELECTED_CLASS); }
if (this.isDisabled()) { element.addClassName(layoutModule.DISABLED_CLASS); }
if (this.getCssClassName()) { element.addClassName(this.getCssClassName()); }
});
/**
*
*/
dynamicList.ListItem.addMethod('isRendered', function() {
return isNotNullORUndefined(this._getElement());
});
/**
*
*/
dynamicList.ListItem.
addMethod('disable', function() { baseList.disableItem(this._getElement()); }).
addMethod('enable', function() { baseList.enableItem(this._getElement()); }).
addMethod('isDisabled', function() { return baseList.isItemDisabled(this._getElement()); });
/**
* Prepare mark up template of the item before render or process existing mark up when refresh the item.
* If template was changed method should be overridden.
*
* @param {DOMElement} - current item element
* @return {DOMElement} - element ready for use
*/
dynamicList.ListItem.addMethod('processTemplate', function(element) {
var wrapper = element.childElements()[0];
wrapper.cleanWhitespace();
var elementsCount = wrapper.childElements().length;
if (elementsCount == wrapper.childNodes.length) {
wrapper.insert(this.getLabel().escapeHTML());
} else {
wrapper.childNodes[elementsCount].data = this.getLabel();
}
// $(wrapper.parentNode).writeAttribute("tabIndex",-1);
return element;
});
/**
* focus on this node's element
*/
dynamicList.ListItem.addMethod('focus', function() {
this._getElement().focus();
});
/**
* Removes it self from the list.
*/
dynamicList.ListItem.addMethod('remove', function() {
this.getList().removeItems([this]);
});
/**
*
*/
dynamicList.ListItem.addMethod('isSelected', function() {
return this.getList().isItemSelected(this);
});
/**
*
*/
dynamicList.ListItem.addMethod('select', function() {
this.getList().selectItem(this, true);
});
/**
*
*/
dynamicList.ListItem.addMethod('deselect', function() {
this.getList().deselectItem(this);
});
dynamicList.ListItem.addMethod('index', function() {
this.getList().getItems().indexOf(this);
});
///////////////////////////////////////////////////////
// Private ListItem methods
///////////////////////////////////////////////////////
/**
* Gets or looking for DOM element of this item.
*
* @return {DOMElement}
*/
dynamicList.ListItem.addMethod('_getElement', function() {
if (!this._element) {
var e = $(this._generateId());
this._element = (Object.isElement(e) ? e : undefined);
}
return this._element;
});
dynamicList.ListItem.addMethod('_getTemplate', function() {
var id = this._templateDomId;
if (!dynamicList._templateHash[id]) {
dynamicList._templateHash[id] = id;
}
var clone = $(dynamicList._templateHash[id]).cloneNode(true);
clone.templateId = id;
clone.templateClassName = clone.className;
return clone;
});
dynamicList.ListItem.addMethod('_generateId', function() {
return this.getList().getId() + "_" + this.DEFAULT_ITEM_ID_PREFIX + this.getId();
});
dynamicList.ListItem.addMethod('_isElementInExcluded', function(event, item) {
var element = event.element();
return this._excludeFromEventHandling && matchAny(element, this._excludeFromEventHandling) != null;
});
dynamicList.ListItem.addMethod('_isExcludedFromSelectionTriggers', function(event) {
var element = event.element();
return this._excludeFromSelectionTriggers && matchAny(element, this._excludeFromSelectionTriggers) != null;
});
///////////////////////////////////////////////////////
// Composite List Item
///////////////////////////////////////////////////////
dynamicList.CompositeItem = function (options) {
dynamicList.ListItem.call(this, options);
this.isComposite = true;
this._items = options.items;
this._openUp = options.openUp;
this._subList = null;
this._subListOptions = (options.listOptions) ? options.listOptions : {};
this._listTagName = 'ul';
this.OPEN_HANDLER_PATTERN = (options.openHandlerPattern) ? options.openHandlerPattern : this.OPEN_HANDLER_PATTERN;
this.CLOSE_HANDLER_PATTERN = (options.closeHandlerPattern) ? options.closeHandlerPattern : this.CLOSE_HANDLER_PATTERN;
};
dynamicList.CompositeItem.prototype = deepClone(dynamicList.ListItem.prototype);
dynamicList.CompositeItem.addVar('OPEN_HANDLER_PATTERN', "[openHandler=openHandler]");
dynamicList.CompositeItem.addVar('CLOSE_HANDLER_PATTERN', "[closeHandler=closeHandler]");
dynamicList.CompositeItem.addMethod('getItems', function() { return this._items; });
dynamicList.CompositeItem.addMethod('setItems', function(items) { this._items = items; });
dynamicList.CompositeItem.addMethod('addItem', function(item) { this._items.push(item); });
dynamicList.CompositeItem.addMethod('removeItems', function(items) {
this._items = this._items.reject(function(item) {
return items.include(item);
});
this._subList.removeItems(items);
});
dynamicList.CompositeItem.addMethod('show', function(container) {
this._listTagName = container.tagName;
dynamicList.ListItem.prototype.show.call(this, container);
baseList.closeItem(this._getElement());
if(!this._items) { return; }
this._showSubList();
});
dynamicList.CompositeItem.addMethod('_showSubList', function() {
var id = this._getSubListId();
var subListElement = new Element(this._listTagName, { id: id });
this._getElement().insert(this._openUp ? {top: subListElement} : {bottom: subListElement});
var opts = this._subListOptions;
this._subList = new dynamicList.List(id, {
responsive: (opts.responsive) ? opts.responsive : this.getList()._responsive,
collapsible: (opts.collapsible) ? opts.collapsible : this.getList()._collapsible,
multiSelect: (opts.multiSelect) ? opts.multiSelect : this.getList()._multiSelect,
cssClassName: (opts.cssClassName) ? opts.cssClassName : this.getList()._cssClassName,
listTemplateDomId: (opts.listTemplateDomId) ? opts.listTemplateDomId : this.getList()._listTemplateDomId,
itemTemplateDomId: (opts.itemTemplateDomId) ? opts.itemTemplateDomId : this.getList()._itemTemplateDomId,
itemCssClassName: (opts.itemCssClassName) ? opts.itemCssClassName : this.getList()._itemCssClassName,
comparator: (opts.comparator) ? opts.comparator : this.getList()._comparator,
items: this._items
});
this._subList._initEvents = function() {};
this._subList.show();
this._subList._parentList = this.getList();
this._subList.getItems().each(function(item) {
item.parentItem = this;
}.bind(this));
});
dynamicList.CompositeItem.addMethod('refresh', function() {
dynamicList.ListItem.prototype.refresh.call(this);
if(!this._items) { return; }
if (this._subList) {
this._subList.refresh();
} else {
this._showSubList();
}
});
dynamicList.CompositeItem.addMethod('getFirstChild', function() {
return this._subList.getItems()[0];
});
dynamicList.CompositeItem.addMethod('refreshStyle', function() {
dynamicList.ListItem.prototype.refreshStyle.call(this);
if (baseList.isItemOpen(this._getElement())) {
baseList.openItem(this._getElement());
} else {
baseList.closeItem(this._getElement());
}
if(!this._subList) { return; }
this._subList.refreshStyle();
});
dynamicList.CompositeItem.
addMethod('_isOpenHandler', function(element) { return element.match(this.OPEN_HANDLER_PATTERN); }).
addMethod('_isCloseHandler', function(element) {return element.match(this.CLOSE_HANDLER_PATTERN); });
dynamicList.CompositeItem.addMethod('_getSubListId', function() {
return this._generateId() + "_" + this.DEFAULT_SUB_LIST_ID_SUFFIX;
});
///////////////////////////////////////////////////////
// Templated list item - uses Mustache to process item content as a template. Takes fields of item.getValue() to fill the template
///////////////////////////////////////////////////////
dynamicList.TemplatedListItem = function(options) {
dynamicList.ListItem.call(this, options);
this.tooltipText = options.tooltipText;
}
//TODO would be nice to be able to get template from uninterpreted script tag, not from DOM part. This would give more flexibility.
dynamicList.TemplatedListItem.prototype = new dynamicList.ListItem();
dynamicList.TemplatedListItem.prototype.constructor = dynamicList.TemplatedListItem;
dynamicList.TemplatedListItem.prototype.processTemplate = function(element) {
var filled = Mustache.to_html(element.innerHTML, this.getValue());
element.innerHTML = filled;
if(this.tooltipText != null) {
new JSTooltip(element, {text: this.tooltipText});
}
return element;
}
///////////////////////////////////////////////////////
// List Component
///////////////////////////////////////////////////////
/**
* Dynamically creates items on specified UL element. Supports sorting and ...
*
* @param id {String} - ID of the UL element, this id will be used as ID of the list.
* @param options {JSON Object}
*
* - items {Array} - array of {@link dynamicList.ListItem}, which need be shown when object is created
*
*/
dynamicList.List = function(id, options) {
this._id = id;
this._items = [];
this._selectedItems = [];
this._lastSelectedItem = null;
this._nextId = 1; // private static var
this.draggables = [];
this._parentList = null;
if (options) {
this._multiSelect = (options.multiSelect) ? options.multiSelect : false;
this._cssClassName = (options.cssClassName) ? options.cssClassName : "";
this._listTemplateDomId = options.listTemplateDomId;
this._itemTemplateDomId = options.itemTemplateDomId;
this._itemCssClassName = options.itemCssClassName;
this._comparator = options.comparator;
this._excludeFromEventHandling = options.excludeFromEventHandling;
this._excludeFromSelectionTriggers = options.excludeFromSelectionTriggers;
this.dragPattern = options.dragPattern;
this.selectOnMousedown = options.selectOnMousedown;
this.scroll = options.scroll;
this.setItems(options.items);
}
this._createFromTemplate();
this._registerCustomScroll();
dynamicList.activeListId = this.getId();
this._msgNItemsSelected = new Template(dynamicList.messages['listNItemsSelected']);
dynamicList.lists[this._id] = this;
};
dynamicList.List.addVar('Event', {
ITEM_SELECTED: "item:selected",
ITEM_UNSELECTED: "item:unselected",
ITEM_MOUSEUP: "item:mouseup",
ITEM_MOUSEDOWN: "item:mousedown",
ITEM_CLICK: "item:click",
ITEM_DBLCLICK: "item:dblclick",
ITEM_OPEN: "item:open",
ITEM_CLOSED: "item:closed",
ITEM_CONTEXTMENU: "item:contextmenu",
ITEM_BEFORE_SELECT_OR_UNSELECT: "item:beforeSelectOrUnselect"
});
dynamicList.List.addVar('DND_WRAPPER_TEMPLATE', "column_two");
dynamicList.List.addVar('DND_ITEM_TEMPLATE', "column_two:resourceName");
///////////////////////////////////////////////////////
// List public methods
///////////////////////////////////////////////////////
dynamicList.List.addMethod('getNextItemId', function() {
return this._nextId ++;
});
/**
* Gets ID of the list which can be used to find this list in map {@see dynamicList.lists}
*
* @return {String} - ID for the list and DOM element
*/
dynamicList.List.addMethod('getId', function() { return this._id; });
/**
* @return {Array}
*/
dynamicList.List.addMethod('getItems', function() { return this._items; });
/**
* Resets list items with new items and sorts items, if comparator is defined.
* @param items {Array} - new array of {@link dynamicList.ListItem}
*/
dynamicList.List.addMethod('setItems', function(items) {
if (!items) { return; }
this._items = [];
this.resetSelected();
this.addItems(items);
});
/**
* Adds items to array items of list and sorts it, if comparator is defined.
* @param items {Array} - array of {@link dynamicList.ListItem}
*/
dynamicList.List.addMethod('addItems', function(items) {
if (!items) { return; }
items.compact().each(function(item) {
this._prepareListItem(item);
this._items.push(item);
}.bind(this));
if (this._comparator) {
this._items = this._items.sort(this._comparator);
}
});
/**
* Inserts items to appropriate position in array items of list.
*
* WARNING: Using this function with sortable lists may cause unexpected results
*
* @param pos {int} - position of element after which new items will be inserted.
* @param items {Array} - array of {@link dynamicList.ListItem}
*/
dynamicList.List.addMethod('insertItems', function(pos, items) {
if (!items) { return; }
items = items.compact();
items.each(function(item) {
this._prepareListItem(item);
}.bind(this));
this._items.splice.apply(this._items, [pos, 0].concat(items));
if (this._comparator) {
this._items = this._items.sort(this._comparator);
}
});
/**
* Prepare List Item for adding to list. Set List reference, template DOM ID and css class name.
*/
dynamicList.List.addMethod('_prepareListItem', function(item) {
if (!item) {
return;
}
item.setList(this);
if (this._itemTemplateDomId && !item.getTemplateDomId()) {
// If list has specified template for all items and there is no other template
item.setTemplateDomId(this._itemTemplateDomId);
}
if (this._itemCssClassName && !item.getCssClassName()) {
// If list has specified CSS class for all items and the item don't has CSS class
item.setCssClassName(this._itemCssClassName);
}
if (this._excludeFromEventHandling && !item._excludeFromEventHandling) {
item._excludeFromEventHandling = this._excludeFromEventHandling;
}
if (this._excludeFromSelectionTriggers && !item._excludeFromSelectionTriggers) {
item._excludeFromSelectionTriggers = this._excludeFromSelectionTriggers;
}
});
/**
* Removes specified items from list
* @param items {Array} - array of {@link dynamicList.ListItem}
*/
dynamicList.List.addMethod('removeItems', function(items) {
if (!items || !isArray(items)) { return; }
this._items = this._items.reject(function(item) {
return items.include(item);
});
items.each(function(item) {
this.deselectItem(item);
}.bind(this));
items.each(function(item) {
item.setList(null);
item.refresh();
});
});
/**
* @param comparator {Function} -
*/
dynamicList.List.addMethod('sort', function(comparator) {
comparator && (this._comparator = comparator);
if (this._comparator) {
this.getItems().sort(this._comparator);
}
});
/**
* @return {Array}
*/
dynamicList.List.addMethod('getSelectedItems', function() { return this._selectedItems; });
/**
* @param item {dynamicList.ListItem} -
*/
dynamicList.List.addMethod('isItemSelected', function(item) {
return this.getSelectedItems().include(item);
});
/**
* @param item {dynamicList.ListItem} -
*/
dynamicList.List.addMethod('selectItem', function(item, isCtrlHeld, isShiftHeld, isContextMenu) {
var event = this.fire(this.Event.ITEM_BEFORE_SELECT_OR_UNSELECT, {item: item});
if (event.stopSelectOrUnselect) {
return;
}
// Fix for multiple DnD.
// If couple items selected and selectOnMousedown enabled
// we need deselect items on mouse up to be able Drag them.
if (this._multiSelect && this._selectedItems.length > 1 && this.isItemSelected(item)
&& !(isCtrlHeld || isShiftHeld || isContextMenu)) {
return;
}
var isContextMenuOnSelected = this.isItemSelected(item) && isContextMenu;
var reset = !(this._multiSelect && isCtrlHeld) && !isContextMenuOnSelected;
var deselect = this.isItemSelected(item) && isCtrlHeld && !isContextMenuOnSelected;
var selectRange = !deselect && isNotNullORUndefined(this._lastSelectedItem) && isShiftHeld;
var select = !deselect && !selectRange;
if (reset) {
this.resetSelected();
}
if (deselect && !reset) {
this._removeItemFromSelected(item);
}
if (selectRange) {
var start = this._items.indexOf(this._lastSelectedItem);
var end = this._items.indexOf(item);
var min = Math.min(start, end), max = Math.max(start, end);
if (min > -1) {
for (var i = min; i <= max; i++) {
this._addItemToSelected(this._items[i], false);
}
} else {
this._addItemToSelected(this._items[max], false);
}
}
if (select) {
this._addItemToSelected(item, !isShiftHeld);
this.cursor = item;
}
});
/**
* @param item {dynamicList.ListItem} -
*/
dynamicList.List.addMethod('deselectItem', function(item) {
this._removeItemFromSelected(item);
});
dynamicList.List.addMethod('deselectOthers', function(item, isCtrlHeld, isShiftHeld, isContextMenu) {
var event = this.fire(this.Event.ITEM_BEFORE_SELECT_OR_UNSELECT, {item: item});
if (event.stopSelectOrUnselect) {
return;
}
// Fix for multiple DnD.
// If couple items selected and selectOnMousedown enabled
// we need deselect items on mouse up to be able Drag them.
if (this._multiSelect && this._selectedItems.length > 1 && this.isItemSelected(item)
&& !(isCtrlHeld || isShiftHeld || isContextMenu)) {
var items = this._selectedItems.findAll(function(i) { return i != item});
items.each(function(i) {
this._removeItemFromSelected(i);
}.bind(this));
}
});
/**
*
*/
dynamicList.List.addMethod('resetSelected', function(skipParent) {
var items = this._selectedItems;
this._selectedItems = [];
items.each(function(item) {
if (item.getList() !== this) {
item.getList().resetSelected(true);
}
item.refreshStyle();
this.fire(this.Event.ITEM_UNSELECTED, {item: item});
}.bind(this));
if (this._parentList && !skipParent) {
this._parentList.resetSelected();
}
});
/**
* @param item - reference item
*/
dynamicList.List.addMethod('getNextItem', function(item) {
var items = this.getItems();
var currentIndex = items.indexOf(item);
return ~currentIndex ? this.getItems()[currentIndex + 1] : null;
});
/**
* @param item - reference item
*/
dynamicList.List.addMethod('getPreviousItem', function(item) {
var items = this.getItems();
var currentIndex = items.indexOf(item);
return ~currentIndex ? this.getItems()[currentIndex - 1] : null;
});
/**
* @param event
*/
dynamicList.List.addMethod('selectNext', function(event) {
var baseEvent = event.memo.targetEvent;
var item = (this.cursor)?this.cursor:this.getSelectedItems()[this.getSelectedItems().length-1];
var nextItem = item.getList().getNextItem(item);
if (nextItem) {
if (isShiftHeld(baseEvent)){
if (this.isItemSelected(nextItem)){
item.getList().deselectItem(item);
}
else{
this._addItemToSelected(nextItem, false);
}
}
else{
this.resetSelected();
item.getList().selectItem(nextItem);
}
nextItem._getElement().focus();
this.cursor = nextItem;
}
});
/**
* @param event
*/
dynamicList.List.addMethod('selectPrevious', function(event) {
var baseEvent = event.memo.targetEvent;
var item = (this.cursor)?this.cursor:this.getSelectedItems()[0];
var previousItem = item.getList().getPreviousItem(item);
if (previousItem) {
if (isShiftHeld(baseEvent)){
if (this.isItemSelected(previousItem)){
item.getList().deselectItem(item);
}
else{
this._addItemToSelected(previousItem, false);
}
}
else{
this.resetSelected();
item.getList().selectItem(previousItem);
}
previousItem._getElement().focus();
this.cursor = previousItem;
}
});
/**
* @param event
*/
dynamicList.List.addMethod('selectOutwards', function(event) {
var item = this.getSelectedItems()[0];
var element = item._getElement();
var outItem = !baseList.isItemOpen(element) && item.parentItem;
if (outItem) {
item.deselect();
outItem.select();
outItem._getElement().focus();
} else {
baseList.closeItem(element);
this.fire(this.Event.ITEM_CLOSED, { targetEvent: event, item: item });
}
});
/**
* @param event
*/
dynamicList.List.addMethod('selectInwards', function(event) {
var item = this.getSelectedItems()[0];
if(item.isComposite) {
var element = item._getElement();
var inItem = baseList.isItemOpen(element) && item.getFirstChild();
if (inItem){
item.deselect();
inItem.select();
element.focus();
} else {
baseList.openItem(element);
this.fire(this.Event.ITEM_OPEN, { targetEvent: event, item: item });
}
}
});
/**
* Render list items. Use this method when you need to re-render list.
*/
dynamicList.List.addMethod('show', function() {
dynamicList.activeListId = this.getId();
this._getElement().update();
this.getItems().each(function(item, index) {
item.first = index === 0;
item.last = index === (this.getItems().length - 1);
item.show(this._getElement());
}.bind(this));
this.draggables = [];
this.scroll && this.scroll.refresh();
this._initEvents();
});
/**
* Updates UI from value of the item and remove unused DOM elements (li elements)
*/
dynamicList.List.addMethod('refresh', function() {
this.refreshStyle();
var elements = this._getElement().childElements();
var itemElements = [];
this.getItems().each(function(item, index) {
item.first = index === 0;
item.last = index === (this.getItems().length - 1);
if (item.isRendered() ) {
if (item.index() != elements.indexOf(item._getElement())) {
item._getElement().remove();
item.show(this._getElement());
} else {
item.refresh();
}
} else {
item.show(this._getElement());
}
itemElements.push(item._getElement());
}.bind(this));
elements.each(function(e){
if (!itemElements.include(e) && e.parentNode) {
e.remove();
}
});
//this.scroll && this.scroll.refresh();
});
/**
* Refreshing classname of the list
*/
dynamicList.List.addMethod('refreshStyle', function(clean) {
var element = this._getElement();
if(element.templateClassName) { element.className = element.templateClassName; }
if(this._cssClassName) { element.addClassName(this._cssClassName); }
});
/**
* Generates custom event of the list.
*
* @param eventName
*/
dynamicList.List.addMethod('fire', function(eventName, memo) {
return this._getElement().fire(eventName, memo);
});
/**
* @param eventName
*/
dynamicList.List.addMethod('observe', function(eventName, handler) {
this._getElement().observe(eventName, handler);
});
/**
* @param eventName
*/
dynamicList.List.addMethod('stopObserving', function(eventName, memo) {
this._getElement().stopObserving(eventName, handler);
});
///////////////////////////////////////////////////////
// List private methods
///////////////////////////////////////////////////////
/**
* Gets list container
* @return {DOMElement}
*/
dynamicList.List.addMethod('_getElement', function() {
if (!this._element) {
this._element = $(this.getId());
}
return this._element;
});
/**
* Gets the item from the event in list
*
* @paran {Event}
*/
dynamicList.List.addMethod('getItemByEvent', function(event) {
if (event) {
var element = Event.element(event); //event.originalTarget || event.srcElement;
while(element && element.readAttribute && element.readAttribute('id') !== this.getId()) {
var item = element.listItem;
// if (item && item.getList() != null && item.getList().getId() == this.getId()) {
if (item && item.getList() != null) {
return item;
} else {
element = $(element.parentNode);
}
}
}
return null;
});
dynamicList.List.addMethod('_createFromTemplate', function() {
var tabindex = this._getElement().readAttribute("tabindex");
this.tabindex = parseInt(tabindex && tabindex.length > 0 ? tabindex : -1);
this._getElement().insert({after: this._getTemplateElement(this._getElement())});
this._getElement().remove();
this._element = null;
this._getElement().update();
this.tabindex && this.tabindex.length > 0 && this._getElement().writeAttribute('tabindex', this.tabindex);
disableSelectionWithoutCursorStyle(this._getElement());
});
dynamicList.List.addMethod('_getTemplateElement', function(currentElement) {
var id = this._listTemplateDomId;
if (!dynamicList._templateHash[id]) {
dynamicList._templateHash[id] = id;
}
var clone = $(dynamicList._templateHash[id]).cloneNode(true);
clone.writeAttribute("id", this.getId());
//clone.down().writeAttribute("tabIndex", -1);
clone.templateId = id;
clone.templateClassName = clone.className;
cloneCustomAttributes(currentElement, clone);
return clone;
});
dynamicList.List.addMethod('_addItemToSelected', function(item, remember) {
if (item && !this.isItemSelected(item)) {
this._selectedItems.push(item);
if (remember) {
this._lastSelectedItem = item;
}
// item.focus();
item.refreshStyle();
if (this._parentList) {
this._parentList._addItemToSelected(item, remember);
} else {
this.fire(this.Event.ITEM_SELECTED, {item: item});
}
}
});
dynamicList.List.addMethod('_removeItemFromSelected', function(item) {
if (item && this.isItemSelected(item)) {
this._selectedItems = this._selectedItems.without(item);
item.refreshStyle();
if (this._parentList) {
this._parentList._removeItemFromSelected(item);
} else {
this.fire(this.Event.ITEM_UNSELECTED, {item: item});
}
}
});
dynamicList.List.addMethod('_buildDnDOverlay', function(element) {
// var wrapper = $(this.DND_WRAPPER_TEMPLATE).cloneNode(true), template = $(this.DND_ITEM_TEMPLATE).cloneNode(true);
var items = [];
element.setStyle({width: null, height: null});
if (element.items.length > 1) {
element.update(this._msgNItemsSelected.evaluate({count: element.items.length}));
} else if (element.items.length == 1) {
element.update(element.items[0].getLabel());
}
});
dynamicList.List.addMethod('_registerCustomScroll', function() {
if (!this.scroll && this._getElement()) {
var scrollBar = this._getElement().up(layoutModule.SWIPE_SCROLL_PATTERN);
if(scrollBar) {
var scroll = layoutModule.scrolls.get(scrollBar.identify());
scroll && (this.scroll = scroll);
// scroll && (console.log(scroll.wrapper.inspect()));
}
}
});
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// List DnD
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
dynamicList.List.addMethod('createDraggableIfNeeded', function(event) {
//make draggable - test in this order - for efficiency
//test 1) does the tree have any drag patterns?
//test 2) is a draggable already created for the clicked element?
//test 3) does clicked element or its ancestors match any draggable patterns?
//test 4) is a draggable already created for the clicked element or matching ancestor?
//5) if it's complex markup then we go up to parent which matching pattern
var thisElem = event.element();
if (this.dragPattern && !this.draggables[thisElem.identify()]) {
var matchingElem = matchAny(thisElem, [this.dragPattern], true);
if (matchingElem) {
//matchingElem = matchingElem.up(this.dragPattern);
if (!matchingElem || this.draggables[matchingElem.identify()]) { return; }
var item = this.getItemByEvent(event);
this.draggables[matchingElem.identify()] = new Draggable(matchingElem, {
superghosting: true,
mouseOffset: true,
onStart: this.setDragStartState.bind(this, item),
onEnd: this.setDragEndState.bind(this, item)
});
}
}
});
dynamicList.List.addMethod('setDragStartState', function(item, draggable, event) {
var templateClassName = item._getElement().templateClassName;
if (templateClassName) { draggable.element.addClassName(templateClassName); }
draggable.element.addClassName(layoutModule.DRAGGING_CLASS).addClassName(this.getId());
draggable.element.items = this.getSelectedItems().slice(0);
this._buildDnDOverlay(draggable.element);
draggable.options.scroll = this._getElement();
draggable.options.scrollSensitivity = layoutModule.SCROLL_SENSITIVITY;
Draggables.dragging = this.regionID || true;
});
dynamicList.List.addMethod('setDragEndState', function(draggable, event, item) {
delete Draggables.dragging;
});
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// List event handling
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
dynamicList.List.addMethod('_mouseupHandler', function(event) {
var element = event.element();
var item = matchMeOrUp(element, layoutModule.BUTTON_PATTERN) && this.getItemByEvent(event);
if(!item || item._isElementInExcluded(event)) { return; }
event.listEvent = true;
if (item._respondOnItemEvents && !event.isInvoked) {
this.fire(this.Event.ITEM_MOUSEUP, { targetEvent: event, item: item });
if(!item._isExcludedFromSelectionTriggers(event)){
var isSelect = !this.selectOnMousedown && !TouchController.element_scrolled && (!isSupportsTouch() || event.changedTouches.length >= 1);
isSelect && item.getList().selectItem(item, isMetaHeld(event), isShiftHeld(event), isRightClick(event));
item.getList().deselectOthers(item, isMetaHeld(event), isShiftHeld(event), isRightClick(event));
if(this.twofingers){
this.twofingers = false;
var li = jQuery(element).parents('li:first');
li.hasClass('selected') && document.fire(layoutModule.ELEMENT_CONTEXTMENU, {targetEvent: event, node: element});
}
}
this.createDraggableIfNeeded(event);
}
event.isInvoked = true;
});
dynamicList.List.addMethod('_mousedownHandler', function(event) {
event = (event.type == 'dataavailable') ? event.memo.targetEvent : event;
var element = event.element();
var item = matchMeOrUp(element, layoutModule.BUTTON_PATTERN) && this.getItemByEvent(event);
if(!item || item._isElementInExcluded(event)) { return; }
event.listEvent = true;
if(event.touches && event.touches.length == 2) {
this.twofingers = true;
//var li = jQuery(element).parents('li:first');
//li.hasClass('selected') && document.fire(layoutModule.ELEMENT_CONTEXTMENU, {targetEvent: event, node: element});
//return;
} else {
this.twofingers = false;
}
if (item._respondOnItemEvents && !event.isInvoked) {
this.fire(this.Event.ITEM_MOUSEDOWN, { targetEvent: event, item: item });
if(!item._isExcludedFromSelectionTriggers(event)){
var isSelect = this.selectOnMousedown;// && (!isSupportsTouch() || event.touches.length == 1);
isSelect && item.getList().selectItem(item, isMetaHeld(event), isShiftHeld(event), isRightClick(event));
}
//item.focus();
}
event.isInvoked = true;
});
dynamicList.List.addMethod('_mouseoverHandler', function(event) {
matchMeOrUp(event.element(), layoutModule.BUTTON_PATTERN) && this.createDraggableIfNeeded(event);
});
dynamicList.List.addMethod('_clickHandler', function(event) {
var item = matchMeOrUp(event.element(), layoutModule.BUTTON_PATTERN) && this.getItemByEvent(event);
if(!item || item._isElementInExcluded(event)) { return; }
if (!event.isInvoked) {
if (item._respondOnItemEvents) {
this.fire(this.Event.ITEM_CLICK, { targetEvent: event, item: item });
}
if(!item.isComposite) return;
var element = item._getElement(), source = event.element();
if (item._isCloseHandler(source) && baseList.isItemOpen(element)) {
baseList.closeItem(element);
this.fire(this.Event.ITEM_CLOSED, { targetEvent: event, item: item });
} else if (item._isOpenHandler(source) && !baseList.isItemOpen(element)) {
baseList.openItem(element);
this.fire(this.Event.ITEM_OPEN, { targetEvent: event, item: item });
}
}
event.isInvoked = true;
});
dynamicList.List.addMethod('_dblclickHandler', function(event) {
var item = matchMeOrUp(event.element(), layoutModule.BUTTON_PATTERN) && this.getItemByEvent(event);
if(!item || item._isElementInExcluded(event)) { return; }
if (item._respondOnItemEvents && !event.isInvoked) {
this.fire(this.Event.ITEM_DBLCLICK, { targetEvent: event, item: item });
}
event.isInvoked = true;
});
dynamicList.List.addMethod('_initEvents', function() {
var container = this._getElement();
this.draggables = [];
if(isSupportsTouch()) {
container.stopObserving('touchstart').observe('touchstart', this._mousedownHandler.bindAsEventListener(this));
//scriptaculous stopped mousedown event but we made it throw this instead
container.stopObserving('drag:touchstart').observe('drag:touchstart', this._mousedownHandler.bindAsEventListener(this));
container.stopObserving('touchend').observe('touchend', this._mouseupHandler.bindAsEventListener(this));
} else {
container.stopObserving('mouseup').observe('mouseup', this._mouseupHandler.bindAsEventListener(this));
container.stopObserving('mousedown').observe('mousedown', this._mousedownHandler.bindAsEventListener(this));
//scriptaculous stopped mousedown event but we made it throw this instead
container.stopObserving('drag:mousedown').observe('drag:mousedown', this._mousedownHandler.bindAsEventListener(this));
}
if(!isIPad) container.stopObserving('mouseover').observe('mouseover', this._mouseoverHandler.bindAsEventListener(this));
container.stopObserving('click').observe('click', this._clickHandler.bindAsEventListener(this));
container.stopObserving('dblclick').observe('dblclick', this._dblclickHandler.bindAsEventListener(this));
container.stopObserving('key:down').observe('key:down', this.selectNext.bindAsEventListener(this));
container.stopObserving('key:up').observe('key:up', this.selectPrevious.bindAsEventListener(this));
container.stopObserving('key:right').observe('key:right', this.selectInwards.bindAsEventListener(this));
container.stopObserving('key:left').observe('key:left', this.selectOutwards.bindAsEventListener(this));
});