Permalink
Find file Copy path
e89d952 Apr 26, 2016
3 contributors

Users who have contributed to this file

@kanreisa @townewgokgok @s-shinoda
8541 lines (7071 sloc) 209 KB
/*jshint laxcomma: true */
/*jslint browser:true, vars:true, plusplus:true, nomen:true, continue:true, white:true */
/*global HTMLElement, Event */
/*!
* Flagrate
*
* Copyright (c) 2013 Webnium and Flagrate Contributors
* Licensed under the MIT-License.
*
* https://flagrate.org/
* https://github.com/webnium/flagrate
**/
(function () {
"use strict";
// flagrate global scope
if (window.flagrate !== void 0) {
throw new Error('[conflict] flagrate is already defined.');
}
var flagrate = window.flagrate = {};
flagrate.className = 'flagrate';
var identity = flagrate.identity = function (a) {
return a;
};
// extend object
var extendObject = flagrate.extendObject = function (b, a) {
/*jslint forin:true */
var k;
for (k in a) { b[k] = a[k]; }
return b;
};
// empty function
var emptyFunction = flagrate.emptyFunction = function () {};
// is element
var isElement;
if (typeof HTMLElement === 'object') {
isElement = function (a) {
return a instanceof HTMLElement;
};
} else {
isElement = function (a) {
return a && a.nodeType === 1 && typeof a.nodeName === "string";
};
}
// jsonpointer
// ref: node-jsonpointer https://github.com/janl/node-jsonpointer
// ref: http://tools.ietf.org/html/draft-ietf-appsawg-json-pointer-08
var jsonPointer = flagrate.jsonPointer = {
untilde: function (str) {
return str.replace(/~[01]/g, function (m) {
switch (m) {
case "~0":
return "~";
case "~1":
return "/";
}
throw new Error("Invalid tilde escape: " + m);
});
},
traverse: function (obj, pointer, value, isSet) {
var part = jsonPointer.untilde(pointer.shift());
if (/^\d+$/.test(part)) {
part = parseInt(part, 10);
}
if (pointer.length !== 0) {// keep traversin!
if (isSet && typeof obj[part] !== 'object') {
if (value === void 0) {
return value;
}
if (/^\d+$/.test(pointer[0])) {
obj[part] = [];
} else {
obj[part] = {};
}
}
return jsonPointer.traverse(obj[part], pointer, value, isSet);
}
// we're done
if (!isSet) {
// just reading
return obj[part];
}
// set new value, and return
if (value === void 0) {
delete obj[part];
} else {
obj[part] = value;
}
return value;
},
validate_input: function (obj, pointer) {
if (typeof obj !== "object") {
throw new Error("Invalid input object.");
}
if (pointer === "") {
return [];
}
if (!pointer) {
throw new Error("Invalid JSON pointer.");
}
pointer = pointer.split("/");
var first = pointer.shift();
if (first !== "") {
throw new Error("Invalid JSON pointer.");
}
return pointer;
},
get: function (obj, pointer) {
pointer = jsonPointer.validate_input(obj, pointer);
if (pointer.length === 0) {
return obj;
}
return jsonPointer.traverse(obj, pointer);
},
set: function (obj, pointer, value) {
if (pointer === '' && typeof value === 'object') {
flagrate.extendObject(obj, value);
return value;
} else {
pointer = jsonPointer.validate_input(obj, pointer);
if (pointer.length === 0) {
throw new Error("Invalid JSON pointer for set.");
}
return jsonPointer.traverse(obj, pointer, value, true);
}
}
};
/*?
* class flagrate.Element
*
* The flagrate.Element object provides a variety of powerful DOM methods for interacting
* with DOM elements.
*
* #### Example
*
* var preview = flagrate.createElement().insertTo(x);
* preview.on('updated', function (e) {
* console.log('fired custom event', e);
* });
*
* var input = flagrate.createTextInput().insertTo(x);
* input.on('change', function () {
* preview.updateText(input.value);
* preview.fire('updated');
* });
*
* #### Inheritance
*
* * [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) (MDN)
**/
/*?
* flagrate.createElement([tagName = "div", attribute])
* new flagrate.Element([tagName = "div", attribute])
* - tagName (String) - The name of the HTML element to create.
* - attribute (Object) - An optional group of attribute/value pairs to set on the element.
*
* Creates an HTML element with `tagName` as the tag name, optionally with the given attributes.
*
* #### Example
*
* // The old way:
* var a = document.createElement('a');
* a.setAttribute('class', 'foo');
* a.setAttribute('href', '/foo.html');
* a.appendChild(document.createTextNode("Next page"));
* x.appendChild(a);
*
* // The new way:
* var a = flagrate.createElement('a', { 'class': 'foo', href: '/foo.html' }).insert("Next page").insertTo(x);
**/
var Element = flagrate.Element = function (tagName, attr) {
tagName = tagName || 'div';
attr = attr || null;
tagName = tagName.toLowerCase();
var node;
if (Element.cache[tagName]) {
node = Element.cache[tagName].cloneNode(false);
} else if ((attr !== null && attr.hasOwnProperty('type')) || tagName === 'select') {
node = document.createElement(tagName);
} else {
node = document.createElement(tagName);
Element.cache[tagName] = node.cloneNode(false);
}
extendObject(node, this);
return attr === null ? node : Element.writeAttribute(node, attr);
};
flagrate.createElement = function (a, b) {
return new Element(a, b);
};
Element.cache = {};
Element.prototype = {
isFlagrated: true,
/*?
* flagrate.Element#visible() -> Boolean
*
* please refer to flagrate.Element.visible
**/
visible: function () {
return Element.visible(this);
},
/*?
* flagrate.Element#exists() -> Boolean
*
* please refer to flagrate.Element.exists
**/
exists: function () {
return Element.exists(this);
},
/*?
* flagrate.Element#toggle() -> flagrate.Element
*
* please refer to flagrate.Element.toggle
**/
toggle: function () {
return Element.toggle(this);
},
/*?
* flagrate.Element#hide() -> flagrate.Element
*
* please refer to flagrate.Element.hide
**/
hide: function () {
return Element.hide(this);
},
/*?
* flagrate.Element#show() -> flagrate.Element
*
* please refer to flagrate.Element.show
**/
show: function () {
return Element.show(this);
},
/*?
* flagrate.Element#remove() -> flagrate.Element
*
* please refer to flagrate.Element.remove
**/
remove: function () {
return Element.remove(this);
},
/*?
* flagrate.Element#update([newContent]) -> flagrate.Element
*
* please refer to flagrate.Element.update
**/
update: function (content) {
return Element.update(this, content);
},
/*?
* flagrate.Element#updateText([newContent]) -> flagrate.Element
*
* please refer to flagrate.Element.updateText
**/
updateText: function (text) {
return Element.updateText(this, text);
},
/*?
* flagrate.Element#insert(content) -> flagrate.Element
*
* please refer to flagrate.Element.insert
**/
insert: function (content) {
return Element.insert(this, content);
},
/*?
* flagrate.Element#insertText(content) -> flagrate.Element
*
* please refer to flagrate.Element.insertText
**/
insertText: function (text) {
return Element.insertText(this, text);
},
/*?
* flagrate.Element#insertTo(element[, position = "bottom"]) -> flagrate.Element
*
* please refer to flagrate.Element.insertTo
**/
insertTo: function (element, pos) {
return Element.insertTo(this, element, pos);
},
/*?
* flagrate.Element#readAttribute(attributeName) -> flagrate.Element
*
* please refer to flagrate.Element.readAttribute
**/
readAttribute: function (name) {
return Element.readAttribute(this, name);
},
/*?
* flagrate.Element#writeAttribute(attribute[, value = true]) -> flagrate.Element
*
* please refer to flagrate.Element.writeAttribute
**/
writeAttribute: function (name, value) {
return Element.writeAttribute(this, name, value);
},
/*?
* flagrate.Element#getDimensions() -> Object
*
* please refer to flagrate.Element.getDimensions
**/
getDimensions: function () {
return Element.getDimensions(this);
},
/*?
* flagrate.Element#getHeight() -> Number
*
* please refer to flagrate.Element.getHeight
**/
getHeight: function () {
return Element.getHeight(this);
},
/*?
* flagrate.Element#getWidth() -> Number
*
* please refer to flagrate.Element.getWidth
**/
getWidth: function () {
return Element.getWidth(this);
},
/*?
* flagrate.Element#cumulativeOffset() -> Object
*
* please refer to flagrate.Element.cumulativeOffset
**/
cumulativeOffset: function () {
return Element.cumulativeOffset(this);
},
/*?
* flagrate.Element#cumulativeScrollOffset() -> Object
*
* please refer to flagrate.Element.cumulativeScrollOffset
**/
cumulativeScrollOffset: function () {
return Element.cumulativeScrollOffset(this);
},
/*?
* flagrate.Element#hasClassName(className) -> Boolean
*
* please refer to flagrate.Element.hasClassName
**/
hasClassName: function (className) {
return Element.hasClassName(this, className);
},
/*?
* flagrate.Element#addClassName(className) -> flagrate.Element
*
* please refer to flagrate.Element.addClassName
**/
addClassName: function (className) {
return Element.addClassName(this, className);
},
/*?
* flagrate.Element#removeClassName(className) -> flagrate.Element
*
* please refer to flagrate.Element.removeClassName
**/
removeClassName: function (className) {
return Element.removeClassName(this, className);
},
/*?
* flagrate.Element#toggleClassName(className) -> flagrate.Element
*
* please refer to flagrate.Element.toggleClassName
**/
toggleClassName: function (className) {
return Element.toggleClassName(this, className);
},
/*?
* flagrate.Element#getStyle(propertyName) -> String | Number | null
*
* please refer to flagrate.Element.getStyle
**/
getStyle: function (propertyName) {
return Element.getStyle(this, propertyName);
},
/*?
* flagrate.Element#setStyle(style) -> flagrate.Element
*
* please refer to flagrate.Element.setStyle
**/
setStyle: function (style) {
return Element.setStyle(this, style);
},
/*?
* flagrate.Element#on(eventName, listener[, useCapture = false]) -> flagrate.Element
*
* please refer to flagrate.Element.on
**/
on: function (name, listener, useCapture) {
return Element.on(this, name, listener, useCapture);
},
/*?
* flagrate.Element#off(eventName, listener[, useCapture = false]) -> flagrate.Element
*
* please refer to flagrate.Element.off
**/
off: function (name, listener, useCapture) {
return Element.off(this, name, listener, useCapture);
},
/*?
* flagrate.Element#fire(eventName[, property]) -> flagrate.Element
*
* please refer to flagrate.Element.fire
**/
fire: function (name, property) {
return Element.fire(this, name, property);
}
};
/*?
* flagrate.Element#emit(eventName[, property]) -> flagrate.Element
* Alias of: flagrate.Element#fire
**/
Element.prototype.emit = Element.prototype.fire;
/*?
* flagrate.Element.visible(element) -> Boolean
* - element (Element) - instance of Element.
*
* Tells whether `element` is visible
*
* This method is similar to http://api.prototypejs.org/dom/Element/visible/
**/
Element.visible = function (element) {
return element.style.display !== 'none';
};
/*?
* flagrate.Element.exists(element) -> Boolean
* - element (Element) - instance of Element.
*
* Tells whether `element` is exists on document.
**/
Element.exists = function (element) {
if (element.parentNode) {
while ((element = element.parentNode) !== null) {
if (element === document) {
return true;
}
}
}
return false;
};
/*?
* flagrate.Element.toggle(element) -> Element
* - element (Element) - instance of Element.
*
* Toggles the visibility of `element`. Returns `element`.
*
* This method is similar to http://api.prototypejs.org/dom/Element/toggle/
**/
Element.toggle = function (element) {
return Element[Element.visible(element) ? 'hide' : 'show'](element);
};
/*?
* flagrate.Element.hide(element) -> Element
* - element (Element) - instance of Element.
*
* Sets `display: none` on `element`. Returns `element`.
*
* This method is similar to http://api.prototypejs.org/dom/Element/hide/
**/
Element.hide = function (element) {
element.style.display = 'none';
return element;
};
/*?
* flagrate.Element.show(element) -> Element
* - element (Element) - instance of Element.
*
* Removes `display: none` on `element`. Returns `element`.
*
* This method is similar to http://api.prototypejs.org/dom/Element/show/
**/
Element.show = function (element) {
element.style.display = '';
return element;
};
/*?
* flagrate.Element.remove(element) -> Element
* - element (Element) - instance of Element.
*
* Completely removes `element` from the document and returns it.
*
* This method is similar to http://api.prototypejs.org/dom/Element/remove/
**/
Element.remove = function (element) {
if (element.parentNode) { element.parentNode.removeChild(element); }
return element;
};
/*?
* flagrate.Element.update(element[, newContent]) -> Element
* - element (Element) - instance of Element.
* - newContent (String|Number|Element) - new content.
*
* Replaces _the content_ of `element` with the `newContent` argument and
* returns `element`.
*
* This method is similar to http://api.prototypejs.org/dom/Element/update/
**/
Element.update = function (element, content) {
var i = element.childNodes.length;
while (i--) { flagrate.Element.remove(element.childNodes[i]); }
if (!content) {
return element;
}
if (isElement(content) === true) {
element.appendChild(content);
return element;
}
if (typeof content !== 'string') {
content = content.toString(10);
}
element.innerHTML = content;
return element;
};
/*?
* flagrate.Element.updateText(element[, newContent]) -> Element
* - element (Element) - instance of Element.
* - newContent (String|Number) - new text content.
**/
Element.updateText = function (element, content) {
var i = element.childNodes.length;
while (i--) { flagrate.Element.remove(element.childNodes[i]); }
if (content === void 0) {
return element;
}
if (isElement(content) === true && (content.toString !== void 0)) {
return Element.updateText(element, content.toString());
}
if (typeof content !== 'string') {
content = content.toString(10);
}
element.appendChild(document.createTextNode(content));
return element;
};
/*?
* flagrate.Element.insert(element, content) -> Element
* - element (Element) - instance of Element.
* - content (String|Element|Object) - The content to insert
*
* Inserts content `above`, `below`, at the `top`, and/or at the `bottom` of
* the given element, depending on the option(s) given.
*
* This method is similar to http://api.prototypejs.org/dom/Element/insert/
**/
Element.insert = function (element, insertion) {
if (typeof insertion === 'string' || typeof insertion === 'number' || isElement(insertion) === true) {
insertion = { bottom: insertion };
}
var position, content, insert, div;
for (position in insertion) {
if (insertion.hasOwnProperty(position)) {
content = insertion[position];
position = position.toLowerCase();
insert = Element._insertionTranslation[position];
if (isElement(content) === true) {
insert(element, content);
continue;
}
if (typeof content !== 'string') { content = content.toString(10); }
div = new Element();
div.innerHTML = content;
if (position === 'top' || position === 'after') { div.childNodes.reverse(); }
while (div.childNodes.length !== 0) {
insert(element, div.childNodes[0]);
}
}
}
return element;
};
/*?
* flagrate.Element.insertText(element, content) -> Element
* - element (Element) - instance of Element.
* - content (String|Number|Object) - The content to insert
*
* Inserts content `above`, `below`, at the `top`, and/or at the `bottom` of
* the given element, depending on the option(s) given.
**/
Element.insertText = function (element, insertion) {
if (typeof insertion === 'string' || typeof insertion === 'number') {
insertion = { bottom: insertion };
}
var position, content, insert;
for (position in insertion) {
if (insertion.hasOwnProperty(position)) {
content = insertion[position];
position = position.toLowerCase();
insert = Element._insertionTranslation[position];
if (typeof content !== 'string') { content = content.toString(10); }
insert(element, document.createTextNode(content));
}
}
return element;
};
/*?
* flagrate.Element.insertTo(element, to[, position = "bottom"]) -> Element
* - element (Element) - insert this.
* - to (Element) - insert to this element.
* - position (String) - `before` or `top` or `bottom` or `after`.
**/
Element.insertTo = function (element, to, pos) {
var insertion = {};
if (pos) {
insertion[pos] = element;
} else {
insertion.bottom = element;
}
Element.insert(to, insertion);
return element;
};
/*?
* flagrate.Element.wrap(element, wrapper[, attribute]) -> Element
* - element (Element) -
* - wrapper (Element|String) - An element to wrap `element` inside, or else a string representing the tag name of an element to be created.
* - attribute (Object) - A set of attributes to apply to the wrapper element. Refer to the flagrate.Element constructor for usage.
*
* Wraps an element inside another, then returns the wrapper.
*
* This method is similar to http://api.prototypejs.org/dom/Element/wrap/
**/
Element.wrap = function (element, wrapper, attr) {
if (isElement(wrapper) === true) {
if (attr) { Element.writeAttribute(wrapper, attr); }
} else if (typeof wrapper === 'string') {
wrapper = new Element(wrapper, attr);
} else {
wrapper = new Element('div', wrapper);
}
if (element.parentNode) { element.parentNode.replaceChild(wrapper, element); }
wrapper.appendChild(element);
return wrapper;
};
/*?
* flagrate.Element.readAttribute(element, attributeName) -> String | null
* - element (Element) - instance of Element.
* - attributeName (String) - attribute name.
*
* Returns the value of `element`'s `attribute` or `null` if `attribute` has
* not been specified.
*
* This method is similar to http://api.prototypejs.org/dom/Element/readAttribute/
**/
Element.readAttribute = function (element, name) {
// ref: https://github.com/sstephenson/prototype/blob/1fb9728/src/dom/dom.js#L1856
return element.getAttribute(name);
};
/*?
* flagrate.Element.writeAttribute(element, attribute[, value = true]) -> Element
* - element (Element) - instance of Element.
* - attribute (String|Object) - attribute name or name/value pairs object.
* - value (Boolean|String) - value of attribute.
*
* Adds, specifies or removes attributes passed as either a hash or a name/value pair.
*
* This method is similar to http://api.prototypejs.org/dom/Element/writeAttribute/
**/
Element.writeAttribute = function (element, name, value) {
var attr = {};
if (typeof name === 'object') {
attr = name;
} else {
attr[name] = (value === void 0) ? true : value;
}
var k;
for (k in attr) {
if (attr.hasOwnProperty(k)) {
value = attr[k];
if (value === false || value === null) {
element.removeAttribute(k);
} else if (value === true) {
element.setAttribute(k, k);
} else if (value !== void 0) {
element.setAttribute(k, value);
}
}
}
return element;
};
/*?
* flagrate.Element.getDimensions(element) -> Object
* - element (Element) - instance of Element.
*
* Finds the computed width and height of `element` and returns them as
* key/value pairs of an object.
*
* This method is similar to http://api.prototypejs.org/dom/Element/getDimensions/
**/
Element.getDimensions = function (element) {
var display = Element.getStyle(element, 'display');
if (display && display !== 'none') {
return { width: element.offsetWidth, height: element.offsetHeight };
}
var before = {
visibility: element.style.visibility,
position : element.style.position,
display : element.style.display
};
var after = {
visibility: 'hidden',
display : 'block'
};
// Switching `fixed` to `absolute` causes issues in Safari.
if (before.position !== 'fixed') { after.position = 'absolute'; }
Element.setStyle(element, after);
var dimensions = {
width: element.offsetWidth,
height: element.offsetHeight
};
Element.setStyle(element, before);
return dimensions;
};
/*?
* flagrate.Element.getHeight(element) -> Number
* - element (Element) - instance of Element.
*
* This method is similar to http://api.prototypejs.org/dom/Element/getHeight/
**/
Element.getHeight = function (element) {
return Element.getDimensions(element).height;
};
/*?
* flagrate.Element.getWidth(element) -> Number
* - element (Element) - instance of Element.
*
* This method is similar to http://api.prototypejs.org/dom/Element/getWidth/
**/
Element.getWidth = function (element) {
return Element.getDimensions(element).width;
};
/*?
* flagrate.Element.cumulativeOffset(element) -> Object
* - element (Element) - instance of Element.
*
* This method is similar to http://api.prototypejs.org/dom/Element/cumulativeOffset/
**/
Element.cumulativeOffset = function (element) {
var t = 0, l = 0;
if (element.parentNode) {
do {
t += element.offsetTop || 0;
l += element.offsetLeft || 0;
element = element.offsetParent;
} while (element);
}
var offset = {
top : t,
left: l
};
return offset;
};
/*?
* flagrate.Element.cumulativeScrollOffset(element) -> Object
* - element (Element) - instance of Element.
*
* This method is similar to http://api.prototypejs.org/dom/Element/cumulativeScrollOffset/
**/
Element.cumulativeScrollOffset = function (element) {
var t = 0, l = 0;
do {
t += element.scrollTop || 0;
l += element.scrollLeft || 0;
// for Chrome
if (element.parentNode === document.body && document.documentElement.scrollTop !== 0) {
element = document.documentElement;
} else {
element = element.parentNode;
}
} while (element);
var offset = {
top : t,
left: l
};
return offset;
};
/*?
* flagrate.Element.hasClassName(element, className) -> Boolean
* - element (Element) - instance of Element.
* - className (String) -
*
* This method is similar to http://api.prototypejs.org/dom/Element/hasClassName/
**/
Element.hasClassName = function (element, className) {
return (element.className.length > 0 && (element.className === className || new RegExp('(^|\\s)' + className + '(\\s|$)').test(element.className)));
};
/*?
* flagrate.Element.addClassName(element, className) -> Boolean
* - element (Element) - instance of Element.
* - className (String) - The class name to add.
*
* This method is similar to http://api.prototypejs.org/dom/Element/addClassName/
**/
Element.addClassName = function (element, className) {
if (!Element.hasClassName(element, className)) {
element.className += (element.className ? ' ' : '') + className;
}
return element;
};
/*?
* flagrate.Element.removeClassName(element, className) -> Boolean
* - element (Element) - instance of Element.
* - className (String) -
*
* This method is similar to http://api.prototypejs.org/dom/Element/removeClassName/
**/
Element.removeClassName = function (element, className) {
element.className = element.className.replace(
new RegExp('(^|\\s+)' + className + '(\\s+|$)'), ' '
).trim();
return element;
};
/*?
* flagrate.Element.toggleClassName(element, className) -> Boolean
* - element (Element) - instance of Element.
* - className (String) -
*
* This method is similar to http://api.prototypejs.org/dom/Element/toggleClassName/
**/
Element.toggleClassName = function (element, className) {
return Element[Element.hasClassName(element, className) ? 'removeClassName' : 'addClassName'](element, className);
};
/*?
* flagrate.Element.getStyle(element, propertyName) -> String | Number | null
* - element (Element) - instance of Element.
* - propertyName (String) - The property name of style to be retrieved.
*
* This method is similar to http://api.prototypejs.org/dom/Element/getStyle/
**/
Element.getStyle = function (element, style) {
style = style === 'float' ? 'cssFloat' : style.replace(/-+([a-z])?/g, function (m, s) { return s ? s.toUpperCase() : ''; });
var value = element.style[style];
if (!value || value === 'auto') {
var css = document.defaultView.getComputedStyle(element, null);
value = css && (css[style] !== void 0) && css[style] !== "" ? css[style] : null;
}
if (style === 'opacity') { return value ? parseFloat(value) : 1.0; }
return value === 'auto' ? null : value;
};
/*?
* flagrate.Element.setStyle(element, style) -> Element
* - element (Element) - instance of Element.
* - style (Object) -
*
* This method is similar to http://api.prototypejs.org/dom/Element/setStyle/
**/
Element.setStyle = function (element, style) {
var p;
for (p in style) {
if (style.hasOwnProperty(p)) {
element.style[(p === 'float' || p === 'cssFloat') ? ((element.style.styleFloat === void 0) ? 'cssFloat' : 'styleFloat') : p] = style[p];
}
}
return element;
};
/*?
* flagrate.Element.on(element, eventName, listener[, useCapture = false]) -> Element
* - element (Element) - instance of Element.
* - eventName (String) - name of event.
* - listener (Function) - The function to call when the event occurs.
* - useCapture (Boolean) -
*
* Registers an event handler on a DOM element.
**/
Element.on = function (element, name, listener, useCapture) {
element.addEventListener(name, listener, useCapture || false);
return element;
};
/*?
* flagrate.Element.off(element, eventName, listener[, useCapture = false]) -> Element
* - element (Element) - instance of Element.
* - eventName (String) - name of event.
* - listener (Function) - The function to call when the event occurs.
* - useCapture (Boolean) -
*
* Registers an event handler on a DOM element.
**/
Element.off = function (element, name, listener, useCapture) {
element.removeEventListener(name, listener, useCapture || false);
return element;
};
/*?
* flagrate.Element.fire(element, eventName[, property]) -> Element
* - element (Element) - instance of Element.
* - eventName (String) - name of event.
* - property (Object) -
*
* Fires a custom event.
**/
Element.fire = function (element, name, property) {
var event = document.createEvent('HTMLEvents');
event.initEvent(name, true, true);
if (property) { extendObject(event, property); }
element.dispatchEvent(event);
return element;
};
/*?
* flagrate.Element.emit(element, eventName[, property]) -> Element
* Alias of: flagrate.Element.fire
**/
Element.emit = Element.fire;
/*?
* flagrate.Element.extend(element) -> flagrate.Element
* - element (Element) - instance of Element.
*
* Extends the given `element` instance.
*
* **Caution**: This method will add flagrate.Element instance methods to given element instance.
**/
Element.extend = function (element) {
if (element.isFlagrated) { return element; }
extendObject(element, Element.prototype);
return element;
};
// from https://github.com/sstephenson/prototype/blob/1fb9728/src/dom/dom.js#L3021-L3041
Element._insertionTranslation = {
before: function (element, node) {
element.parentNode.insertBefore(node, element);
},
top: function (element, node) {
element.insertBefore(node, element.firstChild);
},
bottom: function (element, node) {
element.appendChild(node);
},
after: function (element, node) {
element.parentNode.insertBefore(node, element.nextSibling);
}
};
/*?
* class flagrate.Button
*
* #### Example
*
* var button = flagrate.createButton({
* label : 'foo',
* icon : 'icon.png',
* onSelect: function () {
* alert('hey');
* }
* }).insertTo(x);
*
* #### Structure
*
* <button class="flagrate flagrate-button flagrate-icon" style="background-image: url(icon.png);">foo</button>
*
* #### Event
*
* * `select`:
*
* #### Inheritances
*
* * flagrate.Element
* * [HTMLButtonElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement) (MDN)
**/
/*?
* flagrate.createButton(option)
* new flagrate.Button(option)
* - option (Object) - options.
*
* Button.
*
* #### option
*
* * `id` (String): `id` attribute of `button` element.
* * `className` (String):
* * `attribute` (Object):
* * `style` (Object): (using flagrate.Element.setStyle)
* * `color` (String): (using flagrate.Button#setColor)
* * `label` (String; default `""`):
* * `icon` (String):
* * `isFocused` (Boolean; default `false`):
* * `isDisabled` (Boolean; default `false`):
* * `isRemovableByUser` (Boolean; default `false`):
* * `onSelect` (Function):
* * `onRemove` (Function):
**/
var Button = flagrate.Button = function (opt) {
opt = opt || {};
opt.label = opt.label || '';
opt.isRemovableByUser = opt.isRemovableByUser || false;
this.onSelect = opt.onSelect || emptyFunction;
this.onRemove = opt.onRemove || emptyFunction;
var attr = opt.attribute || {};
if (opt.id) { attr.id = opt.id; }
if (opt.isFocused) { attr.autofocus = true; }
if (!attr.type) { attr.type = 'button'; }
//create
var that = new Element('button', attr);
extendObject(that, this);
that._label = new Element('span').updateText(opt.label).insertTo(that);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-button');
if (opt.className) { that.addClassName(opt.className); }
that.on('click', that._onSelectHandler.bind(that), true);
if (opt.isRemovableByUser) {
that.addClassName(flagrate.className + '-button-removable');
that._removeButton = new Element('button', {
type : 'button',
'class': flagrate.className + '-button-remove'
}).insertTo(that);
that._removeButton.on('click', that._onRemoveHandler.bind(that), true);
}
if (opt.style) { that.setStyle(opt.style); }
if (opt.color) { that.setColor(opt.color); }
if (opt.icon) { that.setIcon(opt.icon); }
if (opt.isDisabled) { that.disable(); }
return that;
};
flagrate.createButton = function (a) {
return new Button(a);
};
Button.prototype = {
/*?
* flagrate.Button#select() -> flagrate.Button
**/
select: function () {
return this._onSelectHandler(null);
}
,
/*?
* flagrate.Button#disable() -> flagrate.Button
**/
disable: function () {
this.addClassName(flagrate.className + '-disabled');
this.writeAttribute('disabled', true);
return this;
}
,
/*?
* flagrate.Button#enable() -> flagrate.Button
**/
enable: function () {
this.removeClassName(flagrate.className + '-disabled');
this.writeAttribute('disabled', false);
return this;
}
,
/*?
* flagrate.Button#isEnabled() -> Boolean
**/
isEnabled: function () {
return !this.hasClassName(flagrate.className + '-disabled');
}
,
/*?
* flagrate.Button#setLabel(text) -> flagrate.Button
* - text (String) - label string.
**/
setLabel: function (text) {
this._label.updateText(text);
return this;
}
,
/*?
* flagrate.Button#setColor(color) -> flagrate.Button
* - color (String) - please see below.
**/
setColor: function (color) {
if (color.charAt(0) === '@') {
this.style.backgroundColor = '';
this.addClassName(flagrate.className + '-button-color-' + color.slice(1));
} else {
this.style.backgroundColor = color;
}
return this;
}
,
/*?
* flagrate.Button#setIcon([url]) -> flagrate.Button
* - url (String) - URL of icon image.
**/
setIcon: function (url) {
if (url) {
return this.addClassName(flagrate.className + '-icon').setStyle({
backgroundImage: 'url(' + url + ')'
});
} else {
return this.removeClassName(flagrate.className + '-icon').setStyle({
backgroundImage: 'none'
});
}
}
,
_onSelectHandler: function (e) {
if (this.isEnabled() === false) { return; }
//for Firefox
if (this._removeButton && e && e.layerX) {
var bw = this.getWidth();
var bh = this.getHeight();
var bp = this._removeButton.getStyle('margin-right') === null ? 0 : parseInt(this._removeButton.getStyle('margin-right').replace('px', ''), 10);
var rw = this._removeButton.getWidth();
var rh = this._removeButton.getHeight();
var lx = e.layerX;
var ly = e.layerY;
if (
lx > bw - bp - rw &&
lx < bw - bp &&
ly > bh - ((bh - rh) / 2) - rh &&
ly < bh - ((bh - rh) / 2)
) {
this._onRemoveHandler(e);
return this;
}
}
e.targetButton = this;
this.onSelect(e, this);
this.fire('select', { targetButton: this });
}
,
_onRemoveHandler: function (e) {
if (this.isEnabled() && this.remove()) { this.onRemove(e); }
}
};
/*?
* class flagrate.Buttons
*
* #### Example
*
* var button = flagrate.createButtons({
* items: [
* { label: 'Left' },
* { label: 'Middle' },
* { label: 'Right' }
* ]
* }).insertTo(x);
*
* #### Structure
*
* <div class="example-container">
* <div class="flagrate flagrate-buttons">
* <button class="flagrate flagrate-button">Left</button><button class="flagrate flagrate-button">Middle</button><button class="flagrate flagrate-button">Right</button>
* </div>
* </div>
*
* <div class="flagrate flagrate-buttons">
* <button class="flagrate flagrate-button">Left</button>
* <button class="flagrate flagrate-button">Middle</button>
* <button class="flagrate flagrate-button">Right</button>
* </div>
*
* #### Inheritances
*
* * flagrate.Element
* * [HTMLDivElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) (MDN)
**/
/*?
* flagrate.createButtons(option)
* new flagrate.Buttons(option)
* - option (Object) - options.
*
* Button group.
*
* #### option
*
* * `id` (String): `id` attribute of container element.
* * `className` (String):
* * `attribute` (Object):
* * `items` (Array): of item
* * `onSelect` (Function):
*
* #### item
*
* * `key` (String):
* * `label` (String; default `""`):
* * `icon` (String):
* * `color` (String):
* * `isDisabled` (Boolean; default `false`):
* * `onSelect` (Function):
**/
var Buttons = flagrate.Buttons = function (opt) {
opt = opt || {};
opt.items = opt.items || [];
this.onSelect = opt.onSelect || emptyFunction;
var attr = opt.attribute || {};
attr.id = opt.id;
attr['class'] = opt.className;
//create
var that = new Element('div', attr);
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-buttons');
var i, l;
for (i = 0, l = opt.items.length; i < l; i++) {
that.push(opt.items[i]);
}
that.on('click', function (e) {
e.stopPropagation();
e.preventDefault();
});
return that;
};
flagrate.createButtons = function (a) {
return new Buttons(a);
};
Buttons.prototype = {
/*?
* flagrate.Buttons#push(item) -> flagrate.Buttons
**/
push: function (a) {
var button = new Button(
{
icon : a.icon,
label : a.label,
isDisabled: a.isDisabled,
color : a.color,
onSelect : function (e) {
if (a.onSelect) { a.onSelect(e); }
this.onSelect(e);
}.bind(this)
}
).insertTo(this);
if (a.key) { button._key = a.key; }
return this;
}
,
/*?
* flagrate.Buttons#getButtonByKey(key) -> flagrate.Button | null
**/
getButtonByKey: function (key) {
var result = null;
var elements = this.childNodes;
var i, l;
for (i = 0, l = elements.length; i < l; i++) {
if (!elements[i]._key) { continue; }
if (elements[i]._key === key) {
result = elements[i];
break;
}
}
return result;
}
,
/*?
* flagrate.Buttons#getButtons() -> Array
**/
getButtons: function () {
return this.childNodes || [];
}
};
/*?
* class flagrate.Menu
*
* #### Example
*
* var menu = flagrate.createMenu({
* items: [
* {
* label: 'foo'
* },
* {
* label: 'bar',
* icon : 'icon.png'
* },
* '--',
* {
* label: 'disabled button',
* isDisabled: true
* }
* ]
* }).insertTo(x);
*
* #### Structure
*
* <div class="example-container">
* <div class="flagrate flagrate-menu">
* <button class="flagrate flagrate-button">foo</button>
* <button class="flagrate flagrate-button flagrate-icon" style="background-image: url(icon.png);">bar</button>
* <hr>
* <button class="flagrate flagrate-button flagrate-disabled" disabled="disabled">disabled button</button>
* </div>
* </div>
*
* <div class="flagrate flagrate-menu">
* <button class="flagrate flagrate-button">foo</button>
* <button class="flagrate flagrate-button flagrate-icon" style="background-image: url(icon.png);">bar</button>
* <hr>
* <button class="flagrate flagrate-button flagrate-disabled" disabled="disabled">disabled button</button>
* </div>
*
* `button` elements are created with flagrate.Button
*
* #### Inheritances
*
* * flagrate.Element
* * [HTMLDivElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) (MDN)
**/
/*?
* flagrate.createMenu(option)
* new flagrate.Menu(option)
* - option (Object) - options.
*
* Menu.
*
* #### option
*
* * `id` (String): `id` attribute of container element.
* * `className` (String):
* * `attribute` (Object):
* * `items` (Array): of item
* * `onSelect` (Function):
*
* #### item
*
* * `key` (String):
* * `label` (String; default `""`):
* * `icon` (String):
* * `isDisabled` (Boolean; default `false`):
* * `onSelect` (Function):
**/
var Menu = flagrate.Menu = function (opt) {
opt = opt || {};
opt.items = opt.items || [];
this.onSelect = opt.onSelect || emptyFunction;
var attr = opt.attribute || {};
if (opt.id) { attr.id = opt.id; }
//create
var that = new Element('div', attr);
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-menu');
if (opt.className) { that.addClassName(opt.className); }
var i, l;
for (i = 0, l = opt.items.length; i < l; i++) {
that.push(opt.items[i]);
}
that.on('click', function (e) {
e.stopPropagation();
e.preventDefault();
});
that.on('mouseup', function (e) {
e.stopPropagation();
});
return that;
};
flagrate.createMenu = function (a) {
return new Menu(a);
};
Menu.prototype = {
/*?
* flagrate.Menu#push(item) -> flagrate.Menu
**/
push: function (a) {
/*if (a instanceof Array) {
//todo
} else */if (typeof a === 'string') {
new Element('hr').insertTo(this);
} else {
var button = new Button(
{
icon : a.icon,
label : a.label,
isDisabled: a.isDisabled,
onSelect : function (e) {
if (a.onSelect) { a.onSelect(e); }
this.onSelect(e);
}.bind(this)
}
).insertTo(this);
if (a.key) { button._key = a.key; }
}
return this;
}
,
/*?
* flagrate.Menu#getButtonByKey(key) -> flagrate.Button | null
**/
getButtonByKey: Buttons.prototype.getButtonByKey
,
/*?
* flagrate.Menu#getButtons() -> Array
**/
getButtons: Buttons.prototype.getButtons
};
/*?
* class flagrate.Pulldown
*
* #### Example
*
* var menu = flagrate.createPulldown({
* label: 'foo',
* items: [
* {
* label: 'bar'
* }
* ]
* }).insertTo(x);
*
* #### Structure
*
* <button class="flagrate flagrate-button flagrate-pulldown">
* "foo"
* <span class="flagrate-pulldown-triangle"></span>
* </button>
* <div class="flagrate-pulldown-menu flagrate flagrate-menu">
* <button class="flagrate flagrate-button">bar</button>
* </div>
*
* menu `div` are created with flagrate.Menu
*
* #### Inheritances
*
* * flagrate.Button
* * [HTMLButtonElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement) (MDN)
**/
/*?
* flagrate.createPulldown(option)
* new flagrate.Pulldown(option)
* - option (Object) - options.
*
* Pulldown.
*
* #### option
*
* * `id` (String): `id` attribute of `button` element.
* * `className` (String):
* * `attribute` (Object):
* * `style` (Object): (using flagrate.Element.setStyle)
* * `items` (Array): of item (see: flagrate.Menu)
* * `isDisabled` (Boolean; default `false`):
* * `onSelect` (Function):
**/
var Pulldown = flagrate.Pulldown = function (opt) {
opt = opt || {};
opt.label = opt.label || '';
opt.items = opt.items || null;
opt.onSelect = opt.onSelect || emptyFunction;
var attr = opt.attribute || {};
if (opt.id) { attr.id = opt.id; }
//create
var that = new Button({
attribute: attr,
label : opt.label,
icon : opt.icon
});
extendObject(that, this);
that.addClassName(flagrate.className + '-pulldown');
if (opt.className) { that.addClassName(opt.className); }
that.onSelect = function (e) {
if (that._menu) {
that._menu.remove();
delete that._menu;
return;
}
var menu = that._menu = new Element('div', {'class': flagrate.className + '-pulldown-menu'}).insert(
new Menu(
{
items : opt.items,
onSelect : function () {
opt.onSelect();
menu.remove();
delete that._menu;
}
}
)
);
menu.style.top = that.offsetTop + that.getHeight() + 'px';
menu.style.left = that.offsetLeft + 'px';
that.insert({ after: menu });
// To prevent overflow.
var menuHeight = menu.getHeight();
var menuMargin = parseInt(menu.getStyle('margin-top').replace('px', ''), 10);
var cummOffsetTop = that.cumulativeOffset().top;
var upsideSpace = - window.pageYOffset + cummOffsetTop;
var downsideSpace = window.pageYOffset + window.innerHeight - cummOffsetTop - that.getHeight();
if (menuHeight + menuMargin > downsideSpace) {
if (upsideSpace > downsideSpace) {
if (upsideSpace < menuHeight + menuMargin) {
menuHeight = (upsideSpace - menuMargin - menuMargin);
menu.style.maxHeight = menuHeight + 'px';
}
menu.style.top = (that.offsetTop - menuHeight - (menuMargin * 2)) + 'px';
} else {
menuHeight = (downsideSpace - menuMargin - menuMargin);
menu.style.maxHeight = menuHeight + 'px';
}
}
var removeMenu = function (e) {
document.body.removeEventListener('click', removeMenu);
if (that.parentNode) {
that.parentNode.removeEventListener('click', removeMenu);
}
that.off('click', removeMenu);
menu.style.opacity = '0';
setTimeout(function (){ menu.remove(); }.bind(this), 500);
delete that._menu;
};
setTimeout(function () {
document.body.addEventListener('click', removeMenu);
that.parentNode.addEventListener('click', removeMenu);
that.on('click', removeMenu);
}, 0);
};
new Element('span', { 'class': flagrate.className + '-pulldown-triangle' }).insertTo(that);
if (opt.style) { that.setStyle(opt.style); }
if (opt.color) { that.setColor(opt.color); }
if (opt.isDisabled) { that.disable(); }
return that;
};
flagrate.createPulldown = function (a) {
return new Pulldown(a);
};
/*?
* class flagrate.ContextMenu
**/
/*?
* flagrate.createContextMenu(option)
* new flagrate.ContextMenu(option)
* - option (Object) - options.
*
* ContextMenu.
*
* #### option
*
* * `target` (Element):
* * `items` (Array): of item (see: flagrate.Menu)
**/
var ContextMenu = flagrate.ContextMenu = function (opt) {
opt = opt || {};
this.target = opt.target || null;
this.items = opt.items || null;
this.isShowing = false;
/*?
* flagrate.ContextMenu#open() -> flagrate.ContextMenu
**/
this.open = function (e) {
e = e || window.event || {};
if (e.preventDefault) {
e.preventDefault();
}
if (this.isShowing) { this.close(); }
this.isShowing = true;
this._menu = new Menu({
className: flagrate.className + '-context-menu',
items : this.items,
onSelect : this.close.bind(this)
});
var x = e.clientX || 0;
var y = e.clientY || 0;
this._menu.style.opacity = 0;
this._menu.insertTo(document.body);
if (x + this._menu.getWidth() > window.innerWidth) {
x = x - this._menu.getWidth();
}
if (y + this._menu.getHeight() > window.innerHeight) {
y = y - this._menu.getHeight();
}
this._menu.style.top = y + 'px';
this._menu.style.left = x + 'px';
this._menu.style.opacity = 1;
document.body.addEventListener('click', this.close);
document.body.addEventListener('mouseup', this.close);
document.body.addEventListener('mousewheel', this.close);
return this;
}.bind(this);
/*?
* flagrate.ContextMenu#close() -> flagrate.ContextMenu
**/
this.close = function () {
document.body.removeEventListener('click', this.close);
document.body.removeEventListener('mouseup', this.close);
document.body.removeEventListener('mousewheel', this.close);
this.isShowing = false;
var menu = this._menu;
setTimeout(function () {
if (menu && menu.remove) { menu.remove(); }
}, 0);
delete this._menu;
return this;
}.bind(this);
if (this.target !== null) {
this.target.addEventListener('contextmenu', this.open);
}
return this;
};
flagrate.createContextMenu = function (a) {
return new ContextMenu(a);
};
ContextMenu.prototype = {
/*?
* flagrate.ContextMenu#visible() -> Boolean
**/
visible: function () {
return this.isShowing;
}
,
/*?
* flagrate.ContextMenu#remove() -> flagrate.ContextMenu
**/
remove: function () {
if (this._menu) { this.close(); }
if (this.target !== null) {
this.target.removeEventListener('contextmenu', this.open);
}
}
};
/*?
* class flagrate.Toolbar
**/
/*?
* flagrate.createToolbar(option)
* new flagrate.Toolbar(option)
* - option (Object) - options.
*
* Toolbar.
*
* #### option
*
* * `id` (String): `id` attribute of container element.
* * `className` (String):
* * `attribute` (Object):
* * `style` (Object): (using flagrate.Element.setStyle)
* * `items` (Array): of item or String to create border, Element to insert any element.
*
* #### item
*
* * `key` (String):
* * `element` (Element):
**/
var Toolbar = flagrate.Toolbar = function (opt) {
opt = opt || {};
opt.items = opt.items || [];
var attr = opt.attribute || {};
attr.id = opt.id;
attr['class'] = opt.className;
//create
var that = new Element('div', attr);
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-toolbar');
var i, l;
for (i = 0, l = opt.items.length; i < l; i++) {
that.push(opt.items[i]);
}
if (opt.style) { that.setStyle(opt.style); }
return that;
};
flagrate.createToolbar = function (a) {
return new Toolbar(a);
};
Toolbar.prototype = {
/*?
* flagrate.Toolbar#push(item) -> flagrate.Toolbar
**/
push: function (a) {
if (typeof a === 'string') {
new Element('hr').insertTo(this);
} else if (a instanceof HTMLElement) {
this.insert(a);
} else {
if (a.element) {
if (a.key) { a.element._key = a.key; }
this.insert(a.element);
}
}
return this;
}
,
/*?
* flagrate.Toolbar#getElementByKey(key) -> Element | null
**/
getElementByKey: function (key) {
var result = null;
var elements = this.childNodes;
var i, l;
for (i = 0, l = elements.length; i < l; i++) {
if (!elements[i]._key) { continue; }
if (elements[i]._key === key) {
result = elements[i];
break;
}
}
return result;
}
};
/*?
* class flagrate.TextInput
*
* #### Inheritance
*
* * [HTMLInputElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement) (MDN)
**/
/*?
* flagrate.createTextInput(option)
* new flagrate.TextInput(option)
* - option (Object) - options.
*
* TextInput.
*
* #### option
*
* * `id` (String): `id` attribute of `input` element.
* * `className` (String):
* * `attribute` (Object):
* * `style` (Object): (using flagrate.Element.setStyle)
* * `value` (String):
* * `placeholder` (String):
* * `icon` (String):
* * `regexp` (RegExp):
* * `isDisabled` (Boolean; default `false`):
**/
var TextInput = flagrate.TextInput = function (opt) {
opt = opt || {};
this.regexp = opt.regexp || null;
var attr = opt.attribute || {};
attr.id = opt.id || null;
attr['class'] = opt.className || null;
attr.value = opt.value || null;
attr.placeholder = opt.placeholder || null;
//create
var that = new Element('input', attr);
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-textinput');
if (opt.icon) {
that.addClassName(flagrate.className + '-icon');
that.setStyle({
backgroundImage: 'url(' + opt.icon + ')'
});
}
if (opt.style) { that.setStyle(opt.style); }
if (opt.isDisabled) { that.disable(); }
return that;
};
flagrate.createTextInput = function (a) {
return new TextInput(a);
};
TextInput.prototype = {
/*?
* flagrate.TextInput#disable() -> flagrate.TextInput
**/
disable: function () {
this.addClassName(flagrate.className + '-disabled');
this.writeAttribute('disabled', true);
return this;
}
,
/*?
* flagrate.TextInput#enable() -> flagrate.TextInput
**/
enable: function () {
this.removeClassName(flagrate.className + '-disabled');
this.writeAttribute('disabled', false);
return this;
}
,
/*?
* flagrate.TextInput#isEnabled() -> Boolean
**/
isEnabled: function () {
return !this.hasClassName(flagrate.className + '-disabled');
}
,
/*?
* flagrate.TextInput#getValue() -> String
**/
getValue: function () {
return this.value;
}
,
/*?
* flagrate.TextInput#setValue(value) -> flagrate.TextInput
**/
setValue: function (value) {
this.value = value;
return this;
}
,
/*?
* flagrate.TextInput#isValid() -> Boolean
**/
isValid: function () {
return this.regexp.test(this.getValue());
}
};
/*?
* class flagrate.Tokenizer
*
* #### Event
*
* * `change`: when the tokens/values is changed.
**/
/*?
* flagrate.createTokenizer(option)
* new flagrate.Tokenizer(option)
* - option (Object) - options.
*
* Tokenizer.
*
* #### option
*
* * `id` (String): `id` attribute of container element.
* * `className` (String):
* * `attribute` (Object):
* * `style` (Object): (using flagrate.Element.setStyle)
* * `placeholder` (String):
* * `icon` (String):
* * `values` (Array; default `[]`):
* * `max` (Number; default `-1`):
* * `tokenize` (Function; default `flagrate.identity`):
* * `isDisabled` (Boolean; default `false`):
* * `onChange` (Function):
**/
var Tokenizer = flagrate.Tokenizer = function (opt) {
opt = opt || {};
opt.placeholder = opt.placeholder || null;
this.values = opt.values || [];
this.max = opt.max || -1;
this.tokenize = opt.tokenize || identity;
this.onChange = opt.onChange || emptyFunction;
var attr = opt.attribute || {};
attr.id = opt.id || null;
attr['class'] = opt.className || null;
//create
var that = new Element('div', attr);
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-tokenizer');
if (opt.icon) {
that.addClassName(flagrate.className + '-icon');
that.setStyle({
backgroundImage: 'url(' + opt.icon + ')'
});
}
that._tokens = new Element('span').insertTo(that);
that._input = new TextInput({ placeholder: opt.placeholder }).insertTo(that);
if (that.values.length !== 0) {
that._updateTokens();
}
that.on('click', that._onClickHandler.bind(that));
that._input.on('keydown', that._onKeydownHandler.bind(that));
that._input.on('focus', that._onFocusHandler.bind(that));
that._input.on('blur', that._onBlurHandler.bind(that));
if (opt.style) { that.setStyle(opt.style); }
if (opt.isDisabled) { that.disable(); }
return that;
};
flagrate.createTokenizer = function (a) {
return new Tokenizer(a);
};
Tokenizer.prototype = {
/*?
* flagrate.Tokenizer#disable() -> flagrate.Tokenizer
**/
disable: function () {
this.addClassName(flagrate.className + '-disabled');
this._input.disable();
return this._updateTokens();
}
,
/*?
* flagrate.Tokenizer#enable() -> flagrate.Tokenizer
**/
enable: function () {
this.removeClassName(flagrate.className + '-disabled');
this._input.enable();
return this._updateTokens();
}
,
/*?
* flagrate.Tokenizer#isEnabled() -> Boolean
**/
isEnabled: function () {
return !this.hasClassName(flagrate.className + '-disabled');
}
,
/*?
* flagrate.Tokenizer#getValues() -> Array
**/
getValues: function () {
return this.values;
}
,
/*?
* flagrate.Tokenizer#setValues(values) -> flagrate.Tokenizer
**/
setValues: function (values) {
this.values = values;
return this._updateTokens();
}
,
/*?
* flagrate.Tokenizer#removeValues() -> flagrate.Tokenizer
**/
removeValues: function () {
this.values = [];
return this._updateTokens();
}
,
/*?
* flagrate.Tokenizer#removeValue(value) -> flagrate.Tokenizer
**/
removeValue: function (value) {
this.values.splice(this.values.indexOf(value), 1);
return this._updateTokens();
}
,
_updateTokens: function () {
this._tokens.update();
var i, l;
for (i = 0, l = this.values.length; i < l; i++) {
var value = this.values[i];
var label = '';
if (typeof value === 'string') {
label = value;
} else {
label = value.label;
}
new Button(
{
isDisabled : (this.isEnabled() === false),
isRemovableByUser: (this.isEnabled()),
onRemove : this._createTokenButtonOnRemoveHandler(this, value),
label : label
}
).insertTo(this._tokens);
}
var vw = this.getWidth();
var bw = this.getStyle('border-width') === null ? 2 : parseInt(this.getStyle('border-width').replace('px', ''), 10);
var pl = this.getStyle('padding-left') === null ? 4 : parseInt(this.getStyle('padding-left').replace('px', ''), 10);
var pr = this.getStyle('padding-right') === null ? 4 : parseInt(this.getStyle('padding-right').replace('px', ''), 10);
var tw = this._tokens.getWidth();
var tm = this._tokens.getStyle('margin-left') === null ? 2 : parseInt(this._tokens.getStyle('margin-left').replace('px', ''), 10);
var im = this._input.getStyle('margin-left') === null ? 2 : parseInt(this._input.getStyle('margin-left').replace('px', ''), 10);
var ip = this._input.getStyle('padding-left') === null ? 2 : parseInt(this._input.getStyle('padding-left').replace('px', ''), 10);
var aw = vw - pl - pr - tw - tm - im - ip - (bw * 2) - 2;
if (aw > 30) {
this._input.style.width = aw + 'px';
} else if (aw < -5) {
this._input.style.width = '';
} else {
this._input.style.width = '100%';
}
this.fire('change');
return this;
}
,
_createTokenButtonOnRemoveHandler: function (that, value) {
return function () { that.removeValue(value); };
}
,
_tokenize: function (e) {
this._candidates = [];
var str = this._input.value;
var result = this.tokenize(str, this._tokenized.bind(this));
if (result !== void 0) { this._tokenized(result); }
this._lastTokenizedValue = this._input.value;
return this;
}
,
_tokenized: function (candidates) {
if (candidates instanceof Array === false) { candidates = [candidates]; }
this._candidates = [];
var menu = new Menu(
{
onSelect: function () {
menu.remove();
}
}
);
menu.style.left = this._input.offsetLeft + 'px';
var i, l;
for (i = 0, l = candidates.length; i < l; i++) {
var candidate = candidates[i];
var a;
if (typeof candidate === 'string') {
if (candidate === '') { continue; }
a = { label: candidate };
} else {
a = candidate;
}
if (a.onSelect) { a._onSelect = a.onSelect; }
a.onSelect = this._createMenuOnSelectHandler(this, candidate, a);
this._candidates.push(candidate);
menu.push(a);
}
if (this._menu) { this._menu.remove(); }
if (this._candidates.length !== 0) {
this.insert({ top: menu });
this._menu = menu;
}
return this;
}
,
_createMenuOnSelectHandler: function (that, candidate, menuItem) {
return function (e) {
if (that.max < 0 || that.max > that.values.length) {
that.values.push(candidate);
}
that._updateTokens();
that.onChange();
if (menuItem._onSelect) { menuItem._onSelect(e); }
};
}
,
_onClickHandler: function (e) {
this._input.focus();
}
,
_onKeydownHandler: function (e) {
// ENTER:13
if (e.keyCode === 13 && this._lastTokenizedValue !== this._input.value) {
e.stopPropagation();
e.preventDefault();
this._lastTokenizedValue = this._input.value;
this._tokenize();
return;
}
if (this._candidates && this._candidates.length !== 0) {
if (
// ENTER:13
(e.keyCode === 13) ||
// right:39
(e.keyCode === 39)
) {
e.stopPropagation();
e.preventDefault();
this._input.value = '';
if (this.max < 0 || this.max > this.values.length) {
this.values.push(this._candidates[0]);
}
this._updateTokens();
this.onChange();
if (this._menu) { this._menu.remove(); }
}
}
//if (!this._candidates || this._candidates.length === 0) {
//
//}
if (this._input.value === '' && this.values.length !== 0) {
if (
// BS:8
(e.keyCode === 8)
) {
e.stopPropagation();
e.preventDefault();
this._input.value = this.values.pop();
this._updateTokens();
this.onChange();
if (this._menu) { this._menu.remove(); }
}
}
setTimeout(function () {
if (this.max > -1 && this.max <= this.values.length && this._input.value !== '') {
e.stopPropagation();
this._input.value = '';
return;
}
this._tokenize();
}.bind(this), 0);
}
,
_onFocusHandler: function (e) {
this._updateTokens();
this._tokenize();
}
,
_onBlurHandler: function (e) {
this._input.value = '';
if (this._menu) {
this._menu.style.opacity = '0';
setTimeout(function (){ this._menu.remove(); }.bind(this), 500);
}
}
};
/*?
* class flagrate.TextArea
**/
/*?
* flagrate.createTextArea(option)
* new flagrate.TextArea(option)
* - option (Object) - options.
*
* TextArea.
*
* #### option
*
* * `id` (String): `id` attribute of `textarea` element.
* * `className` (String):
* * `attribute` (Object):
* * `style` (Object): (using flagrate.Element.setStyle)
* * `value` (String):
* * `placeholder` (String):
* * `icon` (String):
* * `regexp` (RegExp):
* * `isDisabled` (Boolean; default `false`):
**/
var TextArea = flagrate.TextArea = function (opt) {
opt = opt || {};
this.regexp = opt.regexp || null;
var attr = opt.attribute || {};
attr.id = opt.id || null;
attr['class'] = opt.className || null;
attr.placeholder = opt.placeholder || null;
//create
var that = new Element('textarea', attr);
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-textarea');
if (opt.icon) {
that.addClassName(flagrate.className + '-icon');
that.setStyle({
backgroundImage: 'url(' + opt.icon + ')'
});
}
if (opt.value) { that.setValue(opt.value); }
if (opt.style) { that.setStyle(opt.style); }
if (opt.isDisabled) { that.disable(); }
return that;
};
flagrate.createTextArea = function (a) {
return new TextArea(a);
};
TextArea.prototype = {
/*?
* flagrate.TextArea#disable() -> flagrate.TextArea
**/
disable: function () {
this.addClassName(flagrate.className + '-disabled');
this.writeAttribute('disabled', true);
return this;
}
,
/*?
* flagrate.TextArea#enable() -> flagrate.TextArea
**/
enable: function () {
this.removeClassName(flagrate.className + '-disabled');
this.writeAttribute('disabled', false);
return this;
}
,
/*?
* flagrate.TextArea#isEnabled() -> Boolean
**/
isEnabled: function () {
return !this.hasClassName(flagrate.className + '-disabled');
}
,
/*?
* flagrate.TextArea#getValue() -> String
**/
getValue: function () {
return this.value || '';
}
,
/*?
* flagrate.TextArea#setValue(value) -> flagrate.TextArea
**/
setValue: function (value) {
this.value = value;
return this;
}
,
/*?
* flagrate.TextArea#isValid() -> Boolean
**/
isValid: function () {
return this.regexp.test(this.getValue());
}
};
/*?
* class flagrate.Select
*
* #### Event
*
* * `change`:
**/
/*?
* flagrate.createSelect(option)
* new flagrate.Select(option)
* - option (Object) - options.
*
* Select.
*
* #### option
*
* * `id` (String): `id` attribute of container element.
* * `className` (String):
* * `attribute` (Object):
* * `style` (Object): (using flagrate.Element.setStyle)
* * `items` (Array):
* * `listView` (Boolean; default `false`):
* * `multiple` (Boolean; default `false`):
* * `max` (Number; default `-1`):
* * `selectedIndex` (Number):
* * `selectedIndexes` (Array): array of Number.
* * `isDisabled` (Boolean; default `false`):
**/
var Select = flagrate.Select = function (opt) {
opt = opt || {};
this.items = opt.items || [];
this.listView = opt.listView || false;
this.multiple = opt.multiple || false;
this.max = opt.max || -1;
/*?
* flagrate.Select#selectedIndexes -> Array
* readonly property.
*
* flagrate.Select#selectedIndex -> Number
* readonly property.
**/
if (this.multiple) {
this.selectedIndexes = opt.selectedIndexes || [];
} else {
this.selectedIndex = typeof opt.selectedIndex === 'undefined' ? -1 : opt.selectedIndex;
}
var attr = opt.attribute || {};
if (opt.id) { attr.id = opt.id; }
/*?
* flagrate.Select#isPulldown -> Boolean
* readonly property.
**/
this.isPulldown = (!this.listView && !this.multiple);
//create
var that = new Element('div', attr);
var createOnSelectHandler = function (i) {
return function () {
that.select(i);
};
};
var createOnDeselectHandler = function (i) {
return function () {
that.deselect(i);
};
};
// normalize items
var i, l;
for (i = 0, l = this.items.length; i < l; i++) {
if (typeof this.items[i] !== 'object') {
this.items[i] = {
label: typeof this.items[i] === 'string' ? this.items[i] : this.items[i].toString(10),
value: this.items[i]
};
}
}
if (this.isPulldown) {
that._pulldown = new Pulldown({
label : '-',
items : (function () {
var items = [{
label : '-',
onSelect: createOnSelectHandler(-1)
}];
var i, l;
for (i = 0, l = this.items.length; i < l; i++) {
items.push({
label : this.items[i].label,
icon : this.items[i].icon,
onSelect: createOnSelectHandler(i)
});
}
return items;
}.bind(this))()
}).insertTo(that);
} else {
that._grid = new flagrate.Grid({
headless : true,
multiSelect: this.multiple,
cols: [
{
key : 'label'
}
],
rows: (function () {
var rows = [];
var i, l;
for (i = 0, l = this.items.length; i < l; i++) {
rows.push({
cell: {
label: {
text: this.items[i].label,
icon: this.items[i].icon
}
},
onSelect: createOnSelectHandler(i),
onDeselect: createOnDeselectHandler(i)
});
}
return rows;
}.bind(this))()
}).insertTo(that);
}
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-select');
if (!that.isPulldown) { that.addClassName(flagrate.className + '-select-list-view'); }
if (opt.className) { that.addClassName(opt.className); }
if (opt.style) { that.setStyle(opt.style); }
if (opt.isDisabled) { that.disable(); }
if (that.multiple) {
that.selectedIndexes.forEach(function (index) {
that.select(index);
});
} else {
if (that.selectedIndex > -1) {
that.select(that.selectedIndex);
}
}
return that;
};
flagrate.createSelect = function (a) {
return new Select(a);
};
Select.prototype = {
/*?
* flagrate.Select#select(item) -> flagrate.Select
* - item (Number) - Index number of item.
**/
select: function (index) {
if (this.items.length <= index) { return this; }
if (this.multiple) {
if (this.max > -1 && this.selectedIndexes.length >= this.max) {
if (this._grid.rows[index].isSelected === true) {
this._grid.deselect(index);
}
return this;
}
if (this.selectedIndexes.indexOf(index) === -1) {
this.selectedIndexes.push(index);
}
} else {
this.selectedIndex = index;
}
if (this.isPulldown) {
if (index === -1) {
this._pulldown.setLabel('-');
this._pulldown.setIcon(null);
} else {
this._pulldown.setLabel(this.items[index].label);
this._pulldown.setIcon(this.items[index].icon);
}
this.fire('change');
} else {
if (!this._grid.rows[index].isSelected) {
this._grid.select(index);
}
}
return this;
}
,
/*?
* flagrate.Select#deselect([item]) -> flagrate.Select
* - item (Number) - Index number of item.
**/
deselect: function (index) {
if (this.items.length <= index) { return this; }
if (this.multiple) {
var selectedIndex = this.selectedIndexes.indexOf(index);
if (selectedIndex !== -1) {
this.selectedIndexes.splice(this.selectedIndexes.indexOf(index), 1);
}
} else {
this.selectedIndex = -1;
}
if (this.isPulldown) {
this._pulldown.setLabel('-');
this._pulldown.setIcon(null);
this.fire('change');
} else {
if (this.multiple) {
if (this._grid.rows[index].isSelected === true) {
this._grid.deselect(index);
}
} else {
var i, l;
for (i = 0, l = this.items.length; i < l; i++) {
if (this._grid.rows[i].isSelected === true) {
this._grid.deselect(i);
}
}
}
}
return this;
}
,
/*?
* flagrate.Select#selectAll() -> flagrate.Select
**/
selectAll: function () {
if (this.multiple) {
this._grid.selectAll();
this.selectedIndexes = [];
var i, l;
for (i = 0, l = this.items.length; i < l; i++) {
this.selectedIndexes.push(i);
}
}
return this;
}
,
/*?
* flagrate.Select#deselectAll() -> flagrate.Select
**/
deselectAll: function () {
if (this.multiple) {
this._grid.deselectAll();
this.selectedIndexes = [];
} else {
this.deselect();
}
return this;
}
,
/*?
* flagrate.Select#disable() -> flagrate.Select
**/
disable: function () {
this.addClassName(flagrate.className + '-disabled');
if (this.isPulldown) {
this._pulldown.disable();
} else {
this._grid.disable();
}
return this;
}
,
/*?
* flagrate.Select#enable() -> flagrate.Select
**/
enable: function () {
this.removeClassName(flagrate.className + '-disabled');
if (this.isPulldown) {
this._pulldown.enable();
} else {
this._grid.enable();
}
return this;
}
,
/*?
* flagrate.Select#isEnabled() -> Boolean
**/
isEnabled: function () {
return !this.hasClassName(flagrate.className + '-disabled');
}
,
/*?
* flagrate.Select#getValue() -> any
**/
getValue: function () {
if (this.selectedIndex > -1) {
return this.items[this.selectedIndex].value;
} else {
return void 0;
}
}
,
/*?
* flagrate.Select#getValues() -> Array
**/
getValues: function () {
var i, l, result = [];
for (i = 0, l = this.selectedIndexes.length; i < l; i++) {
result.push(this.items[this.selectedIndexes[i]].value);
}
return result;
}
};
/*?
* class flagrate.ComboBox
**/
/*?
* flagrate.createComboBox(option)
* new flagrate.ComboBox(option)
* - option (Object) - options.
*
* Select.
*
* #### option
*
* * `id` (String): `id` attribute of container element.
* * `className` (String):
* * `attribute` (Object):
* * `style` (Object): (using flagrate.Element.setStyle)
* * `value` (String): default value.
* * `items` (Array): of String values.
* * `placeholder` (String):
* * `icon` (String):
* * `regexp` (RegExp):
* * `isDisabled` (Boolean; default `false`):
**/
var ComboBox = flagrate.ComboBox = function (opt) {
opt = opt || {};
this.items = opt.items || [];
this.regexp = opt.regexp || null;
var attr = opt.attribute || {};
if (opt.id) { attr.id = opt.id; }
//create
var that = new Element('div', attr);
that._textinput = new TextInput({
value : opt.value,
placeholder: opt.placeholder,
icon : opt.icon
}).insertTo(that);
var createOnSelectHandler = function (value) {
return function () {
that.setValue(value);
that._textinput.focus();
that.fire('change');
};
};
that._button = new Button({
onSelect: function () {
if (that._menu) {
that._menu.remove();
delete that._menu;
return;
}
var items = [];
var i, l;
for (i = 0, l = that.items.length; i < l; i++) {
items.push({
label : that.items[i],
onSelect: createOnSelectHandler(that.items[i])
});
}
var menu = that._menu = new Menu(
{
className: flagrate.className + '-combobox-menu',
items : items,
onSelect : function () {
menu.remove();
delete that._menu;
}
}
).insertTo(that);
// To prevent overflow.
var menuHeight = menu.getHeight();
var menuMargin = parseInt(menu.getStyle('margin-top').replace('px', ''), 10);
var cummOffsetTop = that.cumulativeOffset().top;
var upsideSpace = - window.pageYOffset + cummOffsetTop;
var downsideSpace = window.pageYOffset + window.innerHeight - cummOffsetTop - that.getHeight();
if (menuHeight + menuMargin > downsideSpace) {
if (upsideSpace > downsideSpace) {
if (upsideSpace < menuHeight + menuMargin) {
menuHeight = (upsideSpace - menuMargin - menuMargin);
menu.style.maxHeight = menuHeight + 'px';
}
menu.addClassName(flagrate.className + '-combobox-menu-upper');
} else {
menuHeight = (downsideSpace - menuMargin - menuMargin);
menu.style.maxHeight = menuHeight + 'px';
}
}
var removeMenu = function (e) {
document.body.removeEventListener('click', removeMenu);
that.parentNode.removeEventListener('click', removeMenu);
that.off('click', removeMenu);
menu.style.opacity = '0';
setTimeout(function (){ menu.remove(); }.bind(this), 500);
delete that._menu;
};
setTimeout(function () {
document.body.addEventListener('click', removeMenu);
that.parentNode.addEventListener('click', removeMenu);
that.on('click', removeMenu);
}, 0);
}
}).insertTo(that);
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-combobox');
if (opt.className) { that.addClassName(opt.className); }
if (opt.style) { that.setStyle(opt.style); }
if (opt.isDisabled) { that.disable(); }
return that;
};
flagrate.createComboBox = function (a) {
return new ComboBox(a);
};
ComboBox.prototype = {
/*?
* flagrate.ComboBox#disable() -> flagrate.ComboBox
**/
disable: function () {
this.addClassName(flagrate.className + '-disabled');
this._textinput.disable();
this._button.disable();
return this;
}
,
/*?
* flagrate.ComboBox#enable() -> flagrate.ComboBox
**/
enable: function () {
this.removeClassName(flagrate.className + '-disabled');
this._textinput.enable();
this._button.enable();
return this;
}
,
/*?
* flagrate.ComboBox#isEnabled() -> Boolean
**/
isEnabled: function () {
return !this.hasClassName(flagrate.className + '-disabled');
}
,
/*?
* flagrate.ComboBox#getValue() -> String
**/
getValue: function () {
return this._textinput.value;
}
,
/*?
* flagrate.ComboBox#setValue(value) -> flagrate.ComboBox
**/
setValue: function (value) {
this._textinput.value = value;
return this;
}
,
/*?
* flagrate.ComboBox#isValid() -> Boolean
**/
isValid: function (value) {
return this.regexp.test(this.getValue());
}
};
/*?
* class flagrate.SearchBox
*
* Smart UI Component for Search Things. **(This is still in preview!!)**
*
* #### Example
*
* var searchBox = flagrate.createSearchBox({
* suggester: function (value, done) {
* someAsyncFunction(function (resultItems) {
* done(resultItems);
* });
* },
* onSearch: function (value) {
* console.log('searching: ' + value);
* }
* }).insertTo(x);
*
* #### Additional Event
*
* * `search`:
**/
/*?
* flagrate.createSearchBox(option)
* new flagrate.SearchBox(option)
* - option (Object) - options.
*
* text input for search.
*
* #### option
*
* * `id` (String): `id` attribute of container element.
* * `className` (String):
* * `attribute` (Object):
* * `style` (Object): (using flagrate.Element.setStyle)
* * `value` (String): default value.
* * `placeholder` (String):
* * `icon` (String):
* * `isDisabled` (Boolean; default `false`):
* * `suggester` (Function):
* * `onSearch` (Function): callback with input value.
**/
var SearchBox = flagrate.SearchBox = function (opt) {
opt = opt || {};
this.suggester = opt.suggester || null;
this.onSearch = opt.onSearch || emptyFunction;
var attr = opt.attribute || {};
attr.id = opt.id || null;
attr['class'] = opt.className || null;
//create
var that = new Element('div', attr);
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-search-box');
that._input = new TextInput({
className: 'search-input',
value: opt.value,
placeholder: opt.placeholder,
icon: opt.icon
}).insertTo(that);
that._button = new Button({
className: 'search-button',
onSelect: that.search.bind(that)
}).insertTo(that);
that._suggest = new Element('div', {
'class': 'search-suggest'
}).hide().insertTo(that);
that._input.on('keydown', that._onKeydownHandler.bind(that));
that._input.on('keyup', that._onKeyupHandler.bind(that));
that._input.on('focus', that._onFocusHandler.bind(that));
that._input.on('blur', that._onBlurHandler.bind(that));
// for Chrome
that._suggest.on('mousedown', function (e) {
e.preventDefault();
});
if (opt.style) { that.setStyle(opt.style); }
if (opt.isDisabled) { that.disable(); }
return that;
};
flagrate.createSearchBox = function (a) {
return new SearchBox(a);
};
SearchBox.prototype = {
/*?
* flagrate.SearchBox#disable() -> flagrate.SearchBox
**/
disable: function () {
this.addClassName(flagrate.className + '-disabled');
this._input.disable();
this._button.disable();
this._suggest.hide();
return this;
},
/*?
* flagrate.SearchBox#enable() -> flagrate.SearchBox
**/
enable: function () {
this.removeClassName(flagrate.className + '-disabled');
this._input.enable();
this._button.enable();
return this;
},
/*?
* flagrate.SearchBox#isEnabled() -> Boolean
**/
isEnabled: function () {
return !this.hasClassName(flagrate.className + '-disabled');
},
/*?
* flagrate.SearchBox#getValue() -> String
**/
getValue: function () {
return this._input.getValue();
},
/*?
* flagrate.SearchBox#setValue(value) -> flagrate.SearchBox
**/
setValue: function (value) {
this._input.setValue(value);
return this;
},
/*?
* flagrate.SearchBox#search() -> flagrate.SearchBox
**/
search: function () {
var value = this.getValue();
this.onSearch(value);
this.fire('search', value);
this._input.blur();
return this;
},
/*?
* flagrate.SearchBox#suggest() -> flagrate.SearchBox
**/
suggest: function () {
if (!this.suggester) {
return this;
}
this._suggest.hide();
var value = this.getValue();
var result = this.suggester(value, this._suggested.bind(this));
if (result !== void 0) {
this._suggested(result);
}
return this;
},
/*?
* flagrate.SearchBox#focus() -> flagrate.SearchBox
**/
focus: function () {
this._input.focus();
return this;
},
/*?
* flagrate.SearchBox#blur() -> flagrate.SearchBox
**/
blur: function () {
this._input.blur();
return this;
},
_suggested: function (suggestedItems) {
if (!suggestedItems) {
return;
}
if (suggestedItems.length === 0) {
this._suggest.hide();
return;
}
var items = [];
var i, l;
for (i = 0, l = suggestedItems.length; i < l; i++) {
if (typeof suggestedItems[i] === 'string' && suggestedItems[i].trim() !== '') {
items.push({
label: suggestedItems[i].trim(),
onSelect: this._createCompletionHandler(this, suggestedItems[i].trim())
});
} else if (typeof suggestedItems[i] === 'object') {
items.push({
label: suggestedItems[i].label,
icon: suggestedItems[i].icon,
onSelect: this._createSuggestionHandler(this, suggestedItems[i].onSelect)
});
}
}
if (items.length === 0) {
this._suggest.hide();
return;
}
var menu = this._menu = new Menu({
items: items,
onSelect: function () {
this._suggest.hide();
}.bind(this)
});
menu.firstChild.addClassName(flagrate.className + '-search-suggest-selected');
this._suggest.update(menu).show();
// To prevent overflow.
var menuHeight = this._suggest.getHeight();
var menuMargin = parseInt(this._suggest.getStyle('margin-top').replace('px', ''), 10);
var cummOffsetTop = this.cumulativeOffset().top;
var upsideSpace = - window.pageYOffset + cummOffsetTop;
var downsideSpace = window.pageYOffset + window.innerHeight - cummOffsetTop - this.getHeight();
if (menuHeight + menuMargin > downsideSpace) {
if (upsideSpace > downsideSpace) {
if (upsideSpace < menuHeight + menuMargin) {
menuHeight = (upsideSpace - menuMargin - menuMargin);
this._suggest.style.maxHeight = menuHeight + 'px';
}
this._suggest.addClassName(flagrate.className + '-search-suggest-upper');
} else {
menuHeight = (downsideSpace - menuMargin - menuMargin);
this._suggest.style.maxHeight = menuHeight + 'px';
this._suggest.removeClassName(flagrate.className + '-search-suggest-upper');
}
} else {
this._suggest.removeClassName(flagrate.className + '-search-suggest-upper');
}
// reset scroll position
this._suggest.scrollTop = 0;
},
_createCompletionHandler: function (that, value) {
return function () {
that._input.setValue(value);
that._input.focus();
};
},
_createSuggestionHandler: function (that, onSelect) {
return function () {
onSelect.call(that);
that._input.blur();
};
},
_onKeydownHandler: function (e) {
// ESC: 27
if (e.keyCode === 27) {
this._input.select();
this._suggest.hide();
} else if (this._suggest.visible() === true) {
// ENTER: 13
if (e.keyCode === 13) {
var target = this._menu.getElementsByClassName(flagrate.className + '-search-suggest-selected')[0];
target.click();
return;
}
// UP: 38, DOWN: 40
if (e.keyCode !== 38 && e.keyCode !== 40) {
return;
}
e.preventDefault();
var elements = this._menu.getElementsByTagName('button');
var i, l;
for (i = 0, l = elements.length; i < l; i++) {
if (elements[i].hasClassName(flagrate.className + '-search-suggest-selected') === true) {
if ((e.keyCode === 38 && i !== 0) || (e.keyCode === 40 && (i + 1) !== l)) {
elements[i].removeClassName(flagrate.className + '-search-suggest-selected');
}
var scrollTop = -1;
if (e.keyCode === 38 && i !== 0) {
elements[i - 1].addClassName(flagrate.className + '-search-suggest-selected');
scrollTop = elements[i - 1].offsetHeight + elements[i - 1].offsetTop;
} else if (e.keyCode === 40 && (i + 1) !== l) {
elements[i + 1].addClassName(flagrate.className + '-search-suggest-selected');
scrollTop = elements[i + 1].offsetHeight + elements[i + 1].offsetTop;
}
if (scrollTop !== -1) {
this._suggest.scrollTop = scrollTop + 4 - this._suggest.getHeight();
}
break;
}
}
} else if (e.keyCode === 13) {
setTimeout(this.search.bind(this), 100);
}
return this;
},
_onKeyupHandler: function (e) {
if (this._lastValue !== this.getValue()) {
this._lastValue = this.getValue();
this.suggest();
}
},
_onFocusHandler: function (e) {
setTimeout(this.suggest.bind(this), 100);
return this;
},
_onBlurHandler: function (e) {
setTimeout(function () {
if (document.activeElement !== this._suggest && this._suggest.visible() === true) {
this._suggest.hide();
}
}.bind(this), 100);
return this;
}
};
/*?
* class flagrate.Checkbox
**/
/*?
* flagrate.createCheckbox(option)
* new flagrate.Checkbox(option)
* - option (Object) - options.
**/
var Checkbox = flagrate.Checkbox = function (opt) {
var id = 'flagrate-checkbox-' + (++Checkbox.idCounter).toString(10);
opt = opt || {};
this.onChange = opt.onChange || null;
this.onCheck = opt.onCheck || null;
this.onUncheck = opt.onUncheck || null;
var attr = opt.attribute || {};
attr.id = opt.id || null;
attr['class'] = opt.className || null;
//create
var that = new Element('label', attr).updateText(opt.label);
that.writeAttribute('for', id);
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-checkbox');
if (opt.icon) {
that.addClassName(flagrate.className + '-icon');
that.setStyle({
backgroundImage: 'url(' + opt.icon + ')'
});
}
that._input = new Element('input', { id: id, type: 'checkbox' });
that.insert({ top: new Element() });
that.insert({ top: that._input });
that._input.on('click', function (e) {
e.stopPropagation();
e.targetCheckbox = that;
if (that.isChecked()) {
that.uncheck();
} else {
that.check();
}
if (that.isChecked()) {
if (that.onCheck) {
that.onCheck(e);
}
} else {
if (that.onUncheck) {
that.onUncheck(e);
}
}
if (that.onChange) {
that.onChange(e);
}
that.fire('change');
});
that._input.on('change', function (e) {
e.stopPropagation();
});
// state
that._checked = false;
if (opt.isChecked) { that.check(); }
if (opt.isDisabled) { that.disable(); }
return that;
};
flagrate.createCheckbox = function (a) {
return new Checkbox(a);
};
Checkbox.idCounter = 0;
Checkbox.prototype = {
/*?
* flagrate.Checkbox#disable() -> flagrate.Checkbox
**/
disable: function () {
this.addClassName(flagrate.className + '-disabled');
this._input.writeAttribute('disabled', true);
return this;
}
,
/*?
* flagrate.Checkbox#enable() -> flagrate.Checkbox
**/
enable: function () {
this.removeClassName(flagrate.className + '-disabled');
this._input.writeAttribute('disabled', false);
return this;
}
,
/*?
* flagrate.Checkbox#isEnabled() -> Boolean
**/
isEnabled: function () {
return !this.hasClassName(flagrate.className + '-disabled');
}
,
/*?
* flagrate.Checkbox#isChecked() -> Boolean
**/
isChecked: function () {
return this._checked;
}
,
/*?
* flagrate.Checkbox#check() -> flagrate.Checkbox
**/
check: function () {
this._input.checked = true;
this._checked = true;
return this;
}
,
/*?
* flagrate.Checkbox#uncheck() -> flagrate.Checkbox
**/
uncheck: function () {
this._input.checked = false;
this._checked = false;
return this;
}
};
/*?
* class flagrate.Checkboxes
**/
/*?
* flagrate.createCheckboxes(option)
* new flagrate.Checkboxes(option)
* - option (Object) - options.
**/
var Checkboxes = flagrate.Checkboxes = function (opt) {
opt = opt || {};
this.items = opt.items || [];
//this.onChange = opt.onChange || emptyFunction;
var attr = opt.attribute || {};
if (opt.id) { attr.id = opt.id; }
//create
var that = new Element('div', attr);
var i, l;
for (i = 0, l = this.items.length; i < l; i++) {
// normalize items
if (typeof this.items[i] !== 'object') {
this.items[i] = {
label: typeof this.items[i] === 'string' ? this.items[i] : this.items[i].toString(10),
value: this.items[i]
};
}
this.items[i]._checkbox = new Checkbox({
label : this.items[i].label,
icon : this.items[i].icon,
isChecked: this.items[i].isChecked,
onCheck : this.items[i].onCheck,
onUncheck: this.items[i].onUncheck
}).insertTo(that);
}
extendObject(that, this);
that.addClassName(flagrate.className + ' ' + flagrate.className + '-checkboxes');
if (opt.className) { that.addClassName(opt.className); }
if (opt.style) { that.setStyle(opt.style); }
if (opt.isDisabled) { that.disable(); }
if (opt.values) {
that.setValues(opt.values);
}
return that;
};
flagrate.createCheckboxes = function (a) {
return new Checkboxes(a);
};
Checkboxes.prototype = {
/*?
* flagrate.Checkboxes#select(index) -> flagrate.Checkboxes
**/
select: function (index) {
if (this.items[index]) {
this.items[index]._checkbox.check();
}
return this;
},
/*?
* flagrate.Checkboxes#deselect(index) -> flagrate.Checkboxes
**/
deselect: function (index) {
if (this.items[index]) {
this.items[index]._checkbox.uncheck();
}
return this;
},
/*?
* flagrate.Checkboxes#getValues() -> Array
**/
getValues: function () {
var values = [];
var i, l;
for (i = 0, l = this.items.length; i < l; i++) {
if (this.items[i]._checkbox.isChecked() === true) {
values.push(this.items[i].value);
}
}