grunt js and css files
parent
44d25bf375
commit
905c8bc5e9
|
@ -2064,6 +2064,18 @@ ul.value-dropdown li.selected {
|
|||
text-shadow: none;
|
||||
}
|
||||
|
||||
ul.value-dropdown li .preview {
|
||||
font-style: italic;
|
||||
font-size: 10px;
|
||||
width: 300px;
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
td.form-table-value > input[type="checkbox"] {
|
||||
height: 15px;
|
||||
margin-top: 7px;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1535,9 +1535,10 @@ PluginEditor = function(jsEditor, valueEditor)
|
|||
var datasourceTool = $('<li><i class="icon-plus icon-white"></i><label>DATASOURCE</label></li>').mousedown(function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
$(input).focus();
|
||||
$(input).insertAtCaret("datasources[\"");
|
||||
$(input).trigger("freeboard-eval");
|
||||
$(input).val("")
|
||||
.focus()
|
||||
.insertAtCaret("datasources[\"")
|
||||
.trigger("freeboard-eval");
|
||||
});
|
||||
|
||||
var jsEditorTool = $('<li><i class="icon-fullscreen icon-white"></i><label>.JS EDITOR</label></li>').mousedown(function(e)
|
||||
|
@ -1705,10 +1706,51 @@ PluginEditor = function(jsEditor, valueEditor)
|
|||
|
||||
ValueEditor = function(theFreeboardModel)
|
||||
{
|
||||
var _veDatasourceRegex = new RegExp(".*datasources\\[\"([^\"]*)(\"\\]\\[\")?(.*)$");
|
||||
var _veDatasourceRegex = new RegExp(".*datasources\\[\"([^\"]*)(\"\\])?(.*)$");
|
||||
|
||||
var dropdown = null;
|
||||
var selectedOptionIndex = 0;
|
||||
var _autocompleteOptions = [];
|
||||
var _autocompleteReplacementString;
|
||||
var currentValue = null;
|
||||
|
||||
var EXPECTED_TYPE = {
|
||||
ANY : "any",
|
||||
ARRAY : "array",
|
||||
OBJECT : "object",
|
||||
STRING : "string",
|
||||
NUMBER : "number",
|
||||
BOOLEAN : "boolean"
|
||||
};
|
||||
|
||||
function _isPotentialTypeMatch(value, expectsType)
|
||||
{
|
||||
if(_.isArray(value) || _.isObject(value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return _isTypeMatch(value, expectsType);
|
||||
}
|
||||
|
||||
function _isTypeMatch(value, expectsType) {
|
||||
switch(expectsType)
|
||||
{
|
||||
case EXPECTED_TYPE.ANY: return true;
|
||||
case EXPECTED_TYPE.ARRAY: return _.isArray(value);
|
||||
case EXPECTED_TYPE.OBJECT: return _.isObject(value);
|
||||
case EXPECTED_TYPE.STRING: return _.isString(value);
|
||||
case EXPECTED_TYPE.NUMBER: return _.isNumber(value);
|
||||
case EXPECTED_TYPE.BOOLEAN: return _.isBoolean(value);
|
||||
}
|
||||
}
|
||||
|
||||
function _checkCurrentValueType(element, expectsType) {
|
||||
$(element).parent().find(".validation-error").remove();
|
||||
if(!_isTypeMatch(currentValue, expectsType)) {
|
||||
$(element).parent().append("<div class='validation-error'>" +
|
||||
"This field expects an expression that evaluates to type " +
|
||||
expectsType + ".</div>");
|
||||
}
|
||||
}
|
||||
|
||||
function _resizeValueEditor(element)
|
||||
{
|
||||
|
@ -1719,25 +1761,27 @@ ValueEditor = function(theFreeboardModel)
|
|||
$(element).css({height: newHeight + "px"});
|
||||
}
|
||||
|
||||
function _autocompleteFromDatasource(inputString, datasources)
|
||||
function _autocompleteFromDatasource(inputString, datasources, expectsType)
|
||||
{
|
||||
var match = _veDatasourceRegex.exec(inputString);
|
||||
|
||||
var options = [];
|
||||
var replacementString;
|
||||
|
||||
if(match)
|
||||
{
|
||||
if(match[1] == "") // List all datasources
|
||||
// Editor value is: datasources["; List all datasources
|
||||
if(match[1] == "")
|
||||
{
|
||||
_.each(datasources, function(datasource)
|
||||
{
|
||||
options.push({value: datasource.name(), follow_char: "\"][\""});
|
||||
options.push({value: datasource.name(), entity: undefined,
|
||||
precede_char: "", follow_char: "\"]"});
|
||||
});
|
||||
}
|
||||
else if(match[1] != "" && _.isUndefined(match[2])) // List partial datasources
|
||||
// Editor value is a partial match for a datasource; list matching datasources
|
||||
else if(match[1] != "" && _.isUndefined(match[2]))
|
||||
{
|
||||
replacementString = match[1];
|
||||
var replacementString = match[1];
|
||||
|
||||
_.each(datasources, function(datasource)
|
||||
{
|
||||
|
@ -1745,12 +1789,15 @@ ValueEditor = function(theFreeboardModel)
|
|||
|
||||
if(dsName != replacementString && dsName.indexOf(replacementString) == 0)
|
||||
{
|
||||
options.push({value: dsName, follow_char: "\"][\""});
|
||||
options.push({value: dsName, entity: undefined,
|
||||
precede_char: "", follow_char: "\"]"});
|
||||
}
|
||||
});
|
||||
}
|
||||
// Editor value matches a datasources; parse JSON in order to populate list
|
||||
else
|
||||
{
|
||||
// We already have a datasource selected; find it
|
||||
var datasource = _.find(datasources, function(datasource)
|
||||
{
|
||||
return (datasource.name() === match[1]);
|
||||
|
@ -1758,191 +1805,189 @@ ValueEditor = function(theFreeboardModel)
|
|||
|
||||
if(!_.isUndefined(datasource))
|
||||
{
|
||||
var dataPath = "";
|
||||
var dataPath = "data";
|
||||
var remainder = "";
|
||||
|
||||
// Parse the partial JSON selectors
|
||||
if(!_.isUndefined(match[2]))
|
||||
{
|
||||
dataPath = match[2] + match[3];
|
||||
}
|
||||
|
||||
var dataPathItems = dataPath.split("\"][\"");
|
||||
dataPath = "data";
|
||||
|
||||
for(var index = 1; index < dataPathItems.length - 1; index++)
|
||||
{
|
||||
if(dataPathItems[index] != "")
|
||||
{
|
||||
dataPathItem = "[\"" + dataPathItems[index] + "\"]";
|
||||
dataPath = dataPath + dataPathItem;
|
||||
}
|
||||
}
|
||||
|
||||
var lastPathObject = _.last(dataPathItems);
|
||||
|
||||
// If the last character is a ", then ignore it
|
||||
if(lastPathObject.charAt(lastPathObject.length - 1) == "\"")
|
||||
{
|
||||
lastPathObject = lastPathObject.replace(/\[\"?$/, "");
|
||||
dataPath = dataPath + "[\"" + lastPathObject + "\"]";
|
||||
// Strip any incomplete field values, and store the remainder
|
||||
var remainderIndex = match[3].lastIndexOf("]") + 1;
|
||||
dataPath = dataPath + match[3].substring(0, remainderIndex);
|
||||
remainder = match[3].substring(remainderIndex, match[3].length);
|
||||
remainder = remainder.replace(/^[\[\"]*/, "");
|
||||
remainder = remainder.replace(/[\"\]]*$/, "");
|
||||
}
|
||||
|
||||
// Get the data for the last complete JSON field
|
||||
var dataValue = datasource.getDataRepresentation(dataPath);
|
||||
currentValue = dataValue;
|
||||
|
||||
// For arrays, list out the indices
|
||||
if(_.isArray(dataValue))
|
||||
{
|
||||
for(var index = 0; index < dataValue.length; index++)
|
||||
{
|
||||
var followChar = "\"]";
|
||||
|
||||
if(_.isObject(dataValue[index]))
|
||||
if(index.toString().indexOf(remainder) == 0)
|
||||
{
|
||||
followChar = followChar + "\"][\"";
|
||||
var value = dataValue[index];
|
||||
if(_isPotentialTypeMatch(value, expectsType))
|
||||
{
|
||||
options.push({value: index, entity: value,
|
||||
precede_char: "[", follow_char: "]",
|
||||
preview: value.toString()});
|
||||
}
|
||||
}
|
||||
else if(_.isArray(dataValue[index]))
|
||||
{
|
||||
followChar = followChar + "\"][";
|
||||
}
|
||||
|
||||
options.push({value: index, follow_char: followChar});
|
||||
}
|
||||
}
|
||||
// For objects, list out the keys
|
||||
else if(_.isObject(dataValue))
|
||||
{
|
||||
replacementString = lastPathObject;
|
||||
|
||||
if(_.keys(dataValue).indexOf(replacementString) == -1)
|
||||
_.each(dataValue, function(value, name)
|
||||
{
|
||||
_.each(dataValue, function(value, name)
|
||||
if(name.indexOf(remainder) == 0)
|
||||
{
|
||||
if(name != lastPathObject && name.indexOf(lastPathObject) == 0)
|
||||
if(_isPotentialTypeMatch(value, expectsType))
|
||||
{
|
||||
var followChar = "\"]";
|
||||
|
||||
if(_.isArray(value))
|
||||
{
|
||||
followChar = "\"][";
|
||||
}
|
||||
else if(_.isObject(value))
|
||||
{
|
||||
followChar = "\"][\"";
|
||||
}
|
||||
|
||||
options.push({value: name, follow_char: followChar});
|
||||
options.push({value: name, entity: value,
|
||||
precede_char: "[\"", follow_char: "\"]"});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// For everything else, do nothing (no further selection possible)
|
||||
else
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_autocompleteOptions = options;
|
||||
_autocompleteReplacementString = replacementString;
|
||||
}
|
||||
|
||||
function createValueEditor(element)
|
||||
function _renderAutocompleteDropdown(element, expectsType)
|
||||
{
|
||||
var dropdown = null;
|
||||
var selectedOptionIndex = 0;
|
||||
var inputString = $(element).val().substring(0, $(element).getCaretPosition());
|
||||
|
||||
$(element).addClass("calculated-value-input").bind("keyup mouseup freeboard-eval",function(event)
|
||||
// Weird issue where the textarea box was putting in ASCII (nbsp) for spaces.
|
||||
inputString = inputString.replace(String.fromCharCode(160), " ");
|
||||
|
||||
_autocompleteFromDatasource(inputString, theFreeboardModel.datasources(), expectsType);
|
||||
|
||||
if(_autocompleteOptions.length > 0)
|
||||
{
|
||||
// Ignore arrow keys and enter keys
|
||||
if(dropdown && event.type == "keyup" && (event.keyCode == 38 || event.keyCode == 40 || event.keyCode == 13))
|
||||
if(!dropdown)
|
||||
{
|
||||
event.preventDefault();
|
||||
return;
|
||||
dropdown = $('<ul id="value-selector" class="value-dropdown"></ul>')
|
||||
.insertAfter(element)
|
||||
.width($(element).outerWidth() - 2)
|
||||
.css("left", $(element).position().left)
|
||||
.css("top", $(element).position().top + $(element).outerHeight() - 1);
|
||||
}
|
||||
|
||||
var inputString = $(element).val().substring(0, $(element).getCaretPosition());
|
||||
inputString = inputString.replace(String.fromCharCode(160), " "); // Weird issue where the textarea box was putting in ASCII (non breaking space) for spaces.
|
||||
dropdown.empty();
|
||||
dropdown.scrollTop(0);
|
||||
|
||||
_autocompleteFromDatasource(inputString, theFreeboardModel.datasources());
|
||||
var selected = true;
|
||||
selectedOptionIndex = 0;
|
||||
|
||||
if(_autocompleteOptions.length > 0)
|
||||
_.each(_autocompleteOptions, function(option, index)
|
||||
{
|
||||
if(!dropdown)
|
||||
var li = _renderAutocompleteDropdownOption(element, inputString, option, index);
|
||||
if(selected)
|
||||
{
|
||||
dropdown = $('<ul id="value-selector" class="value-dropdown"></ul>').insertAfter(element).width($(element).outerWidth() - 2).css("left", $(element).position().left).css("top", $(element).position().top + $(element).outerHeight() - 1);
|
||||
$(li).addClass("selected");
|
||||
selected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_checkCurrentValueType(element, expectsType);
|
||||
$(element).next("ul#value-selector").remove();
|
||||
dropdown = null;
|
||||
selectedOptionIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function _renderAutocompleteDropdownOption(element, inputString, option, currentIndex)
|
||||
{
|
||||
var optionLabel = option.value;
|
||||
if(option.preview)
|
||||
{
|
||||
optionLabel = optionLabel + "<span class='preview'>" + option.preview + "</span>";
|
||||
}
|
||||
var li = $('<li>' + optionLabel + '</li>').appendTo(dropdown)
|
||||
.mouseenter(function()
|
||||
{
|
||||
$(this).trigger("freeboard-select");
|
||||
})
|
||||
.mousedown(function(event)
|
||||
{
|
||||
$(this).trigger("freeboard-insertValue");
|
||||
event.preventDefault();
|
||||
})
|
||||
.data("freeboard-optionIndex", currentIndex)
|
||||
.data("freeboard-optionValue", option.value)
|
||||
.bind("freeboard-insertValue", function()
|
||||
{
|
||||
var optionValue = option.value;
|
||||
optionValue = option.precede_char + optionValue + option.follow_char;
|
||||
|
||||
var replacementIndex = inputString.lastIndexOf("]");
|
||||
if(replacementIndex != -1)
|
||||
{
|
||||
$(element).replaceTextAt(replacementIndex+1, $(element).val().length,
|
||||
optionValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$(element).insertAtCaret(optionValue);
|
||||
}
|
||||
|
||||
dropdown.empty();
|
||||
dropdown.scrollTop(0);
|
||||
|
||||
var selected = true;
|
||||
selectedOptionIndex = 0;
|
||||
|
||||
var currentIndex = 0;
|
||||
|
||||
_.each(_autocompleteOptions, function(option)
|
||||
{
|
||||
var li = $('<li>' + option.value + '</li>').appendTo(dropdown).mouseenter(function()
|
||||
{
|
||||
$(this).trigger("freeboard-select");
|
||||
}).mousedown(function(event)
|
||||
{
|
||||
$(this).trigger("freeboard-insertValue");
|
||||
event.preventDefault();
|
||||
}).data("freeboard-optionIndex", currentIndex).data("freeboard-optionValue", option.value).bind("freeboard-insertValue",function()
|
||||
{
|
||||
var optionValue = option.value;
|
||||
|
||||
if(!_.isUndefined(option.follow_char))
|
||||
{
|
||||
optionValue = optionValue + option.follow_char;
|
||||
}
|
||||
|
||||
if(!_.isUndefined(_autocompleteReplacementString))
|
||||
{
|
||||
var replacementIndex = inputString.lastIndexOf(_autocompleteReplacementString);
|
||||
|
||||
if(replacementIndex != -1)
|
||||
{
|
||||
$(element).replaceTextAt(replacementIndex, replacementIndex + _autocompleteReplacementString.length, optionValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$(element).insertAtCaret(optionValue);
|
||||
}
|
||||
|
||||
$(element).triggerHandler("mouseup");
|
||||
}).bind("freeboard-select", function()
|
||||
{
|
||||
$(this).parent().find("li.selected").removeClass("selected");
|
||||
$(this).addClass("selected");
|
||||
selectedOptionIndex = $(this).data("freeboard-optionIndex");
|
||||
});
|
||||
|
||||
if(selected)
|
||||
{
|
||||
$(li).addClass("selected");
|
||||
selected = false;
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
});
|
||||
}
|
||||
else
|
||||
currentValue = option.entity;
|
||||
$(element).triggerHandler("mouseup");
|
||||
})
|
||||
.bind("freeboard-select", function()
|
||||
{
|
||||
$(element).next("ul#value-selector").remove();
|
||||
dropdown = null;
|
||||
selectedOptionIndex = -1;
|
||||
}
|
||||
}).focus(function()
|
||||
$(this).parent().find("li.selected").removeClass("selected");
|
||||
$(this).addClass("selected");
|
||||
selectedOptionIndex = $(this).data("freeboard-optionIndex");
|
||||
});
|
||||
return li;
|
||||
}
|
||||
|
||||
function createValueEditor(element, expectsType)
|
||||
{
|
||||
$(element).addClass("calculated-value-input")
|
||||
.bind("keyup mouseup freeboard-eval", function(event) {
|
||||
// Ignore arrow keys and enter keys
|
||||
if(dropdown && event.type == "keyup"
|
||||
&& (event.keyCode == 38 || event.keyCode == 40 || event.keyCode == 13))
|
||||
{
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
_renderAutocompleteDropdown(element, expectsType);
|
||||
})
|
||||
.focus(function()
|
||||
{
|
||||
$(element).css({"z-index" : 3001});
|
||||
_resizeValueEditor(element);
|
||||
}).focusout(function()
|
||||
})
|
||||
.focusout(function()
|
||||
{
|
||||
_checkCurrentValueType(element, expectsType);
|
||||
$(element).css({
|
||||
"height": "",
|
||||
"z-index" : 3000
|
||||
});
|
||||
|
||||
$(element).next("ul#value-selector").remove();
|
||||
dropdown = null;
|
||||
selectedOptionIndex = -1;
|
||||
}).bind("keydown", function(event)
|
||||
})
|
||||
.bind("keydown", function(event)
|
||||
{
|
||||
|
||||
if(dropdown)
|
||||
|
@ -1982,7 +2027,8 @@ ValueEditor = function(theFreeboardModel)
|
|||
|
||||
if(selectedOptionIndex != -1)
|
||||
{
|
||||
$(dropdown).find("li").eq(selectedOptionIndex).trigger("freeboard-insertValue");
|
||||
$(dropdown).find("li").eq(selectedOptionIndex)
|
||||
.trigger("freeboard-insertValue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1991,10 +2037,17 @@ ValueEditor = function(theFreeboardModel)
|
|||
|
||||
// Public API
|
||||
return {
|
||||
createValueEditor : function(element)
|
||||
createValueEditor : function(element, expectsType)
|
||||
{
|
||||
createValueEditor(element);
|
||||
}
|
||||
if(expectsType)
|
||||
{
|
||||
createValueEditor(element, expectsType);
|
||||
}
|
||||
else {
|
||||
createValueEditor(element, EXPECTED_TYPE.ANY);
|
||||
}
|
||||
},
|
||||
EXPECTED_TYPE : EXPECTED_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
351
js/freeboard.js
351
js/freeboard.js
|
@ -1535,9 +1535,10 @@ PluginEditor = function(jsEditor, valueEditor)
|
|||
var datasourceTool = $('<li><i class="icon-plus icon-white"></i><label>DATASOURCE</label></li>').mousedown(function(e)
|
||||
{
|
||||
e.preventDefault();
|
||||
$(input).focus();
|
||||
$(input).insertAtCaret("datasources[\"");
|
||||
$(input).trigger("freeboard-eval");
|
||||
$(input).val("")
|
||||
.focus()
|
||||
.insertAtCaret("datasources[\"")
|
||||
.trigger("freeboard-eval");
|
||||
});
|
||||
|
||||
var jsEditorTool = $('<li><i class="icon-fullscreen icon-white"></i><label>.JS EDITOR</label></li>').mousedown(function(e)
|
||||
|
@ -1705,10 +1706,51 @@ PluginEditor = function(jsEditor, valueEditor)
|
|||
|
||||
ValueEditor = function(theFreeboardModel)
|
||||
{
|
||||
var _veDatasourceRegex = new RegExp(".*datasources\\[\"([^\"]*)(\"\\]\\[\")?(.*)$");
|
||||
var _veDatasourceRegex = new RegExp(".*datasources\\[\"([^\"]*)(\"\\])?(.*)$");
|
||||
|
||||
var dropdown = null;
|
||||
var selectedOptionIndex = 0;
|
||||
var _autocompleteOptions = [];
|
||||
var _autocompleteReplacementString;
|
||||
var currentValue = null;
|
||||
|
||||
var EXPECTED_TYPE = {
|
||||
ANY : "any",
|
||||
ARRAY : "array",
|
||||
OBJECT : "object",
|
||||
STRING : "string",
|
||||
NUMBER : "number",
|
||||
BOOLEAN : "boolean"
|
||||
};
|
||||
|
||||
function _isPotentialTypeMatch(value, expectsType)
|
||||
{
|
||||
if(_.isArray(value) || _.isObject(value))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return _isTypeMatch(value, expectsType);
|
||||
}
|
||||
|
||||
function _isTypeMatch(value, expectsType) {
|
||||
switch(expectsType)
|
||||
{
|
||||
case EXPECTED_TYPE.ANY: return true;
|
||||
case EXPECTED_TYPE.ARRAY: return _.isArray(value);
|
||||
case EXPECTED_TYPE.OBJECT: return _.isObject(value);
|
||||
case EXPECTED_TYPE.STRING: return _.isString(value);
|
||||
case EXPECTED_TYPE.NUMBER: return _.isNumber(value);
|
||||
case EXPECTED_TYPE.BOOLEAN: return _.isBoolean(value);
|
||||
}
|
||||
}
|
||||
|
||||
function _checkCurrentValueType(element, expectsType) {
|
||||
$(element).parent().find(".validation-error").remove();
|
||||
if(!_isTypeMatch(currentValue, expectsType)) {
|
||||
$(element).parent().append("<div class='validation-error'>" +
|
||||
"This field expects an expression that evaluates to type " +
|
||||
expectsType + ".</div>");
|
||||
}
|
||||
}
|
||||
|
||||
function _resizeValueEditor(element)
|
||||
{
|
||||
|
@ -1719,25 +1761,27 @@ ValueEditor = function(theFreeboardModel)
|
|||
$(element).css({height: newHeight + "px"});
|
||||
}
|
||||
|
||||
function _autocompleteFromDatasource(inputString, datasources)
|
||||
function _autocompleteFromDatasource(inputString, datasources, expectsType)
|
||||
{
|
||||
var match = _veDatasourceRegex.exec(inputString);
|
||||
|
||||
var options = [];
|
||||
var replacementString;
|
||||
|
||||
if(match)
|
||||
{
|
||||
if(match[1] == "") // List all datasources
|
||||
// Editor value is: datasources["; List all datasources
|
||||
if(match[1] == "")
|
||||
{
|
||||
_.each(datasources, function(datasource)
|
||||
{
|
||||
options.push({value: datasource.name(), follow_char: "\"][\""});
|
||||
options.push({value: datasource.name(), entity: undefined,
|
||||
precede_char: "", follow_char: "\"]"});
|
||||
});
|
||||
}
|
||||
else if(match[1] != "" && _.isUndefined(match[2])) // List partial datasources
|
||||
// Editor value is a partial match for a datasource; list matching datasources
|
||||
else if(match[1] != "" && _.isUndefined(match[2]))
|
||||
{
|
||||
replacementString = match[1];
|
||||
var replacementString = match[1];
|
||||
|
||||
_.each(datasources, function(datasource)
|
||||
{
|
||||
|
@ -1745,12 +1789,15 @@ ValueEditor = function(theFreeboardModel)
|
|||
|
||||
if(dsName != replacementString && dsName.indexOf(replacementString) == 0)
|
||||
{
|
||||
options.push({value: dsName, follow_char: "\"][\""});
|
||||
options.push({value: dsName, entity: undefined,
|
||||
precede_char: "", follow_char: "\"]"});
|
||||
}
|
||||
});
|
||||
}
|
||||
// Editor value matches a datasources; parse JSON in order to populate list
|
||||
else
|
||||
{
|
||||
// We already have a datasource selected; find it
|
||||
var datasource = _.find(datasources, function(datasource)
|
||||
{
|
||||
return (datasource.name() === match[1]);
|
||||
|
@ -1758,191 +1805,189 @@ ValueEditor = function(theFreeboardModel)
|
|||
|
||||
if(!_.isUndefined(datasource))
|
||||
{
|
||||
var dataPath = "";
|
||||
var dataPath = "data";
|
||||
var remainder = "";
|
||||
|
||||
// Parse the partial JSON selectors
|
||||
if(!_.isUndefined(match[2]))
|
||||
{
|
||||
dataPath = match[2] + match[3];
|
||||
}
|
||||
|
||||
var dataPathItems = dataPath.split("\"][\"");
|
||||
dataPath = "data";
|
||||
|
||||
for(var index = 1; index < dataPathItems.length - 1; index++)
|
||||
{
|
||||
if(dataPathItems[index] != "")
|
||||
{
|
||||
dataPathItem = "[\"" + dataPathItems[index] + "\"]";
|
||||
dataPath = dataPath + dataPathItem;
|
||||
}
|
||||
}
|
||||
|
||||
var lastPathObject = _.last(dataPathItems);
|
||||
|
||||
// If the last character is a ", then ignore it
|
||||
if(lastPathObject.charAt(lastPathObject.length - 1) == "\"")
|
||||
{
|
||||
lastPathObject = lastPathObject.replace(/\[\"?$/, "");
|
||||
dataPath = dataPath + "[\"" + lastPathObject + "\"]";
|
||||
// Strip any incomplete field values, and store the remainder
|
||||
var remainderIndex = match[3].lastIndexOf("]") + 1;
|
||||
dataPath = dataPath + match[3].substring(0, remainderIndex);
|
||||
remainder = match[3].substring(remainderIndex, match[3].length);
|
||||
remainder = remainder.replace(/^[\[\"]*/, "");
|
||||
remainder = remainder.replace(/[\"\]]*$/, "");
|
||||
}
|
||||
|
||||
// Get the data for the last complete JSON field
|
||||
var dataValue = datasource.getDataRepresentation(dataPath);
|
||||
currentValue = dataValue;
|
||||
|
||||
// For arrays, list out the indices
|
||||
if(_.isArray(dataValue))
|
||||
{
|
||||
for(var index = 0; index < dataValue.length; index++)
|
||||
{
|
||||
var followChar = "\"]";
|
||||
|
||||
if(_.isObject(dataValue[index]))
|
||||
if(index.toString().indexOf(remainder) == 0)
|
||||
{
|
||||
followChar = followChar + "\"][\"";
|
||||
var value = dataValue[index];
|
||||
if(_isPotentialTypeMatch(value, expectsType))
|
||||
{
|
||||
options.push({value: index, entity: value,
|
||||
precede_char: "[", follow_char: "]",
|
||||
preview: value.toString()});
|
||||
}
|
||||
}
|
||||
else if(_.isArray(dataValue[index]))
|
||||
{
|
||||
followChar = followChar + "\"][";
|
||||
}
|
||||
|
||||
options.push({value: index, follow_char: followChar});
|
||||
}
|
||||
}
|
||||
// For objects, list out the keys
|
||||
else if(_.isObject(dataValue))
|
||||
{
|
||||
replacementString = lastPathObject;
|
||||
|
||||
if(_.keys(dataValue).indexOf(replacementString) == -1)
|
||||
_.each(dataValue, function(value, name)
|
||||
{
|
||||
_.each(dataValue, function(value, name)
|
||||
if(name.indexOf(remainder) == 0)
|
||||
{
|
||||
if(name != lastPathObject && name.indexOf(lastPathObject) == 0)
|
||||
if(_isPotentialTypeMatch(value, expectsType))
|
||||
{
|
||||
var followChar = "\"]";
|
||||
|
||||
if(_.isArray(value))
|
||||
{
|
||||
followChar = "\"][";
|
||||
}
|
||||
else if(_.isObject(value))
|
||||
{
|
||||
followChar = "\"][\"";
|
||||
}
|
||||
|
||||
options.push({value: name, follow_char: followChar});
|
||||
options.push({value: name, entity: value,
|
||||
precede_char: "[\"", follow_char: "\"]"});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
// For everything else, do nothing (no further selection possible)
|
||||
else
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_autocompleteOptions = options;
|
||||
_autocompleteReplacementString = replacementString;
|
||||
}
|
||||
|
||||
function createValueEditor(element)
|
||||
function _renderAutocompleteDropdown(element, expectsType)
|
||||
{
|
||||
var dropdown = null;
|
||||
var selectedOptionIndex = 0;
|
||||
var inputString = $(element).val().substring(0, $(element).getCaretPosition());
|
||||
|
||||
$(element).addClass("calculated-value-input").bind("keyup mouseup freeboard-eval",function(event)
|
||||
// Weird issue where the textarea box was putting in ASCII (nbsp) for spaces.
|
||||
inputString = inputString.replace(String.fromCharCode(160), " ");
|
||||
|
||||
_autocompleteFromDatasource(inputString, theFreeboardModel.datasources(), expectsType);
|
||||
|
||||
if(_autocompleteOptions.length > 0)
|
||||
{
|
||||
// Ignore arrow keys and enter keys
|
||||
if(dropdown && event.type == "keyup" && (event.keyCode == 38 || event.keyCode == 40 || event.keyCode == 13))
|
||||
if(!dropdown)
|
||||
{
|
||||
event.preventDefault();
|
||||
return;
|
||||
dropdown = $('<ul id="value-selector" class="value-dropdown"></ul>')
|
||||
.insertAfter(element)
|
||||
.width($(element).outerWidth() - 2)
|
||||
.css("left", $(element).position().left)
|
||||
.css("top", $(element).position().top + $(element).outerHeight() - 1);
|
||||
}
|
||||
|
||||
var inputString = $(element).val().substring(0, $(element).getCaretPosition());
|
||||
inputString = inputString.replace(String.fromCharCode(160), " "); // Weird issue where the textarea box was putting in ASCII (non breaking space) for spaces.
|
||||
dropdown.empty();
|
||||
dropdown.scrollTop(0);
|
||||
|
||||
_autocompleteFromDatasource(inputString, theFreeboardModel.datasources());
|
||||
var selected = true;
|
||||
selectedOptionIndex = 0;
|
||||
|
||||
if(_autocompleteOptions.length > 0)
|
||||
_.each(_autocompleteOptions, function(option, index)
|
||||
{
|
||||
if(!dropdown)
|
||||
var li = _renderAutocompleteDropdownOption(element, inputString, option, index);
|
||||
if(selected)
|
||||
{
|
||||
dropdown = $('<ul id="value-selector" class="value-dropdown"></ul>').insertAfter(element).width($(element).outerWidth() - 2).css("left", $(element).position().left).css("top", $(element).position().top + $(element).outerHeight() - 1);
|
||||
$(li).addClass("selected");
|
||||
selected = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
_checkCurrentValueType(element, expectsType);
|
||||
$(element).next("ul#value-selector").remove();
|
||||
dropdown = null;
|
||||
selectedOptionIndex = -1;
|
||||
}
|
||||
}
|
||||
|
||||
function _renderAutocompleteDropdownOption(element, inputString, option, currentIndex)
|
||||
{
|
||||
var optionLabel = option.value;
|
||||
if(option.preview)
|
||||
{
|
||||
optionLabel = optionLabel + "<span class='preview'>" + option.preview + "</span>";
|
||||
}
|
||||
var li = $('<li>' + optionLabel + '</li>').appendTo(dropdown)
|
||||
.mouseenter(function()
|
||||
{
|
||||
$(this).trigger("freeboard-select");
|
||||
})
|
||||
.mousedown(function(event)
|
||||
{
|
||||
$(this).trigger("freeboard-insertValue");
|
||||
event.preventDefault();
|
||||
})
|
||||
.data("freeboard-optionIndex", currentIndex)
|
||||
.data("freeboard-optionValue", option.value)
|
||||
.bind("freeboard-insertValue", function()
|
||||
{
|
||||
var optionValue = option.value;
|
||||
optionValue = option.precede_char + optionValue + option.follow_char;
|
||||
|
||||
var replacementIndex = inputString.lastIndexOf("]");
|
||||
if(replacementIndex != -1)
|
||||
{
|
||||
$(element).replaceTextAt(replacementIndex+1, $(element).val().length,
|
||||
optionValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
$(element).insertAtCaret(optionValue);
|
||||
}
|
||||
|
||||
dropdown.empty();
|
||||
dropdown.scrollTop(0);
|
||||
|
||||
var selected = true;
|
||||
selectedOptionIndex = 0;
|
||||
|
||||
var currentIndex = 0;
|
||||
|
||||
_.each(_autocompleteOptions, function(option)
|
||||
{
|
||||
var li = $('<li>' + option.value + '</li>').appendTo(dropdown).mouseenter(function()
|
||||
{
|
||||
$(this).trigger("freeboard-select");
|
||||
}).mousedown(function(event)
|
||||
{
|
||||
$(this).trigger("freeboard-insertValue");
|
||||
event.preventDefault();
|
||||
}).data("freeboard-optionIndex", currentIndex).data("freeboard-optionValue", option.value).bind("freeboard-insertValue",function()
|
||||
{
|
||||
var optionValue = option.value;
|
||||
|
||||
if(!_.isUndefined(option.follow_char))
|
||||
{
|
||||
optionValue = optionValue + option.follow_char;
|
||||
}
|
||||
|
||||
if(!_.isUndefined(_autocompleteReplacementString))
|
||||
{
|
||||
var replacementIndex = inputString.lastIndexOf(_autocompleteReplacementString);
|
||||
|
||||
if(replacementIndex != -1)
|
||||
{
|
||||
$(element).replaceTextAt(replacementIndex, replacementIndex + _autocompleteReplacementString.length, optionValue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$(element).insertAtCaret(optionValue);
|
||||
}
|
||||
|
||||
$(element).triggerHandler("mouseup");
|
||||
}).bind("freeboard-select", function()
|
||||
{
|
||||
$(this).parent().find("li.selected").removeClass("selected");
|
||||
$(this).addClass("selected");
|
||||
selectedOptionIndex = $(this).data("freeboard-optionIndex");
|
||||
});
|
||||
|
||||
if(selected)
|
||||
{
|
||||
$(li).addClass("selected");
|
||||
selected = false;
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
});
|
||||
}
|
||||
else
|
||||
currentValue = option.entity;
|
||||
$(element).triggerHandler("mouseup");
|
||||
})
|
||||
.bind("freeboard-select", function()
|
||||
{
|
||||
$(element).next("ul#value-selector").remove();
|
||||
dropdown = null;
|
||||
selectedOptionIndex = -1;
|
||||
}
|
||||
}).focus(function()
|
||||
$(this).parent().find("li.selected").removeClass("selected");
|
||||
$(this).addClass("selected");
|
||||
selectedOptionIndex = $(this).data("freeboard-optionIndex");
|
||||
});
|
||||
return li;
|
||||
}
|
||||
|
||||
function createValueEditor(element, expectsType)
|
||||
{
|
||||
$(element).addClass("calculated-value-input")
|
||||
.bind("keyup mouseup freeboard-eval", function(event) {
|
||||
// Ignore arrow keys and enter keys
|
||||
if(dropdown && event.type == "keyup"
|
||||
&& (event.keyCode == 38 || event.keyCode == 40 || event.keyCode == 13))
|
||||
{
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
_renderAutocompleteDropdown(element, expectsType);
|
||||
})
|
||||
.focus(function()
|
||||
{
|
||||
$(element).css({"z-index" : 3001});
|
||||
_resizeValueEditor(element);
|
||||
}).focusout(function()
|
||||
})
|
||||
.focusout(function()
|
||||
{
|
||||
_checkCurrentValueType(element, expectsType);
|
||||
$(element).css({
|
||||
"height": "",
|
||||
"z-index" : 3000
|
||||
});
|
||||
|
||||
$(element).next("ul#value-selector").remove();
|
||||
dropdown = null;
|
||||
selectedOptionIndex = -1;
|
||||
}).bind("keydown", function(event)
|
||||
})
|
||||
.bind("keydown", function(event)
|
||||
{
|
||||
|
||||
if(dropdown)
|
||||
|
@ -1982,7 +2027,8 @@ ValueEditor = function(theFreeboardModel)
|
|||
|
||||
if(selectedOptionIndex != -1)
|
||||
{
|
||||
$(dropdown).find("li").eq(selectedOptionIndex).trigger("freeboard-insertValue");
|
||||
$(dropdown).find("li").eq(selectedOptionIndex)
|
||||
.trigger("freeboard-insertValue");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1991,10 +2037,17 @@ ValueEditor = function(theFreeboardModel)
|
|||
|
||||
// Public API
|
||||
return {
|
||||
createValueEditor : function(element)
|
||||
createValueEditor : function(element, expectsType)
|
||||
{
|
||||
createValueEditor(element);
|
||||
}
|
||||
if(expectsType)
|
||||
{
|
||||
createValueEditor(element, expectsType);
|
||||
}
|
||||
else {
|
||||
createValueEditor(element, EXPECTED_TYPE.ANY);
|
||||
}
|
||||
},
|
||||
EXPECTED_TYPE : EXPECTED_TYPE
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue