').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');
}),
leftOrTop: self.options.edge == 'left' || self.options.edge == 'top',
startPos: 0,
startSize: 0
});
function drag(e) {
var d = e[self.clientXY] - self.startPos
size = self.options.size;
self.options.size = Ox.limit(
self.startSize + d * (self.leftOrTop ? 1 : -1),
self.options.resize[0],
self.options.resize[self.options.resize.length - 1]
);
$.each(self.options.resize, function(i, v) {
if (self.options.size > v - 8 && self.options.size < v + 8) {
self.options.size = v;
return false;
}
});
if (self.options.size != size) {
that.css(self.edges[self.leftOrTop ? 2 : 3], self.options.size + 'px');
if (self.leftOrTop) {
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]]());
} else {
self.options.elements[1].css(self.dimensions[1], self.options.size + 'px');
self.options.elements[0].css(self.edges[3], (self.options.size + 1) + 'px');
Ox.Event.trigger(self.ids[1], 'resize', self.options.size);
Ox.Event.trigger(self.ids[0], 'resize', self.options.elements[0][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,
movable: true,
padding: 16,
resizable: true,
width: 384
})
.options(options || {})
.addClass('OxDialog')
.addEvent({
key_escape: function() {
that.close();
}
});
$.extend(self, {
initialWidth: self.options.width,
initialHeight: self.options.height
})
if (!Ox.isArray(self.options.buttons[0])) {
self.options.buttons = [[], self.options.buttons];
}
that.$titlebar = new Ox.Bar({
size: 'medium'
})
.addClass('OxTitleBar')
.appendTo(that);
self.options.movable && that.$titlebar
.mousedown(drag)
.dblclick(center);
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);
});
if (self.options.resizable) {
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() {
$.extend(self.options, {
height: self.initialHeight,
width: self.initialWidth
});
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
triggerResizeEvent();
}
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
});
self.options.width = Ox.limit(
elementWidth - x + event.clientX,
self.options.minWidth, Math.min(documentWidth, documentWidth - offset.left)
);
self.options.height = Ox.limit(
elementHeight - y + event.clientY,
self.options.minHeight, documentHeight - offset.top
);
that.width(self.options.width);
that.height(self.options.height);
that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically
triggerResizeEvent();
});
$window.one('mouseup', function() {
$window.unbind('mousemove');
});
}
function triggerResizeEvent() {
that.triggerEvent('resize', {
width: self.options.width,
height: self.options.height
});
}
self.onChange = function(key, value) {
if (key == 'height' || key == 'width') {
that.animate({
height: self.options.height + 'px',
width: self.options.width + 'px'
}, 100);
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
}, 100, function() {
that.$title.html(value).animate({
opacity: 1
}, 100);
});
}
}
that.append = function($element) {
that.$content.append($element);
return that;
};
that.center = function() {
};
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;
};
that.resize = function(width, height, callback) {
$.extend(self, {
initialWidth: width,
initialHeight: height
});
$.extend(self.options, {
width: width,
height: height
});
// fixme: duplicated
that.animate({
height: self.options.height + 'px',
width: self.options.width + 'px'
}, 100, function() {
that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically
callback();
});
}
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 = $('
')
.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 = $('
')
.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'),
'
$1'
) : 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 = $('
')
.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 = $('
')
.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])
$('
![]()
')
.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 = $('
')
.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 = $('
')
.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'),
'
$1'
) : 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 = $('