8664 lines
304 KiB
JavaScript
8664 lines
304 KiB
JavaScript
/*
|
|
################################################################################
|
|
ox.ui.js
|
|
|
|
requires
|
|
jquery-1.4.js
|
|
ox.js
|
|
################################################################################
|
|
*/
|
|
|
|
// also see test.js, in demos ...
|
|
|
|
(function() {
|
|
|
|
var oxui = {
|
|
defaultTheme: 'classic',
|
|
elements: {},
|
|
getDimensions: function(orientation) {
|
|
return orientation == 'horizontal' ?
|
|
['width', 'height'] : ['height', 'width'];
|
|
},
|
|
getEdges: function(orientation) {
|
|
return orientation == 'horizontal' ?
|
|
['left', 'right', 'top', 'bottom'] :
|
|
['top', 'bottom', 'left', 'right'];
|
|
},
|
|
getBarSize: function(size) {
|
|
var sizes = {
|
|
small: 20,
|
|
medium: 24,
|
|
large: 28,
|
|
};
|
|
return sizes[size];
|
|
},
|
|
jQueryFunctions: function() {
|
|
var functions = [],
|
|
$element = $('<div>');
|
|
//delete $element.length;
|
|
Ox.each($element, function(k, v) {
|
|
if (typeof v == 'function') {
|
|
functions.push(k);
|
|
}
|
|
});
|
|
return functions.sort();
|
|
}(),
|
|
path: $('script[src*=ox.ui.js]').attr('src').replace('js/ox.ui.js', ''),
|
|
scrollbarSize: $.browser.mozilla ? 16 : 12,
|
|
symbols: {
|
|
alt: '\u2325',
|
|
apple: '\uF8FF',
|
|
arrow_down: '\u2193',
|
|
arrow_left: '\u2190',
|
|
arrow_right: '\u2192',
|
|
arrow_up: '\u2191',
|
|
backspace: '\u232B',
|
|
backup: '\u2707',
|
|
ballot: '\u2717',
|
|
black_star: '\u2605',
|
|
burn: '\u2622',
|
|
caps_lock: '\u21EA',
|
|
check: '\u2713',
|
|
//clear: '\u2327',
|
|
clear: '\u00D7',
|
|
click: '\uF803',
|
|
close: '\u2715',
|
|
command: '\u2318',
|
|
control: '\u2303',
|
|
cut: '\u2702',
|
|
'delete': '\u2326',
|
|
diamond: '\u25C6',
|
|
edit: '\uF802',
|
|
eject: '\u23CF',
|
|
escape: '\u238B',
|
|
end: '\u2198',
|
|
enter: '\u2324',
|
|
fly: '\u2708',
|
|
gear: '\u2699',
|
|
home: '\u2196',
|
|
info: '\u24D8',
|
|
navigate: '\u2388',
|
|
option: '\u2387',
|
|
page_up: '\u21DE',
|
|
page_down: '\u21DF',
|
|
redo: '\u21BA',
|
|
'return': '\u21A9',
|
|
//select: '\u21D5',
|
|
select: '\u25BE',
|
|
shift: '\u21E7',
|
|
sound: '\u266B',
|
|
space: '\u2423',
|
|
tab: '\u21E5',
|
|
trash: '\u267A',
|
|
triangle_down: '\u25BC',
|
|
triangle_left: '\u25C0',
|
|
triangle_right: '\u25BA',
|
|
triangle_up: '\u25B2',
|
|
undo: '\u21BB',
|
|
voltage: '\u26A1',
|
|
warning: '\u26A0',
|
|
white_star: '\u2606'
|
|
}
|
|
},
|
|
$window, $document, $body;
|
|
|
|
$(function() {
|
|
$window = $(window),
|
|
$document = $(document),
|
|
$body = $('body'),
|
|
$elements = {};
|
|
Ox.theme(oxui.defaultTheme);
|
|
});
|
|
|
|
/*
|
|
============================================================================
|
|
Application
|
|
============================================================================
|
|
*/
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.App
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.App = function() {
|
|
/*
|
|
options:
|
|
requestTimeout
|
|
requestType
|
|
requestURL
|
|
*/
|
|
return function(options) {
|
|
|
|
options = options || {};
|
|
var self = {},
|
|
that = this;
|
|
|
|
self.time = +new Date();
|
|
|
|
self.options = $.extend({
|
|
id: '',
|
|
init: '',
|
|
name: '',
|
|
requestTimeout: 15000,
|
|
requestType: 'POST',
|
|
requestURL: '',
|
|
}, options);
|
|
|
|
function getUserAgent() {
|
|
var userAgent = '';
|
|
$.each(['Chrome', 'Firefox', 'Internet Explorer', 'Opera', 'Safari'], function(i, v) {
|
|
if (navigator.userAgent.indexOf(v) > -1) {
|
|
userAgent = v;
|
|
return false;
|
|
}
|
|
});
|
|
return userAgent;
|
|
}
|
|
|
|
function getUserData() {
|
|
//return {};
|
|
return {
|
|
navigator: {
|
|
cookieEnabled: navigator.cookieEnabled,
|
|
plugins: $.map(navigator.plugins, function(plugin, i) {
|
|
return plugin.name;
|
|
}),
|
|
userAgent: navigator.userAgent
|
|
},
|
|
screen: screen,
|
|
time: (+new Date() - self.time) / 1000,
|
|
window: {
|
|
innerHeight: window.innerHeight,
|
|
innerWidth: window.innerWidth,
|
|
outerHeight: window.outerHeight,
|
|
outerWidth: window.outerWidth,
|
|
screenLeft: window.screenLeft,
|
|
screenTop: window.screenTop
|
|
}
|
|
};
|
|
}
|
|
|
|
function loadImages(callback) {
|
|
$.getJSON(oxui.path + 'json/ox.ui.images.json', function(data) {
|
|
var counter = 0,
|
|
length = data.length;
|
|
data.forEach(function(src, i) {
|
|
image = new Image()
|
|
image.src = oxui.path + src;
|
|
image.onload = function() {
|
|
(++counter == length) && callback();
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
self.change = function(key, value) {
|
|
|
|
};
|
|
|
|
that.launch = function(callback) {
|
|
var time = +new Date(),
|
|
userAgent = getUserAgent(),
|
|
userAgents = ['Chrome', 'Firefox', 'Safari'];
|
|
document.title = self.options.name;
|
|
$.ajaxSetup({
|
|
timeout: self.options.requestTimeout,
|
|
type: self.options.requestType,
|
|
url: self.options.requestURL
|
|
});
|
|
userAgents.indexOf(userAgent) > -1 ? start() : stop();
|
|
function start() {
|
|
loadImages(function() {
|
|
window.google = function() {};
|
|
$.getScript('http://maps.google.com/maps/api/js?callback=google&sensor=false', function() {
|
|
$.getJSON(self.options.config, function(data) {
|
|
var config = data;
|
|
that.request(self.options.init, getUserData(), function(data) {
|
|
Ox.print('!!!', data)
|
|
var user = data.data.user;
|
|
$(function() {
|
|
$('body').empty();
|
|
callback({
|
|
config: config,
|
|
user: user
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
}
|
|
function stop() {
|
|
that.request(self.options.init, getUserData(), function() {});
|
|
}
|
|
return that;
|
|
};
|
|
|
|
that.options = function() {
|
|
return Ox.getset(self.options, Array.prototype.slice.call(arguments), self.change, that);
|
|
};
|
|
|
|
that.request = function(action, data, callback) {
|
|
if (arguments.length == 2) {
|
|
callback = data;
|
|
data = {};
|
|
}
|
|
return Ox.Request.send({
|
|
url: self.options.requestURL,
|
|
data: {
|
|
action: action,
|
|
data: JSON.stringify(data)
|
|
},
|
|
callback: callback
|
|
});
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
}();
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Event
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Event = function() {
|
|
var $eventHandler = $('<div>'),
|
|
events = {};
|
|
function addEvent(id, type, event, callback) {
|
|
events[id] = events[id] || {};
|
|
events[id][type] = events[id][type] || {};
|
|
events[id][type][event] = events[id][type][event] || [];
|
|
events[id][type][event].push(callback);
|
|
if (type == 'normal' || Ox.Focus.focused() == id) {
|
|
Ox.print('bind', id, event);
|
|
$eventHandler.bind(event + (id ? '_' + id : ''), callback); // requestStart/requestStop currently have '' as id
|
|
}
|
|
}
|
|
function removeEvent(id, type, event, callback) {
|
|
var focused = type == 'normal' || Ox.Focus.focused() == id,
|
|
toString = (callback || '').toString();
|
|
Ox.print('removeEvent', id, type, event, callback);
|
|
if (events[id] && events[id][type] && (!event || events[id][type][event])) {
|
|
$.each(events[id][type], function(e, fns) {
|
|
if (!event || event == e) {
|
|
events[id][type][e] = $.map(events[id][type][e], function(fn, i) {
|
|
if (!callback || toString == fn.toString()) {
|
|
focused && $eventHandler.unbind(e + '_' + id, fn);
|
|
return null;
|
|
} else {
|
|
return fn;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
Ox.print(id, type, events)
|
|
if (!callback || (event && events[id][type][event].length == 0)) {
|
|
delete events[id][type][event];
|
|
}
|
|
if (!event || Ox.length(events[id].normal) == 0) {
|
|
delete events[id][type];
|
|
}
|
|
if (Ox.length(events[id]) == 0) {
|
|
delete events[id];
|
|
}
|
|
}
|
|
}
|
|
function isKeyboardEvent(event) { // fixme: currently unused
|
|
return event.substr(0, 4) == 'key_';
|
|
}
|
|
return {
|
|
_print: function() {
|
|
Ox.print(events);
|
|
},
|
|
add: function(id, event, callback) {
|
|
// add keyboard event
|
|
addEvent(id, 'keyboard', event, callback);
|
|
},
|
|
bind: function(id, event, callback) {
|
|
// bind event
|
|
addEvent(id, 'normal', event, callback);
|
|
},
|
|
bindKeyboard: function(id) {
|
|
// bind all keyboard events
|
|
//Ox.print('binding', 'id', id, 'events', events[id], Ox.length(events[id]), 'keyboardevents', events[id]['keyboard'])
|
|
$.each(events[id], function(k, v) {
|
|
Ox.print('|' + k + '|');
|
|
})
|
|
events[id] && events[id].keyboard && $.each(events[id].keyboard, function(event, callbacks) {
|
|
$.each(callbacks, function(i, callback) {
|
|
Ox.print('bind', id, event);
|
|
$eventHandler.bind(event, callback);
|
|
});
|
|
});
|
|
},
|
|
changeId: function(oldId, newId) {
|
|
// fixme: would it be better to pass that.id instead of self.options.id?
|
|
// then this renaming wouldn't be necessary
|
|
Ox.print('changeId', oldId, newId, events[oldId]);
|
|
$.each($.extend({}, events[oldId] || {}), function(type, events_) {
|
|
var bind = type == 'normal' ? 'bind' : 'add',
|
|
unbind = type == 'normal' ? 'unbind' : 'remove';
|
|
$.each(events_, function(event, callbacks) {
|
|
$.each(callbacks, function(i, callback) {
|
|
Ox.Event[unbind](oldId, event, callback);
|
|
Ox.Event[bind](newId, event, callback);
|
|
});
|
|
});
|
|
});
|
|
},
|
|
remove: function(id, event, callback) {
|
|
// remove keyboard event
|
|
// event and callback are optional
|
|
removeEvent(id, 'keyboard', event, callback);
|
|
},
|
|
unbind: function(id, event, callback) {
|
|
// unbind event
|
|
// event and callback are optional
|
|
removeEvent(id, 'normal', event, callback);
|
|
},
|
|
trigger: function(id, event, data) {
|
|
// trigger event
|
|
// data is optional
|
|
// keyboard handler will call this with '' as id
|
|
Ox.print('trigger', id, event, data || {});
|
|
$eventHandler.trigger(event + (id ? '_' + id : ''), data || {});
|
|
},
|
|
unbindKeyboard: function(id) {
|
|
// unbind all keyboard events
|
|
Ox.print('unbinding', id /*events[id].keyboard*/)
|
|
events[id] && events[id].keyboard && $.each(events[id].keyboard, function(event, callbacks) {
|
|
$.each(callbacks, function(i, callback) {
|
|
Ox.print('unbind', event)
|
|
$eventHandler.unbind(event, callback);
|
|
});
|
|
});
|
|
}
|
|
}
|
|
}();
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Focus
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Focus = function() {
|
|
var stack = [];
|
|
return {
|
|
blur: function(id) {
|
|
var index = stack.indexOf(id);
|
|
if (index == stack.length - 1) {
|
|
stack.length == 1 ? stack.pop() :
|
|
stack.splice(stack.length - 2, 0, stack.pop());
|
|
Ox.Event.unbindKeyboard($elements[id].options('id'));
|
|
stack.length && Ox.Event.bindKeyboard($elements[stack[stack.length - 1]].options('id'));
|
|
//$elements[id].removeClass('OxFocus');
|
|
$('.OxFocus').removeClass('OxFocus'); // fixme: the above is better, and should work
|
|
stack.length && $elements[stack[stack.length - 1]].addClass('OxFocus');
|
|
Ox.print('blur', id, stack);
|
|
}
|
|
},
|
|
focus: function(id) {
|
|
var index = stack.indexOf(id);
|
|
if (index == -1 || index < stack.length - 1) {
|
|
stack.length && Ox.Event.unbindKeyboard($elements[stack[stack.length - 1]].options('id'));
|
|
index > -1 && stack.splice(index, 1);
|
|
stack.push(id);
|
|
Ox.Event.bindKeyboard($elements[id].options('id'));
|
|
$('.OxFocus').removeClass('OxFocus'); // fixme: see above
|
|
$elements[id].addClass('OxFocus');
|
|
Ox.print('focus', id, stack);
|
|
}
|
|
},
|
|
focused: function() {
|
|
return stack[stack.length - 1];
|
|
}
|
|
};
|
|
}();
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.History
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Keyboard
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
(function() {
|
|
|
|
var buffer = '',
|
|
bufferTime = 0,
|
|
bufferTimeout = 1000,
|
|
keyNames = function() {
|
|
return {
|
|
0: 'section',
|
|
8: 'backspace',
|
|
9: 'tab',
|
|
12: 'clear',
|
|
13: 'enter',
|
|
16: 'shift',
|
|
17: 'control',
|
|
18: 'alt',
|
|
20: 'capslock',
|
|
27: 'escape',
|
|
32: 'space',
|
|
33: 'pageup',
|
|
34: 'pagedown',
|
|
35: 'end',
|
|
36: 'home',
|
|
37: 'left',
|
|
38: 'up',
|
|
39: 'right',
|
|
40: 'down',
|
|
45: 'insert',
|
|
46: 'delete',
|
|
47: 'help',
|
|
48: '0',
|
|
49: '1',
|
|
50: '2',
|
|
51: '3',
|
|
52: '4',
|
|
53: '5',
|
|
54: '6',
|
|
55: '7',
|
|
56: '8',
|
|
57: '9',
|
|
65: 'a',
|
|
66: 'b',
|
|
67: 'c',
|
|
68: 'd',
|
|
69: 'e',
|
|
70: 'f',
|
|
71: 'g',
|
|
72: 'h',
|
|
73: 'i',
|
|
74: 'j',
|
|
75: 'k',
|
|
76: 'l',
|
|
77: 'm',
|
|
78: 'n',
|
|
79: 'o',
|
|
80: 'p',
|
|
81: 'q',
|
|
82: 'r',
|
|
83: 's',
|
|
84: 't',
|
|
85: 'u',
|
|
86: 'v',
|
|
87: 'w',
|
|
88: 'x',
|
|
89: 'y',
|
|
90: 'z',
|
|
91: 'meta.left',
|
|
92: 'meta.right',
|
|
93: 'select',
|
|
96: '0.numpad',
|
|
97: '1.numpad',
|
|
98: '2.numpad',
|
|
99: '3.numpad',
|
|
100: '4.numpad',
|
|
101: '5.numpad',
|
|
102: '6.numpad',
|
|
103: '7.numpad',
|
|
104: '8.numpad',
|
|
105: '9.numpad',
|
|
106: 'asterisk.numpad',
|
|
107: 'plus.numpad',
|
|
109: 'minus.numpad',
|
|
108: 'enter.numpad',
|
|
110: 'dot.numpad',
|
|
111: 'slash.numpad',
|
|
112: 'f1',
|
|
113: 'f2',
|
|
114: 'f3',
|
|
115: 'f4',
|
|
116: 'f5',
|
|
117: 'f6',
|
|
118: 'f7',
|
|
119: 'f8',
|
|
120: 'f9',
|
|
121: 'f10',
|
|
122: 'f11',
|
|
123: 'f12',
|
|
124: 'f13',
|
|
125: 'f14',
|
|
126: 'f15',
|
|
127: 'f16',
|
|
144: 'numlock',
|
|
145: 'scrolllock',
|
|
186: 'semicolon',
|
|
187: 'equal',
|
|
188: 'comma',
|
|
189: 'minus',
|
|
190: 'dot',
|
|
191: 'slash',
|
|
192: 'backtick',
|
|
219: 'openbracket',
|
|
220: 'backslash',
|
|
221: 'closebracket',
|
|
222: 'quote'
|
|
// see dojo, for ex.
|
|
};
|
|
}(),
|
|
modifierNames = {
|
|
altKey: 'alt', // mac: option
|
|
ctrlKey: 'control',
|
|
// metaKey: 'meta', // mac: command
|
|
shiftKey: 'shift'
|
|
};
|
|
|
|
$(function() {
|
|
// fixme: how to do this better?
|
|
// in firefox on mac, keypress doesn't fire for up/down
|
|
// if the cursor is at the start/end of an input element
|
|
// on linux, it doesn't seem to fire if the input element has focus
|
|
if ($.browser.mozilla) {
|
|
$document.keypress(keypress);
|
|
$document.keydown(function(event) {
|
|
var $element = $('input:focus');
|
|
if ($element.length) {
|
|
if (
|
|
(
|
|
keyNames[event.keyCode] == 'up' &&
|
|
$element[0].selectionStart + $element[0].selectionEnd == 0
|
|
) || (
|
|
keyNames[event.keyCode] == 'down' &&
|
|
$element[0].selectionStart == $element.val().length &&
|
|
$element[0].selectionEnd == $element.val().length
|
|
)
|
|
) {
|
|
keypress(event);
|
|
}
|
|
}
|
|
});
|
|
} else {
|
|
$document.keydown(keypress);
|
|
}
|
|
});
|
|
function keypress(event) {
|
|
var key,
|
|
keys = [],
|
|
//ret = true,
|
|
time;
|
|
$.each(modifierNames, function(k, v) {
|
|
if (event[k]) {
|
|
keys.push(v);
|
|
}
|
|
});
|
|
// avoid pushing modifier twice
|
|
Ox.print('keys', keys)
|
|
if (keyNames[event.keyCode] && keys.indexOf(keyNames[event.keyCode]) == -1) {
|
|
keys.push(keyNames[event.keyCode]);
|
|
}
|
|
key = keys.join('_');
|
|
if (key.match(/^[\w\d-]$|SPACE/)) {
|
|
time = Ox.getTime();
|
|
if (time - bufferTime > bufferTimeout) {
|
|
buffer = '';
|
|
}
|
|
buffer += key == 'SPACE' ? ' ' : key;
|
|
bufferTime = time;
|
|
}
|
|
Ox.Event.trigger('', 'key_' + key);
|
|
//return false;
|
|
/*
|
|
$.each(stack, function(i, v) {
|
|
// fixme: we dont get the return value!
|
|
ret = Ox.event.trigger(keyboard + Ox.toCamelCase(key) + '.' + v);
|
|
return ret;
|
|
});
|
|
*/
|
|
}
|
|
|
|
})();
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Mouse (??)
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Request
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Request = function() {
|
|
|
|
var cache = {},
|
|
pending = {},
|
|
requests = {},
|
|
self = {
|
|
options: {
|
|
timeout: 15000,
|
|
type: 'POST',
|
|
url: 'api'
|
|
}
|
|
};
|
|
|
|
return {
|
|
|
|
cancel: function() {
|
|
var index;
|
|
if (arguments.length == 0) {
|
|
requests = {};
|
|
} else if (Ox.isFunction(arguments[0])) {
|
|
// cancel with function
|
|
$.each(requests, function(id, req) {
|
|
if (arguments[0](req)) {
|
|
delete requests[id];
|
|
}
|
|
})
|
|
} else {
|
|
// cancel by id
|
|
delete requests[arguments[0]]
|
|
}
|
|
},
|
|
|
|
emptyCache: function() {
|
|
cache = {};
|
|
},
|
|
|
|
options: function(options) {
|
|
return Ox.getset(self.options, options, $.noop(), this);
|
|
},
|
|
|
|
send: function(options) {
|
|
|
|
var options = $.extend({
|
|
age: -1,
|
|
callback: function() {},
|
|
id: Ox.uid(),
|
|
timeout: self.options.timeout,
|
|
type: self.options.type,
|
|
url: self.options.url
|
|
}, options),
|
|
req = JSON.stringify({
|
|
url: options.url,
|
|
data: options.data
|
|
});
|
|
|
|
function callback(data) {
|
|
delete requests[options.id];
|
|
Ox.length(requests) == 0 && Ox.Event.trigger('', 'requestStop');
|
|
options.callback(data); // fixme: doesn't work if callback hasn't been passed
|
|
}
|
|
|
|
function debug(request) {
|
|
var $iframe = $('<iframe>')
|
|
.css({ // fixme: should go into a class
|
|
width: 768,
|
|
height: 384
|
|
}),
|
|
$dialog = new Ox.Dialog({
|
|
title: 'Application Error',
|
|
buttons: [
|
|
{
|
|
title: 'Close',
|
|
click: function() {
|
|
$dialog.close();
|
|
}
|
|
}
|
|
],
|
|
width: 800,
|
|
height: 400
|
|
})
|
|
.append($iframe)
|
|
.open(),
|
|
iframe = $iframe[0].contentDocument || $iframe[0].contentWindow.document;
|
|
iframe.open();
|
|
iframe.write(request.responseText);
|
|
iframe.close();
|
|
}
|
|
|
|
function error(request, status, error) {
|
|
var data;
|
|
if (arguments.length == 1) {
|
|
data = arguments[0]
|
|
} else {
|
|
try {
|
|
data = JSON.parse(request.responseText);
|
|
} catch (err) {
|
|
data = {
|
|
status: {
|
|
code: request.status,
|
|
text: request.statusText
|
|
}
|
|
};
|
|
}
|
|
}
|
|
if (data.status.code < 500) {
|
|
callback(data);
|
|
} else {
|
|
var $dialog = new Ox.Dialog({
|
|
title: 'Application Error',
|
|
buttons: [
|
|
{
|
|
title: 'Details',
|
|
click: function() {
|
|
$dialog.close(function() {
|
|
debug(request);
|
|
});
|
|
}
|
|
},
|
|
{
|
|
title: 'Close',
|
|
click: function() {
|
|
$dialog.close(function() {
|
|
callback(data);
|
|
});
|
|
}
|
|
}
|
|
],
|
|
width: 400,
|
|
height: 200
|
|
})
|
|
.append('Sorry, we have encountered an application error while handling your request. To help us find out what went wrong, you may want to report this error to an administrator. Otherwise, please try again later.')
|
|
.open();
|
|
// fixme: change this to Send / Don't Send
|
|
Ox.print({
|
|
request: request,
|
|
status: status,
|
|
error: error
|
|
});
|
|
}
|
|
pending[options.id] = false;
|
|
}
|
|
|
|
function success(data) {
|
|
pending[options.id] = false;
|
|
cache[req] = {
|
|
data: data,
|
|
time: Ox.getTime()
|
|
};
|
|
callback(data);
|
|
}
|
|
|
|
if (pending[options.id]) {
|
|
setTimeout(function() {
|
|
Ox.Request.send(options);
|
|
}, 0);
|
|
} else {
|
|
requests[options.id] = {
|
|
url: options.url,
|
|
data: options.data
|
|
};
|
|
if (cache[req] && (options.age == -1 || options.age > Ox.getTime() - cache[req].time)) {
|
|
setTimeout(function() {
|
|
callback(cache[req].data);
|
|
}, 0);
|
|
} else {
|
|
pending[options.id] = true;
|
|
$.ajax({
|
|
data: options.data,
|
|
dataType: 'json',
|
|
error: error,
|
|
success: success,
|
|
timeout: options.timeout,
|
|
type: options.type,
|
|
url: options.url
|
|
});
|
|
Ox.print('request', options.data, Ox.length(requests));
|
|
Ox.length(requests) == 1 && Ox.Event.trigger('', 'requestStart');
|
|
}
|
|
}
|
|
|
|
return options.id;
|
|
|
|
},
|
|
|
|
requests: function() {
|
|
return Ox.length(requests);
|
|
}
|
|
|
|
};
|
|
}();
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Theme
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Theme = function() {
|
|
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.URL
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
============================================================================
|
|
Core
|
|
============================================================================
|
|
*/
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Container
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
// fixme: wouldn't it be better to let the elements be,
|
|
// rather then $element, $content, and potentially others,
|
|
// 0, 1, 2, etc, so that append would append 0, and appendTo
|
|
// would append (length - 1)?
|
|
Ox.Container = function(options, self) {
|
|
var that = new Ox.Element('div', self)
|
|
.options(options || {})
|
|
.addClass('OxContainer');
|
|
that.$content = new Ox.Element('div', self)
|
|
.options(options || {})
|
|
.addClass('OxContent')
|
|
.appendTo(that);
|
|
return that;
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Element
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
// check out http://ejohn.org/apps/learn/#36 (-#38, making fns work w/o new)
|
|
|
|
Ox.Element = function() {
|
|
|
|
return function(options, self) {
|
|
|
|
// construct
|
|
options = options || {};
|
|
self = self || {};
|
|
var that = this;
|
|
|
|
// init
|
|
(function() {
|
|
// allow for Ox.Widget('tagname', self)
|
|
if (typeof options == 'string') {
|
|
options = {
|
|
element: options
|
|
};
|
|
}
|
|
that.ox = Ox.version;
|
|
that.id = Ox.uid();
|
|
that.$element = $('<' + (options.element || 'div') + '/>', {
|
|
data: {
|
|
ox: that.id
|
|
}
|
|
});
|
|
$elements[that.id] = that;
|
|
wrapjQuery();
|
|
})();
|
|
|
|
// private
|
|
function wrapjQuery() {
|
|
$.each(oxui.jQueryFunctions, function(i, fn) {
|
|
that[fn] = function() {
|
|
var args = arguments,
|
|
length = args.length,
|
|
id, ret;
|
|
$.each(args, function(i, arg) {
|
|
if (Ox.isUndefined(arg)) {
|
|
Ox.print('fn', fn, 'undefined argument')
|
|
}
|
|
// if an ox object was passed
|
|
// then pass its $element instead
|
|
// so we can do oxObj.jqFn(oxObj)
|
|
if (arg.ox) {
|
|
args[i] = arg.$element;
|
|
}
|
|
/*
|
|
if (arg.ox) { // fixme: or is this too much magic?
|
|
if (fn == 'appendTo' && arg.$content) {
|
|
args[i] = arg.$content
|
|
} else {
|
|
args[i] = arg.$element;
|
|
}
|
|
}
|
|
*/
|
|
});
|
|
/*
|
|
if (fn == 'html' && that.$content) { // fixme: or is this too much magic?
|
|
$element = that.$content;
|
|
} else {
|
|
$element = that.$element;
|
|
}
|
|
*/
|
|
// why does this not work? (that?)
|
|
// ret = that.$element[fn].apply(this, arguments);
|
|
if (length == 0) {
|
|
ret = that.$element[fn]();
|
|
} else if (length == 1) {
|
|
ret = that.$element[fn](args[0]);
|
|
} else if (length == 2) {
|
|
ret = that.$element[fn](args[0], args[1]);
|
|
} else if (length == 3) {
|
|
ret = that.$element[fn](args[0], args[1], args[2]);
|
|
} else if (length == 4) {
|
|
ret = that.$element[fn](args[0], args[1], args[2], args[3]);
|
|
}
|
|
if (fn == 'data') {
|
|
// Ox.print('data ret', ret, $(ret))
|
|
}
|
|
// if the $element of an ox object was returned
|
|
// then return the ox object instead
|
|
// so we can do oxObj.jqFn().oxFn()
|
|
return ret.jquery && $elements[id = ret.data('ox')] ?
|
|
$elements[id] : ret;
|
|
}
|
|
});
|
|
}
|
|
|
|
// shared
|
|
self.onChange = function() {
|
|
// self.onChange(key, value)
|
|
// is called when an option changes
|
|
// (to be implemented by widget)
|
|
};
|
|
|
|
// public
|
|
|
|
that.addEvent = function() {
|
|
/*
|
|
adds a keyboard event, to be bound when focused
|
|
addEvent(event, fn) or addEvent({event0: fn0, event1: fn1, ...})
|
|
*/
|
|
if (arguments.length == 1) {
|
|
$.each(arguments[0], function(event, fn) {
|
|
Ox.Event.add(self.options.id, event, fn);
|
|
});
|
|
} else {
|
|
Ox.Event.add(self.options.id, arguments[0], arguments[1]);
|
|
}
|
|
return that;
|
|
};
|
|
|
|
that.bindEvent = function() {
|
|
/*
|
|
binds a function to an event triggered by this object
|
|
bindEvent(event, fn) or bindEvent({event0: fn0, event1: fn1, ...})
|
|
*/
|
|
if (arguments.length == 1) {
|
|
$.each(arguments[0], function(event, fn) {
|
|
Ox.Event.bind(self.options.id, event, fn);
|
|
});
|
|
} else {
|
|
Ox.Event.bind(self.options.id, arguments[0], arguments[1]);
|
|
}
|
|
return that;
|
|
}
|
|
|
|
that.defaults = function(defaults) {
|
|
/*
|
|
that.defaults({foo: x}) sets self.defaults
|
|
*/
|
|
self.defaults = defaults;
|
|
delete self.options; // fixme: hackish fix for that = Ox.Foo({...}, self).defaults({...}).options({...})
|
|
return that;
|
|
};
|
|
|
|
that.gainFocus = function() {
|
|
Ox.Focus.focus(that.id);
|
|
return that;
|
|
};
|
|
|
|
that.hasFocus = function() {
|
|
return Ox.Focus.focused() == that.id;
|
|
};
|
|
|
|
that.loseFocus = function() {
|
|
Ox.Focus.blur(that.id);
|
|
return that;
|
|
};
|
|
|
|
that.options = function() { // fixme: use Ox.getset
|
|
/*
|
|
that.options() returns self.options
|
|
that.options('foo') returns self.options.foo
|
|
that.options('foo', x) sets self.options.foo,
|
|
returns that
|
|
that.options({foo: x, bar: y}) sets self.options.foo
|
|
and self.options.bar,
|
|
returns that
|
|
*/
|
|
var length = arguments.length,
|
|
id = self.options && self.options.id,
|
|
args, ret;
|
|
if (length == 0) {
|
|
// options()
|
|
ret = self.options || options; // this is silly. make sure self.options get populated with options
|
|
} else if (length == 1 && typeof arguments[0] == 'string') {
|
|
// options(str)
|
|
ret = self.options ? self.options[arguments[0]] : options[arguments[0]];
|
|
} else {
|
|
// options (str, val) or options({str: val, ...})
|
|
// translate (str, val) to ({str: val})
|
|
args = Ox.makeObject.apply(that, arguments);
|
|
/*
|
|
options = self.options;
|
|
*/
|
|
// if options have not been set, extend defaults,
|
|
// otherwise, extend options
|
|
self.options = $.extend(self.options || self.defaults, args);
|
|
$.each(args, function(key, value) {
|
|
key == 'id' && id && Ox.Event.changeId(id, value);
|
|
self.onChange(key, value);
|
|
/*
|
|
fixme: why does this not work?
|
|
Ox.print('options', options, key, value)
|
|
//Ox.print(!options, !options || !options[key], !options || !options[key] || options[key] !== value)
|
|
if (!options || !options[key] || options[key] !== value) {
|
|
Ox.print('onChange...')
|
|
self.onChange(key, value);
|
|
} else {
|
|
Ox.print('NO CHANGE');
|
|
}
|
|
*/
|
|
});
|
|
ret = that;
|
|
}
|
|
return ret;
|
|
};
|
|
|
|
that.remove = function() {
|
|
that.$element.remove();
|
|
delete $elements[that.ox];
|
|
return that;
|
|
};
|
|
|
|
that.removeEvent = function() {
|
|
/*
|
|
removes a keyboard event
|
|
removeEvent(event, fn) or removeEvent({event0: fn0, event1: fn1, ...})
|
|
*/
|
|
if (arguments.length == 1) {
|
|
$.each(arguments[0], function(event, fn) {
|
|
Ox.Event.remove(self.options.id, event, fn);
|
|
});
|
|
} else {
|
|
Ox.Event.remove(self.options.id, arguments[0], arguments[1]);
|
|
}
|
|
return that;
|
|
};
|
|
|
|
that.triggerEvent = function() {
|
|
/*
|
|
triggers an event
|
|
triggerEvent(event) or triggerEvent(event, data) or triggerEvent({event0: data, event1: data, ...})
|
|
*/
|
|
if (Ox.isObject(arguments[0])) {
|
|
$.each(arguments[0], function(event, data) {
|
|
Ox.Event.trigger(self.options.id, event, data);
|
|
});
|
|
} else {
|
|
Ox.Event.trigger(self.options.id, arguments[0], arguments[1] || {});
|
|
}
|
|
return that;
|
|
};
|
|
|
|
that.unbindEvent = function() {
|
|
/*
|
|
unbinds an event
|
|
unbindEvent(event, fn) or unbindEvent({event0: fn0, event1: fn1, ...})
|
|
*/
|
|
if (arguments.length == 1) {
|
|
$.each(arguments[0], function(event, fn) {
|
|
Ox.Event.unbind(self.options.id, event, fn);
|
|
});
|
|
} else {
|
|
Ox.Event.unbind(self.options.id, arguments[0], arguments[1]);
|
|
}
|
|
return that;
|
|
};
|
|
|
|
// return
|
|
return that;
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
Ox._Element = function(element) {
|
|
var that = this;
|
|
that.def = {};
|
|
that.opt = {};
|
|
that.ox = Ox.version;
|
|
that.id = Ox.uid();
|
|
//Ox.print('that.id', that.id)
|
|
that.$element = $('<' + (element || 'div') + '/>')
|
|
//.addClass('OxElement')
|
|
.data('ox', that.id);
|
|
oxui.elements[that.id] = that;
|
|
// Ox.print('oxui.elements', oxui.elements)
|
|
//function setOption() {};
|
|
that.setOption = function() {};
|
|
/*
|
|
*/
|
|
that.destroy = function() {
|
|
that.$element.remove();
|
|
delete oxui.elements[that.ox];
|
|
}
|
|
/*
|
|
*/
|
|
that.disable = function() {
|
|
|
|
}
|
|
/*
|
|
*/
|
|
that.enable = function() {
|
|
|
|
}
|
|
/*
|
|
*/
|
|
///*
|
|
that.defaults = function() {
|
|
var length = arguments.length,
|
|
ret;
|
|
if (length == 0) {
|
|
ret = that.def
|
|
} else if (length == 1 && typeof arguments[0] == 'string') {
|
|
ret = that.def[arguments[0]];
|
|
} else {
|
|
// translate ('key', 'value') to {'key': 'value'}
|
|
that.def = $.extend(
|
|
that.def, Ox.makeObject.apply(that, arguments)
|
|
);
|
|
ret = that;
|
|
}
|
|
return ret;
|
|
}
|
|
//*/
|
|
/*
|
|
Ox.Element.options()
|
|
get options
|
|
Ox.Element.options('foo')
|
|
get options.foo
|
|
Ox.Element.options('foo', 0)
|
|
set options.foo
|
|
Ox.Element.options({foo: 0, bar: 1})
|
|
set options.foo and options.bar
|
|
*/
|
|
///*
|
|
that.options = function() {
|
|
var length = arguments.length,
|
|
args, ret;
|
|
if (length == 0) {
|
|
//Ox.print('getting all options', options);
|
|
ret = that.opt;
|
|
} else if (length == 1 && typeof arguments[0] == 'string') {
|
|
//Ox.print('getting one option', options, arguments[0], options[arguments[0]]);
|
|
ret = that.opt[arguments[0]];
|
|
} else {
|
|
// translate ('key', 'value') to {'key': 'value'}
|
|
args = Ox.makeObject.apply(that, arguments);
|
|
// if options have been set then extend options,
|
|
// otherwise extend defaults
|
|
that.opt = $.extend(Ox.length(that.opt) ?
|
|
that.opt : that.def, args);
|
|
// that.trigger('OxElement' + that.id + 'SetOptions', args);
|
|
$.each(args, function(k, v) {
|
|
that.setOption(k, v);
|
|
//Ox.print('triggering', 'OxElement' + that.id + 'SetOption', {k: v})
|
|
//that.trigger('OxElement' + that.id + 'SetOption', {k: v});
|
|
})
|
|
ret = that;
|
|
}
|
|
return ret;
|
|
}
|
|
// should become self.publish
|
|
that.publish = function(event, data) {
|
|
Ox.Event.publish(event + that.id, data);
|
|
return that;
|
|
}
|
|
that.subscribe = function(event, callback) {
|
|
Ox.Event.subscribe(event, callback);
|
|
return that;
|
|
}
|
|
//that.setOptions = function() {};
|
|
//*/
|
|
// wrap jquery functions
|
|
// so we can do oxObj.jqFn()
|
|
$.each(oxui.jqueryFunctions, function(i, v) {
|
|
that[v] = function() {
|
|
var args = arguments,
|
|
length = args.length,
|
|
$element, id, ret;
|
|
$.each(args, function(i, v) {
|
|
// if an oxui object was passed
|
|
// then pass its $element instead
|
|
// so we can do jqObj.append(oxObj)
|
|
if (v.ox) {
|
|
args[i] = v.$element;
|
|
}
|
|
});
|
|
if (v == 'html' && that.$content) {
|
|
$element = that.$content;
|
|
} else {
|
|
$element = that.$element;
|
|
}
|
|
// why does this not work?
|
|
// ret = that.$element[v].apply(this, arguments);
|
|
// maybe because we pass this, and not that.$element[v] ... ?
|
|
// ret = that.$element[v].apply(that.$element[v], arguments);
|
|
// doesn't work either ...
|
|
if (length == 0) {
|
|
ret = $element[v]();
|
|
} else if (length == 1) {
|
|
ret = $element[v](args[0]);
|
|
} else if (length == 2) {
|
|
ret = $element[v](args[0], args[1]);
|
|
} else if (length == 3) {
|
|
ret = $element[v](args[0], args[1], args[2]);
|
|
} else if (length == 4) {
|
|
ret = $element[v](args[0], args[1], args[2], args[3]);
|
|
}
|
|
// if the $element of an oxui object was returned
|
|
// then return the oxui object instead
|
|
// so we can do oxObj.jqFn().oxFn()
|
|
//Ox.print('v', v, 'arguments', arguments)
|
|
if (ret.jquery) {
|
|
//Ox.print('ret', ret, 'ret.data('id')', ret.data('ox'))
|
|
}
|
|
return ret.jquery && oxui.elements[id = ret.data('ox')] ?
|
|
oxui.elements[id] : ret;
|
|
}
|
|
});
|
|
return that;
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Window
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Window = function(options, self) {
|
|
|
|
self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
draggable: true,
|
|
fullscreenable: true, // fixme: silly name
|
|
height: 225,
|
|
resizeable: true,
|
|
scaleable: true,
|
|
width: 400
|
|
})
|
|
.options(options || {})
|
|
|
|
self.center = function() {
|
|
|
|
};
|
|
|
|
self.drag = function() {
|
|
|
|
};
|
|
|
|
self.fullscreen = function() {
|
|
|
|
};
|
|
|
|
self.onChange = function() {
|
|
|
|
};
|
|
|
|
self.reset = function() {
|
|
|
|
};
|
|
|
|
self.resize = function() {
|
|
|
|
};
|
|
|
|
self.scale = function() {
|
|
|
|
};
|
|
|
|
that.close = function() {
|
|
|
|
};
|
|
|
|
that.open = function() {
|
|
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.theme()
|
|
get theme
|
|
Ox.theme('foo')
|
|
set theme to 'foo'
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
// fixme: this should be Ox.Theme, and provide Ox.Theme.set(), Ox.Theme.load, etc.
|
|
Ox.theme = function() {
|
|
var length = arguments.length,
|
|
classes = $body.attr('class').split(' '),
|
|
arg, theme;
|
|
$.each(classes, function(i, v) {
|
|
if (Ox.startsWith(v, 'OxTheme')) {
|
|
theme = v.replace('OxTheme', '').toLowerCase();
|
|
if (length == 1) {
|
|
$body.removeClass(v);
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
if (length == 1) {
|
|
arg = arguments[0]
|
|
$body.addClass('OxTheme' + Ox.toTitleCase(arg));
|
|
if (theme) {
|
|
$('input[type=image]').each(function() {
|
|
var $this = $(this);
|
|
$this.attr({
|
|
src: $this.attr('src').replace(
|
|
'/ox.ui.' + theme + '/', '/ox.ui.' + arg + '/'
|
|
)
|
|
});
|
|
});
|
|
$('.OxLoadingIcon').each(function() {
|
|
var $this = $(this);
|
|
$this.attr({
|
|
src: $this.attr('src').replace(
|
|
'/ox.ui.' + theme + '/', '/ox.ui.' + arg + '/'
|
|
)
|
|
});
|
|
})
|
|
}
|
|
}
|
|
return theme;
|
|
};
|
|
|
|
/*
|
|
============================================================================
|
|
Bars
|
|
============================================================================
|
|
*/
|
|
|
|
Ox.Bar = function(options, self) {
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
orientation: 'horizontal',
|
|
size: 'medium' // can be int
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxBar Ox' + Ox.toTitleCase(self.options.orientation)),
|
|
dimensions = oxui.getDimensions(self.options.orientation);
|
|
self.options.size = Ox.isString(self.options.size) ?
|
|
oxui.getBarSize(self.options.size) : self.options.size;
|
|
that.css(dimensions[0], '100%')
|
|
.css(dimensions[1], self.options.size + 'px');
|
|
return that;
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Resizebar
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Resizebar = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
collapsed: false,
|
|
collapsible: true,
|
|
edge: 'left',
|
|
elements: [],
|
|
orientation: 'horizontal',
|
|
parent: null,
|
|
resizable: true,
|
|
resize: [],
|
|
size: 0
|
|
})
|
|
.options(options || {}) // fixme: options function should be able to handle undefined, no need for || {}
|
|
.addClass('OxResizebar Ox' + Ox.toTitleCase(self.options.orientation))
|
|
/*
|
|
.attr({
|
|
draggable: 'true'
|
|
})
|
|
.bind('dragstart', function(e) {
|
|
// e.originalEvent.dataTransfer.setDragImage($('<div>')[0], 0, 0);
|
|
})
|
|
.bind('drag', function(e) {
|
|
Ox.print('dragging', e)
|
|
})
|
|
*/
|
|
.mousedown(dragStart)
|
|
.dblclick(toggle)
|
|
.append($('<div>').addClass('OxSpace'))
|
|
.append($('<div>').addClass('OxLine'))
|
|
.append($('<div>').addClass('OxSpace'));
|
|
|
|
$.extend(self, {
|
|
clientXY: self.options.orientation == 'horizontal' ? 'clientY' : 'clientX',
|
|
dimensions: oxui.getDimensions(self.options.orientation), // fixme: should orientation be the opposite orientation here?
|
|
edges: oxui.getEdges(self.options.orientation),
|
|
ids: $.map(self.options.elements, function(v, i) {
|
|
return v.options('id');
|
|
}),
|
|
length: self.options.resize.length,
|
|
startPos: 0,
|
|
startSize: 0
|
|
});
|
|
|
|
function drag(e) {
|
|
var d = e[self.clientXY] - self.startPos, i;
|
|
self.options.size = Ox.limit(self.startSize + d, self.options.resize[0], self.options.resize[self.length - 1]);
|
|
Ox.print('sS', self.startSize, 'd', d, 's.o.s', self.options.size)
|
|
$.each(self.options.resize, function(i, v) {
|
|
if (self.options.size > v - 8 && self.options.size < v + 8) {
|
|
self.options.size = v;
|
|
return false;
|
|
}
|
|
});
|
|
that.css(self.edges[2], self.options.size + 'px');
|
|
self.options.elements[0].css(self.dimensions[1], self.options.size + 'px');
|
|
self.options.elements[1].css(self.edges[2], (self.options.size + 1) + 'px');
|
|
Ox.Event.trigger(self.ids[0], 'resize', self.options.size);
|
|
Ox.Event.trigger(self.ids[1], 'resize', self.options.elements[1][self.dimensions[1]]());
|
|
}
|
|
|
|
function dragStart(e) {
|
|
self.startPos = e[self.clientXY];
|
|
self.startSize = self.options.size;
|
|
Ox.print('startSize', self.startSize)
|
|
$window.mousemove(drag);
|
|
$window.one('mouseup', dragStop);
|
|
}
|
|
|
|
function dragStop() {
|
|
$window.unbind('mousemove');
|
|
}
|
|
|
|
function toggle() {
|
|
Ox.print('toggle');
|
|
if (Ox.isUndefined(self.options.position)) {
|
|
self.options.position = parseInt(self.options.parent.css(self.options.edge)) +
|
|
(self.options.collapsed ? self.options.size : 0);
|
|
}
|
|
var size = self.options.position -
|
|
(self.options.collapsed ? 0 : self.options.size),
|
|
animate = {};
|
|
Ox.print('s.o.e', self.options.edge);
|
|
animate[self.options.edge] = size;
|
|
self.options.parent.animate(animate, 200, function() {
|
|
var i = (self.options.edge == 'left' || self.options.edge == 'top') ? 1 : 0;
|
|
Ox.Event.trigger(self.ids[i], 'resize', self.options.elements[i][self.dimensions[1]]());
|
|
self.options.collapsed = !self.options.collapsed;
|
|
});
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Tabbar
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Tabbar = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Bar({
|
|
size: 20
|
|
}, self)
|
|
.defaults({
|
|
selected: 0,
|
|
tabs: []
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxTabbar');
|
|
|
|
Ox.ButtonGroup({
|
|
buttons: self.options.tabs,
|
|
group: true,
|
|
selectable: true,
|
|
selected: self.options.selected,
|
|
size: 'medium',
|
|
style: 'tab',
|
|
}).appendTo(that);
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
// fixme: no need for this
|
|
Ox.Toolbar = function(options, self) {
|
|
var self = self || {},
|
|
that = new Ox.Bar({
|
|
size: oxui.getBarSize(options.size)
|
|
}, self);
|
|
return that;
|
|
};
|
|
|
|
/*
|
|
============================================================================
|
|
Ox.Dialog
|
|
============================================================================
|
|
*/
|
|
|
|
Ox.Dialog = function(options, self) {
|
|
|
|
// fixme: dialog should be derived from a generic draggable
|
|
// fixme: pass button elements directly
|
|
// fixme: buttons should have a close attribute, or the dialog a close id
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
title: '',
|
|
buttons: [],
|
|
height: 216,
|
|
minHeight: 144,
|
|
minWidth: 256,
|
|
padding: 16,
|
|
width: 384
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxDialog')
|
|
.addEvent({
|
|
key_escape: function() {
|
|
that.close();
|
|
}
|
|
});
|
|
|
|
if (!Ox.isArray(self.options.buttons[0])) {
|
|
self.options.buttons = [[], self.options.buttons];
|
|
}
|
|
|
|
that.$titlebar = new Ox.Bar({
|
|
size: 'medium'
|
|
})
|
|
.addClass('OxTitleBar')
|
|
.mousedown(drag)
|
|
.dblclick(center)
|
|
.appendTo(that);
|
|
|
|
that.$title = new Ox.Element()
|
|
.addClass('OxTitle')
|
|
.html(self.options.title)
|
|
.appendTo(that.$titlebar);
|
|
|
|
// fixme: should the following be a container?
|
|
that.$content = new Ox.Element()
|
|
.addClass('OxContent')
|
|
.css({
|
|
padding: self.options.padding + 'px',
|
|
overflow: 'auto'
|
|
})
|
|
.appendTo(that);
|
|
|
|
that.$buttonsbar = new Ox.Bar({})
|
|
.addClass('OxButtonsBar')
|
|
.appendTo(that);
|
|
|
|
that.$buttons = [];
|
|
$.each(self.options.buttons[0], function(i, button) {
|
|
that.$buttons[i] = new Ox.Button({
|
|
disabled: button.disabled || false,
|
|
size: 'medium',
|
|
title: button.title
|
|
})
|
|
.addClass('OxLeft')
|
|
.click(button.click) // fixme: rather use event?
|
|
.appendTo(that.$buttonsbar);
|
|
});
|
|
|
|
that.$resize = new Ox.Element()
|
|
.addClass('OxResize')
|
|
.mousedown(resize)
|
|
.dblclick(reset)
|
|
.appendTo(that.$buttonsbar);
|
|
|
|
$.each(self.options.buttons[1].reverse(), function(i, button) {
|
|
that.$buttons[that.$buttons.length] = new Ox.Button({
|
|
disabled: button.disabled || false,
|
|
id: button.id,
|
|
size: 'medium',
|
|
title: button.title
|
|
})
|
|
.addClass('OxRight')
|
|
.click(button.click) // fixme: rather use event?
|
|
.appendTo(that.$buttonsbar);
|
|
});
|
|
|
|
that.$buttons[0].focus();
|
|
|
|
that.$layer = new Ox.Element() // fixme: Layer widget that would handle click?
|
|
.addClass('OxLayer')
|
|
.mousedown(mousedownLayer)
|
|
.mouseup(mouseupLayer);
|
|
|
|
function center() {
|
|
var documentHeight = $document.height();
|
|
that.css({
|
|
left: 0,
|
|
top: Math.max(parseInt(-documentHeight / 10), self.options.height - documentHeight + 40) + 'px',
|
|
right: 0,
|
|
bottom: 0,
|
|
margin: 'auto'
|
|
});
|
|
}
|
|
|
|
function drag(event) {
|
|
var bodyWidth = $body.width(),
|
|
bodyHeight = $document.height(),
|
|
elementWidth = that.width(),
|
|
offset = that.offset(),
|
|
x = event.clientX,
|
|
y = event.clientY;
|
|
$window.mousemove(function(event) {
|
|
that.css({
|
|
margin: 0
|
|
});
|
|
var left = Ox.limit(
|
|
offset.left - x + event.clientX,
|
|
24 - elementWidth, bodyWidth - 24
|
|
//0, documentWidth - elementWidth
|
|
),
|
|
top = Ox.limit(
|
|
offset.top - y + event.clientY,
|
|
24, bodyHeight - 24
|
|
//24, documentHeight - elementHeight
|
|
);
|
|
that.css({
|
|
left: left + 'px',
|
|
top: top + 'px'
|
|
});
|
|
});
|
|
$window.one('mouseup', function() {
|
|
$window.unbind('mousemove');
|
|
});
|
|
}
|
|
|
|
function getButtonById(id) {
|
|
var ret = null
|
|
$.each(that.$buttons, function(i, button) {
|
|
if (button.options('id') == id) {
|
|
ret = button;
|
|
return false;
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
function mousedownLayer() {
|
|
that.$layer.stop().animate({
|
|
opacity: 0.5
|
|
}, 0);
|
|
}
|
|
|
|
function mouseupLayer() {
|
|
that.$layer.stop().animate({
|
|
opacity: 0
|
|
}, 0);
|
|
}
|
|
|
|
function reset() {
|
|
that/*.css({
|
|
left: Math.max(that.offset().left, 24 - that.width())
|
|
})*/
|
|
.width(self.options.width)
|
|
.height(self.options.height);
|
|
that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically
|
|
}
|
|
|
|
function resize(event) {
|
|
var documentWidth = $document.width(),
|
|
documentHeight = $document.height(),
|
|
elementWidth = that.width(),
|
|
elementHeight = that.height(),
|
|
offset = that.offset(),
|
|
x = event.clientX,
|
|
y = event.clientY;
|
|
$window.mousemove(function(event) {
|
|
that.css({
|
|
left: offset.left,
|
|
top: offset.top,
|
|
margin: 0
|
|
});
|
|
var width = Ox.limit(
|
|
elementWidth - x + event.clientX,
|
|
self.options.minWidth, Math.min(documentWidth, documentWidth - offset.left)
|
|
),
|
|
height = Ox.limit(
|
|
elementHeight - y + event.clientY,
|
|
self.options.minHeight, documentHeight - offset.top
|
|
);
|
|
that.width(width);
|
|
that.height(height);
|
|
that.$content.height(height - 48 - 2 * self.options.padding); // fixme: this should happen automatically
|
|
});
|
|
$window.one('mouseup', function() {
|
|
$window.unbind('mousemove');
|
|
});
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'height' || key == 'width') {
|
|
that.animate({
|
|
height: self.options.height + 'px',
|
|
width: self.options.width + 'px'
|
|
}, 250);
|
|
that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically
|
|
} else if (key == 'title') {
|
|
that.$title.animate({
|
|
opacity: 0
|
|
}, 250, function() {
|
|
that.$title.html(value).animate({
|
|
opacity: 1
|
|
}, 250);
|
|
});
|
|
}
|
|
}
|
|
|
|
that.append = function($element) {
|
|
that.$content.append($element);
|
|
return that;
|
|
}
|
|
|
|
that.close = function(callback) {
|
|
callback = callback || function() {};
|
|
that.animate({
|
|
opacity: 0
|
|
}, 200, function() {
|
|
that.remove();
|
|
that.$layer.remove();
|
|
callback();
|
|
});
|
|
$window.unbind('mouseup', mouseupLayer)
|
|
return that;
|
|
}
|
|
|
|
that.disable = function() {
|
|
// to be used on submit of form, like login
|
|
that.$layer.addClass('OxFront');
|
|
return that;
|
|
};
|
|
|
|
that.disableButton = function(id) {
|
|
getButtonById(id).options({
|
|
disabled: true
|
|
});
|
|
return that;
|
|
};
|
|
|
|
that.enable = function() {
|
|
that.$layer.removeClass('OxFront');
|
|
return that;
|
|
};
|
|
|
|
that.enableButton = function(id) {
|
|
getButtonById(id).options({
|
|
disabled: false
|
|
});
|
|
return that;
|
|
};
|
|
|
|
that.open = function() {
|
|
that.$layer.appendTo($body);
|
|
that.css({
|
|
opacity: 0
|
|
}).appendTo($body).animate({
|
|
opacity: 1
|
|
}, 200);
|
|
center();
|
|
reset();
|
|
// fixme: reenable, but implement preview-style dialog
|
|
//that.gainFocus();
|
|
$window.bind('mouseup', mouseupLayer)
|
|
return that;
|
|
};
|
|
|
|
return that;
|
|
|
|
}
|
|
|
|
/*
|
|
============================================================================
|
|
Forms
|
|
============================================================================
|
|
*/
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Filter
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Filter = function(options, self) {
|
|
|
|
var self = self || {}
|
|
that = new Ox.Element()
|
|
.defaults({
|
|
|
|
})
|
|
.options(options || {});
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Form
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Form = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
error: '',
|
|
id: '',
|
|
items: [],
|
|
submit: null
|
|
})
|
|
.options(options || {}); // fixme: the || {} can be done once, in the options function
|
|
|
|
$.extend(self, {
|
|
$items: [],
|
|
$messages: [],
|
|
formIsValid: false,
|
|
itemIds: [],
|
|
itemIsValid: []
|
|
});
|
|
|
|
// fixme: form isn't necessarily empty/invalid
|
|
$.map(self.options.items, function(item, i) {
|
|
self.itemIds[i] = item.id || item.element.options('id');
|
|
self.itemIsValid[i] = false;
|
|
});
|
|
|
|
$.each(self.options.items, function(i, item) {
|
|
var id = item.element.options('id');
|
|
that.append(self.$items[i] = new Ox.FormItem(item))
|
|
.append(self.$messages[i] = new Ox.Element().addClass('OxFormMessage'));
|
|
// fixme: use widget.bindEvent()
|
|
Ox.Event.bind(id, 'validate', function(event, data) {
|
|
validate(i, data.valid);
|
|
});
|
|
Ox.Event.bind(id, 'blur', function(event, data) {
|
|
validate(i, data.valid);
|
|
if (data.valid) {
|
|
self.$messages[i].html('').hide();
|
|
} else {
|
|
self.$messages[i].html(data.message).show();
|
|
}
|
|
});
|
|
Ox.Event.bind(id, 'submit', function(event, data) {
|
|
self.formIsValid && that.submit();
|
|
});
|
|
});
|
|
|
|
function getItemPositionById(id) {
|
|
return self.itemIds.indexOf(id);
|
|
}
|
|
|
|
function setMessage(id, message) {
|
|
self.$messages[getItemPositionById(id)].html(message)[message !== '' ? 'show' : 'hide']();
|
|
}
|
|
|
|
function submitCallback(data) {
|
|
$.each(data, function(i, v) {
|
|
setMessage(v.id, v.message);
|
|
});
|
|
}
|
|
|
|
function validate(pos, valid) {
|
|
Ox.print('validate', pos, valid)
|
|
self.itemIsValid[pos] = valid;
|
|
if (Ox.every(self.itemIsValid) != self.formIsValid) {
|
|
self.formIsValid = !self.formIsValid;
|
|
that.triggerEvent('validate', {
|
|
valid: self.formIsValid
|
|
});
|
|
}
|
|
}
|
|
|
|
that.submit = function() {
|
|
self.options.submit(that.values(), submitCallback);
|
|
};
|
|
|
|
that.values = function() { // fixme: can this be private?
|
|
var values = {};
|
|
if (arguments.length == 0) {
|
|
$.each(self.$items, function(i, $item) {
|
|
values[self.itemIds[i]] = self.$items[i].value();
|
|
});
|
|
return values;
|
|
} else {
|
|
$.each(arguments[0], function(key, value) {
|
|
|
|
});
|
|
return that;
|
|
}
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.FormItem = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
element: null,
|
|
error: '',
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxFormItem')
|
|
.append(self.options.element);
|
|
|
|
that.value = function() {
|
|
return self.options.element.$input.val();
|
|
};
|
|
|
|
return that;
|
|
|
|
}
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Form Elements
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Button = function(options, self) {
|
|
/*
|
|
events:
|
|
click non-selectable button was clicked
|
|
deselect selectable button was deselected
|
|
select selectable button was selected
|
|
*/
|
|
var self = self || {},
|
|
that = new Ox.Element('input', self)
|
|
.defaults({
|
|
disabled: false,
|
|
group: false,
|
|
id: '',
|
|
overlap: 'none',
|
|
selectable: false,
|
|
selected: false,
|
|
size: 'medium',
|
|
// fixme: 'default' or ''?
|
|
style: 'default', // can be default, checkbox, symbol, or tab
|
|
title: '',
|
|
tooltip: '',
|
|
type: 'text',
|
|
width: 'auto'
|
|
})
|
|
.options(options || {})
|
|
.attr({
|
|
disabled: self.options.disabled ? 'disabled' : '',
|
|
type: self.options.type == 'text' ? 'button' : 'image'
|
|
})
|
|
.addClass('OxButton Ox' + Ox.toTitleCase(self.options.size) +
|
|
(self.options.disabled ? ' OxDisabled': '') +
|
|
(self.options.selected ? ' OxSelected': '') +
|
|
(self.options.style != 'default' ? ' Ox' + Ox.toTitleCase(self.options.style) : '') +
|
|
(self.options.overlap != 'none' ? ' OxOverlap' + Ox.toTitleCase(self.options.overlap) : ''))
|
|
.css(self.options.width == 'auto' ? {} : {
|
|
width: (self.options.width - 14) + 'px'
|
|
})
|
|
.mousedown(mousedown)
|
|
.click(click);
|
|
|
|
$.extend(self, Ox.isArray(self.options.title) ? {
|
|
selectedTitle: Ox.setPropertyOnce(self.options.title, 'selected'),
|
|
titles: self.options.title
|
|
} : {
|
|
selectedTitle: 0,
|
|
titles: [{
|
|
id: '',
|
|
title: self.options.title
|
|
}]
|
|
});
|
|
|
|
setTitle(self.titles[self.selectedTitle].title);
|
|
|
|
if (self.options.tooltip) {
|
|
self.tooltips = Ox.isArray(self.options.tooltip) ? self.options.tooltip : [self.options.tooltip];
|
|
self.$tooltip = new Ox.Tooltip({
|
|
title: self.tooltips[self.selectedTitle]
|
|
});
|
|
that.mouseenter(mouseenter)
|
|
.mouseleave(mouseleave);
|
|
}
|
|
|
|
function click() {
|
|
var data = self.titles[self.selectedTitle];
|
|
if (!self.options.selectable) {
|
|
that.triggerEvent('click', data);
|
|
} else {
|
|
if (self.options.group) {
|
|
that.triggerEvent('select', data);
|
|
} else {
|
|
that.toggleSelected();
|
|
}
|
|
}
|
|
if (self.titles.length == 2) {
|
|
that.toggleTitle();
|
|
}
|
|
}
|
|
|
|
function mousedown(event) {
|
|
if (self.options.type == 'image' && $.browser.safari) {
|
|
// keep image from being draggable
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
|
|
function mouseenter(event) {
|
|
self.$tooltip.show(event.clientX, event.clientY);
|
|
}
|
|
|
|
function mouseleave(event) {
|
|
self.$tooltip.hide();
|
|
}
|
|
|
|
function setTitle(title) {
|
|
self.title = title;
|
|
if (self.options.type == 'image') {
|
|
that.attr({
|
|
src: oxui.path + 'png/ox.ui.' + Ox.theme() +
|
|
'/symbol' + Ox.toTitleCase(title) + '.png'
|
|
});
|
|
} else {
|
|
that.val(title);
|
|
}
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'disabled') {
|
|
that.attr({
|
|
disabled: value ? 'disabled' : ''
|
|
})
|
|
.toggleClass('OxDisabled');
|
|
} else if (key == 'selected') {
|
|
if (value != that.hasClass('OxSelected')) { // fixme: neccessary?
|
|
that.toggleClass('OxSelected');
|
|
}
|
|
that.triggerEvent('change');
|
|
} else if (key == 'title') {
|
|
setTitle(value);
|
|
} else if (key == 'width') {
|
|
that.$element.css({
|
|
width: (value - 14) + 'px'
|
|
});
|
|
}
|
|
}
|
|
|
|
that.toggleDisabled = function() {
|
|
that.options({
|
|
enabled: !self.options.disabled
|
|
});
|
|
}
|
|
|
|
that.toggleSelected = function() {
|
|
that.options({
|
|
selected: !self.options.selected
|
|
});
|
|
}
|
|
|
|
that.toggleTitle = function() {
|
|
self.selectedTitle = 1 - self.selectedTitle;
|
|
setTitle(self.titles[self.selectedTitle].title);
|
|
self.$tooltip && self.$tooltip.options({
|
|
title: self.tooltips[self.selectedTitle]
|
|
});
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.ButtonGroup = function(options, self) {
|
|
|
|
/*
|
|
Ox.ButtonGroup
|
|
options:
|
|
buttons array of buttons
|
|
max integer, maximum number of selected buttons, 0 for all
|
|
min integer, minimum number of selected buttons, 0 for none
|
|
selectable if true, buttons are selectable
|
|
type string, 'image' or 'text'
|
|
events:
|
|
change {id, value} selection within a group changed
|
|
*/
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
buttons: [],
|
|
max: 1,
|
|
min: 1,
|
|
selectable: false,
|
|
size: 'medium',
|
|
style: '',
|
|
type: 'text',
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxButtonGroup');
|
|
|
|
if (self.options.selectable) {
|
|
self.optionGroup = new Ox.OptionGroup(
|
|
self.options.buttons,
|
|
self.options.min,
|
|
self.options.max,
|
|
'selected'
|
|
);
|
|
self.options.buttons = self.optionGroup.init();
|
|
}
|
|
|
|
self.$buttons = [];
|
|
$.each(self.options.buttons, function(position, button) {
|
|
var id = self.options.id + Ox.toTitleCase(button.id)
|
|
self.$buttons[position] = Ox.Button({
|
|
disabled: button.disabled,
|
|
group: true,
|
|
id: id,
|
|
selectable: self.options.selectable,
|
|
selected: button.selected,
|
|
size: self.options.size,
|
|
style: self.options.style,
|
|
title: button.title,
|
|
type: self.options.type
|
|
})
|
|
.bindEvent('select', function() {
|
|
selectButton(position);
|
|
})
|
|
.appendTo(that);
|
|
});
|
|
|
|
function selectButton(pos) {
|
|
var toggled = self.optionGroup.toggle(pos);
|
|
if (toggled.length) {
|
|
$.each(toggled, function(i, pos) {
|
|
self.$buttons[pos].toggleSelected();
|
|
});
|
|
that.triggerEvent('change', {
|
|
selected: $.map(self.optionGroup.selected(), function(v, i) {
|
|
return self.options.buttons[v].id;
|
|
})
|
|
});
|
|
}
|
|
}
|
|
|
|
return that;
|
|
};
|
|
|
|
Ox.Checkbox = function(options, self) {
|
|
|
|
/***
|
|
Ox.Checkbox
|
|
Checkbox Form Element
|
|
Options:
|
|
disabled boolean, if true, checkbox is disabled
|
|
id element id
|
|
group boolean, if true, checkbox is part of a group
|
|
checked boolean, if true, checkbox is checked
|
|
title string, text on label
|
|
width integer, width in px
|
|
Methods:
|
|
toggleChecked function()
|
|
toggles checked property
|
|
returns that
|
|
Events:
|
|
change triggered when checked property changes
|
|
passes {checked, id, title}
|
|
***/
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
disabled: false,
|
|
id: '',
|
|
group: false,
|
|
checked: false,
|
|
title: '',
|
|
width: 'auto'
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxCheckbox')
|
|
.attr(self.options.disabled ? {
|
|
disabled: 'disabled'
|
|
} : {});
|
|
|
|
if (self.options.title) {
|
|
self.options.width != 'auto' && that.css({
|
|
width: self.options.width + 'px'
|
|
});
|
|
self.$title = new Ox.Label({
|
|
disabled: self.options.disabled,
|
|
id: self.options.id + 'Label',
|
|
overlap: 'left',
|
|
title: self.options.title,
|
|
width: self.options.width - 16
|
|
})
|
|
.css({
|
|
float: 'right'
|
|
})
|
|
.click(clickTitle)
|
|
.appendTo(that);
|
|
}
|
|
|
|
self.$button = new Ox.Button({
|
|
disabled: self.options.disabled,
|
|
id: self.options.id + 'Button',
|
|
title: [
|
|
{id: 'none', title: 'none', selected: !self.options.checked},
|
|
{id: 'check', title: 'check', selected: self.options.checked}
|
|
],
|
|
type: 'image'
|
|
})
|
|
.addClass('OxCheckbox')
|
|
.click(clickButton)
|
|
.appendTo(that);
|
|
|
|
function clickButton() {
|
|
self.options.checked = !self.options.checked;
|
|
// click will have toggled the button,
|
|
// if it is part of a group, we have to revert that
|
|
self.options.group && that.toggleChecked();
|
|
that.triggerEvent('change', {
|
|
checked: self.options.checked,
|
|
id: self.options.id,
|
|
title: self.options.title
|
|
});
|
|
}
|
|
|
|
function clickTitle() {
|
|
!self.options.disabled && self.$button.trigger('click');
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'checked') {
|
|
that.toggleChecked();
|
|
}
|
|
};
|
|
|
|
that.toggleChecked = function() {
|
|
self.$button.toggleTitle();
|
|
return that;
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.CheckboxGroup = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
checkboxes: [],
|
|
max: 1,
|
|
min: 1,
|
|
width: 256
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxCheckboxGroup');
|
|
|
|
self.optionGroup = new Ox.OptionGroup(
|
|
self.options.checkboxes,
|
|
self.options.min,
|
|
self.options.max);
|
|
self.options.checkboxes = self.optionGroup.init();
|
|
|
|
$.extend(self, {
|
|
$checkboxes: [],
|
|
checkboxWidth: $.map(Ox.divideInt(
|
|
self.options.width + (self.options.checkboxes.length - 1) * 6,
|
|
self.options.checkboxes.length
|
|
), function(v, i) {
|
|
return v + (i < self.options.checkboxes.length - 1 ? 10 : 0);
|
|
})
|
|
});
|
|
$.each(self.options.checkboxes, function(position, checkbox) {
|
|
var id = self.options.id + Ox.toTitleCase(checkbox.id)
|
|
self.$checkboxes[position] = new Ox.Checkbox($.extend(checkbox, {
|
|
group: true,
|
|
id: id,
|
|
width: self.checkboxWidth[position]
|
|
}))
|
|
.bindEvent('change', function() {
|
|
change(position);
|
|
})
|
|
.appendTo(that);
|
|
});
|
|
|
|
function change(pos) {
|
|
var toggled = self.optionGroup.toggle(pos);
|
|
//Ox.print('change', pos, 'toggled', toggled)
|
|
if (toggled.length) {
|
|
$.each(toggled, function(i, pos) {
|
|
self.$checkboxes[pos].toggleChecked();
|
|
});
|
|
that.triggerEvent('change', {
|
|
checked: $.map(self.optionGroup.checked(), function(v, i) {
|
|
return self.options.checkboxes[v].id;
|
|
})
|
|
});
|
|
}
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.Input = function(options, self) {
|
|
|
|
/*
|
|
options:
|
|
arrows boolearn, if true, and type is 'float' or 'integer', display arrows
|
|
arrowStep number, step when clicking arrows
|
|
autocomplete array of possible values, or
|
|
function(key, value, callback), returns one or more values
|
|
autocompleteReplace boolean, if true, value is replaced
|
|
autocompleteReplaceCorrect boolean, if true, only valid values can be entered
|
|
autocompleteSelect boolean, if true, menu is displayed
|
|
autocompleteSelectHighlight boolean, if true, value in menu is highlighted
|
|
autocompleteSelectSubmit boolean, if true, submit input on menu selection
|
|
autocorrect string ('email', 'float', 'integer', 'phone', 'url'), or
|
|
regexp(value), or
|
|
function(key, value, blur, callback), returns value
|
|
auto validate --remote validation--
|
|
clear boolean, if true, has clear button
|
|
disabled boolean, if true, is disabled
|
|
height integer, px (for type='textarea' and type='range' with orientation='horizontal')
|
|
id string, element id
|
|
key string, to be passed to autocomplete and autovalidate functions
|
|
max number, max value if type is 'integer' or 'float'
|
|
min number, min value if type is 'integer' or 'float'
|
|
name string, will be displayed by autovalidate function ('invalid ' + name)
|
|
overlap string, '', 'left' or 'right', will cause padding and negative margin
|
|
picker
|
|
//rangeOptions
|
|
arrows boolean, if true, display arrows
|
|
//arrowStep number, step when clicking arrows
|
|
//arrowSymbols array of two strings
|
|
max number, maximum value
|
|
min number, minimum value
|
|
orientation 'horizontal' or 'vertical'
|
|
step number, step
|
|
thumbValue boolean, if true, value is displayed on thumb, or
|
|
array of strings per value, or
|
|
function(value), returns string
|
|
thumbSize integer, px
|
|
trackGradient string, css gradient for track
|
|
trackImage string, image url, or
|
|
array of image urls
|
|
//trackStep number, 0 for 'scroll here', positive for step
|
|
trackValues boolean
|
|
serialize
|
|
textAlign 'left', 'center' or 'right'
|
|
type 'float', 'integer', 'password', 'text'
|
|
value string
|
|
width integer, px
|
|
methods:
|
|
events:
|
|
change
|
|
submit
|
|
*/
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
arrows: false,
|
|
arrowStep: 1,
|
|
autocomplete: null,
|
|
autocompleteReplace: false,
|
|
autocompleteReplaceCorrect: false,
|
|
autocompleteSelect: false,
|
|
autocompleteSelectHighlight: false,
|
|
autocompleteSelectSubmit: false,
|
|
autovalidate: null,
|
|
clear: false,
|
|
key: '',
|
|
min: 0,
|
|
max: 100,
|
|
label: '',
|
|
labelWidth: 64,
|
|
overlap: 'none',
|
|
placeholder: '',
|
|
serialize: null,
|
|
textAlign: 'left',
|
|
type: 'text',
|
|
value: '',
|
|
width: 128
|
|
})
|
|
.options(options)
|
|
.addClass('OxInput OxMedium')
|
|
.addEvent($.extend(self.options.type == 'textarea' ? {} : {
|
|
key_enter: submit
|
|
}, {
|
|
key_escape: cancel
|
|
}));
|
|
|
|
if (
|
|
Ox.isArray(self.options.autocomplete) &&
|
|
self.options.autocompleteReplace &&
|
|
self.options.autocompleteReplaceCorrect &&
|
|
self.options.value === ''
|
|
) {
|
|
self.options.value = self.options.autocomplete[0]
|
|
}
|
|
|
|
// fixme: set to min, not 0
|
|
if (self.options.type == 'float') {
|
|
$.extend(self.options, {
|
|
autovalidate: 'float',
|
|
textAlign: 'right',
|
|
value: self.options.value || '0.0'
|
|
});
|
|
} else if (self.options.type == 'integer') {
|
|
$.extend(self.options, {
|
|
autovalidate: 'integer',
|
|
textAlign: 'right',
|
|
value: self.options.value || '0'
|
|
});
|
|
}
|
|
|
|
if (self.options.label) {
|
|
self.$label = new Ox.Label({
|
|
overlap: 'right',
|
|
textAlign: 'right',
|
|
title: self.options.label,
|
|
width: self.options.labelWidth
|
|
})
|
|
.css({
|
|
float: 'left', // fixme: use css rule
|
|
})
|
|
.click(function() {
|
|
that.focus();
|
|
})
|
|
.appendTo(that);
|
|
}
|
|
|
|
if (self.options.arrows) {
|
|
self.arrows = [];
|
|
self.arrows[0] = [
|
|
new Ox.Button({
|
|
overlap: 'right',
|
|
title: 'previous',
|
|
type: 'image'
|
|
})
|
|
.css({
|
|
float: 'left'
|
|
})
|
|
.click(function() {
|
|
clickArrow(0);
|
|
})
|
|
.appendTo(that),
|
|
new Ox.Button({
|
|
overlap: 'left',
|
|
title: 'next',
|
|
type: 'image'
|
|
})
|
|
.css({
|
|
float: 'right'
|
|
})
|
|
.click(function() {
|
|
clickArrow(1);
|
|
})
|
|
.appendTo(that)
|
|
]
|
|
}
|
|
|
|
$.extend(self, {
|
|
bindKeyboard: self.options.autocomplete || self.options.autovalidate,
|
|
hasPasswordPlaceholder: self.options.type == 'password' && self.options.placeholder,
|
|
inputWidth: getInputWidth()
|
|
});
|
|
|
|
if (self.options.clear) {
|
|
self.$button = new Ox.Button({
|
|
overlap: 'left',
|
|
title: 'clear',
|
|
type: 'image'
|
|
})
|
|
.css({
|
|
float: 'right' // fixme: use css rule
|
|
})
|
|
.click(clear)
|
|
.appendTo(that);
|
|
}
|
|
|
|
self.$input = $('<input>')
|
|
.addClass('OxInput OxMedium')
|
|
.attr({
|
|
disabled: self.options.disabled ? 'disabled' : '',
|
|
type: self.options.type == 'password' ? 'password' : 'text'
|
|
})
|
|
.css({
|
|
width: self.inputWidth + 'px',
|
|
textAlign: self.options.textAlign
|
|
})
|
|
.val(self.options.value)
|
|
.blur(blur)
|
|
.change(change)
|
|
.focus(focus)
|
|
.appendTo(that.$element);
|
|
|
|
if (self.hasPasswordPlaceholder) {
|
|
self.$input.hide();
|
|
self.$placeholder = $('<input>')
|
|
.addClass('OxInput OxMedium OxPlaceholder')
|
|
.attr({
|
|
type: 'text'
|
|
})
|
|
.css({
|
|
//float: 'left',
|
|
width: self.inputWidth + 'px'
|
|
})
|
|
.val(self.options.placeholder)
|
|
.focus(focus)
|
|
.appendTo(that.$element);
|
|
}
|
|
|
|
if (self.options.autocomplete && self.options.autocompleteSelect) {
|
|
self.$autocompleteMenu = new Ox.Menu({
|
|
element: self.$input,
|
|
id: self.options.id + 'Menu', // fixme: we do this in other places ... are we doing it the same way? var name?,
|
|
offset: {
|
|
left: 4,
|
|
top: 0
|
|
},
|
|
size: self.options.size
|
|
})
|
|
.bindEvent('click', clickMenu);
|
|
if (self.options.autocompleteReplace) {
|
|
self.$autocompleteMenu.bindEvent({
|
|
deselect: deselectMenu,
|
|
select: selectMenu,
|
|
});
|
|
}
|
|
}
|
|
|
|
self.options.placeholder && setPlaceholder();
|
|
|
|
function autocomplete(oldValue, oldCursor) {
|
|
|
|
if (self.options.value || self.options.autocompleteReplaceCorrect) {
|
|
Ox.isFunction(self.options.autocomplete) ?
|
|
(self.options.key ? self.options.autocomplete(
|
|
self.options.key,
|
|
self.options.value,
|
|
autocompleteCallback
|
|
) : self.options.autocomplete(
|
|
self.options.value,
|
|
autocompleteCallback
|
|
)) : autocompleteCallback(autocompleteFunction(self.options.value));
|
|
}
|
|
if (!self.options.value) {
|
|
self.options.autocompleteSelect && self.$autocompleteMenu.hideMenu();
|
|
}
|
|
|
|
function autocompleteFunction() {
|
|
var values = Ox.find(self.options.autocomplete, self.options.value);
|
|
return self.options.autocompleteReplace ? values[0] :
|
|
$.merge(values[0], values[1]);
|
|
}
|
|
|
|
function autocompleteCallback(values) {
|
|
|
|
Ox.print('autocompleteCallback', values[0], self.options.value, self.options.value.length, oldValue, oldCursor)
|
|
|
|
var length = self.options.value.length,
|
|
deleted = length <= oldValue.length - (oldCursor[1] - oldCursor[0]),
|
|
newValue = values[0] ?
|
|
((self.options.autocompleteReplaceCorrect || !deleted) ?
|
|
values[0] : self.options.value) :
|
|
(self.options.autocompleteReplaceCorrect ? oldValue : self.options.value),
|
|
newLength = newValue.length,
|
|
pos = cursor(),
|
|
selected = -1,
|
|
selectEnd = length == 0 || (values[0] && values[0].length),
|
|
value;
|
|
|
|
Ox.print('selectEnd', selectEnd)
|
|
|
|
if (self.options.autocompleteReplace) {
|
|
self.options.value = newValue;
|
|
self.$input.val(self.options.value);
|
|
if (selectEnd) {
|
|
cursor(length, newLength);
|
|
} else if (self.options.autocompleteReplaceCorrect) {
|
|
cursor(oldCursor);
|
|
} else {
|
|
cursor(pos);
|
|
}
|
|
selected = 0;
|
|
}
|
|
|
|
if (self.options.autocompleteSelect) {
|
|
value = self.options.value.toLowerCase();
|
|
if (values.length) {
|
|
self.oldCursor = cursor();
|
|
self.oldValue = self.options.value;
|
|
self.$autocompleteMenu.options({
|
|
items: $.map(values, function(v, i) {
|
|
if (value == v.toLowerCase()) {
|
|
selected = i;
|
|
}
|
|
return {
|
|
id: v.toLowerCase().replace(/ /g, '_'), // fixme: need function to do lowercase, underscores etc?
|
|
title: self.options.autocompleteSelectHighlight ? v.replace(
|
|
new RegExp('(' + value + ')', 'ig'),
|
|
'<span class="OxHighlight">$1</span>'
|
|
) : v
|
|
};
|
|
}),
|
|
selected: selected
|
|
}).showMenu();
|
|
} else {
|
|
self.$autocompleteMenu.hideMenu();
|
|
}
|
|
}
|
|
|
|
that.triggerEvent('autocomplete', {
|
|
value: newValue
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function autovalidate() {
|
|
|
|
var blur, oldCursor, oldValue;
|
|
|
|
if (arguments.length == 1) {
|
|
blur = arguments[0];
|
|
} else {
|
|
blur = false;
|
|
oldValue = arguments[0];
|
|
oldCursor = arguments[1];
|
|
}
|
|
|
|
Ox.isFunction(self.options.autovalidate) ?
|
|
(self.options.key ? self.options.autovalidate(
|
|
self.options.key,
|
|
self.options.value,
|
|
blur,
|
|
autovalidateCallback
|
|
) : self.options.autovalidate(
|
|
self.options.value,
|
|
blur,
|
|
autovalidateCallback
|
|
)) : Ox.isRegExp(self.options.autovalidate) ?
|
|
autovalidateCallback(autovalidateFunction(self.options.value)) :
|
|
autovalidateTypeFunction(self.options.type, self.options.value);
|
|
|
|
function autovalidateFunction(value) {
|
|
var regexp = new RegExp(self.options.autovalidate);
|
|
return $.map(value.toLowerCase().split(''), function(v, i) {
|
|
if (regexp(v)) {
|
|
return v;
|
|
} else {
|
|
return null;
|
|
}
|
|
}).join('');
|
|
}
|
|
|
|
function autovalidateTypeFunction(type, value) {
|
|
var cursor,
|
|
regexp = type == 'float' ? /[\d\.]/ : /\d/;
|
|
if (type == 'float') {
|
|
if (value.indexOf('.') != value.lastIndexOf('.')) {
|
|
value = oldValue;
|
|
} else {
|
|
if (self.autovalidateFloatFlag) {
|
|
if (Ox.endsWith(value, '.')) {
|
|
value = value.substr(0, value.length - 1);
|
|
}
|
|
self.autovalidateFloatFlag = false;
|
|
}
|
|
while (Ox.startsWith(value, '.')) {
|
|
if (Ox.startsWith(value, '..')) {
|
|
value = value.substr(1);
|
|
} else {
|
|
value = '0' + value;
|
|
}
|
|
}
|
|
if (Ox.endsWith(value, '.')) {
|
|
value += '0';
|
|
cursor = [value.length - 1, value.length];
|
|
self.autovalidateFloatFlag = true;
|
|
}
|
|
}
|
|
}
|
|
value = $.map(value.split(''), function(v, i) {
|
|
if (regexp(v)) {
|
|
return v;
|
|
} else {
|
|
return null;
|
|
}
|
|
}).join('');
|
|
if (type == 'integer') {
|
|
while (value.length > 1 && Ox.startsWith(value, '0')) {
|
|
value = value.substr(1);
|
|
}
|
|
}
|
|
if (value === '') {
|
|
value = type == 'float' ? '0.0' : '0';
|
|
cursor = [0, value.length];
|
|
} else if (value > self.options.max) {
|
|
value = oldValue;
|
|
}
|
|
autovalidateCallback(value, cursor);
|
|
}
|
|
|
|
function autovalidateCallback(newValue, newCursor) {
|
|
Ox.print('autovalidateCallback', newValue, oldCursor)
|
|
self.options.value = newValue;
|
|
self.$input.val(self.options.value);
|
|
!blur && cursor(
|
|
newCursor || (oldCursor[1] + newValue.length - oldValue.length)
|
|
);
|
|
that.triggerEvent('autovalidate', {
|
|
value: self.options.value
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
function autovalidate(blur) {
|
|
Ox.print('autovalidate', self.options.value, blur || false)
|
|
self.autocorrectBlur = blur || false;
|
|
self.autocorrectCursor = cursor();
|
|
Ox.isFunction(self.options.autocorrect) ?
|
|
(self.options.key ? self.options.autocorrect(
|
|
self.options.key,
|
|
self.options.value,
|
|
self.autocorrectBlur,
|
|
autocorrectCallback
|
|
) : self.options.autocorrect(
|
|
self.options.value,
|
|
self.autocorrectBlur,
|
|
autocorrectCallback
|
|
)) : autocorrectCallback(autocorrect(self.options.value));
|
|
}
|
|
|
|
function autovalidateFunction(value) {
|
|
var length = value.length;
|
|
return $.map(value.toLowerCase().split(''), function(v, i) {
|
|
if (new RegExp(self.options.autocorrect)(v)) {
|
|
return v;
|
|
} else {
|
|
return null;
|
|
}
|
|
}).join('');
|
|
}
|
|
|
|
*/
|
|
|
|
function blur() {
|
|
Ox.print('blur')
|
|
that.loseFocus();
|
|
//that.removeClass('OxFocus');
|
|
self.options.value = self.$input.val();
|
|
self.options.autovalidate && autovalidate(true);
|
|
self.options.placeholder && setPlaceholder();
|
|
if (self.bindKeyboard) {
|
|
$document.unbind('keydown', keypress);
|
|
$document.unbind('keypress', keypress);
|
|
}
|
|
}
|
|
|
|
function cancel() {
|
|
self.$input.blur();
|
|
}
|
|
|
|
function change() {
|
|
self.options.value = self.$input.val();
|
|
that.triggerEvent('change', {
|
|
value: self.options.value
|
|
});
|
|
}
|
|
|
|
function clear() {
|
|
// fixme: set to min, not zero
|
|
// fixme: make this work for password
|
|
var value = '';
|
|
if (self.options.type == 'float') {
|
|
value = '0.0';
|
|
} else if (self.options.type == 'integer') {
|
|
value = '0'
|
|
}
|
|
self.$input.val(value);
|
|
cursor(0, value.length);
|
|
}
|
|
|
|
function clickArrow(i) {
|
|
self.options.value = Ox.limit(
|
|
parseFloat(self.options.value) + (i == 0 ? -1 : 1) * self.options.arrowStep,
|
|
self.options.min,
|
|
self.options.max
|
|
);
|
|
self.$input.val(self.options.value);//.focus();
|
|
}
|
|
|
|
function clickMenu(event, data) {
|
|
Ox.print('clickMenu', data);
|
|
self.options.value = data.title;
|
|
self.$input.val(self.options.value).focus();
|
|
that.gainFocus();
|
|
self.options.autocompleteSelectSubmit && submit();
|
|
}
|
|
|
|
function cursor(start, end) {
|
|
/*
|
|
cursor() returns [start, end]
|
|
cursor(start) sets start
|
|
cursor([start, end]) sets start and end
|
|
cursor(start, end) sets start and end
|
|
*/
|
|
var isArray = Ox.isArray(start);
|
|
if (arguments.length == 0) {
|
|
return [self.$input[0].selectionStart, self.$input[0].selectionEnd];
|
|
} else {
|
|
end = isArray ? start[1] : (end ? end : start);
|
|
start = isArray ? start[0] : start;
|
|
self.$input[0].setSelectionRange(start, end);
|
|
}
|
|
}
|
|
|
|
function deselectMenu() {
|
|
self.options.value = self.oldValue;
|
|
self.$input.val(self.options.value);
|
|
cursor(self.oldCursor);
|
|
}
|
|
|
|
function focus() {
|
|
Ox.print('focus()')
|
|
if (
|
|
that.hasClass('OxFocus') || // fixme: this is just a workaround, since for some reason, focus() gets called twice on focus
|
|
(self.$autocompleteMenu && self.$autocompleteMenu.is(':visible')) ||
|
|
(self.hasPasswordPlaceholder && self.$input.is(':visible'))
|
|
) {
|
|
return;
|
|
}
|
|
that.gainFocus();
|
|
self.options.placeholder && setPlaceholder();
|
|
if (self.bindKeyboard) {
|
|
Ox.print('binding...')
|
|
// fixme: different in webkit and firefox (?), see keyboard handler, need generic function
|
|
$document.keydown(keypress);
|
|
$document.keypress(keypress);
|
|
self.options.autocompleteSelect && setTimeout(autocomplete, 0); // fixme: why is the timeout needed?
|
|
}
|
|
}
|
|
|
|
function getInputWidth() {
|
|
return self.options.width - 14 -
|
|
(self.options.arrows ? 32 : 0) -
|
|
(self.options.clear ? 16 : 0) -
|
|
(self.options.label ? self.options.labelWidth : 0);
|
|
}
|
|
|
|
function keypress(event) {
|
|
var oldCursor = cursor(),
|
|
oldValue = self.options.value,
|
|
newValue = oldValue.substr(0, oldCursor[0] - 1),
|
|
hasDeletedSelectedEnd = (event.keyCode == 8 || event.keyCode == 46) &&
|
|
oldCursor[0] < oldCursor[1] && oldCursor[1] == oldValue.length;
|
|
Ox.print('keypress', event.keyCode)
|
|
if (event.keyCode != 9 && event.keyCode != 13 && event.keyCode != 27) { // fixme: can't 13 and 27 return false?
|
|
setTimeout(function() { // wait for val to be set
|
|
var value = self.$input.val();
|
|
if (self.options.autocompleteReplaceCorrect && hasDeletedSelectedEnd) {
|
|
Ox.print(value, '->', newValue);
|
|
value = newValue; // value.substr(0, value.length - 1);
|
|
self.$input.val(value);
|
|
}
|
|
if (value != self.options.value) {
|
|
self.options.value = value;
|
|
self.options.autocomplete && autocomplete(oldValue, oldCursor);
|
|
self.options.autovalidate && autovalidate(oldValue, oldCursor);
|
|
}
|
|
}, 0);
|
|
}
|
|
if ((event.keyCode == 38 || event.keyCode == 40) && self.options.autocompleteSelect && self.$autocompleteMenu.is(':visible')) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function selectMenu(event, data) {
|
|
var pos = cursor();
|
|
Ox.print('selectMenu', pos)
|
|
self.options.value = data.title
|
|
self.$input.val(self.options.value);
|
|
cursor(pos[0], self.options.value.length)
|
|
}
|
|
|
|
function setPlaceholder() {
|
|
if (self.options.placeholder) {
|
|
if (that.hasClass('OxFocus')) {
|
|
if (self.options.value === '') {
|
|
if (self.options.type == 'password') {
|
|
self.$placeholder.hide();
|
|
self.$input.show().focus();
|
|
} else {
|
|
self.$input
|
|
.removeClass('OxPlaceholder')
|
|
.val('');
|
|
}
|
|
}
|
|
} else {
|
|
if (self.options.value === '') {
|
|
if (self.options.type == 'password') {
|
|
self.$input.hide();
|
|
self.$placeholder.show();
|
|
} else {
|
|
self.$input
|
|
.addClass('OxPlaceholder')
|
|
.val(self.options.placeholder)
|
|
}
|
|
} else {
|
|
self.$input
|
|
.removeClass('OxPlaceholder')
|
|
.val(self.options.value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function setWidth() {
|
|
|
|
}
|
|
|
|
function submit() {
|
|
self.$input.blur();
|
|
that.triggerEvent('submit', {
|
|
value: self.options.value
|
|
});
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
var inputWidth, val;
|
|
if (key == 'placeholder') {
|
|
setPlaceholder();
|
|
} else if (key == 'value') {
|
|
val = self.$input.val();
|
|
self.$input.val(value);
|
|
setPlaceholder();
|
|
} else if (key == 'width') {
|
|
inputWidth = getInputWidth();
|
|
self.$input.css({
|
|
width: inputWidth + 'px'
|
|
});
|
|
self.hasPasswordPlaceholder && self.$placeholder.css({
|
|
width: inputWidth + 'px'
|
|
});
|
|
}
|
|
};
|
|
|
|
that.focus = function() {
|
|
self.$input.focus();
|
|
cursor(0, self.$input.val().length);
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.AutocorrectIntFunction = function(min, max, pad, year) {
|
|
var pad = pad || false,
|
|
year = year || false,
|
|
maxLength = max.toString().length,
|
|
ret = null,
|
|
values = [];
|
|
$.each(Ox.range(min, max + 1), function(i, v) {
|
|
values.push(v + '');
|
|
pad && v.toString().length < maxLength && values.push(Ox.pad(v, maxLength));
|
|
});
|
|
return function(value, blur, callback) {
|
|
var results;
|
|
if (year && value == '1') {
|
|
value = '1900';
|
|
} else {
|
|
results = Ox.find(values, value);
|
|
value = results[0].length == 1 && results[0][0].length < maxLength ?
|
|
(pad ? Ox.pad(results[0][0], maxLength) : results[0][0]) :
|
|
(results[0].length ? results[0][0] : null);
|
|
}
|
|
callback(value);
|
|
};
|
|
};
|
|
|
|
Ox.InputGroup = function(options, self) {
|
|
|
|
/***
|
|
Ox.InputGroup
|
|
Options:
|
|
Methods:
|
|
Events:
|
|
***/
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
id: '',
|
|
inputs: [],
|
|
separators: [],
|
|
width: 0
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxInputGroup')
|
|
.click(click);
|
|
|
|
if (self.options.width) {
|
|
setWidths();
|
|
} else {
|
|
self.options.width = getWidth();
|
|
}
|
|
that.css({
|
|
width: self.options.width + 'px'
|
|
});
|
|
|
|
$.extend(self, {
|
|
//$input: [],
|
|
$separator: []
|
|
});
|
|
|
|
$.each(self.options.separators, function(i, v) {
|
|
self.$separator[i] = new Ox.Label({
|
|
textAlign: 'center',
|
|
title: v.title,
|
|
width: v.width + 32
|
|
})
|
|
.addClass('OxSeparator')
|
|
.css({
|
|
marginLeft: (self.options.inputs[i].options('width') - (i == 0 ? 16 : 32)) + 'px'
|
|
})
|
|
.appendTo(that);
|
|
});
|
|
|
|
$.each(self.options.inputs, function(i, $input) {
|
|
$input.options({
|
|
id: self.options.id + Ox.toTitleCase($input.options('id')),
|
|
parent: that
|
|
})
|
|
.css({
|
|
marginLeft: -Ox.sum($.map(self.options.inputs, function(v_, i_) {
|
|
return i_ > i ? self.options.inputs[i_ - 1].options('width') +
|
|
self.options.separators[i_ - 1].width : (i_ == i ? 16 : 0);
|
|
})) + 'px'
|
|
})
|
|
.bindEvent({
|
|
change: change,
|
|
submit: change
|
|
})
|
|
.appendTo(that);
|
|
});
|
|
|
|
function change(event, data) {
|
|
Ox.print('InputGroup change')
|
|
// fixme: would be good to pass a value here
|
|
that.triggerEvent('change');
|
|
}
|
|
|
|
function click(event) {
|
|
if ($(event.target).hasClass('OxSeparator')) {
|
|
self.options.inputs[0].focus();
|
|
}
|
|
}
|
|
|
|
function getWidth() {
|
|
return Ox.sum($.map(self.options.inputs, function(v, i) {
|
|
return v.options('width');
|
|
})) + Ox.sum($.map(self.options.separators, function(v, i) {
|
|
return v.width;
|
|
}));
|
|
}
|
|
|
|
function setWidths() {
|
|
var length = self.options.inputs.length,
|
|
inputWidths = Ox.divideInt(
|
|
self.options.width - Ox.sum($.map(self.options.separators, function(v, i) {
|
|
return v.width;
|
|
})), length
|
|
);
|
|
$.each(self.options.inputs, function(i, v) {
|
|
v.options({
|
|
width: inputWidths[1]
|
|
});
|
|
});
|
|
}
|
|
|
|
// fixme: is this used?
|
|
that.getInputById = function(id) {
|
|
var input = null;
|
|
$.each(self.options.inputs, function(i, v) {
|
|
Ox.print(v, v.options('id'), id)
|
|
if (v.options('id') == self.options.id + Ox.toTitleCase(id)) {
|
|
input = v;
|
|
return false;
|
|
}
|
|
});
|
|
return input;
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.ColorInput = function(options, self) {
|
|
|
|
var self = $.extend(self || {}, {
|
|
options: $.extend({
|
|
id: '',
|
|
value: '0, 0, 0'
|
|
}, options)
|
|
}),
|
|
that;
|
|
|
|
self.values = self.options.value.split(', ');
|
|
self.$inputs = [];
|
|
$.each(['red', 'green', 'blue'], function(i, v) {
|
|
self.$inputs[i] = new Ox.Input({
|
|
id: v,
|
|
max: 255,
|
|
type: 'integer',
|
|
value: self.values[i],
|
|
width: 36
|
|
})
|
|
.bindEvent('autovalidate', change);
|
|
});
|
|
self.$inputs[3] = new Ox.Label({
|
|
id: 'color',
|
|
width: 36
|
|
})
|
|
.css({
|
|
background: 'rgb(' + self.options.value + ')'
|
|
});
|
|
self.$inputs[4] = new Ox.ColorPicker({
|
|
id: 'picker'
|
|
})
|
|
.bindEvent('change', function(event, data) {
|
|
Ox.print('change function called');
|
|
self.options.value = data.value;
|
|
self.values = data.value.split(', ');
|
|
$.each(Ox.range(3), function(i) {
|
|
self.$inputs[i].options({
|
|
value: self.values[i]
|
|
});
|
|
});
|
|
})
|
|
.options({
|
|
width: 16 // this is just a hack to make the InputGroup layout work
|
|
});
|
|
|
|
that = new Ox.InputGroup({
|
|
id: self.options.id,
|
|
inputs: self.$inputs,
|
|
separators: [
|
|
{title: ',', width: 8},
|
|
{title: ',', width: 8},
|
|
{title: '', width: 8},
|
|
{title: '', width: 8}
|
|
],
|
|
value: self.options.value // fixme: it'd be nicer if this would be taken care of by passing self
|
|
}, self)
|
|
.bindEvent('change', change);
|
|
|
|
function change() {
|
|
self.options.value = $.map(self.$inputs, function(v, i) {
|
|
return v.options('value');
|
|
}).join(', ');
|
|
self.$inputs[3].css({
|
|
background: 'rgb(' + self.options.value + ')'
|
|
});
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.DateInput = function(options, self) {
|
|
|
|
var self = $.extend(self || {}, {
|
|
options: $.extend({
|
|
format: 'short',
|
|
value: Ox.formatDate(new Date(), '%F'),
|
|
weekday: false,
|
|
width: {
|
|
day: 32,
|
|
month: options.format == 'long' ? 80 : (options.format == 'medium' ? 40 : 32),
|
|
weekday: options.format == 'long' ? 80 : 40,
|
|
year: 48
|
|
}
|
|
}, options)
|
|
}),
|
|
that;
|
|
|
|
$.extend(self, {
|
|
date: new Date(self.options.value.replace(/-/g, '/')),
|
|
formats: {
|
|
day: '%d',
|
|
month: self.options.format == 'short' ? '%m' :
|
|
(self.options.format == 'medium' ? '%b' : '%B'),
|
|
weekday: self.options.format == 'long' ? '%A' : '%a',
|
|
year: '%Y'
|
|
},
|
|
months: self.options.format == 'long' ? Ox.MONTHS : $.map(Ox.MONTHS, function(v, i) {
|
|
return v.substr(0, 3);
|
|
}),
|
|
weekdays: self.options.format == 'long' ? Ox.WEEKDAYS : $.map(Ox.WEEKDAYS, function(v, i) {
|
|
return v.substr(0, 3);
|
|
})
|
|
});
|
|
|
|
self.$input = $.extend(self.options.weekday ? {
|
|
weekday: new Ox.Input({
|
|
autocomplete: self.weekdays,
|
|
autocompleteReplace: true,
|
|
autocompleteReplaceCorrect: true,
|
|
id: 'weekday',
|
|
value: Ox.formatDate(self.date, self.formats.weekday),
|
|
width: self.options.width.weekday
|
|
})
|
|
.bindEvent('autocomplete', changeWeekday),
|
|
} : {}, {
|
|
day: new Ox.Input({
|
|
autocomplete: $.map(Ox.range(1, Ox.getDaysInMonth(
|
|
parseInt(Ox.formatDate(self.date, '%Y'), 10),
|
|
parseInt(Ox.formatDate(self.date, '%m'), 10)
|
|
) + 1), function(v, i) {
|
|
return self.options.format == 'short' ? Ox.pad(v, 2) : v.toString();
|
|
}),
|
|
autocompleteReplace: true,
|
|
autocompleteReplaceCorrect: true,
|
|
id: 'day',
|
|
value: Ox.formatDate(self.date, self.formats.day),
|
|
textAlign: 'right',
|
|
width: self.options.width.day
|
|
})
|
|
.bindEvent('autocomplete', changeDay),
|
|
month: new Ox.Input({
|
|
autocomplete: self.options.format == 'short' ? $.map(Ox.range(1, 13), function(v, i) {
|
|
return Ox.pad(v, 2);
|
|
}) : self.months,
|
|
autocompleteReplace: true,
|
|
autocompleteReplaceCorrect: true,
|
|
id: 'month',
|
|
value: Ox.formatDate(self.date, self.formats.month),
|
|
textAlign: self.options.format == 'short' ? 'right' : 'left',
|
|
width: self.options.width.month
|
|
})
|
|
.bindEvent('autocomplete', changeMonthOrYear),
|
|
year: new Ox.Input({
|
|
autocomplete: $.map($.merge(Ox.range(1900, 3000), Ox.range(1000, 1900)), function(v, i) {
|
|
return v.toString();
|
|
}),
|
|
autocompleteReplace: true,
|
|
autocompleteReplaceCorrect: true,
|
|
id: 'year',
|
|
value: Ox.formatDate(self.date, self.formats.year),
|
|
textAlign: 'right',
|
|
width: self.options.width.year
|
|
})
|
|
.bindEvent('autocomplete', changeMonthOrYear)
|
|
});
|
|
|
|
that = new Ox.InputGroup($.extend(self.options, {
|
|
id: self.options.id,
|
|
inputs: $.merge(self.options.weekday ? [
|
|
self.$input.weekday
|
|
] : [], self.options.format == 'short' ? [
|
|
self.$input.year, self.$input.month, self.$input.day
|
|
] : [
|
|
self.$input.month, self.$input.day, self.$input.year
|
|
]),
|
|
separators: $.merge(self.options.weekday ? [
|
|
{title: self.options.format == 'short' ? '' : ',', width: 8},
|
|
] : [], self.options.format == 'short' ? [
|
|
{title: '-', width: 8}, {title: '-', width: 8}
|
|
] : [
|
|
{title: '', width: 8}, {title: ',', width: 8}
|
|
]),
|
|
width: 0
|
|
}), self);
|
|
|
|
Ox.print('SELF', self)
|
|
|
|
function changeDay() {
|
|
self.options.weekday && self.$input.weekday.options({
|
|
value: Ox.formatDate(new Date([
|
|
self.$input.month.options('value'),
|
|
self.$input.day.options('value'),
|
|
self.$input.year.options('value')
|
|
].join(' ')), self.formats.weekday)
|
|
});
|
|
setValue();
|
|
}
|
|
|
|
function changeMonthOrYear() {
|
|
var day = self.$input.day.options('value'),
|
|
month = self.$input.month.options('value'),
|
|
year = self.$input.year.options('value'),
|
|
days = Ox.getDaysInMonth(year, self.options.format == 'short' ? parseInt(month, 10) : month);
|
|
day = day <= days ? day : days;
|
|
Ox.print(year, month, 'day days', day, days)
|
|
self.options.weekday && self.$input.weekday.options({
|
|
value: Ox.formatDate(new Date([month, day, year].join(' ')), self.formats.weekday)
|
|
});
|
|
self.$input.day.options({
|
|
autocomplete: $.map(Ox.range(1, days + 1), function(v, i) {
|
|
return self.options.format == 'short' ? Ox.pad(v, 2) : v.toString();
|
|
}),
|
|
value: self.options.format == 'short' ? Ox.pad(day, 2) : day.toString()
|
|
});
|
|
setValue();
|
|
}
|
|
|
|
function changeWeekday() {
|
|
var date = getDateInWeek(
|
|
self.$input.weekday.options('value'),
|
|
self.$input.month.options('value'),
|
|
self.$input.day.options('value'),
|
|
self.$input.year.options('value')
|
|
);
|
|
self.$input.month.options({value: date.month});
|
|
self.$input.day.options({
|
|
autocomplete: $.map(Ox.range(1, Ox.getDaysInMonth(date.year, date.month) + 1), function(v, i) {
|
|
return self.options.format == 'short' ? Ox.pad(v, 2) : v.toString();
|
|
}),
|
|
value: date.day
|
|
});
|
|
self.$input.year.options({value: date.year});
|
|
setValue();
|
|
}
|
|
|
|
function getDateInWeek(weekday, month, day, year) {
|
|
Ox.print([month, day, year].join(' '))
|
|
var date = new Date([month, day, year].join(' '));
|
|
date = Ox.getDateInWeek(date, weekday);
|
|
return {
|
|
day: Ox.formatDate(date, self.formats.day),
|
|
month: Ox.formatDate(date, self.formats.month),
|
|
year: Ox.formatDate(date, self.formats.year)
|
|
};
|
|
}
|
|
|
|
function setValue() {
|
|
self.options.value = Ox.formatDate(new Date(self.options.format == 'short' ? [
|
|
self.$input.year.options('value'),
|
|
self.$input.month.options('value'),
|
|
self.$input.day.options('value')
|
|
].join('/') : [
|
|
self.$input.month.options('value'),
|
|
self.$input.day.options('value'),
|
|
self.$input.year.options('value')
|
|
].join(' ')), '%F');
|
|
}
|
|
|
|
/*
|
|
function normalize() {
|
|
var year = that.getInputById('year').options('value'),
|
|
month = that.getInputById('month').options('value'),
|
|
day = that.getInputById('day').options('value')
|
|
return {
|
|
year: year,
|
|
month: self.options.format == 'short' ? month :
|
|
Ox.pad((format == 'medium' ? Ox.WEEKDAYS.map(function(v, i) {
|
|
return v.substr(0, 3);
|
|
}) : Ox.WEEKDAYS).indexOf(month), 2),
|
|
day: Ox.pad(day, 2)
|
|
}
|
|
}
|
|
*/
|
|
|
|
/*
|
|
that.serialize = function() {
|
|
var normal = normalize();
|
|
return [normal.year, normal.month, normal.day].join('-');
|
|
}
|
|
*/
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.DateTimeInput = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
ampm: false,
|
|
format: 'short',
|
|
seconds: false,
|
|
value: Ox.formatDate(new Date(), '%F %T'),
|
|
weekday: false
|
|
})
|
|
.options(options || {});
|
|
|
|
self.values = self.options.value.split(' ');
|
|
Ox.print(self.values)
|
|
|
|
that = new Ox.InputGroup({
|
|
inputs: [
|
|
new Ox.DateInput({
|
|
format: self.options.format,
|
|
id: 'date',
|
|
value: self.values[0],
|
|
weekday: self.options.weekday
|
|
}),
|
|
new Ox.TimeInput({
|
|
ampm: self.options.ampm,
|
|
id: 'time',
|
|
value: self.values[1],
|
|
seconds: self.options.seconds
|
|
})
|
|
],
|
|
separators: [
|
|
{title: '', width: 8}
|
|
],
|
|
value: self.options.value
|
|
})
|
|
.bindEvent('change', setValue);
|
|
|
|
function setValue() {
|
|
self.options.value = [
|
|
self.options('inputs')[0].options('value'),
|
|
self.options('inputs')[1].options('value')
|
|
].join(' ');
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.PlaceInput = function(options, self) {
|
|
|
|
var self = $.extend(self || {}, {
|
|
options: $.extend({
|
|
id: '',
|
|
value: 'United States'
|
|
}, options)
|
|
}),
|
|
that;
|
|
|
|
that = new Ox.FormElementGroup({
|
|
id: self.options.id,
|
|
elements: [
|
|
new Ox.Input({
|
|
id: 'input',
|
|
value: self.options.value
|
|
}),
|
|
new Ox.PlacePicker({
|
|
id: 'picker',
|
|
overlap: 'left',
|
|
value: self.options.value
|
|
})
|
|
],
|
|
float: 'right'
|
|
}, self)
|
|
.bindEvent('change', change);
|
|
|
|
function change() {
|
|
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.TimeInput = function(options, self) {
|
|
|
|
// fixme: seconds get set even if options.seconds is false
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
ampm: false,
|
|
seconds: false,
|
|
milliseconds: false,
|
|
value: Ox.formatDate(new Date(), '%T'),
|
|
})
|
|
.options(options || {});
|
|
|
|
if (self.options.milliseconds) {
|
|
self.options.seconds = true;
|
|
if (self.options.value.indexOf('.') == -1) {
|
|
self.options.value += '.000';
|
|
}
|
|
}
|
|
|
|
self.date = getDate();
|
|
self.values = getValues();
|
|
|
|
self.$input = {
|
|
hours: Ox.Input({
|
|
autocomplete: $.map(self.options.ampm ? Ox.range(1, 13) : Ox.range(0, 24), function(v) {
|
|
return Ox.pad(v, 2);
|
|
}),
|
|
autocompleteReplace: true,
|
|
autocompleteReplaceCorrect: true,
|
|
id: 'hours',
|
|
textAlign: 'right',
|
|
value: self.values.hours,
|
|
width: 32
|
|
}),
|
|
minutes: Ox.Input({
|
|
autocomplete: $.map(Ox.range(0, 60), function(v) {
|
|
return Ox.pad(v, 2);
|
|
}),
|
|
autocompleteReplace: true,
|
|
autocompleteReplaceCorrect: true,
|
|
id: 'minutes',
|
|
textAlign: 'right',
|
|
value: self.values.minutes,
|
|
width: 32
|
|
}),
|
|
seconds: Ox.Input({
|
|
autocomplete: $.map(Ox.range(0, 60), function(v) {
|
|
return Ox.pad(v, 2);
|
|
}),
|
|
autocompleteReplace: true,
|
|
autocompleteReplaceCorrect: true,
|
|
id: 'seconds',
|
|
textAlign: 'right',
|
|
value: self.values.seconds,
|
|
width: 32
|
|
}),
|
|
milliseconds: Ox.Input({
|
|
autocomplete: $.map(Ox.range(0, 1000), function(v) {
|
|
return Ox.pad(v, 3);
|
|
}),
|
|
autocompleteReplace: true,
|
|
autocompleteReplaceCorrect: true,
|
|
id: 'milliseconds',
|
|
textAlign: 'right',
|
|
value: self.values.milliseconds,
|
|
width: 40
|
|
}),
|
|
ampm: Ox.Input({
|
|
autocomplete: ['AM', 'PM'],
|
|
autocompleteReplace: true,
|
|
autocompleteReplaceCorrect: true,
|
|
id: 'ampm',
|
|
value: self.values.ampm,
|
|
width: 32
|
|
})
|
|
};
|
|
|
|
that = new Ox.InputGroup($.extend(self.options, {
|
|
inputs: $.merge($.merge($.merge([
|
|
self.$input.hours,
|
|
self.$input.minutes,
|
|
], self.options.seconds ? [
|
|
self.$input.seconds
|
|
] : []), self.options.milliseconds ? [
|
|
self.$input.milliseconds
|
|
] : []), self.options.ampm ? [
|
|
self.$input.ampm
|
|
] : []),
|
|
separators: $.merge($.merge($.merge([
|
|
{title: ':', width: 8},
|
|
], self.options.seconds ? [
|
|
{title: ':', width: 8}
|
|
] : []), self.options.milliseconds ? [
|
|
{title: '.', width: 8}
|
|
] : []), self.options.ampm ? [
|
|
{title: '', width: 8}
|
|
] : []),
|
|
//width: self.options.width || 128
|
|
}), self)
|
|
.bindEvent('change', setValue);
|
|
|
|
setValue();
|
|
|
|
function getDate() {
|
|
return new Date('1970/01/01 ' + (
|
|
self.options.milliseconds ?
|
|
self.options.value.substr(0, self.options.value.length - 4) :
|
|
self.options.value
|
|
));
|
|
}
|
|
|
|
function getValues() {
|
|
self.date = getDate();
|
|
return {
|
|
ampm: Ox.formatDate(self.date, '%p'),
|
|
hours: Ox.formatDate(self.date, self.options.ampm ? '%I' : '%H'),
|
|
milliseconds: self.options.milliseconds ? self.options.value.substr(-3) : '000',
|
|
minutes: Ox.formatDate(self.date, '%M'),
|
|
seconds: Ox.formatDate(self.date, '%S')
|
|
};
|
|
}
|
|
|
|
function setValue() {
|
|
self.options.value = Ox.formatDate(new Date('1970/01/01 ' + [
|
|
self.$input.hours.options('value'),
|
|
self.$input.minutes.options('value'),
|
|
self.options.seconds ? self.$input.seconds.options('value') : '00'
|
|
].join(':') + (self.options.ampm ? ' ' + self.$input.ampm.options('value') : '')),
|
|
(self.options.seconds? '%T' : '%H:%M')) +
|
|
(self.options.milliseconds ? '.' + self.$input.milliseconds.options('value') : '');
|
|
Ox.print('SETVALUE', self.options.value);
|
|
}
|
|
|
|
function setValues() {
|
|
self.values = getValues();
|
|
$.each(self.$input, function(k, v) {
|
|
self.$input[k].options({
|
|
value: self.values[k]
|
|
});
|
|
});
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'value') {
|
|
setValues();
|
|
}
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.Label = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
disabled: false,
|
|
id: '',
|
|
overlap: 'none',
|
|
textAlign: 'left',
|
|
title: '',
|
|
width: 'auto'
|
|
})
|
|
.options(options)
|
|
.addClass(
|
|
'OxLabel' + (self.options.disabled ? ' OxDisabled' : '') +
|
|
(self.options.overlap != 'none' ?
|
|
' OxOverlap' + Ox.toTitleCase(self.options.overlap) : '')
|
|
)
|
|
.css($.extend(self.options.width == 'auto' ? {} : {
|
|
width: (self.options.width - 14) + 'px'
|
|
}, {
|
|
textAlign: self.options.textAlign
|
|
}))
|
|
.html(self.options.title);
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.OptionGroup = function(items, min, max, property) {
|
|
|
|
/*
|
|
to be used by ButtonGroup, CheckboxGroup, Select and Menu
|
|
*/
|
|
|
|
var property = property || 'checked'
|
|
length = items.length,
|
|
max = max == -1 ? length : max;
|
|
|
|
function getLastBefore(pos) {
|
|
// returns the position of the last checked item before position pos
|
|
var last = -1;
|
|
Ox.print(items, items.length, length, $.merge(
|
|
pos > 0 ? Ox.range(pos - 1, -1, -1) : [],
|
|
pos < items.length - 1 ? Ox.range(items.length - 1, pos, -1) : []
|
|
))
|
|
// fixme: why is length not == items.length here?
|
|
$.each($.merge(
|
|
pos > 0 ? Ox.range(pos - 1, -1, -1) : [],
|
|
pos < items.length - 1 ? Ox.range(items.length - 1, pos, -1) : []
|
|
), function(i, v) {
|
|
Ox.print(pos, v)
|
|
if (items[v][property]) {
|
|
last = v;
|
|
return false;
|
|
}
|
|
});
|
|
return last;
|
|
}
|
|
|
|
function getNumber() {
|
|
// returns the number of checked items
|
|
var num = 0;
|
|
$.each(items, function(i, item) {
|
|
if (item[property]) {
|
|
num++;
|
|
}
|
|
})
|
|
return num;
|
|
}
|
|
|
|
this[property] = function() {
|
|
// returns an array with the positions of all checked item
|
|
var checked = [];
|
|
$.each(items, function(i, item) {
|
|
if (item[property]) {
|
|
checked.push(i);
|
|
}
|
|
})
|
|
return checked;
|
|
};
|
|
|
|
this.init = function() {
|
|
var num = getNumber(),
|
|
count = 0;
|
|
//if (num < min || num > max) {
|
|
$.each(items, function(i, item) {
|
|
if (Ox.isUndefined(item[property])) {
|
|
item[property] = false;
|
|
}
|
|
if (item[property]) {
|
|
count++;
|
|
if (count > max) {
|
|
item[property] = false;
|
|
}
|
|
} else {
|
|
if (num < min) {
|
|
item[property] = true;
|
|
num++;
|
|
}
|
|
}
|
|
});
|
|
//}
|
|
return items;
|
|
};
|
|
|
|
this.toggle = function(pos) {
|
|
var last,
|
|
num = getNumber(),
|
|
toggled = [];
|
|
if (!items[pos][property]) { // check
|
|
if (num >= max) {
|
|
last = getLastBefore(pos);
|
|
items[last][property] = false;
|
|
toggled.push(last);
|
|
}
|
|
if (!items[pos][property]) {
|
|
items[pos][property] = true;
|
|
toggled.push(pos);
|
|
}
|
|
} else { // uncheck
|
|
if (num > min) {
|
|
items[pos][property] = false;
|
|
toggled.push(pos);
|
|
}
|
|
}
|
|
return toggled;
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
Ox.Range = function(options, self) {
|
|
|
|
/***
|
|
Ox.Range
|
|
Options:
|
|
arrows boolean if true, show arrows
|
|
arrowStep number step when clicking arrows
|
|
arrowSymbols array arrow symbols, like ['minus', 'plus']
|
|
max number maximum value
|
|
min number minimum value
|
|
orientation string 'horizontal' or 'vertical'
|
|
step number step between values
|
|
size number width or height, in px
|
|
thumbSize number minimum width or height of thumb, in px
|
|
thumbValue boolean if true, display value on thumb
|
|
trackGradient array colors
|
|
trackImages string or array one or multiple track background image URLs
|
|
trackStep number 0 (scroll here) or step when clicking track
|
|
value number initial value
|
|
valueNames array value names to display on thumb
|
|
***/
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
arrows: false,
|
|
arrowStep: 1,
|
|
arrowSymbols: ['previous', 'next'],
|
|
max: 100,
|
|
min: 0,
|
|
orientation: 'horizontal',
|
|
step: 1,
|
|
size: 128,
|
|
thumbSize: 16,
|
|
thumbValue: false,
|
|
trackColors: [],
|
|
trackImages: [],
|
|
trackStep: 0,
|
|
value: 0,
|
|
valueNames: null,
|
|
})
|
|
.options($.extend(options, {
|
|
arrowStep: options.arrowStep ?
|
|
options.arrowStep : options.step,
|
|
trackImages: $.makeArray(options.trackImages || [])
|
|
}))
|
|
.addClass('OxRange')
|
|
.css({
|
|
width: self.options.size + 'px'
|
|
});
|
|
|
|
$.extend(self, {
|
|
trackColors: self.options.trackColors.length,
|
|
trackImages: self.options.trackImages.length,
|
|
trackSize: self.options.size - self.options.arrows * 32,
|
|
values: (self.options.max - self.options.min + self.options.step) /
|
|
self.options.step
|
|
});
|
|
$.extend(self, {
|
|
thumbSize: Math.max(self.trackSize / self.values, self.options.thumbSize),
|
|
trackImageWidths: self.trackImages == 1 ? [self.trackSize - 16] :
|
|
Ox.divideInt(self.trackSize - 2, self.trackImages)
|
|
});
|
|
$.extend(self, {
|
|
trackColorsStart: self.thumbSize / 2 / self.options.size,
|
|
trackColorsStep: (self.options.size - self.thumbSize) / (self.trackColors - 1) / self.options.size
|
|
});
|
|
|
|
if (self.options.arrows) {
|
|
self.$arrows = [];
|
|
$.each(Ox.range(0, 2), function(i) {
|
|
self.$arrows[i] = Ox.Button({
|
|
overlap: i == 0 ? 'right' : 'left',
|
|
title: self.options.arrowSymbols[i],
|
|
type: 'image'
|
|
})
|
|
.addClass('OxArrow')
|
|
.mousedown(function(e) {
|
|
clickArrow(e, i);
|
|
})
|
|
.appendTo(that.$element);
|
|
});
|
|
}
|
|
|
|
self.$track = $('<div>')
|
|
.addClass('OxTrack')
|
|
.css($.extend({
|
|
width: (self.trackSize - 2) + 'px'
|
|
}, self.trackImages == 1 ? {
|
|
background: 'rgb(0, 0, 0)'
|
|
} : {}))
|
|
.mousedown(clickTrack)
|
|
.appendTo(that.$element);
|
|
|
|
self.trackColors && setTrackColors();
|
|
|
|
if (self.trackImages) {
|
|
self.$trackImages = $('<div>')
|
|
.css({
|
|
width: self.trackSize + 'px',
|
|
marginRight: (-self.trackSize - 1) + 'px'
|
|
})
|
|
.appendTo(self.$track);
|
|
$.each(self.options.trackImages, function(i, v) {
|
|
Ox.print(self.trackImageWidths[i])
|
|
$('<img>')
|
|
.attr({
|
|
src: v
|
|
})
|
|
.addClass(i == 0 ? 'OxFirstChild' : '')
|
|
.addClass(i == self.trackImages - 1 ? 'OxLastChild' : '')
|
|
.css({
|
|
width: self.trackImageWidths[i] + 'px'
|
|
})
|
|
.mousedown(function(e) {
|
|
e.preventDefault(); // prevent drag
|
|
})
|
|
.appendTo(self.$trackImages);
|
|
//left += self.trackImageWidths[i];
|
|
});
|
|
}
|
|
|
|
self.$thumb = Ox.Button({
|
|
id: self.options.id + 'Thumb',
|
|
title: self.options.thumbValue ? (self.options.valueNames ?
|
|
self.options.valueNames[self.options.value] :
|
|
self.options.value) : '',
|
|
width: self.thumbSize
|
|
})
|
|
.addClass('OxThumb')
|
|
/*
|
|
.css({
|
|
border: '1px solid rgb(255, 255, 255)',
|
|
background: 'rgba(0, 0, 0, 0)'
|
|
})
|
|
*/
|
|
.appendTo(self.$track);
|
|
|
|
setThumb();
|
|
|
|
function clickArrow(e, i) {
|
|
// fixme: shift doesn't work, see menu scrolling
|
|
var interval,
|
|
timeout = setTimeout(function() {
|
|
interval = setInterval(function() {
|
|
setValue(self.options.value + self.options.arrowStep * (i == 0 ? -1 : 1));
|
|
}, 50);
|
|
}, 500);
|
|
setValue(self.options.value + self.options.arrowStep * (i == 0 ? -1 : 1) * (e.shiftKey ? 2 : 1), true);
|
|
$window.one('mouseup', function() {
|
|
clearInterval(interval);
|
|
clearTimeout(timeout);
|
|
});
|
|
}
|
|
|
|
function clickTrack(e) {
|
|
//Ox.Focus.focus();
|
|
var isThumb = $(e.target).hasClass('OxThumb'),
|
|
left = self.$track.offset().left,
|
|
offset = isThumb ? e.clientX - self.$thumb.offset().left - 8 /*self.thumbSize / 2*/ : 0;
|
|
setValue(val(e), !isThumb);
|
|
$window.mousemove(function(e) {
|
|
setValue(val(e));
|
|
});
|
|
$window.one('mouseup', function() {
|
|
$window.unbind('mousemove');
|
|
});
|
|
function val(e) {
|
|
return getVal(e.clientX - left - offset);
|
|
}
|
|
}
|
|
|
|
function getPx(val) {
|
|
var pxPerVal = (self.trackSize - self.thumbSize) /
|
|
(self.options.max - self.options.min);
|
|
return Math.ceil((val - self.options.min) * pxPerVal);
|
|
}
|
|
|
|
/*
|
|
function getTime(oldValue, newValue) {
|
|
return self.animationTime * Math.abs(oldValue - newValue) / (self.options.max - self.options.min);
|
|
}
|
|
*/
|
|
|
|
function getVal(px) {
|
|
var px = self.trackSize / self.values >= 16 ? px : px - 8,
|
|
valPerPx = (self.options.max - self.options.min) /
|
|
(self.trackSize - self.thumbSize);
|
|
return Ox.limit(self.options.min +
|
|
Math.floor(px * valPerPx / self.options.step) * self.options.step,
|
|
self.options.min, self.options.max);
|
|
}
|
|
|
|
function setThumb(animate) {
|
|
self.$thumb.stop().animate({
|
|
marginLeft: (getPx(self.options.value) - 1) + 'px',
|
|
//width: self.thumbSize + 'px'
|
|
}, animate ? 200 : 0, function() {
|
|
if (self.options.thumbValue) {
|
|
self.$thumb.options({
|
|
title: self.options.valueNames ?
|
|
self.options.valueNames[self.options.value] :
|
|
self.options.value
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
function setTrackColors() {
|
|
self.$track.css({
|
|
backgroundImage: $.browser.mozilla ?
|
|
('-moz-linear-gradient(left, ' +
|
|
self.options.trackColors[0] + ' 0%, ' + $.map(self.options.trackColors, function(v, i) {
|
|
return v + ' ' + ((self.trackColorsStart + self.trackColorsStep * i) * 100) + '%';
|
|
}).join(', ') + ', ' + self.options.trackColors[self.trackColors - 1] + ' 100%)') :
|
|
('-webkit-gradient(linear, left top, right top, color-stop(0, ' +
|
|
self.options.trackColors[0] + '), ' + $.map(self.options.trackColors, function(v, i) {
|
|
return 'color-stop(' + (self.trackColorsStart + self.trackColorsStep * i) + ', ' + v + ')';
|
|
}).join(', ') + ', color-stop(1, ' + self.options.trackColors[self.trackColors - 1] + '))')
|
|
});
|
|
}
|
|
|
|
function setValue(value, animate) {
|
|
var value = Ox.limit(value, self.options.min, self.options.max);
|
|
if (value != self.options.value) {
|
|
//time = getTime(self.options.value, value);
|
|
self.options.value = value;
|
|
setThumb(animate);
|
|
that.triggerEvent('change', {
|
|
value: value
|
|
});
|
|
}
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'trackColors') {
|
|
setTrackColors();
|
|
} else if (key == 'value') {
|
|
setThumb();
|
|
}
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.Select = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self) // fixme: do we use 'div', or {}, or '', by default?
|
|
.defaults({
|
|
id: '',
|
|
items: [],
|
|
max: 1,
|
|
min: 1,
|
|
overlap: 'none', // can be none, left or right
|
|
selectable: true,
|
|
size: 'medium',
|
|
title: '',
|
|
type: 'text', // can be 'text' or 'image'
|
|
width: 'auto'
|
|
})
|
|
.options(options)
|
|
.addClass(
|
|
'OxSelect Ox' + Ox.toTitleCase(self.options.size) +
|
|
(self.options.overlap == 'none' ? '' : ' OxOverlap' +
|
|
Ox.toTitleCase(self.options.overlap))
|
|
)
|
|
.css(self.options.width == 'auto' ? {} : {
|
|
width: self.options.width + 'px'
|
|
})
|
|
.addEvent({
|
|
key_escape: loseFocus,
|
|
key_down: showMenu
|
|
});
|
|
|
|
$.extend(self, {
|
|
buttonId: self.options.id + 'Button',
|
|
groupId: self.options.id + 'Group',
|
|
menuId: self.options.id + 'Menu'
|
|
});
|
|
|
|
if (self.options.selectable) {
|
|
self.optionGroup = new Ox.OptionGroup(
|
|
self.options.items,
|
|
self.options.min,
|
|
self.options.max
|
|
);
|
|
self.options.items = self.optionGroup.init();
|
|
self.checked = self.optionGroup.checked();
|
|
}
|
|
|
|
if (self.options.type == 'text') {
|
|
self.$title = $('<div>')
|
|
.addClass('OxTitle')
|
|
.css({
|
|
width: (self.options.width - 22) + 'px'
|
|
})
|
|
.html(
|
|
self.options.title ? self.options.title :
|
|
self.options.items[self.checked[0]].title
|
|
)
|
|
.click(showMenu)
|
|
.appendTo(that.$element);
|
|
}
|
|
|
|
self.$button = new Ox.Button({
|
|
id: self.buttonId,
|
|
style: 'symbol',
|
|
title: 'select',
|
|
type: 'image'
|
|
})
|
|
.bindEvent('click', showMenu)
|
|
.appendTo(that);
|
|
|
|
self.$menu = new Ox.Menu({
|
|
element: self.$title || self.$button,
|
|
id: self.menuId,
|
|
items: [self.options.selectable ? {
|
|
group: self.groupId,
|
|
items: self.options.items,
|
|
max: self.options.max,
|
|
min: self.options.min
|
|
} : self.options.items],
|
|
side: 'bottom',
|
|
size: self.options.size
|
|
})
|
|
.bindEvent({
|
|
change: changeMenu,
|
|
click: clickMenu,
|
|
hide: hideMenu
|
|
});
|
|
|
|
function clickMenu(event, data) {
|
|
|
|
}
|
|
|
|
function changeMenu(event, data) {
|
|
Ox.print('clickMenu: ', self.options.id, data)
|
|
self.checked = self.optionGroup.checked();
|
|
self.$title && self.$title.html(
|
|
self.options.title ? self.options.title :
|
|
data.checked[0].title
|
|
);
|
|
that.triggerEvent('change', {
|
|
selected: data.checked
|
|
});
|
|
}
|
|
|
|
function hideMenu() {
|
|
that.removeClass('OxSelected');
|
|
self.$button.removeClass('OxSelected');
|
|
}
|
|
|
|
function loseFocus() {
|
|
that.loseFocus();
|
|
}
|
|
|
|
function showMenu() {
|
|
that.gainFocus();
|
|
that.addClass('OxSelected');
|
|
self.$menu.showMenu();
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
|
|
};
|
|
|
|
that.selected = function() {
|
|
return $.map(/*self.checked*/self.optionGroup.checked(), function(v, i) {
|
|
return {
|
|
id: self.options.items[i].id,
|
|
title: self.options.items[i].title
|
|
};
|
|
});
|
|
};
|
|
|
|
that.selectItem = function(id) {
|
|
Ox.print('selectItem', id, Ox.getObjectById(self.options.items, id).title)
|
|
self.options.type == 'text' && self.$title.html(
|
|
Ox.getObjectById(self.options.items, id).title[0] // fixme: title should not have become an array
|
|
);
|
|
self.$menu.checkItem(id);
|
|
self.checked = self.optionGroup.checked();
|
|
};
|
|
|
|
/*
|
|
that.width = function(val) {
|
|
// fixme: silly hack, and won't work for css() ... remove!
|
|
that.$element.width(val + 16);
|
|
that.$button.width(val);
|
|
//that.$symbol.width(val);
|
|
return that;
|
|
};
|
|
*/
|
|
|
|
return that;
|
|
|
|
}
|
|
|
|
Ox.FormElementGroup = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
id: '',
|
|
elements: [],
|
|
float: 'left',
|
|
separators: [],
|
|
width: 0
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxInputGroup');
|
|
|
|
$.each(self.options.float == 'left' ? self.options.elements : self.options.elements.reverse(), function(i, $element) {
|
|
$element.options({
|
|
id: self.options.id + Ox.toTitleCase($element.options('id')),
|
|
parent: that
|
|
})
|
|
.css({
|
|
float: self.options.float // fixme: make this a class
|
|
})
|
|
.appendTo(that);
|
|
});
|
|
|
|
/*
|
|
if (self.options.width) {
|
|
setWidths();
|
|
} else {
|
|
self.options.width = getWidth();
|
|
}
|
|
that.css({
|
|
width: self.options.width + 'px'
|
|
});
|
|
*/
|
|
|
|
function getWidth() {
|
|
|
|
}
|
|
|
|
function setWidth() {
|
|
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'trackColors') {
|
|
|
|
}
|
|
}
|
|
|
|
return that;
|
|
|
|
}
|
|
|
|
Ox.Picker = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
element: null,
|
|
elementHeight: 128,
|
|
elementWidth: 256,
|
|
id: '',
|
|
overlap: 'none'
|
|
})
|
|
.options(options || {});
|
|
|
|
self.$selectButton = new Ox.Button({
|
|
overlap: self.options.overlap,
|
|
title: 'select',
|
|
type: 'image'
|
|
})
|
|
.click(showMenu)
|
|
.appendTo(that);
|
|
|
|
self.$menu = new Ox.Element('div')
|
|
.addClass('OxPicker')
|
|
.css({
|
|
width: self.options.elementWidth + 'px',
|
|
height: (self.options.elementHeight + 24) + 'px'
|
|
});
|
|
|
|
self.options.element
|
|
.css({
|
|
width: self.options.elementWidth + 'px',
|
|
height: self.options.elementHeight + 'px'
|
|
})
|
|
.appendTo(self.$menu);
|
|
|
|
self.$bar = new Ox.Bar({
|
|
orientation: 'horizontal',
|
|
size: 24
|
|
})
|
|
.appendTo(self.$menu);
|
|
|
|
that.$label = new Ox.Label({
|
|
width: self.options.elementWidth - 60
|
|
})
|
|
.appendTo(self.$bar);
|
|
|
|
self.$doneButton = new Ox.Button({
|
|
title: 'Done',
|
|
width: 48
|
|
})
|
|
.click(hideMenu)
|
|
.appendTo(self.$bar);
|
|
|
|
self.$layer = $('<div>')
|
|
.addClass('OxLayer')
|
|
.click(hideMenu);
|
|
|
|
function hideMenu() {
|
|
self.$menu.detach();
|
|
self.$layer.detach();
|
|
self.$selectButton
|
|
.removeClass('OxSelected')
|
|
.css({
|
|
MozBorderRadius: '8px',
|
|
WebkitBorderRadius: '8px'
|
|
});
|
|
that.triggerEvent('hide');
|
|
};
|
|
|
|
function showMenu() {
|
|
var offset = that.offset(),
|
|
left = offset.left,
|
|
top = offset.top + 15;
|
|
self.$selectButton
|
|
.addClass('OxSelected')
|
|
.css({
|
|
MozBorderRadius: '8px 8px 0 0',
|
|
WebkitBorderRadius: '8px 8px 0 0'
|
|
});
|
|
self.$layer.appendTo($body);
|
|
self.$menu
|
|
.css({
|
|
left: left + 'px',
|
|
top: top + 'px'
|
|
})
|
|
.appendTo($body);
|
|
that.triggerEvent('show');
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.ColorPicker = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
id: '',
|
|
value: '0, 0, 0'
|
|
})
|
|
.options(options || {});
|
|
|
|
Ox.print(self)
|
|
self.$ranges = [];
|
|
self.rgb = ['red', 'green', 'blue'];
|
|
self.values = self.options.value.split(', ');
|
|
|
|
$.each(Ox.range(3), function(i) {
|
|
self.$ranges[i] = new Ox.Range({
|
|
arrows: true,
|
|
id: self.options.id + Ox.toTitleCase(self.rgb[i]),
|
|
max: 255,
|
|
size: 328, // 256 + 16 + 40 + 16
|
|
thumbSize: 40,
|
|
thumbValue: true,
|
|
trackColors: getColors(i),
|
|
value: self.values[i]
|
|
})
|
|
.css({
|
|
position: 'absolute',
|
|
top: (i * 15) + 'px'
|
|
})
|
|
.bindEvent('change', function(event, data) {
|
|
change(i, data.value);
|
|
})
|
|
.appendTo(that);
|
|
// fixme: make self.$ranges[i].children() work
|
|
if (i == 0) {
|
|
self.$ranges[i].$element.children('input.OxOverlapRight').css({
|
|
MozBorderRadius: 0,
|
|
WebkitBorderRadius: 0
|
|
});
|
|
self.$ranges[i].$element.children('input.OxOverlapLeft').css({
|
|
MozBorderRadius: '0 8px 0 0',
|
|
WebkitBorderRadius: '0 8px 0 0'
|
|
});
|
|
} else {
|
|
self.$ranges[i].$element.children('input').css({
|
|
MozBorderRadius: 0,
|
|
WebkitBorderRadius: 0
|
|
});
|
|
}
|
|
});
|
|
|
|
that = new Ox.Picker({
|
|
element: that,
|
|
elementHeight: 46,
|
|
elementWidth: 328,
|
|
id: self.options.id
|
|
});
|
|
|
|
function change(index, value) {
|
|
self.values[index] = value;
|
|
self.options.value = self.values.join(', ');
|
|
that.$label.css({
|
|
background: 'rgb(' + self.options.value + ')'
|
|
});
|
|
$.each(Ox.range(3), function(i) {
|
|
if (i != index) {
|
|
self.$ranges[i].options({
|
|
trackColors: getColors(i)
|
|
});
|
|
}
|
|
});
|
|
that.triggerEvent('change', {
|
|
value: self.options.value
|
|
});
|
|
}
|
|
|
|
function getColors(index) {
|
|
return [
|
|
'rgb(' + $.map(Ox.range(3), function(v) {
|
|
return v == index ? 0 : self.values[v];
|
|
}).join(', ') + ')',
|
|
'rgb(' + $.map(Ox.range(3), function(v) {
|
|
return v == index ? 255 : self.values[v];
|
|
}).join(', ') + ')'
|
|
]
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.PlacePicker = function(options, self) {
|
|
|
|
var self = $.extend(self || {}, {
|
|
options: $.extend({
|
|
id: '',
|
|
value: 'United States'
|
|
}, options)
|
|
}),
|
|
that;
|
|
|
|
self.$element = new Ox.Element('div')
|
|
.css({
|
|
width: '256px',
|
|
height: '192px'
|
|
})
|
|
.append(
|
|
self.$topBar = new Ox.Bar({
|
|
size: 16
|
|
})
|
|
.css({
|
|
MozBorderRadius: '0 8px 0 0',
|
|
WebkitBorderRadius: '0 8px 0 0'
|
|
})
|
|
.append(
|
|
self.$input = new Ox.Input({
|
|
clear: true,
|
|
id: self.options.id + 'Input',
|
|
placeholder: 'Find',
|
|
width: 256
|
|
})
|
|
.bindEvent('submit', findPlace)
|
|
)
|
|
)
|
|
.append(
|
|
self.$container = new Ox.Element('div')
|
|
.css({
|
|
width: '256px',
|
|
height: '160px'
|
|
})
|
|
)
|
|
.append(
|
|
self.$bottomBar = new Ox.Bar({
|
|
size: 16
|
|
})
|
|
.append(
|
|
self.$range = new Ox.Range({
|
|
arrows: true,
|
|
id: self.options.id + 'Range',
|
|
max: 22,
|
|
size: 256,
|
|
thumbSize: 32,
|
|
thumbValue: true
|
|
})
|
|
.bindEvent('change', changeZoom)
|
|
)
|
|
);
|
|
|
|
self.$input.$element.children('input[type=text]').css({
|
|
width: '230px',
|
|
paddingLeft: '2px',
|
|
MozBorderRadius: '0 8px 8px 0',
|
|
WebkitBorderRadius: '0 8px 8px 0'
|
|
});
|
|
self.$input.$element.children('input[type=image]').css({
|
|
MozBorderRadius: '0 8px 0 0',
|
|
WebkitBorderRadius: '0 8px 0 0'
|
|
});
|
|
self.$range.$element.children('input').css({
|
|
MozBorderRadius: 0,
|
|
WebkitBorderRadius: 0
|
|
});
|
|
|
|
that = new Ox.Picker({
|
|
element: self.$element,
|
|
elementHeight: 192,
|
|
elementWidth: 256,
|
|
id: self.options.id,
|
|
overlap: self.options.overlap,
|
|
value: self.options.value
|
|
}, self)
|
|
.bindEvent('show', showPicker);
|
|
|
|
that.$label.bind('click', clickLabel)
|
|
|
|
self.map = false;
|
|
|
|
function changeZoom(event, data) {
|
|
Ox.print('changeZoom')
|
|
self.$map.zoom(data.value);
|
|
}
|
|
|
|
function clickLabel() {
|
|
var name = that.$label.html();
|
|
if (name) {
|
|
self.$input.options({
|
|
value: name
|
|
})
|
|
.triggerEvent('submit', {
|
|
value: name
|
|
});
|
|
}
|
|
}
|
|
|
|
function findPlace(event, data) {
|
|
Ox.print('findPlace', data);
|
|
self.$map.find(data.value, function(location) {
|
|
if (location) {
|
|
that.$label.html(location.name.formatted);
|
|
}
|
|
})
|
|
}
|
|
|
|
function onSelect(event, data) {
|
|
that.$label.html(data.name.formatted);
|
|
}
|
|
|
|
function onZoom(event, data) {
|
|
self.$range.options({
|
|
value: data.value
|
|
});
|
|
}
|
|
|
|
function showPicker() {
|
|
if (!self.map) {
|
|
self.$map = new Ox.Map({
|
|
id: self.options.id + 'Map',
|
|
places: [self.options.value]
|
|
})
|
|
.css({
|
|
width: '256px',
|
|
height: '160px'
|
|
})
|
|
.bindEvent({
|
|
select: onSelect,
|
|
zoom: onZoom
|
|
})
|
|
.appendTo(self.$container);
|
|
self.map = true;
|
|
}
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
delete below
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Input_ = function(options, self) {
|
|
|
|
/*
|
|
options:
|
|
clear boolean, clear button, or not
|
|
disabled boolean, disabled, or not
|
|
height height (px), if type is 'textarea'
|
|
id
|
|
label string, or
|
|
array [{ id, title, checked }] (selectable label) or
|
|
array [{ id, label: [{ id, title, checked }], width }] (multiple selectable labels)
|
|
label and placeholder are mutually exclusive
|
|
labelWidth integer (px)
|
|
placeholder string, or
|
|
array [{ id, title, checked }] (selectable placeholder)
|
|
label and placeholder are mutually exclusive
|
|
separator string, or
|
|
array of strings
|
|
to separate multiple values
|
|
separatorWidth integer (px), or
|
|
array of integers
|
|
serialize function
|
|
size 'large', 'medium' or 'small'
|
|
type 'password', 'select' or 'text'
|
|
unit string, or
|
|
array [{ id, title, checked }] (selectable unit)
|
|
unitWidth integer (px)
|
|
value string, or
|
|
array [{ id, value, width }] (multiple values)
|
|
width integer (px)
|
|
methods:
|
|
events:
|
|
*/
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
autocomplete: null,
|
|
autocorrect: null,
|
|
autosuggest: null,
|
|
autosuggestHighlight: false,
|
|
autosuggestSubmit: false,
|
|
autovalidate: null,
|
|
autovalidateName: 'Value',
|
|
clear: false,
|
|
disabled: false,
|
|
height: 128,
|
|
id: '',
|
|
key: '',
|
|
label: '',
|
|
labelWidth: 64,
|
|
placeholder: '',
|
|
separator: '',
|
|
separatorWidth: 16,
|
|
serialize: null,
|
|
size: 'medium',
|
|
type: 'text',
|
|
unit: '',
|
|
unitWidth: 64,
|
|
value: '',
|
|
width: 128
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxInput Ox' + Ox.toTitleCase(self.options.size))
|
|
.css({
|
|
width: self.options.width + 'px'
|
|
});
|
|
|
|
$.extend(self, {
|
|
clearWidth: 16,
|
|
hasMultipleKeys: Ox.isArray(self.options.label) && 'label' in self.options.label[0],
|
|
hasMultipleValues: Ox.isArray(self.options.value) &&
|
|
(self.options.type != 'select' || 'items' in self.options.value[0]),
|
|
hasSelectableKeys: Ox.isArray(self.options.label) || Ox.isArray(self.options.placeholder),
|
|
hasSelectableUnits: Ox.isArray(self.options.unit),
|
|
keyName: self.options.label ? 'label' : (self.options.placeholder ? 'placeholder' : ''),
|
|
placeholderWidth: 16,
|
|
selectedKey: [0], // fixme: only set on demand?
|
|
selectedValue: 0,
|
|
selectedUnit: 0,
|
|
/* valid: autovalidateCall(true) */
|
|
});
|
|
|
|
$.each(['autocomplete', 'autocorrect', 'autosuggest', 'autovalidate'], function(i, v) {
|
|
//if (!Ox.isFunction(self.options[v])) {
|
|
self.options[v] = {
|
|
'': self.options[v]
|
|
};
|
|
//}
|
|
});
|
|
|
|
if (self.keyName && !self.hasMultipleKeys) {
|
|
self.options[self.keyName] = [$.extend({
|
|
id: '',
|
|
label: self.options[self.keyName],
|
|
}, self.keyName == 'label' ? {
|
|
id: '',
|
|
width: self.options.labelWidth
|
|
} : {})];
|
|
if (!self.hasSelectableKeys) {
|
|
self.options[self.keyName][0].label = [{
|
|
id: '',
|
|
title: self.options[self.keyName][0].label
|
|
}];
|
|
}
|
|
}
|
|
if (self.hasSelectableKeys) {
|
|
$.each(self.options[self.keyName], function(keyPos, key) {
|
|
if (key.width) {
|
|
self.options.labelWidth = (keyPos == 0 ? 0 : self.options.labelWidth) + key.width;
|
|
}
|
|
self.selectedKey[keyPos] = 0;
|
|
$.each(key, function(valuePos, value) {
|
|
if (value.checked) {
|
|
self.selectedKey[keyPos] = valuePos;
|
|
return false;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
self.valueWidth = self.options.width -
|
|
(self.options.label ? self.options.labelWidth : 0) -
|
|
((self.options.placeholder && self.options.placeholder[0].label.length > 1) ? self.placeholderWidth : 0) -
|
|
(self.options.unit ? self.options.unitWidth : 0) -
|
|
(self.options.clear ? self.clearWidth : 0);
|
|
/*
|
|
if (self.hasMultipleValues) {
|
|
self.valueWidth -= Ox.isArray(self.options.separatorWidth) ?
|
|
Ox.sum(self.options.separatorWidth) :
|
|
(self.options.value.length - 1) * self.options.separatorWidth;
|
|
}
|
|
*/
|
|
Ox.print('self.hasMulVal', self.hasMultipleValues);
|
|
Ox.print('self.options.value', self.options.value)
|
|
if (!self.hasMultipleValues) {
|
|
if (self.options.type == 'select') {
|
|
self.options.value = [{
|
|
id: '',
|
|
items: self.options.value,
|
|
width: self.valueWidth
|
|
}];
|
|
} else if (self.options.type == 'range') {
|
|
self.options.value = [$.extend({
|
|
id: '',
|
|
size: self.valueWidth
|
|
}, self.options.value)];
|
|
} else {
|
|
self.options.value = [{
|
|
id: '',
|
|
value: self.options.value,
|
|
width: self.valueWidth
|
|
}]
|
|
}
|
|
}
|
|
Ox.print('self.options.value', self.options.value)
|
|
self.values = self.options.value.length;
|
|
Ox.print(self.options.id, 'self.values', self.values)
|
|
|
|
if (Ox.isString(self.options.separator)) {
|
|
self.options.separator = $.map(new Array(self.values - 1), function(v, i) {
|
|
return self.options.separator;
|
|
});
|
|
}
|
|
if (Ox.isNumber(self.options.separatorWidth)) {
|
|
self.options.separatorWidth = $.map(new Array(self.values - 1), function(v, i) {
|
|
return self.options.separatorWidth;
|
|
});
|
|
}
|
|
|
|
if (self.options.unit) {
|
|
if (self.hasSelectableUnits) {
|
|
$.each(self.options.unit, function(pos, unit) {
|
|
if (unit.checked) {
|
|
self.selectedUnit = pos;
|
|
return false;
|
|
}
|
|
});
|
|
} else {
|
|
self.options.unit = [{
|
|
id: '',
|
|
title: self.options.unit
|
|
}];
|
|
}
|
|
}
|
|
|
|
Ox.print('self', self);
|
|
|
|
if (self.keyName) {
|
|
that.$key = [];
|
|
$.each(self.options[self.keyName], function(keyPos, key) {
|
|
Ox.print('keyPos key', keyPos, key)
|
|
if (self.keyName == 'label' && key.label.length == 1) {
|
|
that.$key[keyPos] = new Ox.Label({
|
|
overlap: 'right',
|
|
title: key.label[0].title,
|
|
width: self.options.labelWidth
|
|
})
|
|
.css({
|
|
float: 'left'
|
|
})
|
|
.click(function() {
|
|
that.$input[0].focus();
|
|
})
|
|
.appendTo(that);
|
|
} else if (key.label.length > 1) {
|
|
Ox.print('key.length > 1')
|
|
self.selectKeyId = self.options.id + Ox.toTitleCase(self.keyName) +
|
|
(self.options[self.keyName].length == 1 ? '' : keyPos);
|
|
Ox.print('three', self.selectedKey, keyPos, self.selectedKey[keyPos]);
|
|
that.$key[keyPos] = new Ox.Select({
|
|
id: self.selectKeyId,
|
|
items: $.map(key.label, function(value, valuePos) {
|
|
return {
|
|
checked: valuePos == self.selectedKey[keyPos],
|
|
id: value.id,
|
|
group: self.selectKeyId, // fixme: same id, works here, but should be different
|
|
title: value.title
|
|
};
|
|
}),
|
|
overlap: 'right',
|
|
type: self.options.label ? 'text' : 'image',
|
|
width: self.options.label ? (self.options.label.length == 1 ? self.options.labelWidth : key.width) : self.placeholderWidth
|
|
})
|
|
.css({
|
|
float: 'left'
|
|
})
|
|
.appendTo(that);
|
|
that.bindEvent('change_' + self.selectKeyId, changeKey);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (self.options.clear) {
|
|
that.$clear = new Ox.Button({
|
|
overlap: 'left',
|
|
type: 'image',
|
|
value: 'clear'
|
|
})
|
|
.css({
|
|
float: 'right'
|
|
})
|
|
.click(clear)
|
|
.appendTo(that);
|
|
}
|
|
|
|
if (self.options.unit.length == 1) {
|
|
that.$unit = new Ox.Label({
|
|
overlap: 'left',
|
|
title: self.options.unit[0].title,
|
|
width: self.options.unitWidth
|
|
})
|
|
.css({
|
|
float: 'right'
|
|
})
|
|
.click(function() {
|
|
that.$input[0].focus();
|
|
})
|
|
.appendTo(that);
|
|
} else if (self.options.unit.length > 1) {
|
|
self.selectUnitId = self.options.id + 'Unit';
|
|
that.$unit = new Ox.Select({
|
|
id: self.selectUnitId,
|
|
items: $.map(self.options.unit, function(unit, i) {
|
|
Ox.print('unit', unit)
|
|
return {
|
|
checked: i == 0,
|
|
id: unit.id,
|
|
group: self.selectUnitId, // fixme: same id, works here, but should be different
|
|
title: unit.title
|
|
};
|
|
}),
|
|
overlap: 'left',
|
|
size: self.options.size,
|
|
width: self.options.unitWidth
|
|
})
|
|
.css({
|
|
float: 'right'
|
|
})
|
|
.appendTo(that);
|
|
}
|
|
|
|
if (self.values) {
|
|
that.$separator = [];
|
|
$.each(self.options.value, function(i, v) {
|
|
if (i < self.values - 1) {
|
|
that.$separator[i] = new Ox.Label({
|
|
textAlign: 'center',
|
|
title: self.options.separator[i],
|
|
width: self.options.separatorWidth[i] + 32
|
|
})
|
|
.css({
|
|
float: 'left',
|
|
marginLeft: (v.width - (i == 0 ? 16 : 32)) + 'px'
|
|
})
|
|
.click(function() {
|
|
that.$input[0].focus();
|
|
})
|
|
.appendTo(that);
|
|
}
|
|
});
|
|
}
|
|
that.$input = [];
|
|
//self.margin = 0;
|
|
$.each(self.options.value, function(i, v) {
|
|
Ox.print('o k i', self.options, self.keyName, i);
|
|
var id = self.keyName ? $.map(self.selectedKey, function(v, i) {
|
|
return self.options[self.keyName][i].id;
|
|
}).join('.') : '';
|
|
//self.margin -= (i == 0 ? 16 : self.options.value[i - 1].width)
|
|
Ox.print('v:', v, 'id:', id)
|
|
if (self.options.type == 'select') {
|
|
that.$input[i] = new Ox.Select({
|
|
id: v.id,
|
|
items: v.items,
|
|
width: v.width
|
|
}).
|
|
css({
|
|
float: 'left'
|
|
});
|
|
} else if (self.options.type == 'range') {
|
|
that.$input[i] = new Ox.Range(v)
|
|
.css({
|
|
float: 'left'
|
|
});
|
|
} else {
|
|
that.$input[i] = new Ox.InputElement({
|
|
autocomplete: self.options.autocomplete[id],
|
|
autocorrect: self.options.autocorrect[id],
|
|
autosuggest: self.options.autosuggest[id],
|
|
autosuggestHighlight: self.options.autosuggestHighlight,
|
|
autosuggestSubmit: self.options.autosuggestSubmit,
|
|
autovalidate: self.options.autovalidate[id],
|
|
autovalidateName: self.options.autovalidateName,
|
|
disabled: self.options.disabled,
|
|
height: self.options.height,
|
|
id: self.options.id + 'Input' + Ox.toTitleCase(v.id),
|
|
key: self.hasSelectableKeys ? self.options[self.keyName][0].label[self.selectedKey[0]].id : '',
|
|
parent: that,
|
|
placeholder: self.options.placeholder ? self.options.placeholder[0].label[0].title : '',
|
|
size: self.options.size,
|
|
type: self.options.type,
|
|
value: v.value,
|
|
width: v.width
|
|
});
|
|
}
|
|
that.$input[i]
|
|
.css($.extend({}, self.options.value.length > 1 ? {
|
|
float: 'left',
|
|
marginLeft: -Ox.sum($.map(self.options.value, function(v_, i_) {
|
|
return i_ > i ? self.options.value[i_ - 1].width + self.options.separatorWidth[i_ - 1] : (i_ == i ? 16 : 0);
|
|
}))
|
|
} : {}))
|
|
.appendTo(that);
|
|
});
|
|
|
|
//width(self.options.width);
|
|
|
|
function changeKey(event, data) {
|
|
Ox.print('changeKey', data);
|
|
if (data) { // fixme: necessary?
|
|
self.key = {
|
|
id: data.id,
|
|
title: data.value // fixme: should be data.title
|
|
};
|
|
that.$input[0].options({
|
|
key: data.id
|
|
});
|
|
}
|
|
if (self.options.label) {
|
|
//that.$label.html(self.option.title);
|
|
that.$input[0].focus();
|
|
//autocompleteCall();
|
|
} else {
|
|
that.$input[0].options({
|
|
placeholder: data.value // fixme: should be data.title
|
|
});
|
|
/*
|
|
if (that.$input.hasClass('OxPlaceholder')) {
|
|
that.$input.val(self.key.title);
|
|
//that.$input.focus();
|
|
} else {
|
|
that.$input.focus();
|
|
self.options.autosuggest && autosuggestCall();
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
function changeUnit() {
|
|
that.$input[0].focus();
|
|
}
|
|
|
|
function clear() {
|
|
$.each(that.$input, function(i, v) {
|
|
v.val('');
|
|
});
|
|
that.$input[0].focus();
|
|
}
|
|
|
|
function height(value) {
|
|
var stop = 8 / value;
|
|
if (self.options.type == 'textarea') {
|
|
that.$element
|
|
.height(value)
|
|
.css({
|
|
background: '-moz-linear-gradient(top, rgb(224, 224, 224), rgb(208, 208, 208) ' + (stop * 100) + '%, rgb(208, 208, 208) ' + (100 - stop * 100) + '%, rgb(192, 192, 192))'
|
|
})
|
|
.css({
|
|
background: '-webkit-gradient(linear, left top, left bottom, from(rgb(224, 224, 224)), color-stop(' + stop + ', rgb(208, 208, 208)), color-stop(' + (1 - stop) + ', rgb(208, 208, 208)), to(rgb(192, 192, 192)))'
|
|
});
|
|
that.$input
|
|
.height(value)
|
|
.css({
|
|
background: '-moz-linear-gradient(top, rgb(224, 224, 224), rgb(240, 240, 240) ' + (stop * 100) + '%, rgb(240, 240, 240) ' + (100 - stop * 100) + '%, rgb(255, 255, 255))'
|
|
})
|
|
.css({
|
|
background: '-webkit-gradient(linear, left top, left bottom, from(rgb(224, 224, 224)), color-stop(' + stop + ', rgb(240, 240, 240)), color-stop(' + (1 - stop) + ', rgb(240, 240, 240)), to(rgb(255, 255, 255)))'
|
|
});
|
|
}
|
|
}
|
|
|
|
function selectUnit() {
|
|
self.$selectUnitMenu.show();
|
|
}
|
|
|
|
function submit() {
|
|
Ox.print('submit')
|
|
var value = that.$input.val();
|
|
that.$input.blur();
|
|
that.triggerEvent('submit', self.options.key ? {
|
|
key: self.options.key,
|
|
value: value
|
|
} : value);
|
|
}
|
|
|
|
function width(value) {
|
|
that.$element.width(value);
|
|
that.$input.width(
|
|
value - (self.options.type == 'textarea' ? 0 : 12) -
|
|
(self.options.label ? self.options.labelWidth : 0) -
|
|
(self.options.placeholder.length > 1 ? 16 : 0) -
|
|
(self.options.unit ? self.options.unitWidth : 0) -
|
|
(self.options.clear ? 16 : 0)
|
|
);
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'height') {
|
|
height(value);
|
|
} else if (key == 'width') {
|
|
width(value);
|
|
}
|
|
};
|
|
|
|
that.changeLabel = function(id) {
|
|
that.$key.html(Ox.getObjectById(self.options.label, id).title);
|
|
self.selectMenu.checkItem(id);
|
|
};
|
|
|
|
return that;
|
|
|
|
}
|
|
|
|
Ox.InputElement_ = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element(
|
|
options.type == 'textarea' ? 'textarea' : 'input', self
|
|
)
|
|
.defaults({
|
|
autocomplete: null,
|
|
autocorrect: null,
|
|
autosuggest: null,
|
|
autosuggestHighlight: false,
|
|
autosuggestSubmit: false,
|
|
autovalidate: null,
|
|
disabled: false,
|
|
height: 128,
|
|
id: '',
|
|
key: '',
|
|
parent: null,
|
|
placeholder: '',
|
|
size: 'medium',
|
|
type: 'text',
|
|
value: '',
|
|
width: 128
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxInput Ox' + Ox.toTitleCase(self.options.size) + (
|
|
(self.options.placeholder && self.options.value === '') ?
|
|
' OxPlaceholder' : ''
|
|
))
|
|
.attr(self.options.type == 'textarea' ? {} : {
|
|
type: self.options.type
|
|
})
|
|
.css({
|
|
float: 'left',
|
|
width: (self.options.width - 14) + 'px'
|
|
})
|
|
.val(
|
|
(self.options.placeholder && self.options.value === '') ?
|
|
self.options.placeholder : self.options.value
|
|
)
|
|
.blur(blur)
|
|
.change(change)
|
|
.focus(focus);
|
|
|
|
Ox.print('InputElement self.options', self.options)
|
|
|
|
self.bindKeyboard = self.options.autocomplete || self.options.autocorrect ||
|
|
self.options.autosuggest || self.options.autovalidate;
|
|
|
|
if (self.options.autosuggest) {
|
|
self.autosuggestId = self.options.id + 'Menu'; // fixme: we do this in other places ... are we doing it the same way? var name?
|
|
self.$autosuggestMenu = new Ox.Menu({
|
|
element: that.$element,
|
|
id: self.autosuggestId,
|
|
offset: {
|
|
left: 4,
|
|
top: 0
|
|
},
|
|
size: self.options.size
|
|
});
|
|
that.bindEvent('click_' + self.autosuggestId, clickMenu);
|
|
}
|
|
|
|
that.bindEvent($.extend(self.options.type == 'textarea' ? {} : {
|
|
key_enter: submit
|
|
}, {
|
|
key_escape: cancel
|
|
}));
|
|
|
|
function autocomplete(value) {
|
|
var value = value.toLowerCase(),
|
|
ret = '';
|
|
if (value !== '') {
|
|
$.each(self.options.autocomplete, function(i, v) {
|
|
if (v.toLowerCase().indexOf(value) == 0) {
|
|
ret = v;
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
function autocompleteCall() {
|
|
var value = that.$element.val();
|
|
Ox.isFunction(self.options.autocomplete) ?
|
|
self.options.autocomplete(self.options.key ? {
|
|
key: self.options.key,
|
|
value: value
|
|
} : value, autocompleteCallback) :
|
|
autocompleteCallback(autocomplete(value));
|
|
}
|
|
|
|
function autocompleteCallback(value) {
|
|
var pos = cursor()[0];
|
|
if (value) {
|
|
that.$element.val(value);
|
|
cursor(pos, value.length);
|
|
}
|
|
}
|
|
|
|
function autocorrect(value) {
|
|
var length = value.length;
|
|
return $.map(value.toLowerCase().split(''), function(v, i) {
|
|
if (new RegExp(self.options.autocorrect)(v)) {
|
|
return v
|
|
} else {
|
|
return null;
|
|
}
|
|
}).join('');
|
|
}
|
|
|
|
function autocorrectCall(blur) {
|
|
var blur = blur || false,
|
|
value = that.$element.val(),
|
|
pos = cursor()[0];
|
|
Ox.isFunction(self.options.autocorrect) ?
|
|
self.options.autocorrect(value, blur, autocorrectCallback) :
|
|
autocorrectCallback(autocorrect(value), blue);
|
|
}
|
|
|
|
function autocorrectCallback(value, blur) {
|
|
var length = that.$element.val().length;
|
|
that.$element.val(self.options.value);
|
|
!blur && cursor(pos + value.length - length);
|
|
}
|
|
|
|
function autosuggest(value) {
|
|
var value = value.toLowerCase(),
|
|
values = [[], []];
|
|
if (value !== '') {
|
|
$.each(self.options.key ? self.options.autosuggest[self.options.key] : self.options.autosuggest, function(i, v) {
|
|
//Ox.print('v...', v)
|
|
var index = v.toLowerCase().indexOf(value);
|
|
index > -1 && values[index == 0 ? 0 : 1].push(v);
|
|
});
|
|
}
|
|
return $.merge(values[0], values[1]);
|
|
}
|
|
|
|
function autosuggestCall() {
|
|
var value = that.$element.val();
|
|
Ox.isFunction(self.options.autosuggest) ?
|
|
self.options.autosuggest(self.options.key ? {
|
|
key: self.options.key,
|
|
value: value
|
|
} : value, autosuggestCallback) :
|
|
autosuggestCallback(autosuggest(value));
|
|
}
|
|
|
|
function autosuggestCallback(values) {
|
|
var values = values || [],
|
|
selected = values.length == 1 ? 0 : -1,
|
|
value = that.$element.val().toLowerCase();
|
|
//Ox.print('values', values);
|
|
if (values.length) {
|
|
values = $.map(values, function(v, i) {
|
|
if (value == v.toLowerCase()) {
|
|
selected = i;
|
|
}
|
|
return {
|
|
id: v.toLowerCase().replace(/ /g, '_'), // fixme: need function to do lowercase, underscores etc?
|
|
title: self.options.autosuggestHighlight ? v.replace(
|
|
new RegExp('(' + value + ')', 'ig'),
|
|
'<span class="OxHighlight">$1</span>'
|
|
) : v
|
|
};
|
|
});
|
|
// self.selectMenu && self.selectMenu.hideMenu(); // fixme: need event
|
|
self.$autosuggestMenu.options({
|
|
items: values,
|
|
selected: selected
|
|
}).showMenu();
|
|
} else {
|
|
self.$autosuggestMenu.hideMenu();
|
|
}
|
|
}
|
|
|
|
function autovalidate(value) {
|
|
return {
|
|
valid: self.options.autovalidate(value) != null,
|
|
message: 'Invalid ' + self.options.name
|
|
};
|
|
}
|
|
|
|
function autovalidateCall(blur) {
|
|
var blur = blur || false,
|
|
value = that.$element.val();
|
|
if (value !== '') {
|
|
Ox.isFunction(self.options.autovalidate) ?
|
|
self.options.autovalidate(value, autovalidateCallback) :
|
|
autovalidateCallback(autovalidate(value), blur);
|
|
} else {
|
|
autovalidateCallback({
|
|
blur: blur,
|
|
valid: false,
|
|
message: 'Empty ' + self.options.name
|
|
});
|
|
}
|
|
}
|
|
|
|
function autovalidateCallback(data, blur) {
|
|
if (data.valid != self.valid) {
|
|
self.valid = data.valid;
|
|
that.triggerEvent('validate', $.extend(data, {
|
|
blur: blur
|
|
}));
|
|
}
|
|
}
|
|
|
|
function blur() {
|
|
if (!self.options.autosuggest || self.$autosuggestMenu.is(':hidden')) {
|
|
Ox.print('losing focus...')
|
|
that.loseFocus();
|
|
self.options.parent.removeClass('OxFocus');
|
|
self.options.autocorrect && autocorrectCall(true);
|
|
// self.options.autosuggest && self.$autosuggestMenu.hideMenu();
|
|
self.options.autovalidate && autovalidateCall(true);
|
|
if (self.options.placeholder && that.$element.val() === '') {
|
|
that.$element.addClass('OxPlaceholder').val(self.options.placeholder);
|
|
}
|
|
}
|
|
if (self.bindKeyboard) {
|
|
$document.unbind('keydown', keypress);
|
|
$document.unbind('keypress', keypress);
|
|
}
|
|
}
|
|
|
|
function cancel() {
|
|
that.$element.blur();
|
|
}
|
|
|
|
function change() {
|
|
|
|
}
|
|
|
|
function clear() {
|
|
that.$element.val('').focus();
|
|
}
|
|
|
|
function clickMenu(event, data) {
|
|
Ox.print('clickMenu', data);
|
|
that.$element.val(data.title);
|
|
//self.$autosuggestMenu.hideMenu();
|
|
self.options.autosuggestSubmit && submit();
|
|
}
|
|
|
|
function cursor(start, end) {
|
|
/*
|
|
cursor() returns [start, end]
|
|
cursor(start) sets start
|
|
cursor([start, end]) sets start and end
|
|
cursor(start, end) sets start and end
|
|
*/
|
|
var isArray = Ox.isArray(start);
|
|
if (arguments.length == 0) {
|
|
return [that.$element[0].selectionStart, that.$element[0].selectionEnd];
|
|
} else {
|
|
start = isArray ? start[0] : start;
|
|
end = isArray ? start[1] : (end ? end : start);
|
|
that.$element[0].setSelectionRange(start, end);
|
|
}
|
|
}
|
|
|
|
function focus() {
|
|
var val = that.$element.val();
|
|
that.gainFocus();
|
|
self.options.parent.addClass('OxFocus');
|
|
if (that.$element.hasClass('OxPlaceholder')) {
|
|
that.$element.val('').removeClass('OxPlaceholder');
|
|
}
|
|
if (self.bindKeyboard) {
|
|
// fixme: different in webkit and firefox (?), see keyboard handler, need generic function
|
|
$document.keydown(keypress);
|
|
$document.keypress(keypress);
|
|
Ox.print('calling autosuggest...')
|
|
self.options.autosuggest && setTimeout(autosuggestCall, 0); // fixme: why is the timeout needed?
|
|
}
|
|
}
|
|
|
|
function keypress(event) {
|
|
Ox.print('keyCode', event.keyCode)
|
|
if (event.keyCode != 9 && event.keyCode != 13 && event.keyCode != 27) { // fixme: can't 13 and 27 return false?
|
|
setTimeout(function() { // fixme: document what this timeout is for
|
|
var value = that.$element.val();
|
|
if (value != self.options.value) {
|
|
self.options.value = value;
|
|
self.options.autocomplete && autocompleteCall();
|
|
self.options.autocorrect && autocorrectCall();
|
|
self.options.autosuggest && autosuggestCall();
|
|
self.options.autovalidate && autovalidateCall();
|
|
}
|
|
}, 25);
|
|
}
|
|
}
|
|
|
|
function submit() {
|
|
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'placeholder') {
|
|
that.$element.hasClass('OxPlaceholder') && that.$element.val(value);
|
|
} else if (key == 'value') {
|
|
if (self.options.placeholder) {
|
|
if (value === '') {
|
|
that.$element.addClass('OxPlaceholder').val(self.options.placeholder);
|
|
} else {
|
|
that.$element.removeClass('OxPlaceholder');
|
|
}
|
|
}
|
|
change(); // fixme: keypress too
|
|
}
|
|
}
|
|
|
|
return that;
|
|
|
|
}
|
|
|
|
Ox.Range_ = function(options, self) {
|
|
|
|
/*
|
|
init
|
|
*/
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
animate: false,
|
|
arrows: false,
|
|
arrowStep: 1,
|
|
arrowSymbols: ['previous', 'next'],
|
|
max: 100,
|
|
min: 0,
|
|
orientation: 'horizontal',
|
|
step: 1,
|
|
size: 128,
|
|
thumbSize: 16,
|
|
thumbValue: false,
|
|
trackImages: [],
|
|
trackStep: 0,
|
|
value: 0,
|
|
valueNames: null
|
|
})
|
|
.options($.extend(options, {
|
|
arrowStep: options.arrowStep ?
|
|
options.arrowStep : options.step,
|
|
trackImages: $.makeArray(options.trackImages || [])
|
|
}))
|
|
.addClass('OxRange')
|
|
.css({
|
|
width: self.options.size + 'px'
|
|
});
|
|
|
|
// fixme: self. ... ?
|
|
var trackImages = self.options.trackImages.length,
|
|
values = (self.options.max - self.options.min + self.options.step) /
|
|
self.options.step;
|
|
|
|
/*
|
|
construct
|
|
*/
|
|
that.$element
|
|
.css({
|
|
width: self.options.size + 'px'
|
|
});
|
|
if (self.options.arrows) {
|
|
var $arrowDec = Ox.Button({
|
|
style: 'symbol',
|
|
type: 'image',
|
|
value: self.options.arrowSymbols[0]
|
|
})
|
|
.addClass('OxArrow')
|
|
.mousedown(mousedownArrow)
|
|
.click(clickArrowDec)
|
|
.appendTo(that.$element);
|
|
}
|
|
var $track = new Ox.Element()
|
|
.addClass('OxTrack')
|
|
.mousedown(clickTrack)
|
|
.appendTo(that.$element);
|
|
|
|
if (trackImages) {
|
|
var width = parseFloat(screen.width / trackImages),
|
|
$image = $('<canvas>')
|
|
.attr({
|
|
width: width * trackImages,
|
|
height: 14
|
|
})
|
|
.addClass('OxImage')
|
|
.appendTo($track.$element),
|
|
c = $image[0].getContext('2d');
|
|
c.mozImageSmoothingEnabled = false; // we may want to remove this later
|
|
$.each(self.options.trackImages, function(i, v) {
|
|
var left = 0;
|
|
$('<img/>')
|
|
.attr({
|
|
src: v
|
|
})
|
|
.load(function() {
|
|
c.drawImage(this, left, 0, self.trackImageWidth[i], 14);
|
|
});
|
|
left += self.trackImageWidth[i];
|
|
});
|
|
}
|
|
var $thumb = Ox.Button({})
|
|
.addClass('OxThumb')
|
|
.appendTo($track);
|
|
Ox.print('----')
|
|
if (self.options.arrows) {
|
|
var $arrowInc = Ox.Button({
|
|
style: 'symbol',
|
|
type: 'image',
|
|
value: self.options.arrowSymbols[1]
|
|
})
|
|
.addClass('OxArrow')
|
|
.mousedown(mousedownArrow)
|
|
.click(clickArrowInc)
|
|
.appendTo(that.$element);
|
|
}
|
|
var rangeWidth, trackWidth, imageWidth, thumbWidth;
|
|
setWidth(self.options.size);
|
|
|
|
/*
|
|
private functions
|
|
*/
|
|
|
|
function clickArrowDec() {
|
|
that.removeClass('OxActive');
|
|
setValue(self.options.value - self.options.arrowStep, 200)
|
|
}
|
|
function clickArrowInc() {
|
|
that.removeClass('OxActive');
|
|
setValue(self.options.value + self.options.arrowStep, 200);
|
|
}
|
|
function clickTrack(e) {
|
|
//Ox.Focus.focus();
|
|
var left = $track.offset().left,
|
|
offset = $(e.target).hasClass('OxThumb') ?
|
|
e.clientX - $thumb.offset().left - thumbWidth / 2 - 2 : 0;
|
|
function val(e) {
|
|
return getVal(e.clientX - left - offset);
|
|
}
|
|
setValue(val(e), 200);
|
|
$window.mousemove(function(e) {
|
|
setValue(val(e));
|
|
});
|
|
$window.one('mouseup', function() {
|
|
$window.unbind('mousemove');
|
|
});
|
|
}
|
|
function getPx(val) {
|
|
var pxPerVal = (trackWidth - thumbWidth - 2) /
|
|
(self.options.max - self.options.min);
|
|
return Math.ceil((val - self.options.min) * pxPerVal + 1);
|
|
}
|
|
function getVal(px) {
|
|
var px = trackWidth / values >= 16 ? px : px - 8,
|
|
valPerPx = (self.options.max - self.options.min) /
|
|
(trackWidth - thumbWidth);
|
|
return Ox.limit(self.options.min +
|
|
Math.floor(px * valPerPx / self.options.step) * self.options.step,
|
|
self.options.min, self.options.max);
|
|
}
|
|
function mousedownArrow() {
|
|
that.addClass('OxActive');
|
|
}
|
|
function setThumb(animate) {
|
|
var animate = typeof animate != 'undefined' ? animate : 0;
|
|
$thumb.animate({
|
|
marginLeft: (getPx(self.options.value) - 2) + 'px',
|
|
width: thumbWidth + 'px'
|
|
}, self.options.animate ? animate : 0, function() {
|
|
if (self.options.thumbValue) {
|
|
$thumb.options({
|
|
value: self.options.valueNames ?
|
|
self.options.valueNames[self.options.value] :
|
|
self.options.value
|
|
});
|
|
}
|
|
});
|
|
}
|
|
function setValue(val, animate) {
|
|
val = Ox.limit(val, self.options.min, self.options.max);
|
|
if (val != self.options.value) {
|
|
that.options({
|
|
value: val
|
|
});
|
|
setThumb(animate);
|
|
that.triggerEvent('change', { value: val });
|
|
}
|
|
}
|
|
function setWidth(width) {
|
|
trackWidth = width - self.options.arrows * 32;
|
|
thumbWidth = Math.max(trackWidth / values - 2, self.options.thumbSize - 2);
|
|
that.$element.css({
|
|
width: (width - 2) + 'px'
|
|
});
|
|
$track.css({
|
|
width: (trackWidth - 2) + 'px'
|
|
});
|
|
if (trackImages) {
|
|
$image.css({
|
|
width: (trackWidth - 2) + 'px'
|
|
});
|
|
}
|
|
$thumb.css({
|
|
width: (thumbWidth - 2) + 'px',
|
|
padding: 0
|
|
});
|
|
setThumb();
|
|
}
|
|
|
|
/*
|
|
shared functions
|
|
*/
|
|
|
|
self.onChange = function(key, value) {
|
|
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
/*
|
|
============================================================================
|
|
Lists
|
|
============================================================================
|
|
*/
|
|
|
|
Ox.IconList = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
id: '',
|
|
item: function() {},
|
|
keys: [],
|
|
orientation: 'both',
|
|
request: function() {},
|
|
size: 128,
|
|
sort: [],
|
|
})
|
|
.options(options || {});
|
|
|
|
$.extend(self, {
|
|
itemHeight: self.options.size * 1.5,
|
|
itemWidth: self.options.size
|
|
});
|
|
|
|
that.$element = new Ox.List({
|
|
construct: constructItem,
|
|
itemHeight: self.itemHeight,
|
|
itemWidth: self.itemWidth,
|
|
keys: 'foo',
|
|
orientation: 'both',
|
|
keys: self.options.keys,
|
|
request: self.options.request,
|
|
rowLength: 1,
|
|
size: 128,
|
|
type: 'icon',
|
|
}, self)
|
|
.click(click)
|
|
.dblclick(dblclick)
|
|
.scroll(scroll);
|
|
|
|
function click() {
|
|
|
|
}
|
|
|
|
function constructItem(data) {
|
|
var data = self.options.item(data, self.options.sort);
|
|
return new Ox.IconItem($.extend(data, {
|
|
size: self.options.size
|
|
}));
|
|
}
|
|
|
|
function dblclick() {
|
|
|
|
}
|
|
|
|
function scroll() {
|
|
|
|
}
|
|
|
|
Ox.print('IconList', that.options('id'))
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.IconItem = function(options, self) {
|
|
|
|
var self = self || {}
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
height: 0,
|
|
id: '',
|
|
info: '',
|
|
size: 128,
|
|
title: '',
|
|
width: 0,
|
|
url: ''
|
|
})
|
|
.options(options || {});
|
|
|
|
$.extend(self, {
|
|
height: self.options.size * 1.5,
|
|
url: oxui.path + '/png/ox.ui.' + Ox.theme() + '/icon.png',
|
|
width: self.options.size + 4
|
|
});
|
|
|
|
that.css({
|
|
width: self.width + 'px',
|
|
height: self.height + 'px'
|
|
});
|
|
that.$icon = $('<div>')
|
|
.addClass('OxIcon')
|
|
.css({
|
|
top: self.options.size == 64 ? -70 : -128,
|
|
width: self.options.size + 'px',
|
|
height: self.options.size + 'px'
|
|
});
|
|
that.$iconImage = $('<img>')
|
|
.attr({
|
|
src: self.url
|
|
})
|
|
.css({
|
|
width: iconWidth + 'px',
|
|
height: iconHeight + 'px'
|
|
})
|
|
.mouseenter(mouseenter)
|
|
.mouseleave(mouseleave)
|
|
.one('load', function() {
|
|
that.$iconImage.attr({
|
|
src: self.options.url
|
|
});
|
|
});
|
|
that.$textBox = $('<div>')
|
|
.addClass('OxText')
|
|
.css({
|
|
top: (self.options.size / 2 + 2) + 'px',
|
|
width: self.width + 'px',
|
|
height: (self.options.size == 64 ? 38 : 58) + 'px'
|
|
})
|
|
that.$text = $('<div>')
|
|
.html(self.options.title + '<br/><span class="OxInfo">' + self.options.info + '</span>')
|
|
.mouseenter(mouseenter)
|
|
.mouseleave(mouseleave)
|
|
that.$reflection = $('<div>')
|
|
.addClass('OxReflection')
|
|
.css({
|
|
top: (self.options.size + 2) + 'px',
|
|
width: self.options.size + 'px',
|
|
height: (self.options.size / 2) + 'px'
|
|
});
|
|
that.$reflectionImage = $('<img>')
|
|
.addClass('OxReflection')
|
|
.attr({
|
|
src: self.url
|
|
})
|
|
.css({
|
|
width: self.options.width + 'px',
|
|
height: self.options.height + 'px'
|
|
})
|
|
.one('load', function() {
|
|
that.$reflectionImage.attr({
|
|
src: self.options.url
|
|
});
|
|
});
|
|
that.$gradient = $('<div>')
|
|
.addClass('OxGradient')
|
|
.css({
|
|
top: (-self.options.size - 2) + 'px'
|
|
});
|
|
|
|
that.append(
|
|
that.$reflection.append(
|
|
that.$reflectionImage
|
|
).append(
|
|
that.$gradientImage
|
|
)
|
|
).append(
|
|
that.$textBox.append(
|
|
that.$text
|
|
)
|
|
).append(
|
|
that.$icon.append(
|
|
that.$iconImage
|
|
)
|
|
);
|
|
|
|
function mouseenter() {
|
|
that.addClass('OxHover');
|
|
}
|
|
|
|
function mouseleave() {
|
|
that.removeClass('OxHover');
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.List = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Container({}, self)
|
|
.defaults({
|
|
construct: function() {},
|
|
itemHeight: 16,
|
|
itemWidth: 16,
|
|
keys: [],
|
|
orientation: 'vertical',
|
|
request: function() {}, // {sort:, range:, callback:}, without parameter returns {items, size etc.}
|
|
rowLength: 1,
|
|
sort: [],
|
|
type: 'text',
|
|
unique: ''
|
|
})
|
|
.options(options || {})
|
|
.click(click)
|
|
.scroll(scroll);
|
|
|
|
$.extend(self, {
|
|
$items: [],
|
|
$pages: [],
|
|
ids: {},
|
|
keyboardEvents: {
|
|
key_alt_control_a: invertSelection,
|
|
key_control_a: selectAll,
|
|
key_control_shift_a: selectNone,
|
|
key_end: scrollToFirst,
|
|
key_enter: open,
|
|
key_home: scrollToLast,
|
|
key_pagedown: scrollPageDown,
|
|
key_pageup: scrollPageUp,
|
|
key_space: preview
|
|
},
|
|
page: 0,
|
|
pageLength: 100,
|
|
preview: false,
|
|
requests: [],
|
|
selected: []
|
|
});
|
|
self.keyboardEvents['key_' + (self.options.orientation == 'horizontal' ? 'left' : 'up')] = selectPrevious;
|
|
self.keyboardEvents['key_' + (self.options.orientation == 'horizontal' ? 'right' : 'down')] = selectNext;
|
|
self.keyboardEvents['key_' + (self.options.orientation == 'horizontal' ? 'shift_left' : 'shift_up')] = addPreviousToSelection;
|
|
self.keyboardEvents['key_' + (self.options.orientation == 'horizontal' ? 'shift_right' : 'shift_down')] = addNextToSelection;
|
|
|
|
updateQuery();
|
|
Ox.print('s.o', self.options)
|
|
that.addEvent(self.keyboardEvents);
|
|
|
|
function addAllToSelection(pos) {
|
|
var arr,
|
|
len = self.$items.length;
|
|
if (!isSelected(pos)) {
|
|
if (self.selected.length == 0) {
|
|
addToSelection(pos);
|
|
} else {
|
|
if (Ox.min(self.selected) < pos) {
|
|
var arr = [pos];
|
|
for (var i = pos - 1; i >= 0; i--) {
|
|
if (isSelected(i)) {
|
|
$.each(arr, function(i, v) {
|
|
addToSelection(v);
|
|
});
|
|
break;
|
|
}
|
|
arr.push(i);
|
|
}
|
|
}
|
|
if (Ox.max(self.selected) > pos) {
|
|
var arr = [pos];
|
|
for (var i = pos + 1; i < len; i++) {
|
|
if (isSelected(i)) {
|
|
$.each(arr, function(i, v) {
|
|
addToSelection(v);
|
|
});
|
|
break;
|
|
}
|
|
arr.push(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function addNextToSelection() {
|
|
var pos = getNext();
|
|
if (pos > -1) {
|
|
addToSelection(pos);
|
|
scrollTo(pos);
|
|
}
|
|
}
|
|
|
|
function addPreviousToSelection() {
|
|
var pos = getPrevious();
|
|
if (pos > -1) {
|
|
addToSelection(pos);
|
|
scrollTo(pos);
|
|
}
|
|
}
|
|
|
|
function addToSelection(pos) {
|
|
if (!isSelected(pos)) {
|
|
self.selected.push(pos);
|
|
if (!Ox.isUndefined(self.$items[pos])) {
|
|
self.$items[pos].addClass('OxSelected');
|
|
}
|
|
Ox.print('addToSelection')
|
|
triggerSelectEvent();
|
|
}
|
|
}
|
|
|
|
function clear() {
|
|
$.each(self.requests, function(i, v) {
|
|
Ox.print('Ox.Request.cancel', v);
|
|
Ox.Request.cancel(v);
|
|
});
|
|
$.extend(self, {
|
|
$items: [],
|
|
$pages: [],
|
|
page: 0,
|
|
requests: []
|
|
});
|
|
}
|
|
|
|
function click(e) {
|
|
var $element = $(e.target), pos;
|
|
that.gainFocus();
|
|
while (!$element.hasClass('OxItem') && !$element.hasClass('OxPage')) {
|
|
$element = $element.parent();
|
|
}
|
|
if ($element.hasClass('OxItem')) {
|
|
Ox.print($element.attr('id'), $element.data('position'));
|
|
pos = $element.data('position');
|
|
if (e.shiftKey) {
|
|
addAllToSelection(pos);
|
|
} else if (e.metaKey) {
|
|
toggleSelection(pos);
|
|
} else {
|
|
select(pos);
|
|
}
|
|
} else {
|
|
selectNone();
|
|
}
|
|
}
|
|
|
|
function deselect(pos) {
|
|
if (isSelected(pos)) {
|
|
self.selected.splice(self.selected.indexOf(pos), 1);
|
|
if (!Ox.isUndefined(self.$items[pos])) {
|
|
self.$items[pos].removeClass('OxSelected');
|
|
}
|
|
triggerSelectEvent();
|
|
}
|
|
}
|
|
|
|
function getHeight() {
|
|
return that.height() - (that.$content.width() > that.width() ? oxui.scrollbarSize : 0);
|
|
}
|
|
|
|
function getNext() {
|
|
var pos = -1;
|
|
if (self.selected.length) {
|
|
pos = Ox.max(self.selected) + 1;
|
|
if (pos == self.$items.length) {
|
|
pos = -1;
|
|
}
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
function getPage() {
|
|
return self.options.orientation == 'horizontal'
|
|
? Math.floor(that.scrollLeft() / self.pageWidth)
|
|
: Math.floor(that.scrollTop() / self.pageHeight);
|
|
}
|
|
|
|
function getPositions() {
|
|
Ox.print('getPositions', $.map(self.selected, function(v, i) {
|
|
return self.ids[v];
|
|
}));
|
|
// fixme: optimize: send non-selected ids if more than half of the items are selected
|
|
if (self.selected.length /*&& self.selected.length < self.listLength*/) {
|
|
self.requests.push(self.options.request({
|
|
callback: getPositionsCallback,
|
|
ids: $.map(self.selected, function(v, i) {
|
|
return self.ids[v];
|
|
}),
|
|
sort: self.options.sort
|
|
}));
|
|
} else {
|
|
getPositionsCallback();
|
|
}
|
|
}
|
|
|
|
function getPositionsCallback(result) {
|
|
Ox.print('getPositionsCallback', result)
|
|
if (result) {
|
|
$.extend(self, {
|
|
ids: {},
|
|
selected: []
|
|
});
|
|
$.each(result.data.positions, function(id, pos) {
|
|
Ox.print('id', id, 'pos', pos)
|
|
self.selected.push(pos);
|
|
});
|
|
}
|
|
load();
|
|
}
|
|
|
|
function getPrevious() {
|
|
var pos = -1;
|
|
if (self.selected.length) {
|
|
pos = Ox.min(self.selected) - 1;
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
function getSelectedIds() {
|
|
// fixme: is carring self.ids around the best way?
|
|
return $.map(self.selected, function(v, i) {
|
|
return self.ids[v];
|
|
});
|
|
}
|
|
|
|
function getWidth() {
|
|
return that.width() - (that.$content.height() > that.height() ? oxui.scrollbarSize : 0);
|
|
}
|
|
|
|
function invertSelection() {
|
|
$.each(Ox.range(self.listLength), function(i, v) {
|
|
toggleSelection(v);
|
|
});
|
|
}
|
|
|
|
function isSelected(pos) {
|
|
return self.selected.indexOf(pos) > -1;
|
|
}
|
|
|
|
function load() {
|
|
that.scrollTop(0);
|
|
that.$content.empty();
|
|
loadPages(self.page);
|
|
}
|
|
|
|
function loadPage(page, callback) {
|
|
Ox.print('loadPage', page)
|
|
if (page < 0 || page >= self.pages) {
|
|
!Ox.isUndefined(callback) && callback();
|
|
return;
|
|
}
|
|
var keys = $.inArray('id', self.options.keys) > -1 ? self.options.keys :
|
|
$.merge(self.options.keys, ['id']),
|
|
offset = page * self.pageLength,
|
|
range = [offset, offset + (page < self.pages - 1 ?
|
|
self.pageLength : self.listLength % self.pageLength)];
|
|
if (Ox.isUndefined(self.$pages[page])) {
|
|
self.requests.push(self.options.request({
|
|
callback: function(result) {
|
|
self.$pages[page] = new Ox.ListPage();
|
|
if (self.options.type == 'text') {
|
|
self.$pages[page].css({
|
|
top: (page * self.pageHeight) + 'px'
|
|
});
|
|
} else {
|
|
self.$pages[page].css({
|
|
|
|
});
|
|
}
|
|
$.each(result.data.items, function(i, v) {
|
|
var pos = offset + i;
|
|
self.$items[pos] = new Ox.ListItem({
|
|
construct: self.options.construct,
|
|
data: v,
|
|
id: v[self.options.unique],
|
|
position: pos
|
|
});
|
|
self.ids[pos] = v[self.options.unique];
|
|
if (isSelected(pos)) {
|
|
self.$items[pos].addClass('OxSelected');
|
|
}
|
|
self.$items[pos].appendTo(self.$pages[page]);
|
|
});
|
|
if (self.options.type == 'text' && page == 0) {
|
|
var height = that.height() - (that.width() < that.$content.width() ? oxui.scrollbarSize : 0),
|
|
visibleItems = Math.ceil(height / self.options.itemHeight);
|
|
if (result.data.items.length < visibleItems) {
|
|
self.$pages[page].height(height).css({
|
|
overflow: 'hidden'
|
|
});
|
|
$.each(Ox.range(result.data.items.length, visibleItems), function(i, v) {
|
|
new Ox.ListItem({
|
|
construct: self.options.construct,
|
|
data: {},
|
|
id: '',
|
|
position: v
|
|
}).appendTo(self.$pages[page]);
|
|
});
|
|
}
|
|
}
|
|
self.$pages[page].appendTo(that.$content);
|
|
!Ox.isUndefined(callback) && callback();
|
|
},
|
|
keys: keys,
|
|
range: range,
|
|
sort: self.options.sort
|
|
}));
|
|
} else {
|
|
self.$pages[page].appendTo(that.$content);
|
|
}
|
|
}
|
|
|
|
function loadPages(page, callback) {
|
|
var counter = 0,
|
|
fn = function() {
|
|
counter++;
|
|
counter == 2 && !Ox.isUndefined(callback) && callback();
|
|
};
|
|
loadPage(page, function() {
|
|
loadPage(page - 1, fn);
|
|
loadPage(page + 1, fn);
|
|
});
|
|
}
|
|
|
|
function open() {
|
|
that.triggerEvent('open', {
|
|
ids: getSelectedIds()
|
|
});
|
|
}
|
|
|
|
function preview() {
|
|
self.preview = !self.preview;
|
|
if (self.preview) {
|
|
that.triggerEvent('openpreview', {
|
|
ids: getSelectedIds()
|
|
});
|
|
} else {
|
|
that.triggerEvent('closepreview');
|
|
}
|
|
}
|
|
|
|
function scroll() {
|
|
var page = self.page;
|
|
self.page = getPage();
|
|
setTimeout(function() {
|
|
if (self.page == getPage()) {
|
|
if (self.page == page - 1) {
|
|
unloadPage(self.page + 2);
|
|
loadPage(self.page - 1);
|
|
} else if (self.page == page + 1) {
|
|
unloadPage(self.page - 2);
|
|
loadPage(self.page + 1);
|
|
} else if (self.page == page - 2) {
|
|
unloadPage(self.page + 3);
|
|
unloadPage(self.page + 2);
|
|
loadPage(self.page);
|
|
loadPage(self.page - 1);
|
|
} else if (self.page == page + 2) {
|
|
unloadPage(self.page - 3);
|
|
unloadPage(self.page - 2);
|
|
loadPage(self.page);
|
|
loadPage(self.page + 1);
|
|
} else if (self.page != page) {
|
|
unloadPages(page);
|
|
loadPages(self.page);
|
|
}
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
function scrollPageDown() {
|
|
that.scrollBy(getHeight());
|
|
}
|
|
|
|
function scrollPageUp() {
|
|
that.scrollBy(-getHeight());
|
|
}
|
|
|
|
function scrollTo(pos) {
|
|
var positions = [], scroll, size;
|
|
if (self.options.orientation == 'horizontal') {
|
|
|
|
} else if (self.options.orientation == 'vertical') {
|
|
positions[0] = self.options.itemHeight * pos;
|
|
positions[1] = positions[0] + self.options.itemHeight;
|
|
scroll = that.scrollTop();
|
|
size = getHeight();
|
|
if (positions[0] < scroll) {
|
|
that.animate({
|
|
scrollTop: positions[0] + 'px'
|
|
}, 0);
|
|
} else if (positions[1] > scroll + size) {
|
|
that.animate({
|
|
scrollTop: (positions[1] - size) + 'px'
|
|
}, 0);
|
|
}
|
|
} else {
|
|
|
|
}
|
|
}
|
|
|
|
function scrollToFirst() {
|
|
that.scrollTop(0);
|
|
}
|
|
|
|
function scrollToLast() {
|
|
that.scrollTop(self.listHeight);
|
|
}
|
|
|
|
function select(pos) {
|
|
if (!isSelected(pos) || self.selected.length > 1) {
|
|
selectNone();
|
|
addToSelection(pos);
|
|
}
|
|
}
|
|
|
|
function selectAll() {
|
|
$.each(Ox.range(self.listLength), function(i, v) {
|
|
Ox.print('adding', v);
|
|
addToSelection(v);
|
|
});
|
|
}
|
|
|
|
function selectNext() {
|
|
var pos = getNext();
|
|
if (pos > -1) {
|
|
select(pos);
|
|
scrollTo(pos);
|
|
}
|
|
}
|
|
|
|
function selectNone() {
|
|
$.each(self.$items, function(i, v) {
|
|
deselect(i);
|
|
});
|
|
}
|
|
|
|
function selectPrevious() {
|
|
var pos = getPrevious();
|
|
if (pos > -1) {
|
|
select(pos);
|
|
scrollTo(pos);
|
|
}
|
|
}
|
|
|
|
function selectQuery(str) {
|
|
$.each(self.$items, function(i, v) {
|
|
if (Ox.toLatin(v.title).toUpperCase().indexOf(str) == 0) {
|
|
select(i);
|
|
scrollTo(i);
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
function toggleSelection(pos) {
|
|
if (!isSelected(pos)) {
|
|
addToSelection(pos);
|
|
} else {
|
|
deselect(pos);
|
|
}
|
|
}
|
|
|
|
function triggerSelectEvent() {
|
|
var ids = getSelectedIds();
|
|
setTimeout(function() {
|
|
var ids_ = getSelectedIds();
|
|
if (ids.length == ids_.length && (ids.length == 0 || ids[0] == ids_[0])) {
|
|
that.triggerEvent('select', {
|
|
ids: ids
|
|
});
|
|
self.preview && that.triggerEvent('openpreview', {
|
|
ids: ids
|
|
});
|
|
} else {
|
|
Ox.print('select event not triggered after timeout');
|
|
}
|
|
}, 100);
|
|
}
|
|
|
|
function unloadPage(page) {
|
|
if (page < 0 || page >= self.pages) {
|
|
return;
|
|
}
|
|
Ox.print('unloadPage', page)
|
|
Ox.print('self.$pages', self.$pages)
|
|
Ox.print(!Ox.isUndefined(self.$pages[page]))
|
|
!Ox.isUndefined(self.$pages[page]) && self.$pages[page].remove();
|
|
}
|
|
|
|
function unloadPages(page) {
|
|
unloadPage(page);
|
|
unloadPage(page - 1);
|
|
unloadPage(page + 1)
|
|
}
|
|
|
|
function updateQuery() {
|
|
clear();
|
|
self.requests.push(self.options.request({
|
|
callback: function(result) {
|
|
var keys = {};
|
|
that.triggerEvent('load', result.data);
|
|
$.extend(self, {
|
|
listHeight: result.data.items * self.options.itemHeight, // fixme: should be listSize
|
|
listLength: result.data.items,
|
|
pages: Math.ceil(result.data.items / self.pageLength),
|
|
pageWidth: self.options.orientation == 'horizontal' ?
|
|
self.pageLength * self.options.itemWidth : 0,
|
|
pageHeight: self.options.orientation == 'horizontal' ? 0 :
|
|
self.pageLength * self.options.itemHeight / self.options.rowLength
|
|
});
|
|
that.$content.css({
|
|
height: self.listHeight + 'px'
|
|
});
|
|
getPositions();
|
|
}
|
|
}));
|
|
}
|
|
|
|
function updateSort() {
|
|
if (self.listLength > 1) {
|
|
clear();
|
|
getPositions();
|
|
}
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
Ox.print('list onChange', key, value);
|
|
if (key == 'request') {
|
|
updateQuery();
|
|
}
|
|
};
|
|
|
|
that.clearCache = function() { // used by TextList resizeColumn
|
|
self.$pages = [];
|
|
return that;
|
|
};
|
|
|
|
that.reload = function() {
|
|
clear();
|
|
that.clearCache();
|
|
that.$content.empty();
|
|
loadPages(self.page);
|
|
return that;
|
|
};
|
|
|
|
that.sort = function(key, operator) {
|
|
if (key != self.options.sort[0].key || operator != self.options.sort[0].operator) {
|
|
self.options.sort[0] = {
|
|
key: key,
|
|
operator: operator
|
|
}
|
|
that.triggerEvent('sort', self.options.sort[0]);
|
|
updateSort();
|
|
}
|
|
return that;
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.ListItem = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
construct: function() {},
|
|
data: {},
|
|
id: '',
|
|
position: 0
|
|
})
|
|
.options(options || {});
|
|
|
|
$.each(self.options.data, function(k, v) {
|
|
self.options.data[k] = $.isArray(v) ? v.join(', ') : v;
|
|
});
|
|
|
|
that.$element = self.options.construct(self.options.data)
|
|
.addClass('OxItem')
|
|
.attr({
|
|
id: self.options.id
|
|
})
|
|
.data('position', self.options.position);
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.ListPage = function(options, self) {
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.addClass('OxPage');
|
|
return that;
|
|
};
|
|
|
|
Ox.TextList = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
columns: [],
|
|
columnsMovable: false,
|
|
columnsRemovable: false,
|
|
columnWidth: [40, 800],
|
|
id: '',
|
|
request: function() {}, // {sort, range, keys, callback}
|
|
sort: []
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxTextList');
|
|
|
|
$.each(self.options.columns, function(i, v) { // fixme: can this go into a generic ox.js function?
|
|
if (Ox.isUndefined(v.unique)) {
|
|
v.unique = false;
|
|
}
|
|
if (Ox.isUndefined(v.visible)) {
|
|
v.visible = false;
|
|
}
|
|
if (v.unique) {
|
|
self.unique = v.id;
|
|
}
|
|
});
|
|
|
|
$.extend(self, {
|
|
columnPositions: [],
|
|
columnWidths: [],
|
|
itemHeight: 16,
|
|
page: 0,
|
|
pageLength: 100,
|
|
scrollLeft: 0,
|
|
selectedColumn: getColumnIndexById(self.options.sort[0].key),
|
|
visibleColumns: $.map(self.options.columns, function(v, i) {
|
|
return v.visible ? v : null;
|
|
})
|
|
});
|
|
$.extend(self, {
|
|
pageHeight: self.pageLength * self.itemHeight
|
|
});
|
|
|
|
// Head
|
|
|
|
that.$bar = new Ox.Bar({
|
|
orientation: 'horizontal',
|
|
size: 16
|
|
}).appendTo(that);
|
|
that.$head = new Ox.Container()
|
|
.addClass('OxHead')
|
|
.appendTo(that.$bar);
|
|
that.$head.$content.addClass('OxTitles');
|
|
constructHead();
|
|
if (self.options.columnsRemovable) {
|
|
that.$select = new Ox.Select({
|
|
id: self.options.id + 'SelectColumns',
|
|
items: $.map(self.options.columns, function(v, i) {
|
|
return {
|
|
checked: v.visible,
|
|
disabled: v.removable === false,
|
|
id: v.id,
|
|
title: v.title
|
|
}
|
|
}),
|
|
max: -1,
|
|
min: 1,
|
|
type: 'image'
|
|
})
|
|
.bindEvent('change', changeColumns)
|
|
.appendTo(that.$bar.$element);
|
|
}
|
|
|
|
// Body
|
|
|
|
that.$body = new Ox.List({
|
|
construct: constructItem,
|
|
id: self.options.id,
|
|
itemHeight: 16,
|
|
itemWidth: getItemWidth(),
|
|
keys: $.map(self.visibleColumns, function(v, i) {
|
|
return v.id;
|
|
}),
|
|
orientation: 'vertical',
|
|
request: self.options.request,
|
|
sort: self.options.sort,
|
|
type: 'text',
|
|
unique: self.unique
|
|
})
|
|
.addClass('OxBody')
|
|
.scroll(function() {
|
|
var scrollLeft = $(this).scrollLeft();
|
|
if (scrollLeft != self.scrollLeft) {
|
|
self.scrollLeft = scrollLeft;
|
|
that.$head.scrollLeft(scrollLeft);
|
|
}
|
|
})
|
|
.appendTo(that);
|
|
that.$body.$content.css({
|
|
width: getItemWidth() + 'px'
|
|
});
|
|
|
|
Ox.print('s.vC', self.visibleColumns)
|
|
|
|
function addColumn(id) {
|
|
Ox.print('addColumn', id);
|
|
var column,
|
|
index = 0;
|
|
$.each(self.options.columns, function(i, v) {
|
|
if (v.visible) {
|
|
index++;
|
|
} else if (v.id == id) {
|
|
column = v;
|
|
return false;
|
|
}
|
|
});
|
|
column.visible = true;
|
|
self.visibleColumns.splice(index, 0, column);
|
|
that.$head.$content.empty();
|
|
constructHead();
|
|
that.$body.options({
|
|
keys: $.map(self.visibleColumns, function(v, i) {
|
|
return v.id;
|
|
})
|
|
});
|
|
that.$body.reload();
|
|
}
|
|
|
|
function changeColumns(event, data) {
|
|
var add,
|
|
ids = [];
|
|
$.each(data.selected, function(i, column) {
|
|
var index = getColumnIndexById(column.id);
|
|
if (!self.options.columns[index].visible) {
|
|
addColumn(column.id);
|
|
add = true;
|
|
return false;
|
|
}
|
|
ids.push(column.id);
|
|
});
|
|
if (!add) {
|
|
$.each(self.visibleColumns, function(i, column) {
|
|
if (ids.indexOf(column.id) == -1) {
|
|
removeColumn(column.id);
|
|
return false
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function clickColumn(id) {
|
|
Ox.print('clickColumn', id);
|
|
var i = getColumnIndexById(id),
|
|
isSelected = self.options.sort[0].key == self.options.columns[i].id;
|
|
that.sort(
|
|
self.options.columns[i].id, isSelected ?
|
|
(self.options.sort[0].operator === '' ? '-' : '') :
|
|
self.options.columns[i].operator
|
|
);
|
|
}
|
|
|
|
function constructHead() {
|
|
that.$titles = [];
|
|
self.columnPositions = [];
|
|
self.columnWidths = [];
|
|
$.each(self.visibleColumns, function(i, v) {
|
|
var $order, $resize, $left, $center, $right, timeout = 0;
|
|
self.columnWidths[i] = v.width;
|
|
that.$titles[i] = $('<div>')
|
|
.addClass('OxTitle OxColumn' + Ox.toTitleCase(v.id))
|
|
.css({
|
|
width: (v.width - 9) + 'px',
|
|
textAlign: v.align
|
|
})
|
|
.html(v.title)
|
|
.mousedown(function(e) {
|
|
timeout = setTimeout(function() {
|
|
dragColumn(v.id, e);
|
|
timeout = 0;
|
|
}, 250);
|
|
})
|
|
.mouseup(function() {
|
|
if (timeout) {
|
|
clearTimeout(timeout);
|
|
timeout = 0;
|
|
clickColumn(v.id);
|
|
}
|
|
})
|
|
.appendTo(that.$head.$content.$element);
|
|
self.columnPositions[i] = Ox.sum(self.columnWidths) - self.columnWidths[i] / 2;
|
|
$order = $('<div>')
|
|
.addClass('OxOrder')
|
|
.html(oxui.symbols['triangle_' + (
|
|
v.operator === '' ? 'up' : 'down'
|
|
)])
|
|
.click(function() {
|
|
$(this).prev().trigger('click')
|
|
})
|
|
.appendTo(that.$head.$content.$element);
|
|
$resize = $('<div>')
|
|
.addClass('OxResize')
|
|
.mousedown(function(e) {
|
|
var startWidth = self.columnWidths[i],
|
|
startX = e.clientX;
|
|
$window.mousemove(function(e) {
|
|
var x = e.clientX,
|
|
width = Ox.limit(
|
|
startWidth - startX + x,
|
|
self.options.columnWidth[0],
|
|
self.options.columnWidth[1]
|
|
);
|
|
resizeColumn(v.id, width);
|
|
});
|
|
$window.one('mouseup', function() {
|
|
$window.unbind('mousemove');
|
|
});
|
|
})
|
|
.dblclick(function() {
|
|
resizeColumn(v.id, v.width);
|
|
})
|
|
.appendTo(that.$head.$content.$element);
|
|
$left = $('<div>').addClass('OxLeft').appendTo($resize);
|
|
$center = $('<div>').addClass('OxCenter').appendTo($resize);
|
|
$right = $('<div>').addClass('OxRight').appendTo($resize);
|
|
});
|
|
that.$head.$content.css({
|
|
width: (Ox.sum(self.columnWidths) + 2) + 'px'
|
|
});
|
|
Ox.print('s.sC', self.selectedColumn)
|
|
if (getColumnPositionById(self.options.columns[self.selectedColumn].id) > -1) { // fixme: save in var
|
|
toggleSelected(self.options.columns[self.selectedColumn].id);
|
|
that.$titles[getColumnPositionById(self.options.columns[self.selectedColumn].id)].css({
|
|
width: (self.options.columns[self.selectedColumn].width - 25) + 'px'
|
|
});
|
|
}
|
|
}
|
|
|
|
function constructItem(data) {
|
|
var $item = $('<div>')
|
|
.css({
|
|
width: getItemWidth() + 'px'
|
|
});
|
|
$.each(self.visibleColumns, function(i, v) {
|
|
var $cell = $('<div>')
|
|
.addClass('OxCell OxColumn' + Ox.toTitleCase(v.id))
|
|
.css({
|
|
width: (self.columnWidths[i] - 9) + 'px',
|
|
textAlign: v.align
|
|
})
|
|
.html(!$.isEmptyObject(data) ? data[v.id] : '')
|
|
.appendTo($item);
|
|
});
|
|
return $item;
|
|
}
|
|
|
|
function dragColumn(id, e) {
|
|
var startX = e.clientX,
|
|
startPos = getColumnPositionById(id),
|
|
pos = startPos,
|
|
stopPos = startPos,
|
|
positions = $.map(self.visibleColumns, function(v, i) {
|
|
return self.columnPositions[i] - self.columnPositions[startPos]
|
|
});
|
|
$('.OxColumn' + Ox.toTitleCase(id)).css({
|
|
opacity: 0.1
|
|
});
|
|
that.$titles[startPos].addClass('OxDrag').css({ // fixme: why does the class not work?
|
|
cursor: 'move'
|
|
});
|
|
Ox.print('positions', positions)
|
|
$window.mousemove(function(e) {
|
|
var d = e.clientX - startX;
|
|
$.each(positions, function(i, v) {
|
|
if (d < 0 && d < v) {
|
|
stopPos = i;
|
|
return false;
|
|
} else if (d > 0 && d > v) {
|
|
stopPos = i;
|
|
}
|
|
});
|
|
if (stopPos != pos) {
|
|
pos = stopPos;
|
|
moveColumn(id, pos);
|
|
}
|
|
});
|
|
$window.one('mouseup', function() {
|
|
dropColumn(id, pos);
|
|
$window.unbind('mousemove');
|
|
});
|
|
}
|
|
|
|
function dropColumn(id, pos) {
|
|
Ox.print('dropColumn', id, pos)
|
|
var startPos = getColumnPositionById(id),
|
|
stopPos = pos,
|
|
$title = that.$titles.splice(startPos, 1)[0],
|
|
column = self.visibleColumns.splice(startPos, 1)[0],
|
|
width = self.columnWidths.splice(startPos, 1)[0];
|
|
that.$titles.splice(stopPos, 0, $title);
|
|
self.visibleColumns.splice(stopPos, 0, column);
|
|
self.columnWidths.splice(stopPos, 0, width);
|
|
Ox.print('s.vC', self.visibleColumns)
|
|
$('.OxColumn' + Ox.toTitleCase(id)).css({
|
|
opacity: 1
|
|
});
|
|
that.$titles[stopPos].removeClass('OxDrag').css({
|
|
cursor: 'pointer'
|
|
});
|
|
}
|
|
|
|
function getColumnIndexById(id) {
|
|
var pos = -1;
|
|
$.each(self.options.columns, function(i, v) {
|
|
if (v.id == id) {
|
|
pos = i;
|
|
return false;
|
|
}
|
|
});
|
|
return pos;
|
|
}
|
|
|
|
function getColumnPositionById(id) {
|
|
var pos = -1;
|
|
$.each(self.visibleColumns, function(i, v) {
|
|
if (v.id == id) {
|
|
pos = i;
|
|
return false;
|
|
}
|
|
});
|
|
return pos;
|
|
}
|
|
|
|
function getItemWidth() {
|
|
return Math.max(Ox.sum(self.columnWidths), that.$element.width() - oxui.scrollbarSize);
|
|
//return Ox.sum(self.columnWidths)
|
|
}
|
|
|
|
function moveColumn(id, pos) {
|
|
Ox.print('moveColumn', id, pos)
|
|
var startPos = getColumnPositionById(id),
|
|
stopPos = pos,
|
|
startClassName = '.OxColumn' + Ox.toTitleCase(id),
|
|
stopClassName = '.OxColumn' + Ox.toTitleCase(self.visibleColumns[stopPos].id),
|
|
$column = $('.OxTitle' + startClassName),
|
|
$order = $column.next(),
|
|
$resize = $order.next();
|
|
$column.detach().insertBefore($('.OxTitle' + stopClassName));
|
|
$order.detach().insertAfter($column);
|
|
$resize.detach().insertAfter($order);
|
|
$.each(that.$body.find('.OxItem'), function(i, v) {
|
|
var $v = $(v);
|
|
$v.children(startClassName).detach().insertBefore($v.children(stopClassName));
|
|
});
|
|
}
|
|
|
|
function removeColumn(id) {
|
|
Ox.print('removeColumn', id);
|
|
var className = '.OxColumn' + Ox.toTitleCase(id),
|
|
index = getColumnIndexById(id),
|
|
position = getColumnPositionById(id),
|
|
$column = $('.OxTitle' + className),
|
|
$order = $column.next(),
|
|
$resize = $order.next();
|
|
self.options.columns[index].visible = false;
|
|
self.visibleColumns.splice(position, 1);
|
|
self.columnWidths.splice(position, 1);
|
|
itemWidth = getItemWidth();
|
|
$column.remove();
|
|
$order.remove();
|
|
$resize.remove();
|
|
$.each(that.$body.find('.OxItem'), function(i, v) {
|
|
var $v = $(v);
|
|
$v.children(className).remove();
|
|
$v.css({
|
|
width: itemWidth + 'px'
|
|
});
|
|
});
|
|
that.$body.$content.css({
|
|
width: itemWidth + 'px'
|
|
});
|
|
that.$body.options({
|
|
keys: $.map(self.visibleColumns, function(v, i) {
|
|
return v.id;
|
|
})
|
|
});
|
|
}
|
|
|
|
function resize() {
|
|
|
|
}
|
|
|
|
function resizeColumn(id, width) {
|
|
var i = getColumnIndexById(id),
|
|
pos = getColumnPositionById(id);
|
|
self.columnWidths[pos] = width;
|
|
that.$head.$content.css({
|
|
width: (Ox.sum(self.columnWidths) + 2) + 'px'
|
|
});
|
|
that.$titles[pos].css({
|
|
width: (width - 9 - (i == self.selectedColumn ? 16 : 0)) + 'px'
|
|
});
|
|
that.$body.$content.find('.OxItem').css({ // fixme: can we avoid this lookup?
|
|
width: getItemWidth() + 'px'
|
|
});
|
|
that.$body.$content.css({
|
|
width: getItemWidth() + 'px' // fixme: check if scrollbar visible, and listen to resize/toggle event
|
|
});
|
|
$('.OxCell.OxColumn' + Ox.toTitleCase(self.options.columns[i].id)).css({
|
|
width: (width - 9) + 'px'
|
|
});
|
|
that.$body.clearCache();
|
|
}
|
|
|
|
function toggleSelected(id) {
|
|
var pos = getColumnPositionById(id);
|
|
if (pos > -1) {
|
|
updateOrder(id);
|
|
pos > 0 && that.$titles[pos].prev().children().eq(2).toggleClass('OxSelected');
|
|
that.$titles[pos].toggleClass('OxSelected');
|
|
that.$titles[pos].next().toggleClass('OxSelected');
|
|
that.$titles[pos].next().next().children().eq(0).toggleClass('OxSelected');
|
|
that.$titles[pos].css({
|
|
width: (
|
|
that.$titles[pos].width() + (that.$titles[pos].hasClass('OxSelected') ? -16 : 16)
|
|
) + 'px'
|
|
});
|
|
}
|
|
}
|
|
|
|
function updateOrder(id) {
|
|
var pos = getColumnPositionById(id);
|
|
Ox.print(id, pos)
|
|
that.$titles[pos].next().html(oxui.symbols[
|
|
'triangle_' + (self.options.sort[0].operator === '' ? 'up' : 'down')
|
|
]);
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'request') {
|
|
that.$body.options(key, value);
|
|
}
|
|
};
|
|
|
|
that.closePreview = function() {
|
|
self.preview = false;
|
|
};
|
|
|
|
that.resizeColumn = function(id, width) {
|
|
resizeColumn(id, width);
|
|
return that;
|
|
}
|
|
|
|
that.sort = function(key, operator) {
|
|
var isSelected = key == self.options.sort[0].key;
|
|
self.options.sort = [
|
|
{
|
|
key: key,
|
|
operator: operator
|
|
}
|
|
];
|
|
if (isSelected) {
|
|
updateOrder(self.options.columns[self.selectedColumn].id);
|
|
} else {
|
|
toggleSelected(self.options.columns[self.selectedColumn].id);
|
|
self.selectedColumn = getColumnIndexById(key);
|
|
toggleSelected(self.options.columns[self.selectedColumn].id);
|
|
}
|
|
that.$body.sort(self.options.sort[0].key, self.options.sort[0].operator);
|
|
return that;
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
/*
|
|
============================================================================
|
|
Maps
|
|
============================================================================
|
|
*/
|
|
|
|
Ox.Map = function(options, self) {
|
|
|
|
var self = self || {}
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
places: [],
|
|
type: 'satellite'
|
|
})
|
|
.options(options || {});
|
|
|
|
init();
|
|
|
|
function canContain(outerBounds, innerBounds) {
|
|
var outerSpan = outerBounds.toSpan(),
|
|
innerSpan = innerBounds.toSpan();
|
|
return outerSpan.lat() > innerSpan.lat() &&
|
|
outerSpan.lng() > innerSpan.lng();
|
|
}
|
|
|
|
function click(event) {
|
|
Ox.print('event', event);
|
|
getLocationByLatLng(event.latLng, self.map.getBounds(), function(location) {
|
|
self.marker && self.marker.remove();
|
|
self.polygon && self.polygon.remove();
|
|
if (location) {
|
|
self.marker = location.marker.add();
|
|
self.polygon = location.polygon.add();
|
|
that.triggerEvent('select', location)
|
|
}
|
|
})
|
|
}
|
|
|
|
function getLocationByLatLng(latlng, bounds, callback) {
|
|
Ox.print('ll b', latlng, bounds)
|
|
var callback = arguments.length == 3 ? callback : bounds,
|
|
bounds = arguments.length == 3 ? bounds : null;
|
|
self.geocoder.geocode({
|
|
latLng: latlng
|
|
}, function(results, status) {
|
|
Ox.print('results', results)
|
|
var length = results.length;
|
|
if (status == google.maps.GeocoderStatus.OK) {
|
|
if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
|
|
if (bounds) {
|
|
$.each(results.reverse(), function(i, result) {
|
|
if (
|
|
i == length - 1 ||
|
|
canContain(bounds, result.geometry.bounds || result.geometry.viewport)
|
|
) {
|
|
callback(new Location(results[i]));
|
|
return false;
|
|
}
|
|
});
|
|
} else {
|
|
callback(new Location(results[0]));
|
|
}
|
|
} else {
|
|
callback(null);
|
|
}
|
|
} else {
|
|
Ox.print('geocode failed:', status);
|
|
callback(null);
|
|
}
|
|
});
|
|
}
|
|
|
|
function getLocationByName(name, callback) {
|
|
self.geocoder.geocode({
|
|
address: name
|
|
}, function(results, status) {
|
|
if (status == google.maps.GeocoderStatus.OK) {
|
|
if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
|
|
callback(new Location(results[0]))
|
|
} else {
|
|
callback(null);
|
|
}
|
|
} else {
|
|
Ox.print('geocode failed:', status);
|
|
callback(null);
|
|
}
|
|
});
|
|
}
|
|
|
|
function init() {
|
|
var counter = 0,
|
|
length = self.options.places.length;
|
|
$.extend(self, {
|
|
geocoder: new google.maps.Geocoder(),
|
|
locations: [],
|
|
selectedMarker: null
|
|
});
|
|
$.each(self.options.places, function(i, place) {
|
|
if (Ox.isString(place)) {
|
|
self.options.places[i] = {
|
|
name: place
|
|
};
|
|
} else if (Ox.isArray(place)) {
|
|
self.options.places[i] = {
|
|
name: '',
|
|
point: place
|
|
};
|
|
}
|
|
'point' in self.options.places[i] ?
|
|
getLocationByPoint(self.options.places[i].point, callback) :
|
|
getLocationByName(self.options.places[i].name, callback);
|
|
});
|
|
function callback(location) {
|
|
if (location) {
|
|
Ox.print('counter', counter, location)
|
|
self.locations.push(location);
|
|
self.bounds = counter == 0 ? location.rectangle.bounds :
|
|
self.bounds.union(location.rectangle.bounds);
|
|
}
|
|
if (counter++ == length - 1) {
|
|
loadMap();
|
|
}
|
|
}
|
|
}
|
|
|
|
function loadMap() {
|
|
Ox.print('loadMap');
|
|
$.extend(self, {
|
|
map: new google.maps.Map(that.$element[0], {
|
|
center: self.bounds.getCenter(),
|
|
disableDefaultUI: true,
|
|
mapTypeId: google.maps.MapTypeId[self.options.type.toUpperCase()],
|
|
zoom: 0
|
|
})
|
|
});
|
|
self.map.fitBounds(self.bounds)
|
|
google.maps.event.addListener(self.map, 'click', click);
|
|
google.maps.event.addListener(self.map, 'zoom_changed', zoom);
|
|
$.each(self.locations, function(i, location) {
|
|
location.marker.add();
|
|
});
|
|
resize();
|
|
};
|
|
|
|
function resize() {
|
|
google.maps.event.trigger(self.map, 'resize');
|
|
}
|
|
|
|
function zoom() {
|
|
that.triggerEvent('zoom', {
|
|
value: self.map.getZoom()
|
|
});
|
|
}
|
|
|
|
function Location(geocode) {
|
|
Ox.print('geocode', geocode);
|
|
var bounds = geocode.geometry.bounds || geocode.geometry.viewport,
|
|
area = [
|
|
[bounds.getSouthWest().lat(), bounds.getSouthWest().lng()],
|
|
[bounds.getNorthEast().lat(), bounds.getNorthEast().lng()]
|
|
],
|
|
location = {
|
|
geocode: geocode,
|
|
name: {
|
|
formatted: geocode.formatted_address,
|
|
long: $.map(geocode.address_components, function(v) {
|
|
return v.long_name;
|
|
}).join(', ')
|
|
},
|
|
rectangle: new Rectangle(area),
|
|
};
|
|
Ox.print('area', area)
|
|
return $.extend(location, {
|
|
marker: new Marker(location),
|
|
polygon: new Polygon(location)
|
|
});
|
|
}
|
|
|
|
function Marker(location) {
|
|
var listeners = {},
|
|
marker = new google.maps.Marker({
|
|
icon: icon('red'),
|
|
position: location.rectangle.center,
|
|
title: location.name.formatted
|
|
}),
|
|
selected = false;
|
|
function click() {
|
|
selected = !selected;
|
|
marker.setOptions({
|
|
icon: icon(selected ? 'blue' : 'red')
|
|
});
|
|
location.polygon[selected ? 'add' : 'remove']();
|
|
}
|
|
function dblclick() {
|
|
click();
|
|
self.map.fitBounds(location.rectangle.bounds);
|
|
}
|
|
function icon(color) {
|
|
return 'http://dev.pan.do:8000' + oxui.path + 'png/ox.ui/marker' + Ox.toTitleCase(color) + '.png'
|
|
}
|
|
return {
|
|
add: function() {
|
|
marker.setMap(self.map);
|
|
listeners = {
|
|
click: google.maps.event.addListener(marker, 'click', click),
|
|
dblclick: google.maps.event.addListener(marker, 'dblclick', dblclick),
|
|
};
|
|
return this;
|
|
},
|
|
deselect: function() {
|
|
|
|
},
|
|
remove: function() {
|
|
marker.setMap(null);
|
|
$.each(listeners, function(i, listener) {
|
|
google.maps.event.removeListener(listener);
|
|
});
|
|
return this;
|
|
},
|
|
select: function() {
|
|
|
|
}
|
|
};
|
|
}
|
|
|
|
function Polygon(location) {
|
|
var listeners = {},
|
|
points = [
|
|
location.rectangle.latlng.sw,
|
|
location.rectangle.latlng.nw,
|
|
location.rectangle.latlng.ne,
|
|
location.rectangle.latlng.se,
|
|
location.rectangle.latlng.sw
|
|
],
|
|
polygon = new google.maps.Polygon({
|
|
paths: points
|
|
}),
|
|
selected = false;
|
|
setOptions();
|
|
function click() {
|
|
selected = !selected;
|
|
setOptions();
|
|
}
|
|
function setOptions() {
|
|
var color = selected ? '#8080FF' : '#FFFFFF';
|
|
polygon.setOptions({
|
|
clickable: true,
|
|
fillColor: color,
|
|
fillOpacity: selected ? 0.1 : 0,
|
|
strokeColor: color,
|
|
strokeOpacity: 1,
|
|
strokeWeight: 2
|
|
});
|
|
}
|
|
return {
|
|
add: function() {
|
|
polygon.setMap(self.map);
|
|
listeners.click = google.maps.event.addListener(polygon, 'click', click);
|
|
return this;
|
|
},
|
|
deselect: function() {
|
|
selected = false;
|
|
setOptions();
|
|
},
|
|
remove: function() {
|
|
polygon.setMap(null);
|
|
google.maps.event.removeListener(listeners.click);
|
|
return this;
|
|
},
|
|
select: function() {
|
|
selected = true;
|
|
setOptions();
|
|
}
|
|
};
|
|
}
|
|
|
|
function Rectangle(area) {
|
|
var latlng = {
|
|
sw: new google.maps.LatLng(area[0][0], area[0][1]),
|
|
ne: new google.maps.LatLng(area[1][0], area[1][1])
|
|
},
|
|
bounds = new google.maps.LatLngBounds(latlng.sw, latlng.ne),
|
|
lat = {},
|
|
lng = {};
|
|
latlng.mc = bounds.getCenter();
|
|
$.each(latlng, function(k, v) {
|
|
lat[k] = v.lat();
|
|
lng[k] = v.lng();
|
|
});
|
|
$.extend(latlng, {
|
|
sc: new google.maps.LatLng(lat.sw, lng.mc),
|
|
se: new google.maps.LatLng(lat.sw, lng.ne),
|
|
mw: new google.maps.LatLng(lat.mc, lng.sw),
|
|
mw: new google.maps.LatLng(lat.mc, lng.ne),
|
|
nw: new google.maps.LatLng(lat.ne, lng.sw),
|
|
nc: new google.maps.LatLng(lat.ne, lng.mc),
|
|
});
|
|
return {
|
|
area: area,
|
|
bounds: bounds,
|
|
canContain: function(rectangle) {
|
|
var outerSpan = this.bounds.toSpan(),
|
|
innerSpan = rectangle.bounds.toSpan();
|
|
return outerSpan.lat() > innerSpan.lat() &&
|
|
outerSpan.lng() > innerSpan.lng();
|
|
},
|
|
center: latlng.mc,
|
|
contains: function(rectangle) {
|
|
return this.bounds.contains(rectangle.bounds.getSouthWest()) &&
|
|
this.bounds.contains(rectangle.bounds.getNorthEast());
|
|
},
|
|
latlng: latlng
|
|
};
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'type') {
|
|
|
|
}
|
|
};
|
|
|
|
that.find = function(name, callback) {
|
|
getLocationByName(name, function(location) {
|
|
if (location) {
|
|
self.polygon && self.polygon.remove();
|
|
self.polygon = location.polygon.add();
|
|
self.bounds = location.rectangle.bounds;
|
|
self.map.fitBounds(self.bounds);
|
|
}
|
|
callback(location);
|
|
});
|
|
};
|
|
|
|
that.zoom = function(value) {
|
|
self.map.setZoom(value);
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.MapImage = function(options, self) {
|
|
|
|
/*
|
|
options:
|
|
height image height (px)
|
|
places array of either names (''), points ([0, 0]),
|
|
or objects ({name, point, highlight})
|
|
type map type ('hybrid', 'roadmap', 'satellite', 'terrain')
|
|
width image width (px)
|
|
*/
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('img', self)
|
|
.defaults({
|
|
height: 360,
|
|
markerColorHighlight: 'yellow',
|
|
markerColorNormal: 'blue',
|
|
places: [],
|
|
type: 'satellite',
|
|
width: 640
|
|
})
|
|
.options(options || {})
|
|
|
|
$.extend(self, {
|
|
markers: {
|
|
highlight: [],
|
|
normal: []
|
|
},
|
|
src: 'http://maps.google.com/maps/api/staticmap?sensor=false' +
|
|
'&size=' + self.options.width + 'x' + self.options.height +
|
|
'&maptype=' + self.options.type
|
|
});
|
|
|
|
if (self.options.places.length) {
|
|
$.each(self.options.places, function(i, place) {
|
|
if (Ox.isString(place)) {
|
|
self.markers.normal.push(place);
|
|
} else if (Ox.isArray(place)) {
|
|
self.markers.normal.push(place.join(','));
|
|
} else {
|
|
self.markers[place.highlight ? 'highlight' : 'normal']
|
|
.push('point' in place ? place.point.join(',') : place.name)
|
|
}
|
|
});
|
|
$.each(self.markers, function(k, markers) {
|
|
if (markers.length) {
|
|
self.src += '&markers=icon:' + 'http://dev.pan.do:8000' + oxui.path + 'png/ox.ui/marker' +
|
|
Ox.toTitleCase(self.options['markerColor' + Ox.toTitleCase(k)]) + '.png|' +
|
|
markers.join('|')
|
|
}
|
|
});
|
|
} else {
|
|
self.src += '¢er=0,0&zoom=2'
|
|
}
|
|
|
|
that.attr({
|
|
src: self.src
|
|
});
|
|
|
|
self.onChange = function(key, value) {
|
|
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
/*
|
|
============================================================================
|
|
Menus
|
|
============================================================================
|
|
*/
|
|
|
|
Ox.MainMenu = function(options, self) {
|
|
|
|
/* options:
|
|
* extras
|
|
* menus
|
|
* size
|
|
*/
|
|
|
|
var self = self || {},
|
|
that = new Ox.Bar({}, self)
|
|
.defaults({
|
|
extras: [],
|
|
menus: [],
|
|
size: 'medium'
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxMainMenu Ox' + Ox.toTitleCase(self.options.size)) // fixme: bar should accept small/medium/large ... like toolbar
|
|
.click(click)
|
|
.mousemove(mousemove);
|
|
|
|
self.focused = false;
|
|
self.selected = -1;
|
|
that.menus = [];
|
|
that.titles = [];
|
|
that.layer = $('<div>').addClass('OxLayer');
|
|
|
|
$.each(self.options.menus, function(position, menu) {
|
|
that.titles[position] = $('<div>')
|
|
.addClass('OxTitle')
|
|
.html(menu.title)
|
|
.data('position', position)
|
|
.appendTo(that.$element);
|
|
that.menus[position] = new Ox.Menu($.extend(menu, {
|
|
element: that.titles[position],
|
|
mainmenu: that,
|
|
size: self.options.size
|
|
}))
|
|
.bindEvent({
|
|
hide: onHideMenu
|
|
});
|
|
});
|
|
|
|
if (self.options.extras.length) {
|
|
that.extras = $('<div>')
|
|
.addClass('OxExtras')
|
|
.appendTo(that.$element);
|
|
$.each(self.options.extras, function(position, extra) {
|
|
extra.css({
|
|
float: 'left' // fixme: need class!
|
|
}).appendTo(that.extras);
|
|
});
|
|
}
|
|
|
|
function click(event) {
|
|
var $target = $(event.target),
|
|
position = typeof $target.data('position') != 'undefined' ?
|
|
$target.data('position') : -1;
|
|
clickTitle(position);
|
|
}
|
|
|
|
function clickTitle(position) {
|
|
var selected = self.selected;
|
|
if (self.selected > -1) {
|
|
that.menus[self.selected].hideMenu();
|
|
}
|
|
if (position > -1) {
|
|
if (position != selected) {
|
|
self.focused = true;
|
|
self.selected = position;
|
|
that.titles[self.selected].addClass('OxSelected');
|
|
that.menus[self.selected].showMenu();
|
|
}
|
|
}
|
|
}
|
|
|
|
function mousemove(event) {
|
|
var $target = $(event.target),
|
|
focused,
|
|
position = typeof $target.data('position') != 'undefined' ?
|
|
$target.data('position') : -1;
|
|
if (self.focused && position != self.selected) {
|
|
if (position > -1) {
|
|
clickTitle(position);
|
|
} else {
|
|
focused = self.focused;
|
|
that.menus[self.selected].hideMenu();
|
|
self.focused = focused;
|
|
}
|
|
}
|
|
}
|
|
|
|
function onHideMenu() {
|
|
if (self.selected > -1) {
|
|
that.titles[self.selected].removeClass('OxSelected');
|
|
self.selected = -1;
|
|
}
|
|
self.focused = false;
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
|
|
};
|
|
|
|
that.addMenuAfter = function(id) {
|
|
|
|
};
|
|
|
|
that.addMenuBefore = function(id) {
|
|
|
|
};
|
|
|
|
that.checkItem = function(id) {
|
|
var ids = id.split('_'),
|
|
itemId = ids.pop(),
|
|
menuId = ids.join('_');
|
|
that.getMenu(menuId).checkItem(itemId);
|
|
};
|
|
|
|
that.disableItem = function(id) {
|
|
that.getItem(id).options({
|
|
disabled: true
|
|
});
|
|
};
|
|
|
|
that.enableItem = function(id) {
|
|
that.getItem(id).options({
|
|
disabled: false
|
|
});
|
|
};
|
|
|
|
that.getItem = function(id) {
|
|
var ids = id.split('_'),
|
|
item;
|
|
if (ids.length == 1) {
|
|
$.each(that.menus, function(i, menu) {
|
|
item = menu.getItem(id);
|
|
return !item;
|
|
});
|
|
} else {
|
|
item = that.getMenu(ids.shift()).getItem(ids.join('_'));
|
|
}
|
|
Ox.print('getItem', id, item);
|
|
return item;
|
|
};
|
|
|
|
that.getMenu = function(id) {
|
|
var ids = id.split('_'),
|
|
menu;
|
|
if (ids.length == 1) {
|
|
$.each(that.menus, function(i, v) {
|
|
if (v.options('id') == id) {
|
|
menu = v;
|
|
return false;
|
|
}
|
|
});
|
|
} else {
|
|
menu = that.getMenu(ids.shift()).getSubmenu(ids.join('_'));
|
|
}
|
|
Ox.print('getMenu', id, menu);
|
|
return menu;
|
|
};
|
|
|
|
that.removeMenu = function() {
|
|
|
|
};
|
|
|
|
that.selectNextMenu = function() {
|
|
if (self.selected < self.options.menus.length - 1) {
|
|
clickTitle(self.selected + 1);
|
|
}
|
|
};
|
|
|
|
that.selectPreviousMenu = function() {
|
|
if (self.selected) {
|
|
clickTitle(self.selected - 1);
|
|
}
|
|
};
|
|
|
|
that.uncheckItem = function(id) {
|
|
that.getItem(id).options({
|
|
checked: false
|
|
});
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.Menu = function(options, self) {
|
|
|
|
/*
|
|
|
|
options:
|
|
element the element the menu is attached to
|
|
id the menu id
|
|
items array of menu items
|
|
mainmenu the main menu this menu is part of, if any
|
|
offset offset of the menu, in px
|
|
parent the supermenu, if any
|
|
selected the position of the selected item
|
|
side open to 'bottom' or 'right'
|
|
size 'large', 'medium' or 'small'
|
|
|
|
methods:
|
|
|
|
events:
|
|
change_groupId {id, value} checked item of a group has changed
|
|
click_itemId item not belonging to a group was clicked
|
|
click_menuId {id, value} item not belonging to a group was clicked
|
|
deselect_menuId {id, value} item was deselected not needed, not implemented
|
|
hide_menuId menu was hidden
|
|
select_menuId {id, value} item was selected
|
|
|
|
*/
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.defaults({
|
|
element: null,
|
|
id: '',
|
|
items: [],
|
|
mainmenu: null,
|
|
offset: {
|
|
left: 0,
|
|
top: 0
|
|
},
|
|
parent: null,
|
|
selected: -1,
|
|
side: 'bottom',
|
|
size: 'medium',
|
|
})
|
|
.options(options || {})
|
|
.addClass(
|
|
'OxMenu Ox' + Ox.toTitleCase(self.options.side) +
|
|
' Ox' + Ox.toTitleCase(self.options.size)
|
|
)
|
|
.click(click)
|
|
.mouseenter(mouseenter)
|
|
.mouseleave(mouseleave)
|
|
.mousemove(mousemove)
|
|
.addEvent({
|
|
key_up: selectPreviousItem,
|
|
key_down: selectNextItem,
|
|
key_left: selectSupermenu,
|
|
key_right: selectSubmenu,
|
|
key_escape: hideMenu,
|
|
key_enter: clickSelectedItem
|
|
}),
|
|
itemHeight = self.options.size == 'small' ? 12 : (self.options.size == 'medium' ? 16 : 20),
|
|
// menuHeight,
|
|
scrollSpeed = 1,
|
|
$item; // fixme: used?
|
|
// fixme: attach all private vars to self
|
|
|
|
// construct
|
|
that.items = [];
|
|
that.submenus = {};
|
|
that.$scrollbars = [];
|
|
that.$top = $('<div>')
|
|
.addClass('OxTop')
|
|
.appendTo(that.$element);
|
|
that.$scrollbars.up = constructScrollbar('up')
|
|
.appendTo(that.$element);
|
|
that.$container = $('<div>')
|
|
.addClass('OxContainer')
|
|
.appendTo(that.$element);
|
|
that.$content = $('<table>')
|
|
.addClass('OxContent')
|
|
.appendTo(that.$container);
|
|
constructItems(self.options.items);
|
|
that.$scrollbars.down = constructScrollbar('down')
|
|
.appendTo(that.$element);
|
|
that.$bottom = $('<div>')
|
|
.addClass('OxBottom')
|
|
.appendTo(that.$element);
|
|
that.$layer = $('<div>')
|
|
.addClass(self.options.mainmenu ? 'OxMainMenuLayer' : 'OxLayer')
|
|
.click(click);
|
|
|
|
function click(event) {
|
|
var item,
|
|
position,
|
|
$target = $(event.target),
|
|
$parent = $target.parent();
|
|
// necessary for highlight
|
|
if ($parent.is('.OxCell')) {
|
|
$target = $parent;
|
|
$parent = $target.parent();
|
|
}
|
|
if ($target.is('.OxCell')) {
|
|
position = $parent.data('position');
|
|
item = that.items[position];
|
|
if (!item.options('disabled')) {
|
|
clickItem(position);
|
|
} else {
|
|
that.hideMenu();
|
|
}
|
|
} else {
|
|
that.hideMenu();
|
|
}
|
|
}
|
|
|
|
function clickItem(position) {
|
|
var item = that.items[position],
|
|
toggled;
|
|
that.hideMenu();
|
|
if (!item.options('items').length) {
|
|
if (that.options('parent')) {
|
|
that.options('parent').hideMenu().triggerEvent('click');
|
|
}
|
|
if (item.options('checked') !== null) {
|
|
if (item.options('group')) {
|
|
Ox.print('has group', item.options('group'))
|
|
toggled = self.optionGroups[item.options('group')].toggle(position);
|
|
Ox.print('toggled', toggled)
|
|
if (toggled.length) {
|
|
$.each(toggled, function(i, pos) {
|
|
that.items[pos].toggleChecked();
|
|
});
|
|
Ox.print('--triggering change event--');
|
|
(self.options.mainmenu || that).triggerEvent('change', {
|
|
id: item.options('group'),
|
|
checked: $.map(self.optionGroups[item.options('group')].checked(), function(v, i) {
|
|
return {
|
|
id: that.items[v].options('id'),
|
|
title: Ox.stripTags(that.items[v].options('title')[0])
|
|
};
|
|
})
|
|
});
|
|
}
|
|
} else {
|
|
item.toggleChecked();
|
|
(self.options.mainmenu || that).triggerEvent('change', {
|
|
checked: item.options('checked'),
|
|
id: item.options('id'),
|
|
title: Ox.stripTags(item.options('title')[0])
|
|
});
|
|
}
|
|
} else {
|
|
(self.options.mainmenu || that).triggerEvent('click', {
|
|
id: item.options('id'),
|
|
title: Ox.stripTags(item.options('title')[0])
|
|
});
|
|
}
|
|
if (item.options('title').length == 2) {
|
|
item.toggleTitle();
|
|
}
|
|
}
|
|
}
|
|
|
|
function clickSelectedItem() {
|
|
// called on key.enter
|
|
if (self.options.selected > -1) {
|
|
clickItem(self.options.selected);
|
|
} else {
|
|
that.hideMenu();
|
|
}
|
|
}
|
|
|
|
function constructItems(items) {
|
|
|
|
that.$content.empty();
|
|
scrollMenuUp();
|
|
|
|
self.optionGroups = {};
|
|
$.each(items, function(i, item) {
|
|
if (item.group) {
|
|
items[i] = $.map(item.items, function(v, i) {
|
|
return $.extend(v, {
|
|
group: item.group
|
|
});
|
|
});
|
|
self.optionGroups[item.group] = new Ox.OptionGroup(
|
|
items[i],
|
|
'min' in item ? item.min : 1,
|
|
'max' in item ? item.max : 1
|
|
);
|
|
}
|
|
});
|
|
items = Ox.flatten(items);
|
|
|
|
that.items = [];
|
|
$.each(items, function(i, item) {
|
|
var position;
|
|
if (item.id) {
|
|
that.items.push(new Ox.MenuItem($.extend(item, {
|
|
menu: that,
|
|
position: position = that.items.length
|
|
})).data('position', position).appendTo(that.$content)); // fixme: jquery bug when passing {position: position}? does not return the object?;
|
|
if (item.items) {
|
|
that.submenus[item.id] = new Ox.Menu({
|
|
element: that.items[position],
|
|
id: Ox.toCamelCase(self.options.id + '/' + item.id),
|
|
items: item.items,
|
|
mainmenu: self.options.mainmenu,
|
|
offset: {
|
|
left: 0,
|
|
top: -4
|
|
},
|
|
parent: that,
|
|
side: 'right',
|
|
size: self.options.size,
|
|
});
|
|
}
|
|
} else {
|
|
that.$content.append(constructSpace());
|
|
that.$content.append(constructLine());
|
|
that.$content.append(constructSpace());
|
|
}
|
|
});
|
|
|
|
if (!that.is(':hidden')) {
|
|
that.hideMenu();
|
|
that.showMenu();
|
|
}
|
|
|
|
}
|
|
|
|
function constructLine() {
|
|
return $('<tr>').append(
|
|
$('<td>', {
|
|
'class': 'OxLine',
|
|
colspan: 5
|
|
})
|
|
);
|
|
}
|
|
|
|
function constructScrollbar(direction) {
|
|
var interval,
|
|
speed = direction == 'up' ? -1 : 1;
|
|
return $('<div/>', {
|
|
'class': 'OxScrollbar Ox' + Ox.toTitleCase(direction),
|
|
html: oxui.symbols['triangle_' + direction],
|
|
click: function() { // fixme: do we need to listen to click event?
|
|
return false;
|
|
},
|
|
mousedown: function() {
|
|
scrollSpeed = 2;
|
|
return false;
|
|
},
|
|
mouseenter: function() {
|
|
var $otherScrollbar = that.$scrollbars[direction == 'up' ? 'down' : 'up'];
|
|
$(this).addClass('OxSelected');
|
|
if ($otherScrollbar.is(':hidden')) {
|
|
$otherScrollbar.show();
|
|
that.$container.height(that.$container.height() - itemHeight);
|
|
if (direction == 'down') {
|
|
that.$content.css({
|
|
top: -itemHeight + 'px'
|
|
});
|
|
}
|
|
}
|
|
scrollMenu(speed);
|
|
interval = setInterval(function() {
|
|
scrollMenu(speed);
|
|
}, 100);
|
|
},
|
|
mouseleave: function() {
|
|
$(this).removeClass('OxSelected');
|
|
clearInterval(interval);
|
|
},
|
|
mouseup: function() {
|
|
scrollSpeed = 1;
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
function constructSpace() {
|
|
return $('<tr>').append(
|
|
$('<td>', {
|
|
'class': 'OxSpace',
|
|
colspan: 5
|
|
})
|
|
);
|
|
}
|
|
|
|
function getElement(id) {
|
|
// fixme: needed?
|
|
return $('#' + Ox.toCamelCase(options.id + '/' + id));
|
|
}
|
|
|
|
function getItemPositionById(id) {
|
|
var position;
|
|
$.each(that.items, function(i, v) {
|
|
if (v.options('id') == id) {
|
|
position = i;
|
|
return false;
|
|
}
|
|
});
|
|
return position;
|
|
}
|
|
|
|
function hideMenu() {
|
|
// called on key_escape
|
|
that.hideMenu();
|
|
}
|
|
|
|
function isFirstEnabledItem() {
|
|
var ret = true;
|
|
$.each(that.items, function(i, item) {
|
|
if (i < self.options.selected && !item.options('disabled')) {
|
|
return ret = false;
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
function isLastEnabledItem() {
|
|
var ret = true;
|
|
$.each(that.items, function(i, item) {
|
|
if (i > self.options.selected && !item.options('disabled')) {
|
|
return ret = false;
|
|
}
|
|
});
|
|
return ret;
|
|
}
|
|
|
|
function mouseenter() {
|
|
that.gainFocus();
|
|
}
|
|
|
|
function mouseleave() {
|
|
if (self.options.selected > -1 && !that.items[self.options.selected].options('items').length) {
|
|
selectItem(-1);
|
|
}
|
|
}
|
|
|
|
function mousemove(event) {
|
|
var item,
|
|
position,
|
|
$target = $(event.target);
|
|
$parent = $target.parent();
|
|
if ($parent.is('.OxCell')) {
|
|
$target = $parent;
|
|
$parent = $target.parent();
|
|
}
|
|
if ($target.is('.OxCell')) {
|
|
position = $parent.data('position');
|
|
item = that.items[position];
|
|
if (!item.options('disabled') && position != self.options.selected) {
|
|
selectItem(position);
|
|
}
|
|
} else {
|
|
mouseleave();
|
|
}
|
|
}
|
|
|
|
function scrollMenu(speed) {
|
|
var containerHeight = that.$container.height(),
|
|
contentHeight = that.$content.height(),
|
|
top = parseInt(that.$content.css('top')) || 0,
|
|
min = containerHeight - contentHeight + itemHeight,
|
|
max = 0;
|
|
top += speed * scrollSpeed * -itemHeight;
|
|
if (top <= min) {
|
|
top = min;
|
|
that.$scrollbars.down.hide().trigger('mouseleave');
|
|
that.$container.height(containerHeight + itemHeight);
|
|
that.items[that.items.length - 1].trigger('mouseover');
|
|
} else if (top >= max - itemHeight) {
|
|
top = max;
|
|
that.$scrollbars.up.hide().trigger('mouseleave');
|
|
that.$container.height(containerHeight + itemHeight);
|
|
that.items[0].trigger('mouseover');
|
|
}
|
|
that.$content.css({
|
|
top: top + 'px'
|
|
});
|
|
}
|
|
|
|
function scrollMenuUp() {
|
|
if (that.$scrollbars.up.is(':visible')) {
|
|
that.$content.css({
|
|
top: '0px'
|
|
});
|
|
that.$scrollbars.up.hide();
|
|
if (that.$scrollbars.down.is(':hidden')) {
|
|
that.$scrollbars.down.show();
|
|
} else {
|
|
that.$container.height(that.$container.height() + itemHeight);
|
|
}
|
|
}
|
|
}
|
|
|
|
function selectItem(position) {
|
|
var item;
|
|
if (self.options.selected > -1) {
|
|
//Ox.print('s.o.s', self.options.selected, that.items)
|
|
item = that.items[self.options.selected]
|
|
item.removeClass('OxSelected');
|
|
/* disabled
|
|
that.triggerEvent('deselect', {
|
|
id: item.options('id'),
|
|
title: Ox.stripTags(item.options('title')[0])
|
|
});
|
|
*/
|
|
}
|
|
if (position > -1) {
|
|
item = that.items[position];
|
|
$.each(that.submenus, function(id, submenu) {
|
|
if (!submenu.is(':hidden')) {
|
|
submenu.hideMenu();
|
|
return false;
|
|
}
|
|
});
|
|
item.options('items').length && that.submenus[item.options('id')].showMenu(); // fixme: do we want to switch to this style?
|
|
item.addClass('OxSelected');
|
|
/* disabled
|
|
that.triggerEvent('select', {
|
|
id: item.options('id'),
|
|
title: Ox.stripTags(item.options('title')[0])
|
|
});
|
|
*/
|
|
}
|
|
self.options.selected = position;
|
|
}
|
|
|
|
function selectNextItem() {
|
|
var offset,
|
|
selected = self.options.selected;
|
|
Ox.print('sNI', selected)
|
|
if (!isLastEnabledItem()) {
|
|
if (selected == -1) {
|
|
scrollMenuUp();
|
|
} else {
|
|
that.items[selected].removeClass('OxSelected');
|
|
}
|
|
do {
|
|
selected++;
|
|
} while (that.items[selected].options('disabled'))
|
|
selectItem(selected);
|
|
offset = that.items[selected].offset().top + itemHeight -
|
|
that.$container.offset().top - that.$container.height();
|
|
if (offset > 0) {
|
|
if (that.$scrollbars.up.is(':hidden')) {
|
|
that.$scrollbars.up.show();
|
|
that.$container.height(that.$container.height() - itemHeight);
|
|
offset += itemHeight;
|
|
}
|
|
if (selected == that.items.length - 1) {
|
|
that.$scrollbars.down.hide();
|
|
that.$container.height(that.$container.height() + itemHeight);
|
|
} else {
|
|
that.$content.css({
|
|
top: ((parseInt(that.$content.css('top')) || 0) - offset) + 'px'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function selectPreviousItem() {
|
|
var offset,
|
|
selected = self.options.selected;
|
|
Ox.print('sPI', selected)
|
|
if (selected > - 1) {
|
|
if (!isFirstEnabledItem()) {
|
|
that.items[selected].removeClass('OxSelected');
|
|
do {
|
|
selected--;
|
|
} while (that.items[selected].options('disabled'))
|
|
selectItem(selected);
|
|
}
|
|
offset = that.items[selected].offset().top - that.$container.offset().top;
|
|
if (offset < 0) {
|
|
if (that.$scrollbars.down.is(':hidden')) {
|
|
that.$scrollbars.down.show();
|
|
that.$container.height(that.$container.height() - itemHeight);
|
|
}
|
|
if (selected == 0) {
|
|
that.$scrollbars.up.hide();
|
|
that.$container.height(that.$container.height() + itemHeight);
|
|
}
|
|
that.$content.css({
|
|
top: ((parseInt(that.$content.css('top')) || 0) - offset) + 'px'
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function selectSubmenu() {
|
|
Ox.print('selectSubmenu', self.options.selected)
|
|
if (self.options.selected > -1) {
|
|
var submenu = that.submenus[that.items[self.options.selected].options('id')];
|
|
Ox.print('submenu', submenu, that.submenus);
|
|
if (submenu && submenu.hasEnabledItems()) {
|
|
submenu.gainFocus();
|
|
submenu.selectFirstItem();
|
|
} else if (self.options.mainmenu) {
|
|
self.options.mainmenu.selectNextMenu();
|
|
}
|
|
} else if (self.options.mainmenu) {
|
|
self.options.mainmenu.selectNextMenu();
|
|
}
|
|
}
|
|
|
|
function selectSupermenu() {
|
|
Ox.print('selectSupermenu', self.options.selected)
|
|
if (self.options.parent) {
|
|
self.options.selected > -1 && that.items[self.options.selected].trigger('mouseleave');
|
|
scrollMenuUp();
|
|
self.options.parent.gainFocus();
|
|
} else if (self.options.mainmenu) {
|
|
self.options.mainmenu.selectPreviousMenu();
|
|
}
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'items') {
|
|
constructItems(value);
|
|
} else if (key == 'selected') {
|
|
that.$content.find('.OxSelected').removeClass('OxSelected');
|
|
selectItem(value);
|
|
}
|
|
}
|
|
|
|
that.addItem = function(item, position) {
|
|
|
|
};
|
|
|
|
that.addItemAfter = function(item, id) {
|
|
|
|
};
|
|
|
|
that.addItemBefore = function(item, id) {
|
|
|
|
};
|
|
|
|
that.checkItem = function(id) {
|
|
var item = that.getItem(id);
|
|
if (item.options('group')) {
|
|
var position = getItemPositionById(id),
|
|
toggled = self.optionGroups[item.options('group')].toggle(position);
|
|
if (toggled.length) {
|
|
$.each(toggled, function(i, pos) {
|
|
that.items[pos].toggleChecked();
|
|
});
|
|
}
|
|
} else {
|
|
item.options({
|
|
checked: true
|
|
});
|
|
}
|
|
};
|
|
|
|
that.getItem = function(id) {
|
|
Ox.print('id', id)
|
|
var ids = id.split('_'),
|
|
item;
|
|
if (ids.length == 1) {
|
|
$.each(that.items, function(i, v) {
|
|
if (v.options('id') == id) {
|
|
item = v;
|
|
return false;
|
|
}
|
|
});
|
|
if (!item) {
|
|
$.each(that.submenus, function(k, submenu) {
|
|
item = submenu.getItem(id);
|
|
return !item;
|
|
});
|
|
}
|
|
} else {
|
|
item = that.submenus[ids.shift()].getItem(ids.join('_'));
|
|
}
|
|
return item;
|
|
};
|
|
|
|
that.getSubmenu = function(id) {
|
|
var ids = id.split('_'),
|
|
submenu;
|
|
if (ids.length == 1) {
|
|
submenu = that.submenus[id];
|
|
} else {
|
|
submenu = that.submenus[ids.shift()].getSubmenu(ids.join('_'));
|
|
}
|
|
Ox.print('getSubmenu', id, submenu);
|
|
return submenu;
|
|
}
|
|
|
|
that.hasEnabledItems = function() {
|
|
var ret = false;
|
|
$.each(that.items, function(i, item) {
|
|
if (!item.options('disabled')) {
|
|
return ret = true;
|
|
}
|
|
});
|
|
return ret;
|
|
};
|
|
|
|
that.hideMenu = function() {
|
|
if (that.is(':hidden')) {
|
|
return;
|
|
}
|
|
$.each(that.submenus, function(i, submenu) {
|
|
if (submenu.is(':visible')) {
|
|
submenu.hideMenu();
|
|
return false;
|
|
}
|
|
});
|
|
selectItem(-1);
|
|
scrollMenuUp();
|
|
that.$scrollbars.up.is(':visible') && that.$scrollbars.up.hide();
|
|
that.$scrollbars.down.is(':visible') && that.$scrollbars.down.hide();
|
|
//that.$scrollbars.down.hide();
|
|
if (self.options.parent) {
|
|
//self.options.element.removeClass('OxSelected');
|
|
self.options.parent.options({
|
|
selected: -1
|
|
});
|
|
}
|
|
that.hide()
|
|
.loseFocus()
|
|
.triggerEvent('hide');
|
|
that.$layer.hide();
|
|
return that;
|
|
};
|
|
|
|
that.removeItem = function() {
|
|
|
|
};
|
|
|
|
that.selectFirstItem = function() {
|
|
selectNextItem();
|
|
};
|
|
|
|
that.showMenu = function() {
|
|
if (!that.is(':hidden')) {
|
|
return;
|
|
}
|
|
if (!self.options.parent && !that.$layer.parent().length) {
|
|
that.$layer.appendTo($body);
|
|
}
|
|
that.parent().length == 0 && that.appendTo($body);
|
|
that.css({
|
|
left: '-1000px',
|
|
top: '-1000px',
|
|
}).show();
|
|
var offset = self.options.element.offset(),
|
|
width = self.options.element.outerWidth(),
|
|
height = self.options.element.outerHeight(),
|
|
left = Ox.limit(
|
|
offset.left + self.options.offset.left + (self.options.side == 'bottom' ? 0 : width),
|
|
0, $window.width() - that.width()
|
|
),
|
|
top = offset.top + self.options.offset.top + (self.options.side == 'bottom' ? height : 0),
|
|
menuHeight = that.$content.outerHeight(); // fixme: why is outerHeight 0 when hidden?
|
|
menuMaxHeight = Math.floor($window.height() - top - 16);
|
|
if (self.options.parent) {
|
|
if (menuHeight > menuMaxHeight) {
|
|
top = Ox.limit(top - menuHeight + menuMaxHeight, self.options.parent.offset().top, top);
|
|
menuMaxHeight = Math.floor($window.height() - top - 16);
|
|
}
|
|
}
|
|
that.css({
|
|
left: left + 'px',
|
|
top: top + 'px'
|
|
});
|
|
if (menuHeight > menuMaxHeight) {
|
|
that.$container.height(menuMaxHeight - itemHeight - 8); // margin
|
|
that.$scrollbars.down.show();
|
|
} else {
|
|
that.$container.height(menuHeight);
|
|
}
|
|
if (!self.options.parent) {
|
|
that.gainFocus();
|
|
}
|
|
that.$layer.show();
|
|
return that;
|
|
//that.triggerEvent('show');
|
|
};
|
|
|
|
that.toggleMenu = function() {
|
|
that.is(':hidden') ? that.showMenu() : that.hideMenu();
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.MenuItem = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('tr', self)
|
|
.defaults({
|
|
bind: [], // fixme: what's this?
|
|
checked: null,
|
|
disabled: false,
|
|
group: '',
|
|
icon: '',
|
|
id: '',
|
|
items: [],
|
|
keyboard: '',
|
|
menu: null, // fixme: is passing the menu to 100s of menu items really memory-neutral?
|
|
position: 0,
|
|
title: [],
|
|
})
|
|
.options($.extend(options, {
|
|
keyboard: parseKeyboard(options.keyboard || self.defaults.keyboard),
|
|
title: Ox.makeArray(options.title || self.defaults.title)
|
|
}))
|
|
.addClass('OxItem' + (self.options.disabled ? ' OxDisabled' : ''))
|
|
.attr({
|
|
id: Ox.toCamelCase(self.options.menu.options('id') + '/' + self.options.id)
|
|
})
|
|
.data('group', self.options.group); // fixme: why?
|
|
|
|
if (self.options.group && self.options.checked === null) {
|
|
self.options.checked = false;
|
|
}
|
|
|
|
// construct
|
|
that.append(
|
|
that.$status = $('<td>', {
|
|
'class': 'OxCell OxStatus',
|
|
html: self.options.checked ? oxui.symbols.check : ''
|
|
})
|
|
)
|
|
.append(
|
|
that.$icon = $('<td>', {
|
|
'class': 'OxCell OxIcon'
|
|
})
|
|
.append(self.options.icon ?
|
|
$('<img>', {
|
|
src: self.options.icon
|
|
}) : null
|
|
)
|
|
)
|
|
.append(
|
|
that.$title = $('<td>', {
|
|
'class': 'OxCell OxTitle',
|
|
html: self.options.title[0]
|
|
})
|
|
)
|
|
.append(
|
|
$('<td>', {
|
|
'class': 'OxCell OxModifiers',
|
|
html: $.map(self.options.keyboard.modifiers, function(modifier) {
|
|
return oxui.symbols[modifier];
|
|
}).join('')
|
|
})
|
|
)
|
|
.append(
|
|
$('<td>', {
|
|
'class': 'OxCell Ox' + (self.options.items.length ? 'Submenu' : 'Key'),
|
|
html: self.options.items.length ? oxui.symbols.triangle_right :
|
|
oxui.symbols[self.options.keyboard.key] ||
|
|
self.options.keyboard.key.toUpperCase()
|
|
})
|
|
);
|
|
|
|
function parseKeyboard(str) {
|
|
var modifiers = str.split(' '),
|
|
key = modifiers.pop();
|
|
return {
|
|
modifiers: modifiers,
|
|
key: key
|
|
};
|
|
}
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'checked') {
|
|
that.$status.html(value ? oxui.symbols.check : '')
|
|
} else if (key == 'disabled') {
|
|
that.toggleClass('OxDisabled'); // fixme: this will only work if onChange is only invoked on actual change
|
|
} else if (key == 'title') {
|
|
self.options.title = Ox.makeArray(value);
|
|
that.$title.html(self.options.title[0]);
|
|
}
|
|
}
|
|
|
|
that.toggle = function() {
|
|
// toggle id and title
|
|
};
|
|
|
|
that.toggleChecked = function() {
|
|
that.options({
|
|
checked: !self.options.checked
|
|
});
|
|
};
|
|
|
|
that.toggleDisabled = function() {
|
|
|
|
};
|
|
|
|
that.toggleTitle = function() {
|
|
Ox.print('s.o.t', self.options.title)
|
|
that.options({
|
|
title: self.options.title.reverse()
|
|
});
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
/*
|
|
============================================================================
|
|
Panels
|
|
============================================================================
|
|
*/
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.CollapsePanel
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.CollapsePanel = function(options, self) {
|
|
var self = self || {},
|
|
that = new Ox.Panel({}, self)
|
|
.defaults({
|
|
collapsed: false,
|
|
size: 20,
|
|
title: ''
|
|
})
|
|
.options(options)
|
|
.addClass('OxCollapsePanel'),
|
|
title = self.options.collapsed ?
|
|
[{id: 'expand', title: 'expand'}, {id: 'collapse', title: 'collapse'}] :
|
|
[{id: 'collapse', title: 'collapse'}, {id: 'expand', title: 'expand'}];
|
|
$titlebar = new Ox.Bar({
|
|
orientation: 'horizontal',
|
|
size: self.options.size,
|
|
})
|
|
.dblclick(dblclickTitlebar)
|
|
.appendTo(that),
|
|
$switch = new Ox.Button({
|
|
id: self.options.id + 'Switch',
|
|
style: 'symbol',
|
|
title: title,
|
|
type: 'image',
|
|
})
|
|
.click(toggleCollapsed)
|
|
.appendTo($titlebar),
|
|
$title = new Ox.Element()
|
|
.addClass('OxTitle')
|
|
.html(self.options.title/*.toUpperCase()*/)
|
|
.appendTo($titlebar);
|
|
that.$content = new Ox.Element()
|
|
.addClass('OxContent')
|
|
.appendTo(that);
|
|
// fixme: doesn't work, content still empty
|
|
// need to hide it if collapsed
|
|
if (self.options.collapsed) {
|
|
that.$content.css({
|
|
marginTop: -that.$content.height() + 'px'
|
|
});
|
|
}
|
|
function dblclickTitlebar(e) {
|
|
if (!$(e.target).hasClass('OxButton')) {
|
|
$switch.trigger('click');
|
|
}
|
|
}
|
|
function toggleCollapsed() {
|
|
var marginTop;
|
|
self.options.collapsed = !self.options.collapsed;
|
|
marginTop = self.options.collapsed ? -that.$content.height() : 0;
|
|
that.$content.animate({
|
|
marginTop: marginTop + 'px'
|
|
}, 200);
|
|
}
|
|
self.onChange = function(key, value) {
|
|
if (key == 'collapsed') {
|
|
|
|
} else if (key == 'title') {
|
|
$title.html(self.options.title);
|
|
}
|
|
};
|
|
return that;
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Panel
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Panel = function(options, self) {
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self)
|
|
.addClass('OxPanel');
|
|
return that;
|
|
};
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.SplitPanel
|
|
options:
|
|
elements: [{ array of one, two or three elements
|
|
collapsible: false, collapsible or not (only for outer elements)
|
|
collapsed: false, collapsed or not (only for collapsible elements)
|
|
element: {}, OxElement (if any element is resizable or
|
|
collapsible, all OxElements must have an id)
|
|
resizable: false, resizable or not (only for outer elements)
|
|
resize: [], array of sizes (only for resizable elements,
|
|
first value is min, last value is max,
|
|
other values are 'snappy' points in between)
|
|
size: 0 size in px (one element must have no size)
|
|
}],
|
|
orientation: '' 'horizontal' or 'vertical'
|
|
methods:
|
|
isCollapsed(id) element is collapsed or not
|
|
resize(id, size) resize element to size px
|
|
toggle(id) collapse or expand element
|
|
events:
|
|
resize
|
|
toggle
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.SplitPanel = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element({}, self) // fixme: Container
|
|
.defaults({
|
|
elements: [],
|
|
orientation: 'horizontal'
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxSplitPanel');
|
|
|
|
$.extend(self, {
|
|
dimensions: oxui.getDimensions(self.options.orientation),
|
|
edges: oxui.getEdges(self.options.orientation),
|
|
length: self.options.elements.length
|
|
});
|
|
|
|
that.$elements = [];
|
|
|
|
$.each(self.options.elements, function(i, v) {
|
|
self.options.elements[i] = $.extend({
|
|
collapsible: false,
|
|
collapsed: false,
|
|
resizable: false,
|
|
resize: [],
|
|
size: 'auto'
|
|
}, v);
|
|
that.$elements[i] = v.element
|
|
.css(self.edges[2], 0)
|
|
.css(self.edges[3], 0);
|
|
});
|
|
|
|
setSizes();
|
|
|
|
$.each(self.options.elements, function(i, v) {
|
|
//that.append(element)
|
|
//Ox.print('V: ', v, that.$elements[i])
|
|
that.$elements[i].appendTo(that.$element); // fixme: that.$content
|
|
if (v.collapsible || v.resizable) {
|
|
Ox.print('v.size', v.size)
|
|
var $resizebar = new Ox.Resizebar({
|
|
collapsible: v.collapsible,
|
|
edge: self.edges[i == 0 ? 0 : 1],
|
|
elements: i < 2 ?
|
|
[that.$elements[0], that.$elements[1]] :
|
|
[that.$elements[1], that.$elements[2]],
|
|
id: v.element.options('id'),
|
|
orientation: self.options.orientation == 'horizontal' ? 'vertical' : 'horizontal',
|
|
parent: that, // fixme: that.$content
|
|
resizable: v.resizable,
|
|
resize: v.resize,
|
|
size: v.size
|
|
})
|
|
.css(self.edges[i == 0 ? 0 : 1], v.size);
|
|
$resizebar[i == 0 ? 'insertAfter' : 'insertBefore'](that.$elements[i]);
|
|
}
|
|
});
|
|
|
|
function getPositionById(id) {
|
|
var position = -1;
|
|
$.each(self.options.elements, function(i, element) {
|
|
if (element.element.options('id') == id) {
|
|
position = i;
|
|
return false;
|
|
}
|
|
});
|
|
Ox.print('getPositionById', id, position);
|
|
return position;
|
|
}
|
|
|
|
function getSize(element) {
|
|
return element.size + element.resizable;
|
|
}
|
|
|
|
function setSizes() {
|
|
$.each(self.options.elements, function(i, v) {
|
|
v.size != 'auto' && that.$elements[i].css(self.dimensions[0], v.size + 'px');
|
|
if (i == 0) {
|
|
that.$elements[i].css(self.edges[0], 0);
|
|
v.size != 'auto' && that.$elements[i].css(
|
|
self.edges[1], (getSize(self.options.elements[1]) + (length == 3 ? getSize(self.options.elements[2]) : 0)) + 'px'
|
|
);
|
|
} else if (i == 1) {
|
|
self.options.elements[0].size != 'auto' && that.$elements[i].css(
|
|
self.edges[0], getSize(self.options.elements[0]) + 'px'
|
|
);
|
|
(self.options.elements[0].size != 'auto' || v.size != 'auto') && that.$elements[i].css(
|
|
self.edges[1], (self.length == 3 ? getSize(self.options.elements[2]) : 0) + 'px'
|
|
);
|
|
} else {
|
|
that.$elements[i].css(self.edges[1], 0);
|
|
v.size != 'auto' && that.$elements[i].css(
|
|
self.edges[0], (getSize(self.options.elements[0]) + getSize(self.options.elements[1])) + 'px'
|
|
);
|
|
}
|
|
});
|
|
}
|
|
|
|
that.isCollapsed = function(id) {
|
|
return self.options.elements[getPositionById(id)].collapsed;
|
|
};
|
|
|
|
that.replace = function(id, element) {
|
|
// one can pass pos instead of id
|
|
var pos = Ox.isNumber(id) ? id : getPositionById(id);
|
|
Ox.print('element', self.options.elements[pos].element, element)
|
|
self.options.elements[pos].element.replaceWith(element.$element.$element || element.$element);
|
|
self.options.elements[pos].element = element;
|
|
that.$elements[pos] = element
|
|
.css(self.edges[2], 0)
|
|
.css(self.edges[3], 0);
|
|
setSizes();
|
|
Ox.print(self.options.elements[pos])
|
|
};
|
|
|
|
that.resize = function(id, size) {
|
|
// one can pass pos instead of id
|
|
var pos = Ox.isNumber(id) ? id : getPositionById(id);
|
|
// Ox.print('pos', pos, self.options.elements, $.map(self.options.elements, function(v, i) { return v.element.options('id'); }))
|
|
Ox.print('pos', pos, 'size', size)
|
|
self.options.elements[pos].size = size;
|
|
setSizes();
|
|
return that;
|
|
};
|
|
|
|
that.toggle = function(id) {
|
|
Ox.print('toggle', id);
|
|
/*
|
|
// something like this is needed to load in collapsed state
|
|
if (Ox.isUndefined(self.options.position)) {
|
|
self.options.position = parseInt(self.options.parent.css(self.options.edge)) +
|
|
(self.options.collapsed ? self.options.size : 0);
|
|
}
|
|
var size = self.options.position -
|
|
(self.options.collapsed ? 0 : self.options.size),
|
|
animate = {};
|
|
Ox.print('s.o.e', self.options.edge);
|
|
*/
|
|
var pos = getPositionById(id),
|
|
size = self.options.elements[pos].collapsed ? 0 : self.options.elements[pos].size,
|
|
animate = {};
|
|
animate[self.edges[pos == 0 ? 0 : 1]] = size;
|
|
self.options.parent.animate(animate, 200, function() {
|
|
var i = (self.options.edge == 'left' || self.options.edge == 'top') ? 1 : 0;
|
|
that.triggerEvent('resize', self.options.elements[i][self.dimensions[1]]());
|
|
self.options.elements[pos].collapsed = !self.options.elements[pos].collapsed;
|
|
});
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
Ox.TabPanel = function(options, self) {
|
|
|
|
};
|
|
|
|
/*
|
|
============================================================================
|
|
Requests
|
|
============================================================================
|
|
*/
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.LoadingIcon
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.LoadingIcon = function(options, self) {
|
|
var self = self || {},
|
|
that = new Ox.Element('img', self)
|
|
.defaults({
|
|
size: 'medium'
|
|
})
|
|
.options(options || {})
|
|
.attr({
|
|
src: oxui.path + '/png/ox.ui.' + Ox.theme() + '/loading.png' // fixme: oxui.themePath needed?
|
|
})
|
|
.addClass(
|
|
'OxLoadingIcon Ox' + Ox.toTitleCase(self.options.size)
|
|
);
|
|
self.deg = 0;
|
|
self.interval = 0;
|
|
self.isRunning = false;
|
|
function clear() {
|
|
clearInterval(self.interval);
|
|
self.deg = 0;
|
|
self.interval = 0;
|
|
update();
|
|
}
|
|
function update() {
|
|
that.css({
|
|
MozTransform: 'rotate(' + self.deg + 'deg)',
|
|
WebkitTransform: 'rotate(' + self.deg + 'deg)'
|
|
});
|
|
}
|
|
that.start = function() {
|
|
self.isRunning = true;
|
|
clear();
|
|
that.animate({
|
|
opacity: 1
|
|
}, 250);
|
|
self.interval = setInterval(function() {
|
|
self.deg = (self.deg + 30) % 360;
|
|
update();
|
|
}, 83);
|
|
return that;
|
|
};
|
|
that.stop = function() {
|
|
that.animate({
|
|
opacity: 0
|
|
}, 250, function() {
|
|
!self.isRunning && clear();
|
|
self.isRunning = false;
|
|
});
|
|
return that;
|
|
}
|
|
return that;
|
|
}
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Progressbar
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
============================================================================
|
|
Miscellaneous
|
|
============================================================================
|
|
*/
|
|
|
|
/*
|
|
----------------------------------------------------------------------------
|
|
Ox.Tooltip
|
|
----------------------------------------------------------------------------
|
|
*/
|
|
|
|
Ox.Tooltip = function(options, self) {
|
|
|
|
var self = self || {},
|
|
that = new Ox.Element('div', self)
|
|
.defaults({
|
|
title: ''
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxTooltip')
|
|
.html(self.options.title);
|
|
|
|
self.onChange = function(key, value) {
|
|
if (key == 'title') {
|
|
that.html(value);
|
|
}
|
|
};
|
|
|
|
that.hide = function() {
|
|
that.animate({
|
|
opacity: 0
|
|
}, 0, function() {
|
|
that.remove();
|
|
});
|
|
return that;
|
|
};
|
|
|
|
that.show = function(x, y) {
|
|
var left, top, width, height;
|
|
$('.OxTooltip').remove(); // fixme: don't use dom
|
|
that.appendTo($body);
|
|
width = that.width();
|
|
height = that.height();
|
|
left = Ox.limit(x - width / 2, 0, $document.width() - width);
|
|
top = y > $document.height() - height - 16 ? y - 32 : y + 16;
|
|
that.css({
|
|
left: left + 'px',
|
|
top: top + 'px'
|
|
})
|
|
.animate({
|
|
opacity: 1
|
|
}, 0);
|
|
return that;
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|
|
|
|
})(); |