diff --git a/build/css/ox.ui.classic.css b/build/css/ox.ui.classic.css index 70bee45d..2ae64cb5 100644 --- a/build/css/ox.ui.classic.css +++ b/build/css/ox.ui.classic.css @@ -58,60 +58,82 @@ Forms */ .OxThemeClassic .OxButton, -.OxThemeClassic .OxInput, +.OxThemeClassic input.OxCheckbox, +.OxThemeClassic input.OxInput, .OxThemeClassic .OxLabel, -.OxThemeClassic .OxRange, .OxThemeClassic .OxTrack { border: 1px solid rgb(176, 176, 176); //border: 1px solid rgb(160, 160, 160); color: rgb(64, 64, 64); } +.OxThemeClassic .OxSelect { + border: 1px solid rgb(176, 176, 176); +} +.OxThemeClassic .OxSelect > .OxTitle { + color: rgb(64, 64, 64); +} .OxThemeClassic .OxInputLabel { color: rgb(64, 64, 64); } .OxThemeClassic .OxButton, .OxThemeClassic div.OxInput, -.OxThemeClassic .OxRange { +.OxThemeClassic .OxSelect { //background: -moz-linear-gradient(left top, left bottom, from(rgb(192, 192, 192)), to(rgb(160, 160, 160))); //background: -webkit-gradient(linear, left top, left bottom, from(rgb(192, 192, 192)), to(rgb(160, 160, 160))); background: -moz-linear-gradient(top, rgb(224, 224, 224), rgb(192, 192, 192)); background: -webkit-gradient(linear, left top, left bottom, from(rgb(224, 224, 224)), to(rgb(192, 192, 192))); } -.OxThemeClassic .OxButton:focus { - -moz-box-shadow: 0 0 2px rgb(128, 128, 128); - -webkit-box-shadow: 0 2 4px rgb(128, 128, 128); -} -.OxThemeClassic .OxButton:active, -.OxThemeClassic .OxRange.OxActive { +.OxThemeClassic .OxButton:active { //background: rgb(160, 160, 160); background: rgb(192, 192, 192); color: rgb(48, 48, 48); } -.OxThemeClassic .OxButton.OxDisabled { - background: rgb(192, 192, 192); - color: rgb(128, 128, 128); +.OxThemeClassic .OxCheckbox:active { + background: -moz-linear-gradient(top, rgb(192, 192, 192), rgb(240, 240, 240)); + background: -webkit-gradient(linear, left top, left bottom, from(rgb(192, 192, 192)), to(rgb(240, 240, 240))); } -.OxThemeClassic .OxButton.OxSelected { - //background: -moz-linear-gradient(left top, left bottom, from(rgb(128, 128, 128)), to(rgb(160, 160, 160))); - //background: -webkit-gradient(linear, left top, left bottom, from(rgb(128, 128, 128)), to(rgb(160, 160, 160))); - background: -moz-linear-gradient(top, rgb(128, 128, 128), rgb(160, 160, 160) 10%, rgb(192, 192, 192)); - background: -webkit-gradient(linear, left top, left bottom, from(rgb(128, 128, 128)), color-stop(0.1, rgb(160, 160, 160)), to(rgb(192, 192, 192))); +.OxThemeClassic .OxButton:focus { + -moz-box-shadow: 0 0 2px rgb(128, 128, 128); + -webkit-box-shadow: 0 2 4px rgb(128, 128, 128); +} +.OxThemeClassic .OxButton.OxSelected, +.OxThemeClassic .OxSelect.OxSelected { + background: -moz-linear-gradient(top, rgb(128, 128, 128), rgb(160, 160, 160)); + background: -webkit-gradient(linear, left top, left bottom, from(rgb(160, 160, 160)), to(rgb(192, 192, 192))); + //background: -moz-linear-gradient(top, rgb(128, 128, 128), rgb(160, 160, 160) 10%, rgb(192, 192, 192)); + //background: -webkit-gradient(linear, left top, left bottom, from(rgb(128, 128, 128)), color-stop(0.1, rgb(160, 160, 160)), to(rgb(192, 192, 192))); color: rgb(32, 32, 32); } .OxThemeClassic .OxButton.OxTab.OxSelected { border-bottom: 1px solid rgb(192, 192, 192); } +.OxThemeClassic .OxButton.OxDisabled, +.OxThemeClassic .OxLabel.OxDisabled { + color: rgb(128, 128, 128); +} +.OxThemeClassic .OxButton.OxDisabled { + background: rgb(192, 192, 192); +} + +.OxThemeClassic input.OxCheckbox, .OxThemeClassic input.OxInput, .OxThemeClassic .OxTrack { - background: -moz-linear-gradient(top, rgb(224, 224, 224), rgb(255, 255, 255)); - background: -webkit-gradient(linear, left top, left bottom, from(rgb(224, 224, 224)), to(rgb(255, 255, 255))); + //background: -moz-linear-gradient(top, rgb(224, 224, 224), rgb(255, 255, 255)); + //background: -webkit-gradient(linear, left top, left bottom, from(rgb(224, 224, 224)), to(rgb(255, 255, 255))); + background: -moz-linear-gradient(top, rgb(208, 208, 208), rgb(255, 255, 255)); + background: -webkit-gradient(linear, left top, left bottom, from(rgb(208, 208, 208)), to(rgb(255, 255, 255))); } .OxThemeClassic .OxInput:focus { - border: 1px solid rgb(160, 160, 160); + //border: 1px solid rgb(160, 160, 160); + //-moz-box-shadow: 0 0 2px rgb(128, 128, 128); + //-webkit-box-shadow: 0 0 2px rgb(128, 128, 128); +} +.OxThemeClassic div.OxInput.OxFocus { -moz-box-shadow: 0 0 2px rgb(128, 128, 128); -webkit-box-shadow: 0 0 2px rgb(128, 128, 128); } + .OxThemeClassic .OxInput.OxPlaceholder { color: rgb(160, 160, 160) } @@ -120,6 +142,13 @@ Forms background: rgb(208, 208, 208); } +.OxThemeClassic input.OxCheckbox.OxDisabled, +.OxThemeClassic input.OxInput:disabled { + background: rgb(224, 224, 224); +} + + + /* ================================================================================ Lists @@ -266,5 +295,5 @@ Scrollbars } .OxThemeClassic ::-webkit-scrollbar:active, .OxThemeClassic ::-webkit-scrollbar-thumb:active { - background: rgb(64, 64, 64); + background: rgb(208, 208, 208); } \ No newline at end of file diff --git a/build/css/ox.ui.css b/build/css/ox.ui.css index d278f562..02a1ff15 100644 --- a/build/css/ox.ui.css +++ b/build/css/ox.ui.css @@ -276,6 +276,14 @@ textarea { -moz-border-radius: 8px; -webkit-border-radius: 8px; } +/* +-------------------------------------------------------------------------------- +OxButton +-------------------------------------------------------------------------------- +*/ +.OxButton { + text-align: center; +} .OxButton.OxSymbol, .OxButton.OxSymbol:active, .OxButton.OxSymbol:focus { @@ -317,16 +325,16 @@ OxButtonGroup -webkit-border-bottom-right-radius: 6px; } .OxButtonGroup > .OxButton.OxMedium:first-child { - -moz-border-radius-topleft: 4px; - -moz-border-radius-bottomleft: 4px; - -webkit-border-top-left-radius: 4px; - -webkit-border-bottom-left-radius: 4px; + -moz-border-radius-topleft: 8px; + -moz-border-radius-bottomleft: 8px; + -webkit-border-top-left-radius: 8px; + -webkit-border-bottom-left-radius: 8px; } .OxButtonGroup > .OxButton.OxMedium:last-child { - -moz-border-radius-topright: 4px; - -moz-border-radius-bottomright: 4px; - -webkit-border-top-right-radius: 4px; - -webkit-border-bottom-right-radius: 4px; + -moz-border-radius-topright: 8px; + -moz-border-radius-bottomright: 8px; + -webkit-border-top-right-radius: 8px; + -webkit-border-bottom-right-radius: 8px; } .OxButtonGroup > .OxButton.OxSmall:first-child { -moz-border-radius-topleft: 2px; @@ -378,36 +386,62 @@ OxForm } /* -------------------------------------------------------------------------------- +OxCheckbox +-------------------------------------------------------------------------------- +*/ +div.OxCheckbox { + height: 16px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; +} +input.OxCheckbox { + width: 14px; + height: 14px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; +} +.OxCheckboxGroup > div.OxCheckbox { + float: left; +} +.OxCheckboxGroup > div.OxCheckbox { + //padding-right: 16px; + margin-right: -16px; +} +.OxCheckboxGroup > div.OxCheckbox:last-child { + //padding-right: 0; + margin-right: 0; +} +/* +-------------------------------------------------------------------------------- OxInput -------------------------------------------------------------------------------- */ div.OxInput { + height: 16px; -moz-border-radius: 8px; -webkit-border-radius: 8px; } div.OxInput.OxMedium { - height: 14px; + height: 16px; } div.OxInput > .OxInputLabel { float: left; - padding-left: 8px; - text-overflow: ellipsis; + padding: 0 6px 0 6px; cursor: default; - overflow: hidden; -} -div.OxInput > .OxButton { - float: left; - //margin-left: 1px; - margin-top: -1px; -} -div.OxInput > .OxButton:last-child { - float: left; - margin-left: -1px; - margin-top: -1px; } input.OxInput { float: left; - margin: -1px -1px 0 -1px; +} +/* +-------------------------------------------------------------------------------- +OxInputGroup +-------------------------------------------------------------------------------- +*/ +.OxInputGroup { + height: 16px; +} +.OxInputGroup > div { + float: left; } /* -------------------------------------------------------------------------------- @@ -417,50 +451,99 @@ OxLabel .OxLabel { height: 14px; border: 1px; - padding: 0 8px 0 8px; + padding: 0 6px 0 6px; + text-overflow: ellipsis; + cursor: default; + overflow: hidden; + white-space: nowrap; -moz-border-radius: 8px; -webkit-border-radius: 8px; } /* -------------------------------------------------------------------------------- +OxPicker +-------------------------------------------------------------------------------- +*/ +.OxPicker { + position: absolute; + z-index: 11; + -moz-border-radius: 0 8px 8px 8px; + -webkit-border-radius: 0 8px 8px 8px; + -moz-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.75); + -webkit-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.75); +} +.OxPicker > div:first-child { + background: rgb(240, 240, 240); + -moz-border-radius: 0 8px 0 0; + -webkit-border-radius: 0 8px 0 0; +} +.OxPicker > .OxBar { + -moz-border-radius: 0 0 8px 8px; + -webkit-border-radius: 0 0 8px 8px; +} +.OxPicker > .OxBar > .OxLabel { + float: left; + margin: 4px 0 4px 4px; +} +.OxPicker > .OxBar > .OxButton { + float: right; + margin: 4px 4px 4px 0; +} +/* +-------------------------------------------------------------------------------- OxRange -------------------------------------------------------------------------------- */ .OxRange { - height: 14px; - border: 1px; - -moz-border-radius: 8px; - -webkit-border-radius: 8px; + height: 16px; +} +.OxRange > .OxArrow { + float: right; } .OxRange > .OxArrow:first-child { float: left; - margin-top: -1px; -} -.OxRange > .OxArrow:last-child { - float: left; - margin-left: -1px; - margin-top: -1px; } .OxRange > .OxTrack { - float: left; + float: right; height: 14px; - margin-left: -1px; - margin-top: -1px; -moz-border-radius: 8px; -webkit-border-radius: 8px; } -.OxRange > .OxTrack > .OxImage { +.OxRange > .OxTrack > div { + float: left; + height: 16px; + padding: 1px; + margin: -1px; +} +.OxRange > .OxTrack > div > img { float: left; height: 14px; - -moz-border-radius: 6px; - -webkit-border-radius: 6px; + -webkit-user-select: none; +} +.OxRange > .OxTrack > div > img.OxFirstChild { + -moz-border-radius: 7px; + -webkit-border-top-left-radius: 7px; + -webkit-border-bottom-left-radius: 7px; +} +.OxRange > .OxTrack > div > img.OxLastChild { + -moz-border-radius: 0 7px 7px 0; + -webkit-border-top-right-radius: 7px; + -webkit-border-bottom-right-radius: 7px; +} +.OxRange > .OxTrack > div > img.OxFirstChild.OxLastChild { + margin-left: 7px; + -moz-border-radius: 0; + -webkit-border-radius: 0; } .OxRange > .OxTrack > .OxThumb { float: left; - margin-top: -15px; + margin: -1px; + //margin-left: -1px; + //margin-top: -1px; + text-align: center; } .OxRange > .OxTrack > .OxThumb:first-child { - margin-top: -1px; + //margin-top: -1px; } /* -------------------------------------------------------------------------------- @@ -468,22 +551,83 @@ OxSelect -------------------------------------------------------------------------------- */ .OxSelect.OxMedium { - margin-left: 0; + height: 14px; + -moz-border-radius: 8px; + -webkit-border-radius: 8px; +} +.OxSelect.OxSelected { + -moz-border-radius: 8px 8px 0 0; + -webkit-border-radius: 8px 8px 0 0; +} +.OxSelect > .OxTitle { + float: left; + height: 14px; + padding-left: 6px; + text-align: left; + text-overflow: ellipsis; + cursor: default; + overflow: hidden; + white-space: nowrap; + //margin-right: -16px; +} +.OxSelect.OxOverlapLeft > .OxTitle { + //padding-left: 20px; + //padding-right: 8px; + //margin-left: -32px; +} +.OxSelect.OxOverlapRight > .OxTitle { + //padding-left: 8px; + //padding-right: 20px; + //margin-right: -32px; } .OxSelect > .OxButton { float: right; - text-align: left; + margin: -1px; } -.OxSelect > .OxSymbol { - text-align: right; - cursor: default; - -moz-user-select: none; - -webkit-user-select: none; +.OxSelect.OxOverlapLeft > .OxButton { + //padding-left: 15px; + //padding-right: 1px; + //margin-left: -16px; } -.OxSelect.OxMedium > .OxSymbol { - float: right; - margin: -16px 0 0 -4px; - z-index: 9; +.OxSelect.OxOverlapRight > .OxButton { + //padding-left: 1px; + //padding-right: 15px; + //margin-right: -16px; +} +/* + +*/ +.OxButton.OxOverlapLeft, +.OxLabel.OxOverlapLeft, +.OxxxSelect.OxOverlapLeft { + padding-left: 20px; + padding-right: 8px; + margin-left: -16px; +} +.OxButton.OxOverlapRight, +.OxLabel.OxOverlapRight, +.OxxxSelect.OxOverlapRight { + padding-left: 8px; + padding-right: 20px; + margin-right: -16px; +} +.OxButton[type=image].OxOverlapLeft { + padding-left: 15px; + padding-right: 1px; +} +.OxButton[type=image].OxOverlapRight { + padding-left: 1px; + padding-right: 15px; +} +.OxSelect.OxOverlapLeft { + //padding-left: 8px; + padding-left: 16px; + margin-left: -18px; +} +.OxSelect.OxOverlapRight { + //padding-left: 8px; + padding-right: 16px; + margin-right: -18px; } /* @@ -498,7 +642,6 @@ Layers top: 0; right: 0; bottom: 0; - background: rgb(0, 0, 0); opacity: 0; overflow: hidden; z-index: 10; @@ -509,7 +652,7 @@ Layers .OxMainMenuLayer { position: absolute; width: 100%; - top: 24px; + top: 20px; bottom: 0px; overflow: hidden; z-index: 10; @@ -976,9 +1119,10 @@ Miscellaneous .OxThemeModern .OxTooltip { position: absolute; - padding: 2px; + padding: 1px 2px 1px 2px; + font-size: 9px; opacity: 0; z-index: 10; - -moz-border-radius: 2px; - -webkit-border-radius: 2px; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; } diff --git a/build/css/ox.ui.modern.css b/build/css/ox.ui.modern.css index fc7e1cda..65dda6c2 100644 --- a/build/css/ox.ui.modern.css +++ b/build/css/ox.ui.modern.css @@ -10,6 +10,9 @@ body.OxThemeModern { //color: rgb(0, 0, 0); color: rgb(255, 255, 0); } +.OxThemeModern .OxBright { + color: rgb(255, 255, 255); +} /* ================================================================================ @@ -54,9 +57,13 @@ Dialog Forms ================================================================================ */ +.OxThemeModern .OxInput::-selection { + background: rgb(80, 80, 80); +} + .OxThemeModern .OxButton, -.OxThemeModern .OxInput, +.OxThemeModern input.OxInput, .OxThemeModern .OxRange, .OxThemeModern .OxTrack { //border: 1px solid rgb(80, 80, 80); @@ -64,7 +71,6 @@ Forms color: rgb(192, 192, 192); } .OxThemeModern .OxButton, -.OxThemeModern div.OxInput, .OxThemeModern .OxRange { background: -moz-linear-gradient(top, rgb(96, 96, 96), rgb(64, 64, 64)); background: -webkit-gradient(linear, left top, left bottom, from(rgb(96, 96, 96)), to(rgb(64, 64, 64))); @@ -93,20 +99,27 @@ Forms .OxThemeModern .OxFormMessage { color: rgb(255, 64, 64); } -.OxThemeModern .OxInput, +.OxThemeModern input.OxInput, .OxThemeModern .OxTrack { background: -moz-linear-gradient(top, rgb(0, 0, 0), rgb(32, 32, 32)); background: -webkit-gradient(linear, left top, left bottom, from(rgb(0, 0, 0)), to(rgb(32, 32, 32))); } -.OxThemeModern div.OxInput.OxFocus, -.OxThemeModern .OxInput:focus { - border: 1px solid rgb(80, 80, 80); +.OxThemeModern div.OxInput.OxFocus { + //border: 1px solid rgb(80, 80, 80); -moz-box-shadow: 0 0 2px rgb(128, 128, 128); -webkit-box-shadow: 0 0 2px rgb(128, 128, 128); } +.OxThemeModern div.OxInput.OxFocus > .OxInputLabel { + border-color: rgb(80, 80, 80); +} .OxThemeModern .OxInput.OxPlaceholder { color: rgb(96, 96, 96) } +.OxThemeModern .OxLabel { + border: 1px solid rgb(48, 48, 48); + background: rgb(80, 80, 80); + color: rgb(192, 192, 192); +} /* ================================================================================ @@ -250,8 +263,9 @@ 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); + border: 1px solid rgba(128, 128, 128, 0.75); + background: rgba(0, 0, 0, 0.75); + color: rgba(128, 128, 128, 1); + -moz-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); + -webkit-box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); } \ No newline at end of file diff --git a/build/js/ox.js b/build/js/ox.js index 250b91cb..5ede5799 100644 --- a/build/js/ox.js +++ b/build/js/ox.js @@ -5,6 +5,19 @@ Ox = { version: "0.1.2" }; +/* +================================================================================ +Constants +================================================================================ +*/ + +Ox.AMPM = ["AM", "PM"]; +Ox.DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; +Ox.MONTHS = ["January", "February", "March", "April", "May", "June", + "July", "August", "September", "October", "November", "December"]; +Ox.WEEKDAYS = ["Monday", "Tuesday", "Wednesday", "Thursday", + "Friday", "Saturday", "Sunday"]; + /* ================================================================================ Core functions @@ -60,10 +73,12 @@ Ox.print = function() { } Ox.uid = function() { - /* + /*** + Ox.uid + returns a unique id >>> Ox.uid() == Ox.uid() false - */ + ***/ var uid = 0; return function() { return uid++; @@ -105,12 +120,14 @@ Array and Object functions */ Ox.avg = function(obj) { - /* + /*** + Ox.avg(obj) + returns the average of an array's values, or an object's properties >>> Ox.avg([-1, 0, 1]) 0 >>> Ox.avg({"a": 1, "b": 2, "c": 3}) 2 - */ + ***/ return Ox.sum(obj) / Ox.length(obj); }; @@ -186,6 +203,40 @@ Ox.filter = function(arr, fn) { return ret; }; +Ox.flatten = function(arr) { + /* + >>> Ox.flatten([1, [2, [3], 4], 5]) + [1, 2, 3, 4, 5] + */ + var ret = []; + arr.forEach(function(v) { + if (Ox.isArray(v)) { + Ox.flatten(v).forEach(function(v) { + ret.push(v); + }); + } else { + ret.push(v); + } + }); + return ret; +} + +Ox.find = function(arr, str) { + /* + >>> Ox.find(["foo", "bar", "foobar", "barfoo"], "foo") + [["foo", "foobar"], ["barfoo"]] + */ + var arrLowerCase = arr.map(function(v) { + return v.toLowerCase(); + }), + ret = [[], []]; + str && arrLowerCase.forEach(function(v, i) { + var index = v.indexOf(str.toLowerCase()); + index > -1 && ret[index == 0 ? 0 : 1].push(arr[i]); + }); + return ret; +} + Ox.getObjectById = function(arr, id) { var ret = null; Ox.each(arr, function(i, v) { @@ -244,6 +295,7 @@ Ox.makeArray = function(arr) { >>> (function() { return Ox.makeArray(arguments); })(["foo"]) ["foo"] */ + // fixme: this doesn't work for numbers var ret = [], i = 0, len = arr.length; if (Ox.isString(arr)) { ret = [arr]; @@ -335,6 +387,10 @@ Ox.range = function(start, stop, step) { }; Ox.serialize = function(obj) { + /* + >>> Ox.serialize({a: 0, b: 1}) + a=0&b=1 + */ var arr = []; Ox.each(obj, function(k, v) { arr.push(k + "=" + v); @@ -342,6 +398,22 @@ Ox.serialize = function(obj) { return arr.join("&"); }; +Ox.setPropertyOnce = function(arr, str) { + var pos = -1; + Ox.each(arr, function(i, v) { + if (pos == -1 && arr[i][str]) { + pos = i; + } else if (pos > -1 && arr[i][str]) { + delete arr[i][str]; + } + }); + if (pos == -1) { + arr[0][str] = true; + pos = 0; + } + return pos; +} + Ox.shuffle = function(arr) { /* >>> Ox.shuffle([1, 2, 3]).length @@ -514,19 +586,27 @@ Date functions ================================================================================ */ -Ox.getFirstDayOfTheYear = function(date) { +Ox.getDateInWeek = function(date, weekday) { /* - Decimal weekday of January 1 (0-6, Sunday as first day) - >>> Ox.getFirstDayOfTheYear(new Date("01/01/00")) - 6 + >>> Ox.formatDate(Ox.getDateInWeek(new Date("January 1 2000"), "Sunday"), "%A, %B %e, %Y") + "Sunday, January 2, 2000" + >>> Ox.formatDate(Ox.getDateInWeek(new Date("Jan 1 2000"), "Fri"), "%A, %B %e, %Y") + "Friday, December 31, 1999" + >>> Ox.formatDate(Ox.getDateInWeek(new Date("1/1/2000"), 1), "%A, %B %e, %Y") + "Monday, December 27, 1999" */ - var date_ = date ? new Date(date.valueOf()) : new Date(); - date_.setMonth(0); - date_.setDate(1); - return date_.getDay(); -}; + Ox.print("getDateInWeek", date.toString(), weekday) + var date = date || new Date(), + sourceWeekday = Ox.formatDate(date, "%u"); + targetWeekday = Ox.isNumber(weekday) ? weekday : + Ox.map(Ox.WEEKDAYS, function(v, i) { + return v.substr(0, 3) == weekday.substr(0, 3) ? i + 1 : null; + })[0]; + date.setDate(date.getDate() - sourceWeekday + targetWeekday); + return date; +} -Ox.getDayOfTheYear = function() { +Ox.getDayOfTheYear = function(date) { /* >>> Ox.getDayOfTheYear(new Date("12/31/2004")) 366 @@ -547,6 +627,36 @@ Ox.getDayOfTheYear = function() { }; }(); +Ox.getDaysInMonth = function(year, month) { + /* + >>> Ox.getDaysInMonth(2000, 2) + 28 + >>> Ox.getDaysInMonth("2002", "Feb") + 28 + >>> Ox.getDaysInMonth("2004", "February") + 29 + */ + Ox.print("getDaysInMonth", year, month) + var year = parseInt(year), + month = Ox.isNumber(month) ? month : + Ox.map(Ox.MONTHS, function(v, i) { + return v.substr(0, 3) == month.substr(0, 3) ? i + 1 : null; + })[0]; + return Ox.DAYS[month - 1] + (month == 2 && Ox.isLeapYear(year)); +} + +Ox.getFirstDayOfTheYear = function(date) { + /* + Decimal weekday of January 1 (0-6, Sunday as first day) + >>> Ox.getFirstDayOfTheYear(new Date("01/01/00")) + 6 + */ + var date_ = date ? new Date(date.valueOf()) : new Date(); + date_.setMonth(0); + date_.setDate(1); + return date_.getDay(); +}; + Ox.getISODate = function(date) { /* >>> Ox.getISODate(new Date("01/01/2000")) @@ -656,9 +766,7 @@ Ox.canvas = function() { c.context = (c.canvas = Ox.element("canvas").attr({ width: image.width, height: image.height })[0]).getContext("2d"); - if (isImage) { - c.context.drawImage(image, 0, 0); - } + isImage && c.context.drawImage(image, 0, 0); c.data = (c.imageData = c.context.getImageData(0, 0, image.width, image.height)).data; return c; @@ -1321,6 +1429,23 @@ Ox.deg = function(rad) { return rad * 180 / Math.PI; }; +Ox.divideInt = function(num, by) { + /* + >>> Ox.divideInt(100, 3) + [33, 33, 34] + >>> Ox.divideInt(100, 6) + [16, 16, 17, 17, 17, 17] + */ + var arr = [], + div = parseInt(num / by), + mod = num % by, + i; + for (i = 0; i < by; i++) { + arr[i] = div + (i > by - 1 - mod); + } + return arr; +} + Ox.limit = function(num, min, max) { /* >>> Ox.limit(1, 2, 3) @@ -1328,7 +1453,6 @@ Ox.limit = function(num, min, max) { >>> Ox.limit(2, 1) 1 */ - Ox.print(num, min, max) var len = arguments.length; max = arguments[len - 1]; min = len == 3 ? min : 0; @@ -1413,6 +1537,16 @@ Ox.clean = function(str) { return Ox.trim(str.replace(/\s+/g, " ")); }; +Ox.contains = function(str, chr) { + /* + >>> Ox.contains("foo", "bar") + false + >>> Ox.contains("foobar", "bar") + true + */ + return str.indexOf(chr) > -1; +}; + Ox.endsWith = function(str, sub) { /* >>> Ox.endsWith("foobar", "bar") diff --git a/build/js/ox.ui.js b/build/js/ox.ui.js index e1d605c4..67109c32 100644 --- a/build/js/ox.ui.js +++ b/build/js/ox.ui.js @@ -183,109 +183,123 @@ requires ---------------------------------------------------------------------------- Ox.Event ---------------------------------------------------------------------------- - - naming convention for event/trigger - verb.id.namespace, i.e. verb.sourceId.targetId (?) - ... - bind("keydown.shift+dot.numpad", function() { - // ... - }) - keyboard handler then would: - $.each(stack, function(i, v) { - elements[v].trigger("keydown.shift+0.numpad"); - }); - and the element would implement - this.trigger(event, data) { - - } - ... - keyboard handler also triggers keydown.buffer */ - // use dom elements / jquery instead - Ox.Event = function() { - var keyboardEvents = {}; - $eventHandler = $("
"); - function isKeyboardEvent(event) { + var $eventHandler = $("
"), + events = {}; + function addEvent(id, type, event, callback) { + events[id] = events[id] || {}; + events[id][type] = events[id][type] || {}; + events[id][type][event] = events[id][type][event] || []; + events[id][type][event].push(callback); + if (type == "normal" || Ox.Focus.focused() == id) { + Ox.print("bind", id, event); + $eventHandler.bind(event + "_" + id, callback); + } + } + function removeEvent(id, type, event, callback) { + var focused = type == "normal" || Ox.Focus.focused() == id, + toString = (callback || "").toString(); + Ox.print("removeEvent", id, type, event, callback); + if (events[id] && events[id][type] && (!event || events[id][type][event])) { + $.each(events[id][type], function(e, fns) { + if (!event || event == e) { + events[id][type][e] = $.map(events[id][type][e], function(fn, i) { + if (!callback || toString == fn.toString()) { + focused && $eventHandler.unbind(e + "_" + id, fn); + return null; + } else { + return fn; + } + }); + } + }); + Ox.print(id, type, events) + if (!callback || (event && events[id][type][event].length == 0)) { + delete events[id][type][event]; + } + if (!event || Ox.length(events[id].normal) == 0) { + delete events[id][type]; + } + if (Ox.length(events[id]) == 0) { + delete events[id]; + } + } + } + function isKeyboardEvent(event) { // fixme: currently unused return event.substr(0, 4) == "key_"; } return { + _print: function() { + Ox.print(events); + }, + add: function(id, event, callback) { + // add keyboard event + addEvent(id, "keyboard", event, callback); + }, bind: function(id, event, callback) { - if (arguments.length == 2) { - callback = event; - event = id; - } - if (isKeyboardEvent(event)) { - keyboardEvents[id] = keyboardEvents[id] || {}; - keyboardEvents[id][event] = callback; - } - if (!isKeyboardEvent(event) || Ox.Focus.focused() == id) { - Ox.print("bind", id, event) - $eventHandler.bind(event, callback); - } + // bind event + addEvent(id, "normal", event, callback); }, bindKeyboard: function(id) { - $.each(keyboardEvents[id] || [], function(event, callback) { - Ox.Event.bind(id, event, callback); - //$eventHandler.bind(event, callback); + // bind all keyboard events + //Ox.print("binding", "id", id, "events", events[id], Ox.length(events[id]), "keyboardevents", events[id]["keyboard"]) + $.each(events[id], function(k, v) { + Ox.print("|" + k + "|"); + }) + events[id] && events[id].keyboard && $.each(events[id].keyboard, function(event, callbacks) { + $.each(callbacks, function(i, callback) { + Ox.print("bind", id, event); + $eventHandler.bind(event, callback); + }); }); }, - trigger: function(event, data) { - Ox.print("trigger", event, data || {}); - $eventHandler.trigger(event, data || {}); + changeId: function(oldId, newId) { + // fixme: would it be better to pass that.id instead of self.options.id? + // then this renaming wouldn't be necessary + Ox.print("changeId", oldId, newId, events[oldId]); + $.each($.extend({}, events[oldId] || {}), function(type, events_) { + var bind = type == "normal" ? "bind" : "add", + unbind = type == "normal" ? "unbind" : "remove"; + $.each(events_, function(event, callbacks) { + $.each(callbacks, function(i, callback) { + Ox.Event[unbind](oldId, event, callback); + Ox.Event[bind](newId, event, callback); + }); + }); + }); + }, + remove: function(id, event, callback) { + // remove keyboard event + // event and callback are optional + removeEvent(id, "keyboard", event, callback); }, unbind: function(id, event, callback) { - if (isKeyboardEvent(event)) { - $.each(keyboardEvents[id] || [], function(e, callback) { - if (e == event) { - delete keyboardEvents[id][e]; - return false; - } - }); - } - Ox.print("unbind", id, event) - $eventHandler.unbind(event, callback); + // unbind event + // event and callback are optional + removeEvent(id, "normal", event, callback); + }, + trigger: function(id, event, data) { + // trigger event + // data is optional + // keyboard handler will call this with "" as id + Ox.print("trigger", id, event, data || {}); + $eventHandler.trigger(event + (id ? "_" + id : ""), data || {}); }, unbindKeyboard: function(id) { - $.each(keyboardEvents[id] || [], function(event, callback) { - Ox.print("unbind", id, event) - $eventHandler.unbind(event, callback); + // unbind all keyboard events + Ox.print("unbinding", id /*events[id].keyboard*/) + events[id] && events[id].keyboard && $.each(events[id].keyboard, function(event, callbacks) { + $.each(callbacks, function(i, callback) { + Ox.print("unbind", event) + $eventHandler.unbind(event, callback); + }); }); } } }(); - Ox.Event_ = function() { // unused - var events = {}; - return { - // make these bind, trigger, unbind - publish: function(event, data) { - if (events[event]) { - $.each(events[event], function(i, v) { - setTimeout(function() { - v(data); - }, 0); - }); - } - }, - subscribe: function(event, callback) { - if (events[event]) { - events[event].push(callback); - } else { - events[event] = [callback]; - } - }, - unsubscribe: function(event, callback) { - $.each(events[event], function(i, v) { - if (Ox.startsWith(callback.toString(), v.toString())) { - events[event].splice(i, 1); - } - }); - } - }; - }(); - /* ---------------------------------------------------------------------------- Ox.Focus @@ -296,27 +310,27 @@ requires var stack = []; return { blur: function(id) { - if (stack.indexOf(id) == stack.length - 1) { - $elements[Ox.Focus.focused()].removeClass("OxFocus"); - $(".OxFocus").removeClass("OxFocus"); // fixme: the above is better, and should work - stack.splice(stack.length - 2, 0, stack.pop()); - Ox.Event.unbindKeyboard(id); - Ox.Event.bindKeyboard(stack[stack.length - 1]); + var index = stack.indexOf(id); + if (index == stack.length - 1) { + $elements[id].removeClass("OxFocus"); + //$(".OxFocus").removeClass("OxFocus"); // fixme: the above is better, and should work + stack.length == 1 ? stack.pop() : + stack.splice(stack.length - 2, 0, stack.pop()); + Ox.Event.unbindKeyboard($elements[id].options("id")); + stack.length && Ox.Event.bindKeyboard($elements[stack[stack.length - 1]].options("id")); Ox.print("blur", id, stack); } }, focus: function(id) { var index = stack.indexOf(id); - if (stack.length) { - Ox.Event.unbindKeyboard(stack[stack.length - 1]) + if (index == -1 || index < stack.length - 1) { + stack.length && Ox.Event.unbindKeyboard($elements[stack[stack.length - 1]].options("id")); + index > -1 && stack.splice(index, 1); + stack.push(id); + $elements[id].addClass("OxFocus"); + Ox.Event.bindKeyboard($elements[id].options("id")); + Ox.print("focus", id, stack); } - if (index > -1) { - stack.splice(index, 1); - } - stack.push(id); - $elements[Ox.Focus.focused()].addClass("OxFocus"); - Ox.Event.bindKeyboard(id); - Ox.print("focus", id, stack); }, focused: function() { return stack[stack.length - 1]; @@ -511,7 +525,7 @@ requires buffer += key == "SPACE" ? " " : key; bufferTime = time; } - Ox.Event.trigger("key_" + key); + Ox.Event.trigger("", "key_" + key); //return false; /* $.each(stack, function(i, v) { @@ -750,9 +764,11 @@ requires // 0, 1, 2, etc, so that append would append 0, and appendTo // would append (length - 1)? Ox.Container = function(options, self) { - var that = new Ox.Element(options, self) + var that = new Ox.Element("div", self) + .options(options || {}) .addClass("OxContainer"); - that.$content = new Ox.Element(options, self) + that.$content = new Ox.Element("div", self) + .options(options || {}) .addClass("OxContent") .appendTo(that); return that; @@ -768,8 +784,6 @@ requires Ox.Element = function() { - var elements = {}; // fixme: unused, we need this outside Element (for Focus) - return function(options, self) { // construct @@ -863,21 +877,37 @@ requires }; // public - that.bindEvent = function() { - // fixme: shouldn't this work the other way around, - // and bind a function to an event triggered by this widget? + + that.addEvent = function() { /* + adds a keyboard event, to be bound when focused + addEvent(event, fn) or addEvent({event0: fn0, event1: fn1, ...}) + */ + if (arguments.length == 1) { + $.each(arguments[0], function(event, fn) { + Ox.Event.add(self.options.id, event, fn); + }); + } else { + Ox.Event.add(self.options.id, arguments[0], arguments[1]); + } + return that; + }; + + that.bindEvent = function() { + /* + binds a function to an event triggered by this object bindEvent(event, fn) or bindEvent({event0: fn0, event1: fn1, ...}) */ if (arguments.length == 1) { $.each(arguments[0], function(event, fn) { - Ox.Event.bind(that.id, event, fn); + Ox.Event.bind(self.options.id, event, fn); }); } else { - Ox.Event.bind(that.id, arguments[0], arguments[1]); + Ox.Event.bind(self.options.id, arguments[0], arguments[1]); } return that; - }; + } + that.defaults = function(defaults) { /* that.defaults({foo: x}) sets self.defaults @@ -886,17 +916,21 @@ requires delete self.options; // fixme: hackish fix for that = Ox.Foo({...}, self).defaults({...}).options({...}) return that; }; + that.gainFocus = function() { Ox.Focus.focus(that.id); return that; }; + that.hasFocus = function() { return Ox.Focus.focused() == that.id; }; + that.loseFocus = function() { Ox.Focus.blur(that.id); return that; }; + that.options = function() { // fixme: use Ox.getset /* that.options() returns self.options @@ -908,7 +942,7 @@ requires returns that */ var length = arguments.length, - // args, options, ret; + id = self.options && self.options.id, args, ret; if (length == 0) { // options() @@ -927,6 +961,7 @@ requires // otherwise, extend options self.options = $.extend(self.options || self.defaults, args); $.each(args, function(key, value) { + key == "id" && id && Ox.Event.changeId(id, value); self.onChange(key, value); /* fixme: why does this not work? @@ -944,34 +979,54 @@ requires } return ret; }; + that.remove = function() { that.$element.remove(); delete $elements[that.ox]; return that; }; - that.triggerEvent = function() { + + that.removeEvent = function() { /* - triggerEvent(event, fn) or triggerEvent({event0: fn0, event1: fn1, ...}) + removes a keyboard event + removeEvent(event, fn) or removeEvent({event0: fn0, event1: fn1, ...}) */ - if (Ox.isObject(arguments[0])) { + if (arguments.length == 1) { $.each(arguments[0], function(event, fn) { - Ox.Event.trigger(event + "_" + self.options.id, fn); + Ox.Event.remove(self.options.id, event, fn); }); } else { - Ox.Event.trigger(arguments[0] + "_" + self.options.id, arguments[1] || {}); + Ox.Event.remove(self.options.id, arguments[0], arguments[1]); } return that; }; + + that.triggerEvent = function() { + /* + triggers an event + triggerEvent(event) or triggerEvent(event, data) or triggerEvent({event0: data, event1: data, ...}) + */ + if (Ox.isObject(arguments[0])) { + $.each(arguments[0], function(event, data) { + Ox.Event.trigger(self.options.id, event, data); + }); + } else { + Ox.Event.trigger(self.options.id, arguments[0], arguments[1] || {}); + } + return that; + }; + that.unbindEvent = function() { /* + unbinds an event unbindEvent(event, fn) or unbindEvent({event0: fn0, event1: fn1, ...}) */ if (arguments.length == 1) { $.each(arguments[0], function(event, fn) { - Ox.Event.unbind(that.id, event, fn); - }) + Ox.Event.unbind(self.options.id, event, fn); + }); } else { - Ox.Event.unbind(that.id, arguments[0], arguments[1]); + Ox.Event.unbind(self.options.id, arguments[0], arguments[1]); } return that; }; @@ -1326,8 +1381,8 @@ requires that.css(self.edges[2], self.options.size + "px"); self.options.elements[0].css(self.dimensions[1], self.options.size + "px"); self.options.elements[1].css(self.edges[2], (self.options.size + 1) + "px"); - Ox.Event.trigger("resize_" + self.ids[0], self.options.size); - Ox.Event.trigger("resize_" + self.ids[1], self.options.elements[1][self.dimensions[1]]()); + Ox.Event.trigger(self.ids[0], "resize", self.options.size); + Ox.Event.trigger(self.ids[1], "resize", self.options.elements[1][self.dimensions[1]]()); } function dragStart(e) { @@ -1460,7 +1515,7 @@ requires that.$buttons[i] = new Ox.Button({ disabled: button.disabled || false, size: "medium", - value: button.value // fixme: use title + title: button.title }) .addClass("OxLeft") .click(button.click) // fixme: rather use event? @@ -1476,7 +1531,7 @@ requires disabled: button.disabled || false, id: button.id, size: "medium", - value: button.value + title: button.title }) .addClass("OxRight") .click(button.click) // fixme: rather use event? @@ -1730,10 +1785,11 @@ requires 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) { + // fixme: use widget.bindEvent() + Ox.Event.bind(id, "validate", function(event, data) { validate(i, data.valid); }); - Ox.Event.bind("blur_" + id, function(event, data) { + Ox.Event.bind(id, "blur", function(event, data) { validate(i, data.valid); if (data.valid) { self.$messages[i].html("").hide(); @@ -1741,7 +1797,7 @@ requires self.$messages[i].html(data.message).show(); } }); - Ox.Event.bind("submit_" + id, function(event, data) { + Ox.Event.bind(id, "submit", function(event, data) { self.formIsValid && that.submit(); }); }); @@ -1816,92 +1872,116 @@ requires /* ---------------------------------------------------------------------------- - Ox.Button + Form Elements ---------------------------------------------------------------------------- */ Ox.Button = function(options, self) { /* events: - click non-selectable button was clicked - deselect selectable button was deselected - select selectable button was selected + 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: null, + group: false, id: "", + overlap: "none", selectable: false, selected: false, size: "medium", - style: "default", // can be default, symbol or tab + // fixme: "default" or ""? + style: "default", // can be default, checkbox, symbol, or tab + title: "", + tooltip: "", type: "text", - value: "", - values: [] // fixme: shouldn't this go into self.values? + width: "auto" }) - .options($.extend(options, { - value: $.isArray(options.value) ? - options.value[0] : options.value, - values: $.makeArray(options.value) - })) + .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.style != "default" ? " Ox" + Ox.toTitleCase(self.options.style) : "") + (self.options.disabled ? " OxDisabled": "") + - (self.options.selected ? " OxSelected": "")) + (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); - //Ox.print(self.options.value, self.options.disabled) - /* - that.bind("OxElement" + that.id + "SetOptions", function(e, data) { - if (typeof data.selected != "undefined") { - if (data.selected != that.hasClass("OxSelected")) { - that.toggleClass("OxSelected"); - } - } - if (typeof data.value != "undefined") { - if (self.options.type == "image") { - that.attr({ - src: oxui.path + "png/" + Ox.theme() + - "/button" + Ox.toTitleCase(options.value) + ".png" - }); - } else { - that.val(self.options.value); - } - } - }) - */ - function mousedown(e) { - if (self.options.type == "image" && $.browser.safari) { - // keep image from being draggable - e.preventDefault(); - } + + $.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"); - } else if (!self.options.group || !self.options.selected) { + that.triggerEvent("click", data); + } else { if (self.options.group) { - that.triggerEvent("select"); + that.triggerEvent("select", data); } else { that.toggleSelected(); } } - if (self.options.values.length == 2) { - that.options({ - value: self.options.value == self.options.values[0] ? - self.options.values[1] : self.options.values[0] - }); + if (self.titles.length == 2) { + that.toggleTitle(); } - //self.options.click(); } + + 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) { - //Ox.print("setOption", option, value) if (key == "disabled") { that.attr({ disabled: value ? "disabled" : "" @@ -1912,493 +1992,2882 @@ requires that.toggleClass("OxSelected"); } that.triggerEvent("change"); - } else if (key == "value") { - if (self.options.type == "image") { - that.attr({ - src: oxui.path + "png/ox.ui." + Ox.theme() + - "/button" + Ox.toTitleCase(value) + ".png" - }); - } else { - that.val(value); - } + } 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.options("value", self.options.value); - return that; - }; - /* - ---------------------------------------------------------------------------- - Ox.ButtonGroup - ---------------------------------------------------------------------------- - */ + that.toggleTitle = function() { + self.selectedTitle = 1 - self.selectedTitle; + setTitle(self.titles[self.selectedTitle].title); + 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 + change {id, value} selection within a group changed */ var self = self || {}, that = new Ox.Element({}, self) .defaults({ buttons: [], - group: false, + max: 1, + min: 1, selectable: false, - selected: -1, size: "medium", style: "", type: "text", }) .options(options || {}) .addClass("OxButtonGroup"); - self.position = {}; - that.$buttons = []; - $.each(self.options.buttons, function(position, button) { - that.$buttons[position] = Ox.Button({ - disabled: button.disabled, - group: self.options.group ? that : null, - id: button.id, - selectable: self.options.selectable, - selected: position == self.options.selected, - size: self.options.size, - style: self.options.style, - type: self.options.type, - value: button.value - }).appendTo(that); - self.position[that.$buttons.id] = position; - that.bindEvent("select_" + that.$buttons[position].options("id"), function() { - selectButton(position); - }); - }); - - function onChange(event, data) { - console.log("event", event, "data", data) - var id = event.split("_")[1]; - if (self.options.selected > -1) { - that.$buttons[self.options.selected].toggleSelected(); - } - self.options.selected = self.position[id]; - that.triggerEvent("change", { - value: id - }); + 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(); } - function selectButton(position) { - that.$buttons[self.options.selected].toggleSelected(); - self.options.selected = position; - that.$buttons[self.options.selected].toggleSelected(); - }; + 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.Input - ---------------------------------------------------------------------------- - */ + Ox.Checkbox = function(options, self) { - Ox.Input = function(options, self) { - - /* 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 - * label string or array [{id, title}] (select) -- label and placeholder are mutually exclusive - * labelWidth integer (px) - * placeholder string or array [{id, title}] (select) -- label and placeholder are mutually exclusive - * 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 - */ + /*** + 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({ - autocomplete: null, - autocorrect: null, - clear: false, - highlight: false, + disabled: false, id: "", - label: "", - labelWidth: 64, - placeholder: "", - selected: 0, - size: "medium", - type: "text", - validate: null, - value: "" + group: false, + checked: false, + title: "", + width: "auto" }) .options(options || {}) - .addClass("OxInput Ox" + Ox.toTitleCase(self.options.size)), - autocomplete; // fixme: should be self.autocomplete + .addClass("OxCheckbox") + .attr(self.options.disabled ? { + disabled: "disabled" + } : {}); - if (self.options.label) { - self.options.label = Ox.makeArray(self.options.label); - self.option = self.options.label[self.options.selected]; // fixme: id or title? or not use this at all? - self.items = self.options.label; - } else if (self.options.placeholder) { - self.options.placeholder = Ox.makeArray(self.options.placeholder); - self.option = self.options.placeholder[self.options.selected]; - self.items = self.options.placeholder; - } - if (Ox.isArray(self.options.autocomplete)) { - autocomplete = self.options.autocomplete; - self.options.autocomplete = {}; - self.options.autocomplete[self.placeholder] = autocomplete; - } - - if (self.options.label) { - that.$label = new Ox.Element() - .addClass("OxInputLabel") - .width(self.options.labelWidth) - .html(self.options.label.length == 1 ? self.options.label[0] : self.options.label[0].title) - .appendTo(that); - } - if (self.options.label.length > 1 || self.options.placeholder.length > 1) { - that.$label && that.$label.click(select); - that.$select = new Ox.Button({ - style: "symbol", - type: "image", - value: "select" - // value: oxui.symbols.select + 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 }) - .click(select) + .css({ + float: "right" + }) + .click(clickTitle) .appendTo(that); - self.selectId = Ox.toCamelCase( - self.options.id + "_" + (self.options.label.length > 1 ? "label" : "placeholder") - ); - self.selectMenu = new Ox.Menu({ - element: that, - id: self.selectId, - items: $.map(self.items, function(item, position) { - return { - checked: position == self.options.selected, - id: item.id, - group: self.selectId, // fixme: same id, works here, but should be different - title: item.title - }; - }), - offset: { - left: 4, - top: 0 - } - }); - that.bindEvent("change_" + self.selectId, change); } - that.$input = new Ox.Element( - self.options.type == "textarea" ? "textarea" : "input", self - ) - .attr({ - type: self.options.type == "textarea" ? undefined : self.options.type // fixme: make conditional? + 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( - "OxInput Ox" + Ox.toTitleCase(self.options.size) + - " OxPlaceholder" - ) - .focus(focus) - .blur(blur) - .change(change) + .addClass("OxCheckbox") + .click(clickButton) .appendTo(that); - self.options.placeholder && that.$input.val(self.option.title); + 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 + autovalidate string ("email", "float", "integer", "phone", "url"), or + regexp(value), or + function(key, value, blur, callback), returns value + 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) { - that.$clear = new Ox.Button({ - style: "symbol", - type: "image", - value: "clear" - //value: oxui.symbols.clear + self.$button = new Ox.Button({ + overlap: "left", + title: "clear", + type: "image" + }) + .css({ + float: "right" // fixme: use css rule }) .click(clear) .appendTo(that); } - if (self.options.autocomplete) { - that.$input.attr({ - autocomplete: "off" - }); - self.autocompleteId = self.options.id + "_menu"; // fixme: we do this in other places ... are we doing it the same way? var name? - self.autocompleteMenu = new Ox.Menu({ - element: that.$input, - id: self.autocompleteId, - offset: { - left: 4, - top: 0 - }, - size: self.options.size - }); - that.bindEvent("click_" + self.autocompleteId, onClick); + 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.type != "textarea") { - that.bindEvent({ - key_enter: submit, - }); + 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 + }); + } + } /* - if (self.options.validate) { - self.valid = self.options.validate(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(""); + } + */ - that.bindEvent({ - key_escape: cancel - }); - - function autocomplete(value) { - var value = value.toLowerCase(), - items = []; - if (value === "") { - // items = self.options.autocomplete[self.placeholder]; - } else { - $.each(self.options.autocomplete[self.option], function(i, item) { - if (item.toLowerCase().indexOf(value) > -1) { - items.push(item); - } - }); - } - autocompleteCallback(items); - } - - 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, autocompleteCallback) : - self.options.autocomplete(value, autocompleteCallback) - ) : autocomplete(value); - } else { - autocompleteCallback(); - } - } - } - - function autocompleteCallback(items) { - Ox.print("autocomplete callback", items) - var items = items || [], - selected = items.length == 1 ? 0 : -1, - value = that.$input.val().toLowerCase(); - if (items.length) { - items = $.map(items, function(title, position) { - if (value == Ox.stripTags(title.toLowerCase())) { - selected = position; - } - return { - id: title.toLowerCase(), // fixme: need function to do lowercase, underscores etc? - title: self.options.highlight ? title.replace( - new RegExp("(" + value + ")", "ig"), - "$1" - ) : title - }; - }); - self.selectMenu.hideMenu(); - self.autocompleteMenu.options({ - items: items, - selected: selected - }).showMenu(); - } else { - self.autocompleteMenu.hideMenu(); - } - } - - 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() { + Ox.print("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.title); - } - if (self.options.autocomplete || self.options.autocorrect || self.options.validate) { // fixme: duplicated, make a var + //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() { - that.$input.blur(); + self.$input.blur(); } - function change(event, data) { - Ox.print("input change", event, data) - if (data) { - self.option = { - id: data.id, - title: data.value // fixme: should be data.title - }; - } - if (self.options.label) { - that.$label.html(self.option.title); - that.$input.focus(); - autocompleteCall(); - } else { - if (that.$input.is(".OxPlaceholder")) { - that.$input.val(self.option.title); - //that.$input.focus(); - } else { - that.$input.focus(); - autocompleteCall(); - } - } - } - - function clear() { - that.$input.val("").focus(); - //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() { - var value; - that.gainFocus(); - that.addClass("OxFocus"); - if (that.$input.is(".OxPlaceholder")) { - that.$input.val("").removeClass("OxPlaceholder"); - } - value = that.$input.val(); - 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); - if (self.options.autocomplete) { - value !== "" && Ox.isFunction(self.options.autocomplete) ? - self.options.autocomplete(self.option.id, value, autocompleteCallback) : - autocomplete(value); - } - } - } - - function keypress(event) { - if (event.keyCode != 13) { - setTimeout(function() { - var value = that.$input.val(); - if (value != self.value) { - self.value = value; - self.options.autocomplete && autocompleteCall(); - self.options.autocorrect && autocorrect(); - self.options.validate && validateCall(); - } - }, 25); - } - } - - function onClick(event, data) { - Ox.print("onClick", data); - that.$input.val(Ox.stripTags(data.title)); - self.autocompleteMenu.hideMenu(); - submit(); - } - - function select() { - self.selectMenu.showMenu(); - } - - function selection() { - // fixme: not used! - var start, end; - if (arguments.length == 0) { - return [self.element.selectionStart, self.element.selectionEnd]; - } else { - start = arguments[0]; - end = arguments[1] || start; - self.element.setSelectionRange(start, end); - } - } - - function submit() { - Ox.print("input submit", that.$input.val()) - that.$input.trigger("blur"); - that.triggerEvent("submit", self.option ? { - key: self.option.id, - value: that.$input.val() - } : 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 change() { + self.options.value = self.$input.val(); + that.triggerEvent("change", { + value: self.options.value }); } - 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); + 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 { - validateCallback({ - valid: false, - message: "" - }); + 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 validateCallback(data) { - if (data.valid != self.valid) { - self.message = data.message; - self.valid = data.valid; - that.triggerEvent("validate", data); + function setWidth() { + + } + + function submit() { + self.$input.blur(); + that.triggerEvent("submit", { + value: self.options.value + }); + } + + self.onChange = function(key, value) { + var inputWidth, val; + 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(); } } - that.changeLabel = function(id) { - that.$label.html(Ox.getObjectById(self.options.label, id).title); - self.selectMenu.checkItem(id); + 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; }; - that.height = function(value) { + 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 < length - 1 ? Ox.range(length - 1, pos, -1) : [] + )) + $.each($.merge( + pos > 0 ? Ox.range(pos - 1, -1, -1) : [], + pos < length - 1 ? Ox.range(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" + }); + + $.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(clickButton) + .appendTo(that.$element); + } + + self.$button = new Ox.Button({ + id: self.buttonId, + style: "symbol", + title: "select", + type: "image" + }) + .bindEvent("click", clickButton) + .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("hide", hideMenu); + + if (self.options.selectable) { + // fixme: the menu should fire a change event + // so we can attach a bindEvent() directly + // (may fire one already, but needs to pass group id) + Ox.Event.bind(self.groupId, "change", clickMenu); + } + + function clickButton() { + that.addClass("OxSelected"); + self.$menu.showMenu(); + } + + function clickMenu(event, data) { + Ox.print("clickMenu: ", self.options.id, data) + self.$title && self.$title.html( + self.options.title ? self.options.title : + data.checked[0].title + ); + that.triggerEvent("change", data); + } + + function hideMenu() { + that.removeClass("OxSelected"); + self.$button.removeClass("OxSelected"); + } + + self.onChange = function(key, value) { + + }; + + that.selectItem = function(id) { + self.options.type == "text" && self.$title.html( + Ox.getObjectById(self.options.items, id).title + ); + self.$menu.checkItem(id); + }; + + /* + 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 @@ -2418,65 +4887,370 @@ requires 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)))" }); } - return that; } - that.width = function(value) { + 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 : 2) - - (self.options.label ? self.options.labelWidth + 25 : 0) - - (self.options.placeholder.length > 1 ? 16 : 0) - - (self.options.clear ? 25 : 0)); - // fixme: the values above are all weird guesswork - return that; + 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.Label - ---------------------------------------------------------------------------- - */ - - Ox.Label = function(options, self) { - var self = self || {}, - that = new Ox.Element({}, self) - .defaults({ - id: "", - title: "" - }) - .options(options) - .addClass("OxLabel"); - that.html(self.options.title); - return that; - }; - - /* - ---------------------------------------------------------------------------- - Ox.Range - - options: - animate boolean if true, animate thumb - 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 - 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 - ---------------------------------------------------------------------------- - */ - - Ox.Range = function(options, self) { + Ox.Range_ = function(options, self) { /* init @@ -2497,14 +5271,18 @@ requires thumbValue: false, trackImages: [], trackStep: 0, - value: 0 + value: 0, + valueNames: null }) .options($.extend(options, { arrowStep: options.arrowStep ? options.arrowStep : options.step, trackImages: $.makeArray(options.trackImages || []) })) - .addClass("OxRange"); + .addClass("OxRange") + .css({ + width: self.options.size + "px" + }); // fixme: self. ... ? var trackImages = self.options.trackImages.length, @@ -2532,32 +5310,35 @@ requires var $track = new Ox.Element() .addClass("OxTrack") .mousedown(clickTrack) - .appendTo(that.$element); // fixme: make that work + .appendTo(that.$element); if (trackImages) { var width = parseFloat(screen.width / trackImages), - $image = $("") + $image = $("") .attr({ width: width * trackImages, height: 14 }) .addClass("OxImage") - .appendTo($track.$element), // fixme: make that work + .appendTo($track.$element), c = $image[0].getContext('2d'); c.mozImageSmoothingEnabled = false; // we may want to remove this later $.each(self.options.trackImages, function(i, v) { + var left = 0; $("") .attr({ src: v }) .load(function() { - c.drawImage(this, i * width, 0, width, 14); + c.drawImage(this, left, 0, self.trackImageWidth[i], 14); }); + left += self.trackImageWidth[i]; }); } var $thumb = Ox.Button({}) .addClass("OxThumb") .appendTo($track); + Ox.print("----") if (self.options.arrows) { var $arrowInc = Ox.Button({ style: "symbol", @@ -2585,7 +5366,7 @@ requires setValue(self.options.value + self.options.arrowStep, 200); } function clickTrack(e) { - Ox.Focus.focus(); + //Ox.Focus.focus(); var left = $track.offset().left, offset = $(e.target).hasClass("OxThumb") ? e.clientX - $thumb.offset().left - thumbWidth / 2 - 2 : 0; @@ -2602,7 +5383,7 @@ requires } function getPx(val) { var pxPerVal = (trackWidth - thumbWidth - 2) / - (self.options.max - self.options.min); + (self.options.max - self.options.min); return Math.ceil((val - self.options.min) * pxPerVal + 1); } function getVal(px) { @@ -2610,8 +5391,8 @@ requires valPerPx = (self.options.max - self.options.min) / (trackWidth - thumbWidth); return Ox.limit(self.options.min + - Math.floor(px * valPerPx / self.options.step) * self.options.step, - self.options.min, self.options.max); + Math.floor(px * valPerPx / self.options.step) * self.options.step, + self.options.min, self.options.max); } function mousedownArrow() { that.addClass("OxActive"); @@ -2624,7 +5405,9 @@ requires }, self.options.animate ? animate : 0, function() { if (self.options.thumbValue) { $thumb.options({ - value: self.options.value + value: self.options.valueNames ? + self.options.valueNames[self.options.value] : + self.options.value }); } }); @@ -2664,7 +5447,7 @@ requires shared functions */ - self.onChange = function(option, value) { + self.onChange = function(key, value) { } @@ -2672,102 +5455,6 @@ requires }; - /* - ---------------------------------------------------------------------------- - Ox.Select - ---------------------------------------------------------------------------- - */ - - 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: [], - size: "medium" - }) - .options(options) - .addClass("OxSelect Ox" + Ox.toTitleCase(self.options.size)); - self.buttonId = self.options.id + "_button"; - self.groupId = self.options.id; // + "_group" - self.menuId = self.options.id + "_menu", - - $.each(self.options.items, function(i, item) { - self.options.items[i] = $.extend(self.options.items[i], { - checked: item.checked || false, - group: self.groupId - }); - if (item.checked) { - self.selected = i; - } - }); - - Ox.print(self.options.id) - that.$button = new Ox.Button($.extend(self.options, { - id: self.buttonId, - type: "text", // fixme: this shouldn't be necessary - value: self.options.items[self.selected].title // fixme: title instead of value? - }), {}) - .click(clickButton) - .appendTo(that); - - that.$symbol = new Ox.Button({ - style: "symbol", - type: "image", - value: "select" - }) - .click(clickButton) - .appendTo(that); - - that.$menu = new Ox.Menu({ - element: that.$button, - id: self.menuId, - items: self.options.items, - offset: { - left: 8, - top: 0 - }, - side: "bottom", - size: self.options.size - }); - - that.bindEvent("change_" + self.groupId, clickMenu); - - function clickButton() { - that.$menu.toggleMenu(); - } - - function clickMenu(event, data) { - that.$button.options({ - value: data.value - }); - that.triggerEvent("change", data.value); - } - - self.onChange = function(key, value) { - - }; - - that.selectItem = function(id) { - that.$button.options({ - value: Ox.getObjectById(self.options.items, id).title - }); - that.$menu.checkItem(id); - }; - - that.width = function(val) { - // fixme: silly hack, and won't work for css() - that.$element.width(val + 16); - that.$button.width(val); - //that.$symbol.width(val); - return that; - }; - - return that; - - } - /* ============================================================================ Lists @@ -2993,7 +5680,7 @@ requires updateQuery(); Ox.print("s.o", self.options) - that.bindEvent(self.keyboardEvents); + that.addEvent(self.keyboardEvents); function addAllToSelection(pos) { var arr, @@ -3632,9 +6319,8 @@ requires ); resizeColumn(v.id, width); }); - $window.mouseup(function() { + $window.one("mouseup", function() { $window.unbind("mousemove"); - $window.unbind("mouseup"); }); }) .dblclick(function() { @@ -3755,10 +6441,9 @@ requires moveColumn(id, pos); } }); - $window.mouseup(function() { + $window.one("mouseup", function() { dropColumn(id, pos); $window.unbind("mousemove"); - $window.unbind("mouseup"); }); } @@ -3951,6 +6636,7 @@ requires if (location) { self.marker = location.marker.add(); self.polygon = location.polygon.add(); + that.triggerEvent("select", location) } }) } @@ -4058,6 +6744,7 @@ requires $.each(self.locations, function(i, location) { location.marker.add(); }); + resize(); }; function resize() { @@ -4065,7 +6752,9 @@ requires } function zoom() { - + that.triggerEvent("zoom", { + value: self.map.getZoom() + }); } function Location(geocode) { @@ -4235,6 +6924,22 @@ requires } }; + that.find = function(name, callback) { + getLocationByName(name, function(location) { + if (location) { + self.polygon && self.polygon.remove(); + self.polygon = location.polygon.add(); + self.bounds = location.rectangle.bounds; + self.map.fitBounds(self.bounds); + } + callback(location); + }); + }; + + that.zoom = function(value) { + self.map.setZoom(value); + }; + return that; }; @@ -4345,11 +7050,11 @@ requires .data("position", position) .appendTo(that.$element); that.menus[position] = new Ox.Menu($.extend(menu, { - element: that.titles[position], - mainmenu: that, - size: self.options.size - })); - that.bindEvent("hide_" + that.menus[position].options("id"), onHideMenu); + element: that.titles[position], + mainmenu: that, + size: self.options.size + })) + .bindEvent("hide", onHideMenu); }); if (self.options.extras.length) { @@ -4495,25 +7200,25 @@ requires /* options: - element the element the menu is attached to - id the menu id - items array of menu items - mainmenu the main menu this menu is part of, if any - offset offset of the menu, in px - parent the supermenu, if any - selected the position of the selected item - side open to "bottom" or "right" - size "large", "medium" or "small" + element the element the menu is attached to + id the menu id + items array of menu items + mainmenu the main menu this menu is part of, if any + offset offset of the menu, in px + parent the supermenu, if any + selected the position of the selected item + side open to "bottom" or "right" + size "large", "medium" or "small" methods: events: - change_groupId {id, value} checked item of a group has changed - click_itemId item not belonging to a group was clicked - click_menuId {id, value} item not belonging to a group was clicked - deselect_menuId {id, value} item was deselected not needed, not implemented - hide_menuId menu was hidden - select_menuId {id, value} item was selected not needed, not implemented + change_groupId {id, value} checked item of a group has changed + click_itemId item not belonging to a group was clicked + click_menuId {id, value} item not belonging to a group was clicked + deselect_menuId {id, value} item was deselected not needed, not implemented + hide_menuId menu was hidden + select_menuId {id, value} item was selected */ @@ -4533,7 +7238,7 @@ requires side: "bottom", size: "medium", }) - .options(options) + .options(options || {}) .addClass( "OxMenu Ox" + Ox.toTitleCase(self.options.side) + " Ox" + Ox.toTitleCase(self.options.size) @@ -4541,21 +7246,20 @@ requires .click(click) .mouseenter(mouseenter) .mouseleave(mouseleave) - .mousemove(mousemove), + .mousemove(mousemove) + .addEvent({ + key_up: selectPreviousItem, + key_down: selectNextItem, + key_left: selectSupermenu, + key_right: selectSubmenu, + key_escape: hideMenu, + key_enter: clickSelectedItem + }), itemHeight = self.options.size == "small" ? 12 : (self.options.size == "medium" ? 16 : 20), // menuHeight, scrollSpeed = 1, $item; // fixme: used? - // fixme: attach all private vars to self? - - self.keyboardEvents = { - key_up: selectPreviousItem, - key_down: selectNextItem, - key_left: selectSupermenu, - key_right: selectSubmenu, - key_escape: hideMenu, - key_enter: clickSelectedItem - }; + // fixme: attach all private vars to self // construct that.items = []; @@ -4579,14 +7283,21 @@ requires .addClass("OxBottom") .appendTo(that.$element); that.$layer = $("
") - .addClass(self.options.mainmenu ? "OxMainMenuLayer" : "OxLayer"); + .addClass(self.options.mainmenu ? "OxMainMenuLayer" : "OxLayer") + .click(click); function click(event) { var item, position, - $target = $(event.target); + $target = $(event.target), + $parent = $target.parent(); + // necessary for highlight + if ($parent.is(".OxCell")) { + $target = $parent; + $parent = $target.parent(); + } if ($target.is(".OxCell")) { - position = $target.parent().data("position"); + position = $parent.data("position"); item = that.items[position]; if (!item.options("disabled")) { clickItem(position); @@ -4599,26 +7310,44 @@ requires } function clickItem(position) { - var item = that.items[position]; + var item = that.items[position], + toggled; if (!item.options("items").length) { if (that.options("parent")) { - Ox.print("t.o.p", that.options("parent")) that.options("parent").hideMenu().triggerEvent("click"); } - if (item.options("checked") !== null && (!item.options("group") || !item.options("checked"))) { - item.options({ - checked: !item.options("checked") - }); - Ox.Event.trigger("change_" + item.options("group"), { - id: item.options("id"), - value: item.options("title")[0] // fixme: value or title? - }); + if (item.options("checked") !== null) { + if (item.options("group")) { + Ox.print("has group", item.options("group")) + toggled = self.optionGroups[item.options("group")].toggle(position); + Ox.print("toggled", toggled) + if (toggled.length) { + $.each(toggled, function(i, pos) { + that.items[pos].toggleChecked(); + }); + Ox.Event.trigger(item.options("group"), "change", { + checked: $.map(self.optionGroups[item.options("group")].checked(), function(v, i) { + return { + id: that.items[v].options("id"), + title: Ox.stripTags(that.items[v].options("title")[0]) + } + }) + }); + } + } else { + item.toggleChecked(); + item.triggerEvent("change", { + checked: item.options("checked"), + id: item.options("id"), + title: Ox.stripTags(item.options("title")[0]) + }); + } } else { - Ox.Event.trigger("click_" + self.options.id, { + that.triggerEvent("click", { id: item.options("id"), - title: item.options("title")[0] + title: Ox.stripTags(item.options("title")[0]) }); - Ox.Event.trigger("click_" + item.options("id")); + item.triggerEvent("click"); } if (item.options("title").length == 2) { item.toggleTitle(); @@ -4637,9 +7366,28 @@ requires } function constructItems(items) { - that.items = []; + that.$content.empty(); scrollMenuUp(); + + self.optionGroups = {}; + $.each(items, function(i, item) { + if (item.group) { + items[i] = $.map(item.items, function(v, i) { + return $.extend(v, { + group: item.group + }); + }); + self.optionGroups[item.group] = new Ox.OptionGroup( + items[i], + "min" in item ? item.min : 1, + "max" in item ? item.max : 1 + ); + } + }); + items = Ox.flatten(items); + + that.items = []; $.each(items, function(i, item) { var position; if (item.id) { @@ -4668,10 +7416,12 @@ requires that.$content.append(constructSpace()); } }); + if (!that.is(":hidden")) { that.hideMenu(); that.showMenu(); } + } function constructLine() { @@ -4777,8 +7527,13 @@ requires var item, position, $target = $(event.target); + $parent = $target.parent(); + if ($parent.is(".OxCell")) { + $target = $parent; + $parent = $target.parent(); + } if ($target.is(".OxCell")) { - position = $target.parent().data("position"); + position = $parent.data("position"); item = that.items[position]; if (!item.options("disabled") && position != self.options.selected) { selectItem(position); @@ -4828,8 +7583,13 @@ requires function selectItem(position) { var item; if (self.options.selected > -1) { + //Ox.print("s.o.s", self.options.selected, that.items) item = that.items[self.options.selected] item.removeClass("OxSelected"); + that.triggerEvent("deselect", { + id: item.options("id"), + title: Ox.stripTags(item.options("title")[0]) + }); } if (position > -1) { item = that.items[position]; @@ -4841,6 +7601,10 @@ requires }); item.options("items").length && that.submenus[item.options("id")].showMenu(); // fixme: do we want to switch to this style? item.addClass("OxSelected"); + that.triggerEvent("select", { + id: item.options("id"), + title: Ox.stripTags(item.options("title")[0]) + }); } self.options.selected = position; } @@ -5011,12 +7775,11 @@ requires } that.hide() .loseFocus() - .unbindEvent(self.keyboardEvents) + //.removeEvent(self.keyboardEvents) .triggerEvent("hide"); that.$layer.hide(); - $document.unbind("click", click); + //$document.unbind("click", click); return that; - //that.triggerEvent("hide"); }; that.removeItem = function() { @@ -5034,7 +7797,7 @@ requires if (!self.options.parent && !that.$layer.parent().length) { that.$layer.appendTo($body); } - that.parent().length || that.appendTo($body); + that.parent().length == 0 && that.appendTo($body); that.css({ left: "-1000px", top: "-1000px", @@ -5048,7 +7811,7 @@ requires menuMaxHeight = Math.floor($window.height() - top - 16); if (self.options.parent) { if (menuHeight > menuMaxHeight) { - top = Ox.limit(top - menuHeight + menuMaxHeight, self.options.parent.offset().top, top); + top = Ox.limit(top - menuHeight + menuMaxHeight, self.options.parent.offset().top, top); menuMaxHeight = Math.floor($window.height() - top - 16); } } @@ -5062,11 +7825,12 @@ requires } else { that.$container.height(menuHeight); } - !self.options.parent && that.gainFocus(); - that.bindEvent(self.keyboardEvents); - setTimeout(function() { - $document.bind("click", click); - }, 100); + if (self.options.parent) { + Ox.Event.bindKeyboard(self.options.id); + } else { + that.gainFocus(); + } + that.$layer.show(); return that; //that.triggerEvent("show"); }; @@ -5084,7 +7848,7 @@ requires var self = self || {}, that = new Ox.Element("tr", self) .defaults({ - bind: [], + bind: [], // fixme: what's this? checked: null, disabled: false, group: "", @@ -5106,6 +7870,10 @@ requires }) .data("group", self.options.group); // fixme: why? + if (self.options.group && self.options.checked === null) { + self.options.checked = false; + } + // construct that.append( that.$status = $("", { @@ -5157,20 +7925,6 @@ requires self.onChange = function(key, value) { if (key == "checked") { - if (value && self.options.group) { - $.each(self.options.menu.items, function(i, item) { - if ( - item.options("id") != self.options.id && - item.options("group") == self.options.group && - item.options("checked") - ) { - item.options({ - checked: false - }); - return false; - } - }); - } that.$status.html(value ? oxui.symbols.check : "") } else if (key == "disabled") { that.toggleClass("OxDisabled"); // fixme: this will only work if onChange is only invoked on actual change @@ -5185,7 +7939,9 @@ requires }; that.toggleChecked = function() { - + that.options({ + checked: !self.options.checked + }); }; that.toggleDisabled = function() { @@ -5356,8 +8112,8 @@ requires $.each(self.options.elements, function(i, v) { //that.append(element) - Ox.print("V: ", v) - that.$elements[i].appendTo(that); // fixme: that.$content + //Ox.print("V: ", v, that.$elements[i]) + that.$elements[i].appendTo(that.$element); // fixme: that.$content if (v.collapsible || v.resizable) { Ox.print("v.size", v.size) var $resizebar = new Ox.Resizebar({ @@ -5449,7 +8205,7 @@ requires animate[self.edges[pos == 0 ? 0 : 1]] = size; self.options.parent.animate(animate, 200, function() { var i = (self.options.edge == "left" || self.options.edge == "top") ? 1 : 0; - Ox.Event.trigger("resize_" + id, self.options.elements[i][self.dimensions[1]]()); + that.triggerEvent("resize", self.options.elements[i][self.dimensions[1]]()); self.options.elements[pos].collapsed = !self.options.elements[pos].collapsed; }); }; @@ -5512,6 +8268,7 @@ requires self.deg = (self.deg + 30) % 360; update(); }, 83); + return that; }; that.stop = function() { that.animate({ @@ -5520,6 +8277,7 @@ requires !self.isRunning && clear(); self.isRunning = false; }); + return that; } return that; } @@ -5545,16 +8303,16 @@ requires Ox.Tooltip = function(options, self) { var self = self || {}, - that = new Ox.Element() + that = new Ox.Element("div", self) .defaults({ - text: "" + title: "" }) .options(options || {}) .addClass("OxTooltip") - .html(self.options.text); + .html(self.options.title); self.onChange = function(key, value) { - if (key == "text") { + if (key == "title") { that.html(value); } }; @@ -5562,26 +8320,27 @@ requires that.hide = function() { that.animate({ opacity: 0 - }, 250, function() { + }, 0, function() { that.remove(); }); return that; }; - that.show = function(e) { + that.show = function(x, y) { var left, top, width, height; + $(".OxTooltip").remove(); // fixme: don't use dom that.appendTo($body); width = that.width(); height = that.height(); - left = Ox.limit(e.clientX - width / 2, 0, $document.width() - width); - top = e.clientY > $document.height() - height - 16 ? e.clientY - 32 : e.clientY + 16; + left = Ox.limit(x - width / 2, 0, $document.width() - width); + top = y > $document.height() - height - 16 ? y - 32 : y + 16; that.css({ left: left + "px", top: top + "px" }) .animate({ opacity: 1 - }, 250); + }, 0); return that; }; diff --git a/build/png/ox.ui.classic/buttonPlay.png b/build/png/ox.ui.classic/buttonPlay.png deleted file mode 100644 index 3fd3d955..00000000 Binary files a/build/png/ox.ui.classic/buttonPlay.png and /dev/null differ diff --git a/build/png/ox.ui.modern/buttonCollapse.png b/build/png/ox.ui.classic/symbolAbove.png similarity index 94% rename from build/png/ox.ui.modern/buttonCollapse.png rename to build/png/ox.ui.classic/symbolAbove.png index 4f1180f5..eee8090b 100644 Binary files a/build/png/ox.ui.modern/buttonCollapse.png and b/build/png/ox.ui.classic/symbolAbove.png differ diff --git a/build/png/ox.ui.classic/symbolAdd.png b/build/png/ox.ui.classic/symbolAdd.png new file mode 100644 index 00000000..1e782ded Binary files /dev/null and b/build/png/ox.ui.classic/symbolAdd.png differ diff --git a/build/png/ox.ui.classic/symbolBelow.png b/build/png/ox.ui.classic/symbolBelow.png new file mode 100644 index 00000000..97c0f23b Binary files /dev/null and b/build/png/ox.ui.classic/symbolBelow.png differ diff --git a/build/png/ox.ui.classic/symbolCheck.png b/build/png/ox.ui.classic/symbolCheck.png new file mode 100644 index 00000000..64470d4e Binary files /dev/null and b/build/png/ox.ui.classic/symbolCheck.png differ diff --git a/build/png/ox.ui.classic/buttonClear.png b/build/png/ox.ui.classic/symbolClear.png similarity index 100% rename from build/png/ox.ui.classic/buttonClear.png rename to build/png/ox.ui.classic/symbolClear.png diff --git a/build/png/ox.ui.classic/symbolClose.png b/build/png/ox.ui.classic/symbolClose.png new file mode 100644 index 00000000..58af4ea2 Binary files /dev/null and b/build/png/ox.ui.classic/symbolClose.png differ diff --git a/build/png/ox.ui.classic/symbolCollapse.png b/build/png/ox.ui.classic/symbolCollapse.png new file mode 100644 index 00000000..392b5f1d Binary files /dev/null and b/build/png/ox.ui.classic/symbolCollapse.png differ diff --git a/build/png/ox.ui.classic/symbolDate.png b/build/png/ox.ui.classic/symbolDate.png new file mode 100644 index 00000000..16344d28 Binary files /dev/null and b/build/png/ox.ui.classic/symbolDate.png differ diff --git a/build/png/ox.ui.classic/symbolEdit.png b/build/png/ox.ui.classic/symbolEdit.png new file mode 100644 index 00000000..f2071a83 Binary files /dev/null and b/build/png/ox.ui.classic/symbolEdit.png differ diff --git a/build/png/ox.ui.classic/buttonExpand.png b/build/png/ox.ui.classic/symbolExpand.png similarity index 94% rename from build/png/ox.ui.classic/buttonExpand.png rename to build/png/ox.ui.classic/symbolExpand.png index 60b75dfd..5f2b79e7 100644 Binary files a/build/png/ox.ui.classic/buttonExpand.png and b/build/png/ox.ui.classic/symbolExpand.png differ diff --git a/build/png/ox.ui.classic/symbolFind.png b/build/png/ox.ui.classic/symbolFind.png new file mode 100644 index 00000000..6393086c Binary files /dev/null and b/build/png/ox.ui.classic/symbolFind.png differ diff --git a/build/png/ox.ui.classic/symbolHelp.png b/build/png/ox.ui.classic/symbolHelp.png new file mode 100644 index 00000000..1eeca48a Binary files /dev/null and b/build/png/ox.ui.classic/symbolHelp.png differ diff --git a/build/png/ox.ui.classic/symbolInfo.png b/build/png/ox.ui.classic/symbolInfo.png new file mode 100644 index 00000000..a49814e8 Binary files /dev/null and b/build/png/ox.ui.classic/symbolInfo.png differ diff --git a/build/png/ox.ui.classic/symbolLocation.png b/build/png/ox.ui.classic/symbolLocation.png new file mode 100644 index 00000000..85a3dc51 Binary files /dev/null and b/build/png/ox.ui.classic/symbolLocation.png differ diff --git a/build/png/ox.ui.classic/buttonCollapse.png b/build/png/ox.ui.classic/symbolLock.png similarity index 94% rename from build/png/ox.ui.classic/buttonCollapse.png rename to build/png/ox.ui.classic/symbolLock.png index 2ddc0379..b717a935 100644 Binary files a/build/png/ox.ui.classic/buttonCollapse.png and b/build/png/ox.ui.classic/symbolLock.png differ diff --git a/build/png/ox.ui.classic/symbolMute.png b/build/png/ox.ui.classic/symbolMute.png new file mode 100644 index 00000000..f369aaa6 Binary files /dev/null and b/build/png/ox.ui.classic/symbolMute.png differ diff --git a/build/png/ox.ui.classic/buttonNext.png b/build/png/ox.ui.classic/symbolNext.png similarity index 100% rename from build/png/ox.ui.classic/buttonNext.png rename to build/png/ox.ui.classic/symbolNext.png diff --git a/build/png/ox.ui.classic/buttonRemove.png b/build/png/ox.ui.classic/symbolNone.png similarity index 96% rename from build/png/ox.ui.classic/buttonRemove.png rename to build/png/ox.ui.classic/symbolNone.png index c9098920..3a377555 100644 Binary files a/build/png/ox.ui.classic/buttonRemove.png and b/build/png/ox.ui.classic/symbolNone.png differ diff --git a/build/png/ox.ui.classic/buttonAdd.png b/build/png/ox.ui.classic/symbolPause.png similarity index 95% rename from build/png/ox.ui.classic/buttonAdd.png rename to build/png/ox.ui.classic/symbolPause.png index 0407da60..76d9f13b 100644 Binary files a/build/png/ox.ui.classic/buttonAdd.png and b/build/png/ox.ui.classic/symbolPause.png differ diff --git a/build/png/ox.ui.classic/symbolPlay.png b/build/png/ox.ui.classic/symbolPlay.png new file mode 100644 index 00000000..d9cbd4f9 Binary files /dev/null and b/build/png/ox.ui.classic/symbolPlay.png differ diff --git a/build/png/ox.ui.classic/buttonPrevious.png b/build/png/ox.ui.classic/symbolPrevious.png similarity index 100% rename from build/png/ox.ui.classic/buttonPrevious.png rename to build/png/ox.ui.classic/symbolPrevious.png diff --git a/build/png/ox.ui.classic/buttonPause.png b/build/png/ox.ui.classic/symbolRemove.png similarity index 95% rename from build/png/ox.ui.classic/buttonPause.png rename to build/png/ox.ui.classic/symbolRemove.png index 53c266c7..e2380492 100644 Binary files a/build/png/ox.ui.classic/buttonPause.png and b/build/png/ox.ui.classic/symbolRemove.png differ diff --git a/build/png/ox.ui.classic/buttonSelect.png b/build/png/ox.ui.classic/symbolSelect.png similarity index 100% rename from build/png/ox.ui.classic/buttonSelect.png rename to build/png/ox.ui.classic/symbolSelect.png diff --git a/build/png/ox.ui.classic/symbolSpin.png b/build/png/ox.ui.classic/symbolSpin.png new file mode 100644 index 00000000..c1eaa1b5 Binary files /dev/null and b/build/png/ox.ui.classic/symbolSpin.png differ diff --git a/build/png/ox.ui.classic/symbolTime.png b/build/png/ox.ui.classic/symbolTime.png new file mode 100644 index 00000000..1c42abd9 Binary files /dev/null and b/build/png/ox.ui.classic/symbolTime.png differ diff --git a/build/png/ox.ui.classic/symbolUnlock.png b/build/png/ox.ui.classic/symbolUnlock.png new file mode 100644 index 00000000..d0671b02 Binary files /dev/null and b/build/png/ox.ui.classic/symbolUnlock.png differ diff --git a/build/png/ox.ui.classic/symbolUnmute.png b/build/png/ox.ui.classic/symbolUnmute.png new file mode 100644 index 00000000..3b5e9c7e Binary files /dev/null and b/build/png/ox.ui.classic/symbolUnmute.png differ diff --git a/build/png/ox.ui.classic/symbolUser.png b/build/png/ox.ui.classic/symbolUser.png new file mode 100644 index 00000000..01f312ba Binary files /dev/null and b/build/png/ox.ui.classic/symbolUser.png differ diff --git a/build/png/ox.ui.classic/buttonClose.png b/build/png/ox.ui.classic/symbolVolume.png similarity index 93% rename from build/png/ox.ui.classic/buttonClose.png rename to build/png/ox.ui.classic/symbolVolume.png index 620e25bd..e5085dda 100644 Binary files a/build/png/ox.ui.classic/buttonClose.png and b/build/png/ox.ui.classic/symbolVolume.png differ diff --git a/build/png/ox.ui.classic/symbolWarning.png b/build/png/ox.ui.classic/symbolWarning.png new file mode 100644 index 00000000..f7d9b7be Binary files /dev/null and b/build/png/ox.ui.classic/symbolWarning.png differ diff --git a/build/png/ox.ui.modern/buttonExpand.png b/build/png/ox.ui.modern/buttonExpand.png deleted file mode 100644 index f52790e3..00000000 Binary files a/build/png/ox.ui.modern/buttonExpand.png and /dev/null differ diff --git a/build/png/ox.ui.modern/buttonFind.png b/build/png/ox.ui.modern/buttonFind.png deleted file mode 100644 index caa13101..00000000 Binary files a/build/png/ox.ui.modern/buttonFind.png and /dev/null differ diff --git a/build/png/ox.ui.modern/buttonNext.png b/build/png/ox.ui.modern/buttonNext.png deleted file mode 100644 index 566124cd..00000000 Binary files a/build/png/ox.ui.modern/buttonNext.png and /dev/null differ diff --git a/build/png/ox.ui.modern/buttonPlay.png b/build/png/ox.ui.modern/buttonPlay.png deleted file mode 100644 index f7aefa16..00000000 Binary files a/build/png/ox.ui.modern/buttonPlay.png and /dev/null differ diff --git a/build/png/ox.ui.modern/buttonPrevious.png b/build/png/ox.ui.modern/buttonPrevious.png deleted file mode 100644 index e65cef9d..00000000 Binary files a/build/png/ox.ui.modern/buttonPrevious.png and /dev/null differ diff --git a/build/png/ox.ui.modern/icon.png b/build/png/ox.ui.modern/icon.png new file mode 100644 index 00000000..6e062a1d Binary files /dev/null and b/build/png/ox.ui.modern/icon.png differ diff --git a/build/png/ox.ui.modern/symbolAbove.png b/build/png/ox.ui.modern/symbolAbove.png new file mode 100644 index 00000000..ac9d5a65 Binary files /dev/null and b/build/png/ox.ui.modern/symbolAbove.png differ diff --git a/build/png/ox.ui.modern/buttonAdd.png b/build/png/ox.ui.modern/symbolAdd.png similarity index 100% rename from build/png/ox.ui.modern/buttonAdd.png rename to build/png/ox.ui.modern/symbolAdd.png diff --git a/build/png/ox.ui.modern/symbolBelow.png b/build/png/ox.ui.modern/symbolBelow.png new file mode 100644 index 00000000..854e1952 Binary files /dev/null and b/build/png/ox.ui.modern/symbolBelow.png differ diff --git a/build/png/ox.ui.modern/symbolCheck.png b/build/png/ox.ui.modern/symbolCheck.png new file mode 100644 index 00000000..96112adc Binary files /dev/null and b/build/png/ox.ui.modern/symbolCheck.png differ diff --git a/build/png/ox.ui.modern/buttonClose.png b/build/png/ox.ui.modern/symbolClear.png similarity index 100% rename from build/png/ox.ui.modern/buttonClose.png rename to build/png/ox.ui.modern/symbolClear.png diff --git a/build/png/ox.ui.modern/symbolClose.png b/build/png/ox.ui.modern/symbolClose.png new file mode 100644 index 00000000..4a6a7c14 Binary files /dev/null and b/build/png/ox.ui.modern/symbolClose.png differ diff --git a/build/png/ox.ui.modern/symbolCollapse.png b/build/png/ox.ui.modern/symbolCollapse.png new file mode 100644 index 00000000..22ec80af Binary files /dev/null and b/build/png/ox.ui.modern/symbolCollapse.png differ diff --git a/build/png/ox.ui.modern/symbolDate.png b/build/png/ox.ui.modern/symbolDate.png new file mode 100644 index 00000000..fa0de541 Binary files /dev/null and b/build/png/ox.ui.modern/symbolDate.png differ diff --git a/build/png/ox.ui.modern/symbolEdit.png b/build/png/ox.ui.modern/symbolEdit.png new file mode 100644 index 00000000..782caea9 Binary files /dev/null and b/build/png/ox.ui.modern/symbolEdit.png differ diff --git a/build/png/ox.ui.modern/symbolExpand.png b/build/png/ox.ui.modern/symbolExpand.png new file mode 100644 index 00000000..7e628187 Binary files /dev/null and b/build/png/ox.ui.modern/symbolExpand.png differ diff --git a/build/png/ox.ui.modern/symbolFind.png b/build/png/ox.ui.modern/symbolFind.png new file mode 100644 index 00000000..e40a6cdc Binary files /dev/null and b/build/png/ox.ui.modern/symbolFind.png differ diff --git a/build/png/ox.ui.modern/symbolGoToIn.png b/build/png/ox.ui.modern/symbolGoToIn.png new file mode 100644 index 00000000..a3f5f311 Binary files /dev/null and b/build/png/ox.ui.modern/symbolGoToIn.png differ diff --git a/build/png/ox.ui.modern/symbolGoToOut.png b/build/png/ox.ui.modern/symbolGoToOut.png new file mode 100644 index 00000000..1ba0e0b1 Binary files /dev/null and b/build/png/ox.ui.modern/symbolGoToOut.png differ diff --git a/build/png/ox.ui.modern/symbolHelp.png b/build/png/ox.ui.modern/symbolHelp.png new file mode 100644 index 00000000..022715b0 Binary files /dev/null and b/build/png/ox.ui.modern/symbolHelp.png differ diff --git a/build/png/ox.ui.modern/symbolInfo.png b/build/png/ox.ui.modern/symbolInfo.png new file mode 100644 index 00000000..05058998 Binary files /dev/null and b/build/png/ox.ui.modern/symbolInfo.png differ diff --git a/build/png/ox.ui.modern/symbolLocation.png b/build/png/ox.ui.modern/symbolLocation.png new file mode 100644 index 00000000..dc5133a1 Binary files /dev/null and b/build/png/ox.ui.modern/symbolLocation.png differ diff --git a/build/png/ox.ui.modern/symbolLock.png b/build/png/ox.ui.modern/symbolLock.png new file mode 100644 index 00000000..627f78b8 Binary files /dev/null and b/build/png/ox.ui.modern/symbolLock.png differ diff --git a/build/png/ox.ui.modern/symbolMute.png b/build/png/ox.ui.modern/symbolMute.png new file mode 100644 index 00000000..a8c2734f Binary files /dev/null and b/build/png/ox.ui.modern/symbolMute.png differ diff --git a/build/png/ox.ui.modern/symbolNext.png b/build/png/ox.ui.modern/symbolNext.png new file mode 100644 index 00000000..7e628187 Binary files /dev/null and b/build/png/ox.ui.modern/symbolNext.png differ diff --git a/build/png/ox.ui.modern/symbolNone.png b/build/png/ox.ui.modern/symbolNone.png new file mode 100644 index 00000000..3a377555 Binary files /dev/null and b/build/png/ox.ui.modern/symbolNone.png differ diff --git a/build/png/ox.ui.modern/buttonPause.png b/build/png/ox.ui.modern/symbolPause.png similarity index 100% rename from build/png/ox.ui.modern/buttonPause.png rename to build/png/ox.ui.modern/symbolPause.png diff --git a/build/png/ox.ui.modern/symbolPlay.png b/build/png/ox.ui.modern/symbolPlay.png new file mode 100644 index 00000000..fb239790 Binary files /dev/null and b/build/png/ox.ui.modern/symbolPlay.png differ diff --git a/build/png/ox.ui.modern/symbolPlayInToOut.png b/build/png/ox.ui.modern/symbolPlayInToOut.png new file mode 100644 index 00000000..f3886bf6 Binary files /dev/null and b/build/png/ox.ui.modern/symbolPlayInToOut.png differ diff --git a/build/png/ox.ui.modern/symbolPrevious.png b/build/png/ox.ui.modern/symbolPrevious.png new file mode 100644 index 00000000..5d52a415 Binary files /dev/null and b/build/png/ox.ui.modern/symbolPrevious.png differ diff --git a/build/png/ox.ui.modern/buttonRemove.png b/build/png/ox.ui.modern/symbolRemove.png similarity index 100% rename from build/png/ox.ui.modern/buttonRemove.png rename to build/png/ox.ui.modern/symbolRemove.png diff --git a/build/png/ox.ui.modern/buttonSelect.png b/build/png/ox.ui.modern/symbolSelect.png similarity index 100% rename from build/png/ox.ui.modern/buttonSelect.png rename to build/png/ox.ui.modern/symbolSelect.png diff --git a/build/png/ox.ui.modern/symbolSetIn.png b/build/png/ox.ui.modern/symbolSetIn.png new file mode 100644 index 00000000..4b84fc65 Binary files /dev/null and b/build/png/ox.ui.modern/symbolSetIn.png differ diff --git a/build/png/ox.ui.modern/symbolSetOut.png b/build/png/ox.ui.modern/symbolSetOut.png new file mode 100644 index 00000000..6f65b332 Binary files /dev/null and b/build/png/ox.ui.modern/symbolSetOut.png differ diff --git a/build/png/ox.ui.modern/symbolTime.png b/build/png/ox.ui.modern/symbolTime.png new file mode 100644 index 00000000..c3d56765 Binary files /dev/null and b/build/png/ox.ui.modern/symbolTime.png differ diff --git a/build/png/ox.ui.modern/symbolUnlock.png b/build/png/ox.ui.modern/symbolUnlock.png new file mode 100644 index 00000000..8f76accf Binary files /dev/null and b/build/png/ox.ui.modern/symbolUnlock.png differ diff --git a/build/png/ox.ui.modern/symbolUnmute.png b/build/png/ox.ui.modern/symbolUnmute.png new file mode 100644 index 00000000..bf594326 Binary files /dev/null and b/build/png/ox.ui.modern/symbolUnmute.png differ diff --git a/build/png/ox.ui.modern/symbolUser.png b/build/png/ox.ui.modern/symbolUser.png new file mode 100644 index 00000000..35e9bf76 Binary files /dev/null and b/build/png/ox.ui.modern/symbolUser.png differ diff --git a/build/png/ox.ui.modern/symbolVolume.png b/build/png/ox.ui.modern/symbolVolume.png new file mode 100644 index 00000000..ef343876 Binary files /dev/null and b/build/png/ox.ui.modern/symbolVolume.png differ diff --git a/build/png/ox.ui.modern/symbolWarning.png b/build/png/ox.ui.modern/symbolWarning.png new file mode 100644 index 00000000..cbab3fbd Binary files /dev/null and b/build/png/ox.ui.modern/symbolWarning.png differ diff --git a/build/png/ox.ui/videoMarkerCut.png b/build/png/ox.ui/videoMarkerCut.png new file mode 100644 index 00000000..22ed4e8c Binary files /dev/null and b/build/png/ox.ui/videoMarkerCut.png differ diff --git a/build/png/ox.ui/videoMarkerIn.png b/build/png/ox.ui/videoMarkerIn.png new file mode 100644 index 00000000..0c2f94f9 Binary files /dev/null and b/build/png/ox.ui/videoMarkerIn.png differ diff --git a/build/png/ox.ui/videoMarkerInBottom.png b/build/png/ox.ui/videoMarkerInBottom.png new file mode 100644 index 00000000..f4c08545 Binary files /dev/null and b/build/png/ox.ui/videoMarkerInBottom.png differ diff --git a/build/png/ox.ui/videoMarkerInTop.png b/build/png/ox.ui/videoMarkerInTop.png new file mode 100644 index 00000000..c533819e Binary files /dev/null and b/build/png/ox.ui/videoMarkerInTop.png differ diff --git a/build/png/ox.ui/videoMarkerOut.png b/build/png/ox.ui/videoMarkerOut.png new file mode 100644 index 00000000..c53c7395 Binary files /dev/null and b/build/png/ox.ui/videoMarkerOut.png differ diff --git a/build/png/ox.ui/videoMarkerOutBottom.png b/build/png/ox.ui/videoMarkerOutBottom.png new file mode 100644 index 00000000..f29ef4f5 Binary files /dev/null and b/build/png/ox.ui/videoMarkerOutBottom.png differ diff --git a/build/png/ox.ui/videoMarkerOutTop.png b/build/png/ox.ui/videoMarkerOutTop.png new file mode 100644 index 00000000..2508f9bd Binary files /dev/null and b/build/png/ox.ui/videoMarkerOutTop.png differ diff --git a/build/png/ox.ui/videoMarkerPlay.png b/build/png/ox.ui/videoMarkerPlay.png new file mode 100644 index 00000000..4a32e48b Binary files /dev/null and b/build/png/ox.ui/videoMarkerPlay.png differ diff --git a/demos/form2/color.html b/demos/form2/color.html new file mode 100644 index 00000000..72b6d75e --- /dev/null +++ b/demos/form2/color.html @@ -0,0 +1,76 @@ + + + + color + + + + + + + + + + + \ No newline at end of file diff --git a/demos/form2/css/form.css b/demos/form2/css/form.css new file mode 100644 index 00000000..46b657bf --- /dev/null +++ b/demos/form2/css/form.css @@ -0,0 +1,17 @@ +body { + overflow: hidden; +} +#panel { + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + padding: 4px; + background: rgb(240, 240, 240); + overflow: auto; +} +.margin { + float: left; + margin: 4px; +} diff --git a/demos/form2/events.html b/demos/form2/events.html new file mode 100644 index 00000000..6068e3a1 --- /dev/null +++ b/demos/form2/events.html @@ -0,0 +1,44 @@ + + + + test + + + + + + + + + + + \ No newline at end of file diff --git a/demos/form2/index.html b/demos/form2/index.html new file mode 100644 index 00000000..22d5d336 --- /dev/null +++ b/demos/form2/index.html @@ -0,0 +1,24 @@ + + + + ox.js form demo + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demos/form2/jpg/Children's Games.jpg b/demos/form2/jpg/Children's Games.jpg new file mode 100644 index 00000000..cee7b0b5 Binary files /dev/null and b/demos/form2/jpg/Children's Games.jpg differ diff --git a/demos/form2/jpg/Dulle Griet.jpg b/demos/form2/jpg/Dulle Griet.jpg new file mode 100644 index 00000000..a1defabc Binary files /dev/null and b/demos/form2/jpg/Dulle Griet.jpg differ diff --git a/demos/form2/jpg/Landscape with the Fall of Icarus.jpg b/demos/form2/jpg/Landscape with the Fall of Icarus.jpg new file mode 100644 index 00000000..fe5539c4 Binary files /dev/null and b/demos/form2/jpg/Landscape with the Fall of Icarus.jpg differ diff --git a/demos/form2/jpg/Netherlandish Proverbs.jpg b/demos/form2/jpg/Netherlandish Proverbs.jpg new file mode 100644 index 00000000..14303009 Binary files /dev/null and b/demos/form2/jpg/Netherlandish Proverbs.jpg differ diff --git a/demos/form2/jpg/The Fight Between Carnival and Lent.jpg b/demos/form2/jpg/The Fight Between Carnival and Lent.jpg new file mode 100644 index 00000000..4e0d1b24 Binary files /dev/null and b/demos/form2/jpg/The Fight Between Carnival and Lent.jpg differ diff --git a/demos/form2/jpg/The Hunters in the Snow.jpg b/demos/form2/jpg/The Hunters in the Snow.jpg new file mode 100644 index 00000000..b8779e44 Binary files /dev/null and b/demos/form2/jpg/The Hunters in the Snow.jpg differ diff --git a/demos/form2/jpg/The Procession to Calvary.jpg b/demos/form2/jpg/The Procession to Calvary.jpg new file mode 100644 index 00000000..42840072 Binary files /dev/null and b/demos/form2/jpg/The Procession to Calvary.jpg differ diff --git a/demos/form2/jpg/The Tower of Babel.jpg b/demos/form2/jpg/The Tower of Babel.jpg new file mode 100644 index 00000000..66752270 Binary files /dev/null and b/demos/form2/jpg/The Tower of Babel.jpg differ diff --git a/demos/form2/jpg/The Triumph of Death.jpg b/demos/form2/jpg/The Triumph of Death.jpg new file mode 100644 index 00000000..bb7ec0b8 Binary files /dev/null and b/demos/form2/jpg/The Triumph of Death.jpg differ diff --git a/demos/form2/jpg/Winter Landscape with a Bird Trap.jpg b/demos/form2/jpg/Winter Landscape with a Bird Trap.jpg new file mode 100644 index 00000000..c77952b5 Binary files /dev/null and b/demos/form2/jpg/Winter Landscape with a Bird Trap.jpg differ diff --git a/demos/form2/js/form.js b/demos/form2/js/form.js new file mode 100644 index 00000000..19e185b7 --- /dev/null +++ b/demos/form2/js/form.js @@ -0,0 +1,1082 @@ +$(function() { + + var $body = $("body"), + $panel = $("
") + .attr({ + id: "panel" + }) + .appendTo($body), + objects = { + "Button": [ + { + options: { + style: "symbol", + title: "add", + type: "image" + }, + title: "Symbol Button" + }, + { + options: { + title: "add", + type: "image", + }, + title: "Image Button" + }, + { + options: { + title: "Button", + }, + title: "Text Button" + }, + { + options: { + selectable: true, + title: "Button", + }, + title: "Selectable Button" + }, + { + options: { + disabled: true, + title: "Button", + }, + title: "Disabled Button" + }, + { + options: { + title: [ + {id: "one", title: "expand"}, + {id: "two", title: "collapse"}, + ], + type: "image" + }, + title: "Multi-Title Image Button" + }, + { + options: { + selectable: true, + title: [ + {id: "off", title: "Off"}, + {id: "on", title: "On"}, + ], + width: 32 + }, + title: "Multi-Title Selectable Text Button" + } + ], + "ButtonGroup": [ + { + options: { + buttons: [ + { id: "", title: "close" }, + { id: "", title: "remove" }, + { id: "", title: "add" }, + { id: "", title: "check" }, + { id: "", title: "edit" }, + { id: "", title: "find" }, + { id: "", title: "select" }, + { id: "", title: "unlock" }, + { id: "", title: "lock" }, + { id: "", title: "previous" }, + { id: "", title: "next" }, + { id: "", title: "above" }, + { id: "", title: "below" }, + { id: "", title: "play" }, + { id: "", title: "pause" }, + { id: "", title: "mute" }, + { id: "", title: "volume" }, + { id: "", title: "unmute" }, + { id: "", title: "expand" }, + { id: "", title: "collapse" }, + { id: "", title: "info" }, + { id: "", title: "help" }, + { id: "", title: "warning" }, + { id: "", title: "user" }, + { id: "", title: "location" }, + { id: "", title: "date" }, + { id: "", title: "time" }, + ], + id: "buttonGroupImage", + type: "image" + }, + title: "Image ButtonGroup" + }, + { + options: { + buttons: [ + { + id: "one", + title: "do something", + }, + { + id: "two", + title: "do something else", + } + ], + id: "buttonGroupText" + }, + title: "Text ButtonGroup" + }, + { + options: { + buttons: [ + { + id: "either", + title: "select me", + }, + { + id: "or1", + title: "or select me", + }, + { + id: "or2", + title: "or select me", + } + ], + id: "selectableButtonGroupOne", + selectable: true + }, + title: "Selectable ButtonGroup, select one" + }, + { + options: { + buttons: [ + { + id: "both", + title: "select me", + }, + { + id: "and", + title: "and select me", + }, + { + id: "or", + title: "or select me", + } + ], + id: "selectableButtonGroupOneTwo", + max: 2, + min: 1, + selectable: true + }, + title: "Selectable ButtonGroup, select one or two" + }, + { + options: { + buttons: [ + { + id: "both", + title: "select me", + }, + { + id: "and", + title: "and select me", + }, + { + id: "or", + title: "or select me", + } + ], + id: "selectableButtonGroupTwo", + max: 2, + min: 2, + selectable: true + }, + title: "Selectable ButtonGroup, select two" + }, + { + options: { + buttons: [ + { + id: "both", + title: "select me", + }, + { + id: "and1", + title: "and select me", + }, + { + id: "and2", + title: "and select me", + } + ], + id: "selectableButtonGroupAny", + max: -1, + min: 0, + selectable: true + }, + title: "Selectable ButtonGroup, select any" + }, + { + options: { + buttons: [ + { + id: "either", + title: "select me", + }, + { + id: "or1", + title: "or select me", + }, + { + id: "or2", + title: "or select me", + } + ], + id: "tabButtonGroup", + selectable: true, + style: "tab" + }, + title: "Tab ButtonGroup" + } + ], + "Checkbox": [ + { + options: {}, + title: "Checkbox" + }, + { + options: { + title: "check me", + }, + title: "Checkbox with Title" + }, + { + options: { + disabled: true, + title: "check me", + }, + title: "Disabled Checkbox with Title" + }, + { + options: { + title: "check me", + width: 300, + }, + title: "Checkbox with Title and Width" + } + ], + "CheckboxGroup": [ + { + options: { + checkboxes: [ + {id: "either", title: "check me"}, + {id: "or1", title: "or check me"}, + {id: "or2", title: "or check me"}, + ], + id: "CheckboxGroupOne", + width: 300 + }, + title: "CheckboxGroup, select one" + }, + { + options: { + checkboxes: [ + {id: "either", title: "check me"}, + {id: "or1", title: "or check me"}, + {id: "or2", title: "or check me"}, + ], + id: "CheckboxGroupTwo", + max: 2, + min: 2, + width: 300 + }, + title: "CheckboxGroup, select two" + }, + { + options: { + checkboxes: [ + {id: "both", title: "check me"}, + {id: "and1", title: "and check me"}, + {id: "and2", title: "and check me"}, + ], + id: "CheckboxGroupAny", + max: -1, + min: 0, + width: 300 + }, + title: "CheckboxGroup, select any" + }, + ], + "ColorInput": [ + { + options: { + id: "colorInput", + value: "255, 0, 0" + }, + title: "ColorInput" + } + ], + "DateInput": [ + { + options: { + id: "dateInputShort" + }, + title: "Short DateInput" + }, + { + options: { + format: "medium", + id: "dateInputMedium" + }, + title: "Medium DateInput" + }, + { + options: { + format: "long", + id: "dateInputLong", + }, + title: "Long DateInput" + }, + { + options: { + id: "dateInputShortValue", + value: "2000-01-01" + }, + title: "Short DateInput with Value" + }, + { + options: { + format: "medium", + id: "dateInputMediumWeekday", + weekday: true + }, + title: "Medium DateInput with Weekday" + }, + { + options: { + format: "long", + id: "dateInputLongWeekday", + weekday: true + }, + title: "Long DateInput with Weekday" + } + ], + "DateTimeInput": [ + { + options: { + id: "dateTimeInputShort" + }, + title: "Short DateTimeInput" + }, + { + options: { + ampm: true, + format: "long", + id: "dateTimeInputLong", + seconds: true, + weekday: true, + }, + title: "Long DateTimeInput" + }, + ], + "FormElementGroup": [ + { + options: { + elements: [ + new Ox.Select({ + id: "select", + items: [ + {id: "one", title: "Find: One"}, + {id: "two", title: "Find: Two"}, + {id: "three", title: "Find: Three"} + ], + overlap: "right", + width: 128 + }), + new Ox.Input({ + id: "input", + width: 128 + }) + ], + id: "formElementGroupSelectInput" + }, + title: "FormElementGroup (Select and Input)" + }, + { + options: { + elements: [ + new Ox.Input({ + id: "input_", + width: 128 + }), + new Ox.Select({ + id: "select_", + items: [ + {id: "kb", title: "KB"}, + {id: "mb", title: "MB"}, + {id: "gb", title: "GB"}, + {id: "tb", title: "TB"} + ], + overlap: "left", + width: 128 + }), + ], + float: "right", + id: "formElementGroupInputSelect" + }, + title: "FormElementGroup (Input and Select)" + }, + { + options: { + elements: [ + new Ox.Select({ + id: "selectOne", + items: [ + {id: "one", title: "Title"}, + {id: "two", title: "Date"}, + {id: "three", title: "Place"} + ], + overlap: "right", + width: 128 + }), + new Ox.Select({ + id: "selectTwo", + items: [ + {id: "one", title: "is"}, + {id: "two", title: "is between"}, + {id: "three", title: "is not between"} + ], + overlap: "right", + width: 128 + }), + new Ox.InputGroup({ + id: "input", + inputs: [ + new Ox.DateInput({ + id: "inputOne", + width: { + day: 36, + month: 36, + year: 56 + } + }), + new Ox.DateInput({ + id: "inputTwo", + width: { + day: 36, + month: 36, + year: 56 + } + }), + ], + separators: [ + {title: "and", width: 32} + ] + }) + ], + id: "formElementGroupSelectSelectInput" + }, + title: "FormElementGroup (two Selects, one Input)" + } + ], + "Input": [ + { + options: { + value: "Value" + }, + title: "Input" + }, + { + options: { + disabled: true, + value: "Disabled" + }, + title: "Disabled Input" + }, + { + options: { + placeholder: "Placeholder" + }, + title: "Input with Placeholder" + }, + { + options: { + label: "Label" + }, + title: "Input with Label" + }, + { + options: { + clear: true + }, + title: "Input with Clear Button" + }, + { + options: { + clear: true, + label: "Label", + placeholder: "Placeholder", + width: 256 + }, + title: "Input with Label, Placeholder and Button" + }, + { + options: { + type: "password" + }, + title: "Password Input" + }, + { + options: { + clear: true, + placeholder: "Password", + type: "password" + }, + title: "Password Input with Placeholder" + }, + { + options: { + type: "float" + }, + title: "Float Input (0.0 - 100.0)" + }, + { + options: { + max: 255, + type: "integer" + }, + title: "Integer Input (0 - 255)" + }, + { + options: { + clear: true, + id: "integerClear", + max: 255, + type: "integer" + }, + title: "Integer Input with Clear Button" + }, + { + options: { + arrows: true, + id: "integerArrows", + max: 255, + type: "integer" + }, + title: "Integer Input with Arrows" + }, + { + options: { + autocomplete: $.map(Ox.getCountries(), function(v, i) { + return v.name; + }), + autocompleteReplace: true, + id: "autocompleteReplace" + }, + title: "Autocomplete with Replace" + }, + { + options: { + autocomplete: $.map(Ox.getCountries(), function(v, i) { + return v.name; + }), + autocompleteReplace: true, + autocompleteReplaceCorrect: true, + id: "autocompleteReplaceCorrect" + }, + title: "Autocomplete with Replace and Correct" + }, + { + options: { + autocomplete: $.map(Ox.getCountries(), function(v, i) { + return v.name; + }), + autocompleteSelect: true, + autocompleteSelectHighlight: true, + id: "autocompleteSelect" + }, + title: "Autocomplete with Select" + }, + { + options: { + autocomplete: $.map(Ox.getCountries(), function(v, i) { + return v.name; + }), + autocompleteReplace: true, + autocompleteSelect: true, + autocompleteSelectHighlight: true, + id: "autocompleteReplaceSelect" + }, + title: "Autocomplete with Replace and Select" + }, + { + options: { + autocomplete: $.map(Ox.getCountries(), function(v, i) { + return v.name; + }), + autocompleteReplace: true, + autocompleteReplaceCorrect: true, + autocompleteSelect: true, + autocompleteSelectHighlight: true, + id: "autocompleteReplaceCorrectSelect" + }, + title: "Autocomplete with Replace, Correct and Select" + }, + /* + { + options: { + autocomplete: function(value, callback) { + callback([value]); + }, + autocompleteSelect: true, + autocompleteSelectHighlight: true, + id: "autocompleteFunction" + }, + title: "Autocomplete with Select (Function)" + }, + { + options: { + autocorrect: /\d/, + }, + title: "Autocorrect with RegExp (Integer)" + }, + { + options: { + autocorrect: function(value, blur, callback) { + var length = value.length; + value = $.map(value.toLowerCase().split(""), function(v, i) { + Ox.print(new RegExp("[\d" + ((i == 0 || (i == length - 1 && blur)) ? "" : "\.") + "]")) + if (new RegExp("[\\d" + ((i == 0 || (i == length - 1 && blur) || i != value.indexOf(".")) ? "" : "\\.") + "]")(v)) { + return v; + } else { + return null; + } + }).join(""); + callback(blur ? parseFloat(value) : value); + }, + }, + title: "Autocorrect with Function (Float)" + }, + */ + ], + "InputGroup": [ + { + options: { + id: "inputGroup", + inputs: [ + Ox.Input({id: "width", placeholder: "Width", width: 64}), + Ox.Input({id: "height", placeholder: "Height", width: 64}) + ], + separators: [ + {title: "x", width: 16} + ] + }, + title: "InputGroup" + }, + { + options: { + id: "inputGroupWithWidth", + inputs: [ + Ox.Input({id: "width", placeholder: "Width"}), + Ox.Input({id: "height", placeholder: "Height"}) + ], + separators: [ + {title: "x", width: 16} + ], + width: 128 + }, + title: "InputGroup with Width" + }, + { + options: { + id: "inputGroupUsernamePassword", + inputs: [ + Ox.Input({clear: true, id: "username", label: "Username", labelWidth: 80, placeholder: "Username"}), + Ox.Input({clear: true, id: "password", label: "Password", labelWidth: 80, placeholder: "Password", type: "password"}) + ], + separators: [ + {title: "", width: 8}, + ], + width: 400 + }, + title: "Complex InputGroup" + }, + { + options: { + id: "inputGroupTest", + inputs: [ + Ox.Select({ + id: "select", + items: [ + {id: "one", title: "One"}, + {id: "two", title: "Two"}, + {id: "three", title: "Three"} + ], + overlap: "right", + width: 128 + }), + Ox.Input({ + id: "input", + placeholder: "Placeholder", + width: 128 + }) + ], + separators: [ + {title: "", width: -8} + ] + } + } + ], + "Label": [ + { + options: { + title: "Title" + }, + title: "Label" + }, + { + options: { + disabled: true, + title: "Title" + }, + title: "Disabled Label" + }, + { + options: { + title: "Title", + width: 128 + }, + title: "Label with Width" + }, + { + options: { + textAlign: "right", + title: "Title", + width: 128 + }, + title: "Right-Aligned Label" + } + ], + "PlaceInput": [ + { + options: { + id: "placeInput" + }, + title: "PlaceInput" + } + ], + "Range": [ + { + options: { + id: "range" + }, + title: "Range" + }, + { + options: { + arrows: true, + id: "rangeArrows" + }, + title: "Range with Arrows" + }, + { + options: { + arrows: true, + id: "rangeThumbValue", + max: 10, + min: 0, + thumbSize: 32, + thumbValue: true, + }, + title: "Range with Thumb Value" + }, + { + options: { + id: "rangeValueNames", + max: 1, + min: 0, + size: 48, + thumbSize: 32, + thumbValue: true, + valueNames: ["Off", "On"] + }, + title: "Range with Value Names" + }, + { + options: { + id: "rangeTrackColors", + max: 255, + min: 0, + size: 400, + thumbSize: 40, + thumbValue: true, + trackColors: [ + "rgb(0, 0, 0)", + "rgb(255, 255, 255)" + ], + }, + title: "Range with Track Colors" + }, + { + options: { + id: "rangeTrackColorsColor", + max: 359, + min: 0, + size: 400, + thumbSize: 40, + thumbValue: true, + trackColors: [ + "rgb(255, 0, 0)", + "rgb(255, 255, 0)", + "rgb(0, 255, 0)", + "rgb(0, 255, 255)", + "rgb(0, 0, 255)", + "rgb(255, 0, 255)", + "rgb(255, 0, 0)" + ] + }, + title: "Range with Track Colors" + }, + { + options: { + id: "rangeTrackImage", + size: 400, + trackImages: ["png/timeline.png"] + }, + title: "Range with Track Image" + }, + { + options: { + id: "rangeTrackImages", + max: 5, + size: 386, + thumbValue: true, + trackImages: [ + "png/red.png", + "png/yellow.png", + "png/green.png", + "png/cyan.png", + "png/blue.png", + "png/magenta.png" + ], + valueNames: ["Red", "Yellow", "Green", "Cyan", "Blue", "Magenta"] + }, + title: "Range with Track Images" + }, + { + options: { + id: "rangeTrackImages", + max: 9, + size: 240, + trackImages: [ + "jpg/Children's Games.jpg", + "jpg/Dulle Griet.jpg", + "jpg/Landscape With the Fall of Icarus.jpg", + "jpg/Netherlandish Proverbs.jpg", + "jpg/The Fight Between Carnival and Lent.jpg", + "jpg/The Hunters in the Snow.jpg", + "jpg/The Procession to Calvary.jpg", + "jpg/The Tower of Babel.jpg", + "jpg/The Triumph of Death.jpg", + "jpg/Winter Landscape With a Bird Trap.jpg", + ] + }, + title: "Range with Track Images" + }, + ], + "Select": [ + { + options: { + id: "imageSelectOne", + items: [ + {id: "one", title: "select me"}, + {id: "two", title: "or select me"}, + {id: "three", title: "or select me"} + ], + type: "image" + }, + title: "Image Select, select one" + }, + { + options: { + id: "imageSelectTwo", + items: [ + {id: "one", title: "select me"}, + {id: "two", title: "or select me"}, + {id: "three", title: "or select me"} + ], + max: 2, + min: 2, + type: "image" + }, + title: "Image Select, select two" + }, + { + options: { + id: "imageSelectAny", + items: [ + {id: "one", title: "select me"}, + {id: "two", title: "and select me"}, + {id: "three", title: "and select me"} + ], + max: -1, + min: 0, + type: "image" + }, + title: "Image Select, select any" + }, + { + options: { + id: "textSelectOne", + items: [ + {id: "one", title: "Item One"}, + {id: "two", title: "Item Two"}, + {id: "three", title: "Item Three"} + ] + }, + title: "Text Select, select one" + }, + { + options: { + id: "textSelectAny", + items: [ + {id: "one", title: "Item One"}, + {id: "two", title: "Item Two"}, + {id: "three", title: "Item Three"} + ], + max: -1, + min: 0, + title: "Title..." + }, + title: "Text Select, select any" + }, + { + options: { + id: "textSelectNone", + items: [ + {id: "one", title: "Item One"}, + {id: "two", title: "Item Two"}, + {id: "three", title: "Item Three"} + ], + selectable: false, + title: "Title..." + }, + title: "Text Select, select none" + }, + { + options: { + id: "textSelectWidth", + items: [ + {id: "one", title: "Item One"}, + {id: "two", title: "Item Two"}, + {id: "three", title: "Item Three Has a Long Title"} + ], + width: 128 + }, + title: "Text Select with Width" + } + ], + "TimeInput": [ + { + options: { + id: "timeInput", + width: 72 + }, + title: "TimeInput" + }, + { + options: { + id: "timeInputSeconds", + seconds: true, + width: 112 + }, + title: "TimeInput with Seconds" + }, + { + options: { + id: "timeInputMilliseconds", + milliseconds: true, + //width: 168 + }, + title: "TimeInput with Milliseconds" + }, + { + options: { + ampm: true, + id: "timeInputAmPm", + seconds: true, + width: 152 + }, + title: "TimeInput with am/pm" + }, + { + options: { + id: "timeInputValue", + seconds: true, + value: "00:00:00", + width: 112 + }, + title: "TimeInput with Value" + } + ] + }; + + $.each(objects, function(object, elements) { + var $div = $("
").appendTo($panel); + new Ox.Label({ + title: "Ox." + object, + width: 840 + }) + .addClass("margin") + .appendTo($div); + $("
").appendTo($panel); + // if (object != "PlaceInput") return; + $.each(elements, function(i, element) { + var $div = $("
").appendTo($panel), + $label, $element, $button; + $label = Ox.Label({ + textAlign: "right", + title: element.title, + width: 256 + }) + .addClass("margin") + .appendTo($div); + try { + $label.attr({ + title: "Ox." + object + "(" + JSON.stringify(element.options) + ")" + }); + } catch(error) { + + } + $element = new Ox[object](element.options) + .addClass("margin") + .appendTo($div); + if (object != "Button" && object != "Label") { + $button = new Ox.Button({ + id: "id" + Ox.uid(), + title: "Value" + }) + .addClass("margin") + .bindEvent("click", function() { + var value = $element.options("value"), + $dialog = new Ox.Dialog({ + buttons: [ + { + click: function() { $dialog.close(); }, + id: "close", + title: "Close" + } + ], + height: 128, + id: "value", + title: "Value", + width: 256 + }) + .append(Ox.isUndefined(value) ? "undefined" : value) + .open(); + }) + .appendTo($div); + } + $("
").appendTo($panel); + }); + }); + + $("
") + .css({ + height: "256px" + }) + .appendTo($panel); + +}); \ No newline at end of file diff --git a/demos/form2/png/blue.png b/demos/form2/png/blue.png new file mode 100644 index 00000000..2d9dec38 Binary files /dev/null and b/demos/form2/png/blue.png differ diff --git a/demos/form2/png/cyan.png b/demos/form2/png/cyan.png new file mode 100644 index 00000000..797f1102 Binary files /dev/null and b/demos/form2/png/cyan.png differ diff --git a/demos/form2/png/green.png b/demos/form2/png/green.png new file mode 100644 index 00000000..8d3f710e Binary files /dev/null and b/demos/form2/png/green.png differ diff --git a/demos/form2/png/magenta.png b/demos/form2/png/magenta.png new file mode 100644 index 00000000..6e616901 Binary files /dev/null and b/demos/form2/png/magenta.png differ diff --git a/demos/form2/png/red.png b/demos/form2/png/red.png new file mode 100644 index 00000000..9a79a465 Binary files /dev/null and b/demos/form2/png/red.png differ diff --git a/demos/form2/png/timeline.png b/demos/form2/png/timeline.png new file mode 100644 index 00000000..d8813cc1 Binary files /dev/null and b/demos/form2/png/timeline.png differ diff --git a/demos/form2/png/yellow.png b/demos/form2/png/yellow.png new file mode 100644 index 00000000..92b515ff Binary files /dev/null and b/demos/form2/png/yellow.png differ diff --git a/demos/form2/test.html b/demos/form2/test.html new file mode 100644 index 00000000..5e5dda71 --- /dev/null +++ b/demos/form2/test.html @@ -0,0 +1,22 @@ + + + + + +
+
+
+
+ Legend +
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/source/psd/buttons.psd b/source/psd/buttons.psd index fbe946e0..9b5979dd 100644 Binary files a/source/psd/buttons.psd and b/source/psd/buttons.psd differ diff --git a/source/psd/videoMarkerPoint.psd b/source/psd/videoMarkerPoint.psd new file mode 100644 index 00000000..9bd23905 Binary files /dev/null and b/source/psd/videoMarkerPoint.psd differ diff --git a/source/psd/videoMarkerPointCorner.psd b/source/psd/videoMarkerPointCorner.psd new file mode 100644 index 00000000..71b12901 Binary files /dev/null and b/source/psd/videoMarkerPointCorner.psd differ diff --git a/source/psd/videoMarkerPosition.psd b/source/psd/videoMarkerPosition.psd new file mode 100644 index 00000000..24b72a0d Binary files /dev/null and b/source/psd/videoMarkerPosition.psd differ