| 7 | | // if (!$) |
| 8 | | // throw "Kauri Forms DoubleSelectControl requires jQuery"; |
| 9 | | // if (!$.org.kauriproject.forms) |
| 10 | | // throw "Kauri Forms DoubleSelectControl requires the kauri-form namespace"; |
| 11 | | // |
| 12 | | // var kp = $.org.kauriproject; |
| 13 | | // var kf = $.org.kauriproject.forms; |
| 14 | | // |
| 15 | | // kf.controlTypes.put("dblselect-control", DoubleSelectControl); |
| 16 | | // |
| 17 | | // $.inherit(DoubleSelectControl, kf.Control); |
| 18 | | // |
| 19 | | // function DoubleSelectControl(id, form, conf) { |
| 20 | | // this['<super.init>'](id, form, conf); |
| 21 | | // |
| 22 | | // this.availableOptions = { |
| 23 | | // userValues: [], |
| 24 | | // labels: [] |
| 25 | | // } |
| 26 | | // } |
| 27 | | // |
| 28 | | // DoubleSelectControl.prototype.elements = {}; |
| 29 | | // $.extend(DoubleSelectControl.prototype.elements, kf.Control.prototype.elements); |
| 30 | | // $.extend(DoubleSelectControl.prototype.elements, { |
| 31 | | // input : { |
| 32 | | // /* Kauri Forms needs an input-role element, but we don't, therefore |
| 33 | | // simply create a hidden div. */ |
| 34 | | // create : "<div style='display:none'/>" |
| 35 | | // }, |
| 36 | | // available : { |
| 37 | | // create : "<select size='5' multiple='multiple'/>" |
| 38 | | // }, |
| 39 | | // selected : { |
| 40 | | // create : "<select size='5' multiple='multiple'/>" |
| 41 | | // }, |
| 42 | | // add : { |
| 43 | | // create: "<button>></button>" |
| 44 | | // }, |
| 45 | | // remove : { |
| 46 | | // create: "<button><</button>" |
| 47 | | // } |
| 48 | | // }); |
| 49 | | // |
| 50 | | // DoubleSelectControl.prototype.initType = function(conf) { |
| 51 | | // this["<super.call>"]("initType", [conf]); |
| 52 | | // // Force the field type to being multivalue |
| 53 | | // this.getType().multivalue = true; |
| 54 | | // } |
| 55 | | // |
| 56 | | // DoubleSelectControl.prototype.initElements = function(create) { |
| 57 | | // $available = kf.ControlElements.lookup(this, "available", true); |
| 58 | | // $add = kf.ControlElements.lookup(this, "add", true); |
| 59 | | // $remove = kf.ControlElements.lookup(this, "remove", true); |
| 60 | | // $selected = kf.ControlElements.lookup(this, "selected", true); |
| 61 | | // $addAll = kf.ControlElements.lookup(this, "addAll", false); |
| 62 | | // $removeAll = kf.ControlElements.lookup(this, "removeAll", false); |
| 63 | | // $other = kf.ControlElements.lookup(this, "other", false); |
| 64 | | // $addOther = kf.ControlElements.lookup(this, "addOther", false); |
| 65 | | // |
| 66 | | // var me = this; |
| 67 | | // |
| 68 | | // $add.click(function(event) { |
| 69 | | // event.preventDefault(); |
| 70 | | // event.stopPropagation(); |
| 71 | | // me.handleAdd(); |
| 72 | | // }); |
| 73 | | // |
| 74 | | // $available.keypress(function(event) { |
| 75 | | // // Ctrl + key right |
| 76 | | // if (event.ctrlKey && event.keyCode == 39) { |
| 77 | | // me.handleAdd(); |
| 78 | | // } |
| 79 | | // }); |
| 80 | | // |
| 81 | | // $available.dblclick(function() { |
| 82 | | // me.handleAdd(); |
| 83 | | // }); |
| 84 | | // |
| 85 | | // $remove.click(function(event) { |
| 86 | | // event.preventDefault(); |
| 87 | | // event.stopPropagation(); |
| 88 | | // me.handleRemove(); |
| 89 | | // }); |
| 90 | | // |
| 91 | | // $selected.keypress(function(event) { |
| 92 | | // // Ctrl + key left |
| 93 | | // if (event.ctrlKey && event.keyCode == 37) { |
| 94 | | // me.handleRemove(); |
| 95 | | // } else if (event.keyCode == 46) { |
| 96 | | // // delete key |
| 97 | | // me.handleRemove(); |
| 98 | | // } |
| 99 | | // }) |
| 100 | | // |
| 101 | | // $selected.dblclick(function() { |
| 102 | | // me.handleRemove(); |
| 103 | | // }); |
| 104 | | // |
| 105 | | // if ($addAll) { |
| 106 | | // $addAll.click(function(event) { |
| 107 | | // event.preventDefault(); |
| 108 | | // event.stopPropagation(); |
| 109 | | // me.handleAddAll(); |
| 110 | | // }); |
| 111 | | // } |
| 112 | | // |
| 113 | | // if ($removeAll) { |
| 114 | | // $removeAll.click(function(event) { |
| 115 | | // event.preventDefault(); |
| 116 | | // event.stopPropagation(); |
| 117 | | // me.handleRemoveAll(); |
| 118 | | // }); |
| 119 | | // } |
| 120 | | // |
| 121 | | // $available.change(function() { |
| 122 | | // me.refreshButtonsState(); |
| 123 | | // }); |
| 124 | | // |
| 125 | | // $selected.change(function() { |
| 126 | | // me.refreshButtonsState(); |
| 127 | | // }); |
| 128 | | // |
| 129 | | // if ($other && $addOther) { |
| 130 | | // $addOther.click(function(event) { |
| 131 | | // event.preventDefault(); |
| 132 | | // event.stopPropagation(); |
| 133 | | // me.handleAddOther(); |
| 134 | | // }); |
| 135 | | // |
| 136 | | // $other.keypress(function(event) { |
| 137 | | // if (event.keyCode == 13) { |
| 138 | | // event.preventDefault(); |
| 139 | | // event.stopPropagation(); |
| 140 | | // me.handleAddOther(); |
| 141 | | // } |
| 142 | | // }) |
| 143 | | // |
| 144 | | // $other.keyup(function(event) { |
| 145 | | // me.refreshAddOtherButtonState(); |
| 146 | | // }); |
| 147 | | // } |
| 148 | | // |
| 149 | | // this.valueChanged(function() { |
| | 10 | var kp = $.org.kauriproject; |
| | 11 | var kf = $.org.kauriproject.forms; |
| | 12 | |
| | 13 | kf.controlTypes.put("autocomplete-control", AutocompleteControl); |
| | 14 | |
| | 15 | $.inherit(AutocompleteControl, kf.Control); |
| | 16 | |
| | 17 | function AutocompleteControl(id, form, conf) { |
| | 18 | this['<super.init>'](id, form, conf); |
| | 19 | |
| | 20 | this.availableOptions = { |
| | 21 | userValues: [], |
| | 22 | labels: [] |
| | 23 | } |
| | 24 | } |
| | 25 | |
| | 26 | AutocompleteControl.prototype.elements = {}; |
| | 27 | $.extend(AutocompleteControl.prototype.elements, kf.Control.prototype.elements); |
| | 28 | $.extend(AutocompleteControl.prototype.elements, { |
| | 29 | input : { |
| | 30 | /* Kauri Forms needs an input-role element, but we don't, therefore |
| | 31 | simply create a hidden div. */ |
| | 32 | create : "<div style='display:none'/>" |
| | 33 | }, |
| | 34 | acinput : { |
| | 35 | create : "<input type='text'/>" |
| | 36 | } |
| | 37 | }); |
| | 38 | |
| | 39 | AutocompleteControl.prototype.initType = function(conf) { |
| | 40 | this["<super.call>"]("initType", [conf]); |
| | 41 | |
| | 42 | } |
| | 43 | |
| | 44 | AutocompleteControl.prototype.initElements = function(create) { |
| | 45 | $acinput = kf.ControlElements.lookup(this, "acinput", true); |
| | 46 | |
| | 47 | var me = this; |
| | 48 | |
| | 49 | var inputTemplate = new kp.UriTemplate("{email}"); |
| | 50 | var listTemplate = new kp.UriTemplate("{id} - {email})"); |
| | 51 | var valueTemplate = new kp.UriTemplate("{id}"); |
| | 52 | |
| | 53 | $acinput.autocomplete(this.options.dataUri, { |
| | 54 | dataType: "json", |
| | 55 | parse: function(data) { |
| | 56 | var parsed = []; |
| | 57 | for (var i=0;i<data.length;i++) { |
| | 58 | parsed.push({ data: data[i], value: valueTemplate.expand(data[i], true), result: inputTemplate.expand(data[i], true)}); |
| | 59 | } |
| | 60 | return parsed; |
| | 61 | }, |
| | 62 | formatItem: function(data, current, last) { |
| | 63 | return listTemplate.expand(data, true); |
| | 64 | } |
| | 65 | }).bind("result", function(event, objData, valueData) { |
| | 66 | alert('You selected ' + valueData); |
| | 67 | }); |
| | 68 | |
| | 69 | this.valueChanged(function() { |
| 156 | | // this.refreshButtonsState(); |
| 157 | | // this.refreshAddOtherButtonState(); |
| 158 | | // } |
| 159 | | // |
| 160 | | // DoubleSelectControl.prototype.getUserValue = function() { |
| 161 | | // // Not needed, should never be called |
| 162 | | // } |
| 163 | | // |
| 164 | | // DoubleSelectControl.prototype.setUserValue = function(value) { |
| 165 | | // // Not needed, should never be called |
| 166 | | // } |
| 167 | | // |
| 168 | | // DoubleSelectControl.prototype.getUpdateElement = function() { |
| 169 | | // // There is no 'change' event to react to |
| 170 | | // return undefined; |
| 171 | | // } |
| 172 | | // |
| 173 | | // DoubleSelectControl.prototype.updateOptions = function(userValues, labels) { |
| 174 | | // // This callback might be called before initElements() is complete, therefore |
| 175 | | // // the check if the 'available' binding does already exist. |
| 176 | | // var $available = this.getElement("available"); |
| 177 | | // if ($available) { |
| 178 | | // this.refreshAvailableSelect(); |
| 179 | | // this.refreshSelectedSelect(); |
| 180 | | // this.refreshButtonsState(); |
| 181 | | // } |
| 182 | | // } |
| 183 | | // |
| 184 | | // DoubleSelectControl.prototype.refreshButtonsState = function() { |
| 185 | | // var $available = this.getElement("available"); |
| 186 | | // var $selected = this.getElement("selected"); |
| 187 | | // |
| 188 | | // var buttons = [ |
| 189 | | // {role: "add", enabled: $available.val() !== null}, |
| 190 | | // {role: "addAll", enabled: $available[0].options.length > 0}, |
| 191 | | // {role: "remove", enabled: $selected.val() !== null}, |
| 192 | | // {role: "removeAll", enabled: $selected[0].options.length > 0} |
| 193 | | // ] |
| 194 | | // |
| 195 | | // for (var i = 0; i < buttons.length; i++) { |
| 196 | | // var $el = this.getElement(buttons[i].role); |
| 197 | | // if ($el) { |
| 198 | | // $el.attr("disabled", !buttons[i].enabled); |
| 199 | | // } |
| 200 | | // } |
| 201 | | // } |
| 202 | | // |
| 203 | | // DoubleSelectControl.prototype.refreshAddOtherButtonState = function() { |
| 204 | | // var $other = this.getElement("other"); |
| 205 | | // var $addOther = this.getElement("addOther"); |
| 206 | | // |
| 207 | | // if ($other && $addOther) { |
| 208 | | // $addOther.attr('disabled', $.isEmpty($other.val())); |
| 209 | | // } |
| 210 | | // } |
| 211 | | // |
| 212 | | // DoubleSelectControl.prototype.refreshAvailableSelect = function() { |
| 213 | | // var options = this.options || {}; |
| 214 | | // var values = options.values || []; |
| 215 | | // var userValues = options.userValues || []; |
| 216 | | // var labels = options.labels || userValues; |
| 217 | | // |
| 218 | | // var $available = this.getElement("available"); |
| 219 | | // var availableSelect = $available[0]; |
| 220 | | // |
| 221 | | // var selectOptions = availableSelect.options; |
| 222 | | // var prevSelectedIndex = selectOptions.selectedIndex; |
| 223 | | // selectOptions.length = 0; |
| 224 | | // |
| 225 | | // var controlValue = this.getValue(); |
| 226 | | // |
| 227 | | // var last = values.length; |
| 228 | | // for (var i = 0; i < last; i++) { |
| 229 | | // var isSelectedValue = false; |
| 230 | | // if (controlValue) { |
| 231 | | // // TODO make this more efficient? |
| 232 | | // for (var j = 0; j < controlValue.length; j++) { |
| 233 | | // if ($.valueCompare(values[i], controlValue[j])) { |
| 234 | | // isSelectedValue = true; |
| 235 | | // break; |
| 236 | | // } |
| 237 | | // } |
| 238 | | // } |
| 239 | | // |
| 240 | | // if (!isSelectedValue) { |
| 241 | | // availableSelect.options[availableSelect.options.length] = new Option(labels[i], userValues[i]) |
| 242 | | // } |
| 243 | | // } |
| 244 | | // |
| 245 | | // if (prevSelectedIndex >= 0) { |
| 246 | | // prevSelectedIndex = prevSelectedIndex >= selectOptions.length ? selectOptions.length - 1 : prevSelectedIndex; |
| 247 | | // availableSelect.options.selectedIndex = prevSelectedIndex; |
| 248 | | // } |
| 249 | | // } |
| 250 | | // |
| 251 | | // DoubleSelectControl.prototype.refreshSelectedSelect = function() { |
| 252 | | // var $selected = this.getElement("selected"); |
| 253 | | // var selectedSelect = $selected[0]; |
| 254 | | // |
| 255 | | // var selectOptions = selectedSelect.options; |
| 256 | | // var prevSelectedIndex = selectOptions.selectedIndex; |
| 257 | | // selectOptions.length = 0; |
| 258 | | // |
| 259 | | // var values = this.getValue(); |
| 260 | | // if (!values) |
| 261 | | // return; |
| 262 | | // |
| 263 | | // for (var i = 0; i < values.length; i++) { |
| 264 | | // var userValue = this.getType().toUserValue(values[i]); |
| 265 | | // var label = this.getLabel(userValue); |
| 266 | | // selectedSelect.options[selectedSelect.options.length] = new Option(label, userValue); |
| 267 | | // } |
| 268 | | // |
| 269 | | // if (prevSelectedIndex >= 0) { |
| 270 | | // prevSelectedIndex = prevSelectedIndex >= selectOptions.length ? selectOptions.length - 1 : prevSelectedIndex; |
| 271 | | // selectedSelect.options.selectedIndex = prevSelectedIndex; |
| 272 | | // } |
| 273 | | // } |
| 274 | | // |
| 275 | | // DoubleSelectControl.prototype.getLabel = function(userValue) { |
| 276 | | // var options = this.options || {}; |
| 277 | | // var userValues = options.userValues || []; |
| 278 | | // var labels = options.labels; |
| 279 | | // |
| 280 | | // if (!labels) { |
| 281 | | // return userValue; |
| 282 | | // } |
| 283 | | // |
| 284 | | // // TODO maybe we need hash-based lookup for performance? |
| 285 | | // for (var i = 0; i < userValues.length; i++) { |
| 286 | | // if ($.valueCompare(userValues[i], userValue)) { |
| 287 | | // var label = labels[i]; |
| 288 | | // return label || userValue; |
| 289 | | // } |
| 290 | | // } |
| 291 | | // |
| 292 | | // return userValue; |
| 293 | | // } |
| 294 | | // |
| 295 | | // /** |
| 296 | | // * Adds the value entered into the 'other' input to the list |
| 297 | | // * of selected values. |
| 298 | | // */ |
| 299 | | // DoubleSelectControl.prototype.handleAddOther = function() { |
| 300 | | // var value = this.getValue(); |
| 301 | | // var $other = this.getElement("other"); |
| 302 | | // var other = $other.val(); |
| 303 | | // |
| 304 | | // if ($.isEmpty(other)) { |
| 305 | | // return; |
| 306 | | // } |
| 307 | | // |
| 308 | | // try { |
| 309 | | // other = this.getType().parseUserValue(other); |
| 310 | | // } catch (e) { |
| 311 | | // var text = $.isFunction(e.getText) ? e.getText() : "Formatting error " + e; |
| 312 | | // alert(text); |
| 313 | | // return; |
| 314 | | // } |
| 315 | | // |
| 316 | | // if (!value) { |
| 317 | | // value = []; |
| 318 | | // } |
| 319 | | // |
| 320 | | // // Check that the value is not already among the selected values. |
| 321 | | // for (var i = 0; i < value.length; i++) { |
| 322 | | // if ($.valueCompare(value[i], other)) { |
| 323 | | // // TODO i18n |
| 324 | | // alert("This value is already in the list"); |
| 325 | | // return; |
| 326 | | // } |
| 327 | | // } |
| 328 | | // |
| 329 | | // this.setValue([].concat(value, other)); |
| 330 | | // $other.val(""); |
| 331 | | // this.refreshAddOtherButtonState(); |
| 332 | | // } |
| 333 | | // |
| 334 | | // DoubleSelectControl.prototype.handleAdd = function() { |
| 335 | | // var $available = this.getElement("available"); |
| 336 | | // |
| 337 | | // var selectedValues = []; |
| 338 | | // var options = $available[0].options; |
| 339 | | // for (var i = 0; i < options.length; i++) { |
| 340 | | // if (options[i].selected) { |
| 341 | | // selectedValues.push(this.getType().parseUserValue(options[i].value)); |
| 342 | | // } |
| 343 | | // } |
| 344 | | // |
| 345 | | // var values = this.getValue(); |
| 346 | | // if (!values) { |
| 347 | | // values = selectedValues; |
| 348 | | // } else { |
| 349 | | // values = values.concat(selectedValues); |
| 350 | | // } |
| 351 | | // |
| 352 | | // this.setValue(values); |
| 353 | | // } |
| 354 | | // |
| 355 | | // DoubleSelectControl.prototype.handleAddAll = function() { |
| 356 | | // // Note that we don't simply set the value to the complete list of option |
| 357 | | // // values, as to avoid that any custom-entered values would be lost |
| 358 | | // |
| 359 | | // var $available = this.getElement("available"); |
| 360 | | // |
| 361 | | // var selectedValues = []; |
| 362 | | // var options = $available[0].options; |
| 363 | | // for (var i = 0; i < options.length; i++) { |
| 364 | | // selectedValues.push(this.getType().parseUserValue(options[i].value)); |
| 365 | | // } |
| 366 | | // |
| 367 | | // var values = this.getValue(); |
| 368 | | // if (!values) { |
| 369 | | // values = selectedValues; |
| 370 | | // } else { |
| 371 | | // values = [].concat(values, selectedValues); |
| 372 | | // } |
| 373 | | // |
| 374 | | // this.setValue(values); |
| 375 | | // } |
| 376 | | // |
| 377 | | // DoubleSelectControl.prototype.handleRemove = function() { |
| 378 | | // var $selected = this.getElement("selected"); |
| 379 | | // |
| 380 | | // var values = this.getValue(); |
| 381 | | // if (!values) { |
| 382 | | // // nothing useful can have happened |
| 383 | | // return; |
| 384 | | // } |
| 385 | | // |
| 386 | | // // make sure we don't modify the original array |
| 387 | | // values = [].concat(values); |
| 388 | | // |
| 389 | | // var options = $selected[0].options; |
| 390 | | // for (var i = options.length - 1; i >= 0; i--) { |
| 391 | | // if (options[i].selected) { |
| 392 | | // values.splice(i, 1) |
| 393 | | // } |
| 394 | | // } |
| 395 | | // |
| 396 | | // this.setValue(values); |
| 397 | | // } |
| 398 | | // |
| 399 | | // DoubleSelectControl.prototype.handleRemoveAll = function() { |
| 400 | | // this.setValue(undefined); |
| 401 | | // } |
| 402 | | // |
| 403 | | // DoubleSelectControl.prototype.setValue = function(value) { |
| 404 | | // if (value && value.constructor == Array) { |
| 405 | | // // TODO this sort is always alphabetically, let the field type provide an order function? |
| 406 | | // // TODO consider sorting on the labels (if any) rather than the value? |
| 407 | | // value.sort() |
| 408 | | // } |
| 409 | | // this["<super.call>"]("setValue", [value]); |
| 410 | | // } |
| | 74 | } |
| | 75 | |
| | 76 | AutocompleteControl.prototype.getUserValue = function() { |
| | 77 | // Not needed, should never be called |
| | 78 | } |
| | 79 | |
| | 80 | AutocompleteControl.prototype.setUserValue = function(value) { |
| | 81 | // Not needed, should never be called |
| | 82 | } |
| | 83 | |
| | 84 | AutocompleteControl.prototype.getUpdateElement = function() { |
| | 85 | // There is no 'change' event to react to |
| | 86 | return undefined; |
| | 87 | } |
| | 88 | |
| | 89 | AutocompleteControl.prototype.setValue = function(value) { |
| | 90 | // FIXME: implement? |
| | 91 | this["<super.call>"]("setValue", [value]); |
| | 92 | } |