diff --git a/build/css/ox.ui.css b/build/css/ox.ui.css index da9044c6..d278f562 100644 --- a/build/css/ox.ui.css +++ b/build/css/ox.ui.css @@ -136,10 +136,11 @@ Dialog .OxDialog > .OxContent { top: 24px; + bottom: 24px; height: 100%; - padding: 16px; - font-size: 12px; - line-height: 16px; + //padding: 16px; + //font-size: 12px; + //line-height: 16px; } .OxDialog > .OxButtonsBar { @@ -359,7 +360,7 @@ OxButtonGroup } /* -------------------------------------------------------------------------------- -OxFormItem +OxForm -------------------------------------------------------------------------------- */ .OxFormItem { @@ -368,6 +369,13 @@ OxFormItem .OxFormItem:first-child { margin-top: 0; } +.OxFormMessage { + width: 100%; + height: 10px; + margin-top: 2px; + text-align: right; + display: none; +} /* -------------------------------------------------------------------------------- OxInput @@ -960,4 +968,17 @@ Scrollbars -webkit-border-radius: 6px; } +/* +================================================================================ +Miscellaneous +================================================================================ +*/ +.OxThemeModern .OxTooltip { + position: absolute; + padding: 2px; + opacity: 0; + z-index: 10; + -moz-border-radius: 2px; + -webkit-border-radius: 2px; +} diff --git a/build/css/ox.ui.modern.css b/build/css/ox.ui.modern.css index 3fc7d876..fc7e1cda 100644 --- a/build/css/ox.ui.modern.css +++ b/build/css/ox.ui.modern.css @@ -35,8 +35,8 @@ Dialog */ .OxThemeModern .OxDialog { - -moz-box-shadow: 0px 0px 8px rgba(0, 0, 0, 1); - -webkit-box-shadow: 0px 0px 8px rgba(0, 0, 0, 1); + -moz-box-shadow: 0 0 8px rgba(0, 0, 0, 1); + -webkit-box-shadow: 0 0 8px rgba(0, 0, 0, 1); } .OxThemeModern .OxDialog .OxBar { @@ -90,6 +90,9 @@ Forms .OxThemeModern .OxButton.OxTab.OxSelected { border-bottom: 1px solid rgb(64, 64, 64); } +.OxThemeModern .OxFormMessage { + color: rgb(255, 64, 64); +} .OxThemeModern .OxInput, .OxThemeModern .OxTrack { background: -moz-linear-gradient(top, rgb(0, 0, 0), rgb(32, 32, 32)); @@ -238,4 +241,17 @@ Scrollbars .OxThemeModern ::-webkit-scrollbar:active, .OxThemeModern ::-webkit-scrollbar-thumb:active { background: rgb(64, 64, 64); +} + +/* +================================================================================ +Miscellaneous +================================================================================ +*/ + +.OxThemeModern .OxTooltip { + border: 1px solid rgb(255, 255, 255); + background: rgba(0, 0, 0, 0.9); + -moz-box-shadow: 2px 2px 4px rgba(0, 0, 0, 1); + -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 1); } \ No newline at end of file diff --git a/build/js/ox.ui.js b/build/js/ox.ui.js index a1173b68..9f6da7de 100644 --- a/build/js/ox.ui.js +++ b/build/js/ox.ui.js @@ -1131,6 +1131,66 @@ requires 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() @@ -1354,6 +1414,7 @@ requires Ox.Dialog = function(options, self) { // fixme: dialog should be derived from a generic draggable + // fixme: pass button elements directly var self = self || {}, that = new Ox.Element("div", self) .defaults({ @@ -1362,6 +1423,7 @@ requires height: 216, minHeight: 144, minWidth: 256, + padding: 16, width: 384 }) .options(options || {}) @@ -1382,8 +1444,13 @@ requires .addClass("OxTitle") .html(self.options.title) .appendTo(that.$titlebar); - that.$content = new Ox.Container() + // 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") @@ -1407,6 +1474,7 @@ requires $.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", value: button.value }) @@ -1462,6 +1530,17 @@ requires }); } + 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 @@ -1476,16 +1555,15 @@ requires function reset() { that.css({ - left: Math.max(that.offset().left, 24 - self.options.width) + left: Math.max(that.offset().left, 24 - that.width()) }) .width(self.options.width) .height(self.options.height); - that.$content.height(self.options.height - 80); + that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically } function resize(event) { - var contentHeight = that.$content.height(), - documentWidth = $document.width(), + var documentWidth = $document.width(), documentHeight = $document.height(), elementWidth = that.width(), elementHeight = that.height(), @@ -1508,7 +1586,7 @@ requires ); that.width(width); that.height(height); - that.$content.height(height - 80); + that.$content.height(height - 48 - 2 * self.options.padding); // fixme: this should happen automatically }); $window.one("mouseup", function() { $window.unbind("mousemove"); @@ -1516,7 +1594,13 @@ requires } self.onChange = function(key, value) { - if (key == "title") { + 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() { @@ -1551,10 +1635,22 @@ requires return that; }; + that.disableButton = function(id) { + getButtonById(id).options({ + disabled: true + }); + }; + that.enable = function() { that.$layer.removeClass("OxFront"); return that; - } + }; + + that.enableButton = function(id) { + getButtonById(id).options({ + disabled: false + }); + }; that.open = function() { that.$layer.appendTo($body); @@ -1567,7 +1663,7 @@ requires }, 200); $window.bind("mouseup", mouseupLayer) return that; - } + }; return that; @@ -1579,6 +1675,31 @@ requires ============================================================================ */ + /* + ---------------------------------------------------------------------------- + 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 || {}, @@ -1586,40 +1707,79 @@ requires .defaults({ error: "", id: "", - items: [] + items: [], + submit: null }) .options(options || {}); // fixme: the || {} can be done once, in the options function $.extend(self, { + $items: [], + $messages: [], formIsValid: false, - itemIsValid: $.map(self.options.items, function(item, i) { - return 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) { - item.element.change(function() { - validate(i); - }) - that.append(new Ox.FormItem(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")); + Ox.Event.bind("validate_" + id, function(event, data) { + validate(i, data.valid); + }); + Ox.Event.bind("blur_" + id, 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("submit_" + id, function(event, data) { + self.formIsValid && that.submit(); + }); }); - function validate(pos) { - var item = self.options.items[pos]; - self.itemIsValid[pos] = item[item.validate ? "validate" : "regexp"](item.element.$input.val()); // fixme: Input elements should overwrite / have their own val() + 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("change", { - valid: formIsValid + that.triggerEvent("validate", { + valid: self.formIsValid }); } } - that.values = function() { + 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.options.items, function(i, item) { - values[item.id] = item.val(); + $.each(self.$items, function(i, $item) { + values[self.itemIds[i]] = self.$items[i].value(); }); return values; } else { @@ -1641,13 +1801,15 @@ requires .defaults({ element: null, error: "", - regexp: /.*/, - validate: null }) .options(options || {}) .addClass("OxFormItem") .append(self.options.element); + that.value = function() { + return self.options.element.$input.val(); + }; + return that; } @@ -1740,7 +1902,12 @@ requires } self.onChange = function(key, value) { //Ox.print("setOption", option, value) - if (key == "selected") { + if (key == "disabled") { + that.attr({ + disabled: value ? "disabled" : "" + }) + .toggleClass("OxDisabled"); + } else if (key == "selected") { if (value != that.hasClass("OxSelected")) { // fixme: neccessary? that.toggleClass("OxSelected"); } @@ -1848,6 +2015,7 @@ requires /* options: * autocomplete function, or array of values, or dict with array of values per label or placeholder + * autocorrect function for live correction * clear boolean, clear button, or not * highlight boolean, highlight value in autocomplete menu, or not * id @@ -1857,12 +2025,15 @@ requires * selected integer, selected label or placeholder * size "large", "medium" or "small" * type "text", "password", "textarea", etc. + * validate function vor live validation, returns { message: "", valid: false } + * value string */ var self = self || {}, that = new Ox.Element("div", self) .defaults({ autocomplete: null, + autocorrect: null, clear: false, highlight: false, id: "", @@ -1871,7 +2042,9 @@ requires placeholder: "", selected: 0, size: "medium", - type: "text" + type: "text", + validate: null, + value: "" }) .options(options || {}) .addClass("OxInput Ox" + Ox.toTitleCase(self.options.size)), @@ -1981,11 +2154,18 @@ requires key_enter: submit, }); } + + /* + if (self.options.validate) { + self.valid = self.options.validate(self.options.value); + } + */ + that.bindEvent({ key_escape: cancel }); - function autocomplete(value, callback) { + function autocomplete(value) { var value = value.toLowerCase(), items = []; if (value === "") { @@ -1997,38 +2177,26 @@ requires } }); } - callback(items); + autocompleteCallback(items); } - function blur() { - that.loseFocus(); - that.removeClass("OxFocus"); - if (self.options.placeholder && that.$input.val() === "") { - that.$input.addClass("OxPlaceholder").val(self.option); - } - if (self.options.autocomplete) { - $document.unbind("keydown", keypress); - $document.unbind("keypress", keypress); - } - } - - function call() { + function autocompleteCall() { var value = that.$input.val(); Ox.print("autocomplete call", self.option, value) if (self.options.autocomplete) { if (value !== "") { Ox.isFunction(self.options.autocomplete) ? ( self.option ? - self.options.autocomplete(self.option.id, value, callback) : - self.options.autocomplete(value, callback) - ) : autocomplete(value, callback); + self.options.autocomplete(self.option.id, value, autocompleteCallback) : + self.options.autocomplete(value, autocompleteCallback) + ) : autocomplete(value); } else { - callback(); + autocompleteCallback(); } } } - function callback(items) { + function autocompleteCallback(items) { Ox.print("autocomplete callback", items) var items = items || [], selected = items.length == 1 ? 0 : -1, @@ -2056,6 +2224,34 @@ requires } } + function autocorrect(blur) { + var blur = blur || false, + length = self.value.length; + pos = cursor(), + self.value = self.options.autocorrect(self.value, blur); + that.$input.val(self.value); + if (!blur) { + cursor(pos + self.value.length - length); + } + } + + function blur() { + that.loseFocus(); + that.removeClass("OxFocus"); + self.options.autocorrect && autocorrect(that.$input.val(), true); + self.options.validate && that.triggerEvent("blur", { // fixme: is this a good event name for validation on blur? + message: self.message, + valid: self.valid + }); + if (self.options.placeholder && that.$input.val() === "") { + that.$input.addClass("OxPlaceholder").val(self.option); + } + if (self.options.autocomplete || self.options.autocorrect || self.options.validate) { // fixme: duplicated, make a var + $document.unbind("keydown", keypress); + $document.unbind("keypress", keypress); + } + } + function cancel() { that.$input.blur(); } @@ -2071,21 +2267,30 @@ requires if (self.options.label) { that.$label.html(self.option.title); that.$input.focus(); - call(); + autocompleteCall(); } else { if (that.$input.is(".OxPlaceholder")) { that.$input.val(self.option); //that.$input.focus(); } else { that.$input.focus(); - call(); + autocompleteCall(); } } } function clear() { that.$input.val("").focus(); - //call(); + //autocompleteCall(); + } + + function cursor() { + // fixme: see selection() + if (arguments.length == 0) { + return that.$input.$element[0].selectionStart; + } else { + that.$input.$element[0].setSelectionRange(arguments[0], arguments[0]); + } } function focus() { @@ -2096,13 +2301,15 @@ requires that.$input.val("").removeClass("OxPlaceholder"); } value = that.$input.val(); - if (self.options.autocomplete) { + if (self.options.autocomplete || self.options.autocorrect || self.options.validate) { // fixme: different in webkit and firefox (?), see keyboard handler, need generic function $document.bind("keydown", keypress); $document.bind("keypress", keypress); - value !== "" && Ox.isFunction(self.options.autocomplete) ? - self.options.autocomplete(self.option.id, value, callback) : - autocomplete(value, callback); + if (self.options.autocomplete) { + value !== "" && Ox.isFunction(self.options.autocomplete) ? + self.options.autocomplete(self.option.id, value, autocompleteCallback) : + autocomplete(value); + } } } @@ -2112,7 +2319,9 @@ requires var value = that.$input.val(); if (value != self.value) { self.value = value; - call(); + self.options.autocomplete && autocompleteCall(); + self.options.autocorrect && autocorrect(); + self.options.validate && validateCall(); } }, 25); } @@ -2150,6 +2359,40 @@ requires } : that.$input.val()); } + function validate() { + var value = that.$input.val(), + valid = self.options.validate(value) != null; + validateCallback({ + valid: valid, + message: "Invalid " + self.options.label + }); + } + + function validateCall() { + var value = that.$input.val(); + Ox.print("validate call", value) + if (self.options.validate) { + if (value !== "") { + Ox.isFunction(self.options.validate) ? + self.options.validate(value, validateCallback) : + validate(value); + } else { + validateCallback({ + valid: false, + message: "" + }); + } + } + } + + function validateCallback(data) { + if (data.valid != self.valid) { + self.message = data.message; + self.valid = data.valid; + that.triggerEvent("validate", data); + } + } + that.changeLabel = function(id) { that.$label.html(Ox.getObjectById(self.options.label, id).title); self.selectMenu.checkItem(id); @@ -3405,10 +3648,13 @@ requires that.$head.$content.css({ width: (Ox.sum(self.columnWidths) + 2) + "px" }); - 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" - }); + 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" + }); + } that.$select = new Ox.Button({ style: "symbol", type: "image", @@ -3672,6 +3918,331 @@ requires }; + /* + ============================================================================ + Maps + ============================================================================ + */ + + Ox.Map = function(options, self) { + + var self = self || {} + that = new Ox.Element("div", self) + .defaults({ + places: [], + type: "satellite" + }) + .options(options || {}); + + init(); + + 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 getLocationByPoint(point, area, callback) { + var callback = arguments.length == 2 ? area : callback, + area = arguments.length == 2 ? null : area, + options = $.extend({ + "latLng": new google.maps.LatLng(point[0], point[1]) + }, area ? { + "bounds": new google.maps.LatLngBounds( + new google.maps.LatLng(area[0][0], area[0][1]), + new google.maps.LatLng(area[1][0], area[1][1]) + ) + } : {}); + self.geocoder.geocode(options, function(results, status) { + var length = results.length; + if (status == google.maps.GeocoderStatus.OK) { + if (status != google.maps.GeocoderStatus.ZERO_RESULTS) { + + } + } + }); + } + + function init() { + var counter = 0, + length = self.options.places.length; + $.extend(self, { + geocoder: new google.maps.Geocoder(), + locations: [] + }); + $.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, "zoom_changed", zoom); + $.each(self.locations, function(i, location) { + location.marker.add(); + }); + }; + + function zoom() { + + } + + 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("blue"), + position: location.rectangle.center, + title: location.name.formatted + }); + function click() { + marker.setOptions({ + icon: icon("red") + }); + location.polygon.add(); + } + 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); + }, + remove: function() { + marker.setMap(null); + google.maps.event.removeListener(listeners.click); + } + }; + } + + 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; + function setOptions() { + var color = selected ? "#8080FF" : "#FFFFFF"; + polygon.setOptions({ + clickable: true, + fillColor: color, + fillOpacity: selected ? 0.1 : 0, + geodesic: true, + strokeColor: color, + strokeOpacity: 1, + strokeWeight: 2 + }); + } + return { + add: function() { + polygon.setMap(self.map); + listeners.click = google.maps.event.addListener(polygon, "click", this.select); + }, + deselect: function() { + selected = false; + setOptions(); + }, + remove: function() { + polygon.setMap(null); + google.maps.event.removeListener(listeners.click); + }, + 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.mc), + mw: new google.maps.LatLng(lat.sw, lng.mc), + mw: new google.maps.LatLng(lat.sw, lng.mc), + nw: new google.maps.LatLng(lat.sw, lng.mc), + nc: new google.maps.LatLng(lat.sw, 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") { + + } + }; + + 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 @@ -4541,7 +5112,8 @@ requires } 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]); } } @@ -4558,9 +5130,9 @@ requires }; that.toggleTitle = function() { + Ox.print("s.o.t", self.options.title) that.options({ - title: that.$title.html() == self.options.title[0] ? - self.options.title[1] : self.options.title[0] + title: self.options.title.reverse() }); }; @@ -4895,5 +5467,63 @@ requires ---------------------------------------------------------------------------- */ + /* + ============================================================================ + Miscellaneous + ============================================================================ + */ + + /* + ---------------------------------------------------------------------------- + Ox.Tooltip + ---------------------------------------------------------------------------- + */ + + Ox.Tooltip = function(options, self) { + + var self = self || {}, + that = new Ox.Element() + .defaults({ + text: "" + }) + .options(options || {}) + .addClass("OxTooltip") + .html(self.options.text); + + self.onChange = function(key, value) { + if (key == "text") { + that.html(value); + } + }; + + that.hide = function() { + that.animate({ + opacity: 0 + }, 250, function() { + that.remove(); + }); + return that; + }; + + that.show = function(e) { + var left, top, width, height; + that.appendTo($body); + width = that.width(); + height = that.height(); + left = Ox.limit(e.clientX - width / 2, 0, $document.width() - width); + top = e.clientY > $document.height() - height - 16 ? e.clientY - 32 : e.clientY + 16; + that.css({ + left: left + "px", + top: top + "px" + }) + .animate({ + opacity: 1 + }, 250); + return that; + }; + + return that; + + }; })(); \ No newline at end of file diff --git a/build/png/ox.ui/markerBlue.png b/build/png/ox.ui/markerBlue.png new file mode 100644 index 00000000..27873e77 Binary files /dev/null and b/build/png/ox.ui/markerBlue.png differ diff --git a/build/png/ox.ui/markerGreen.png b/build/png/ox.ui/markerGreen.png new file mode 100644 index 00000000..5d614b64 Binary files /dev/null and b/build/png/ox.ui/markerGreen.png differ diff --git a/build/png/ox.ui/markerRed.png b/build/png/ox.ui/markerRed.png new file mode 100644 index 00000000..dac82304 Binary files /dev/null and b/build/png/ox.ui/markerRed.png differ diff --git a/build/png/ox.ui/markerYellow.png b/build/png/ox.ui/markerYellow.png new file mode 100644 index 00000000..6d129a3d Binary files /dev/null and b/build/png/ox.ui/markerYellow.png differ diff --git a/source/psd/buttons.psd b/source/psd/buttons.psd index 06c894ff..fbe946e0 100644 Binary files a/source/psd/buttons.psd and b/source/psd/buttons.psd differ diff --git a/source/psd/loading.psd b/source/psd/loading.psd new file mode 100644 index 00000000..10b5f7fc Binary files /dev/null and b/source/psd/loading.psd differ diff --git a/source/psd/marker.psd b/source/psd/marker.psd new file mode 100644 index 00000000..46bcef83 Binary files /dev/null and b/source/psd/marker.psd differ