blob: 2af73ce73076c80fbce4f23d99f09f199bda9a97 [file] [log] [blame]
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
define(["dojo/_base/declare",
"dojo/_base/array",
"dojo/_base/lang",
"dojo/string",
"dojo/dom-construct",
"dojo/dom-style",
"dojo/sniff",
"dojo/text!query/CriteriaPane.html",
"dojox/html/entities",
"dojo/Evented",
"dojo/number",
"dijit/_WidgetBase",
"dijit/_TemplatedMixin",
"dijit/_WidgetsInTemplateMixin",
"dijit/layout/ContentPane",
"dijit/TitlePane",
"dijit/form/Button",
"dijit/form/ValidationTextBox",
"dijit/form/TextBox",
"dijit/form/NumberTextBox",
"dijit/form/MultiSelect",
"dijit/form/TimeTextBox",
"dijit/form/DateTextBox",
"dijit/form/NumberSpinner",
"dojo/domReady!"],
function (declare, array, lang, string, domConstruct, domStyle, has, template, entities, Evented, number)
{
var ANY = "any";
var IS_NULL = "is null";
var IS_NOT_NULL = "is not null";
var CONTAINS = "contains";
var NOT_CONTAINS = "not contains";
var STARTS_WITH = "starts with";
var ENDS_WIDTH = "ends with";
var NOT_STARTS_WITH = "not starts with";
var NOT_ENDS_WIDTH = "not ends with";
var NOT = "not";
var EQUAL = "=";
var NOT_EQUAL = "<>";
var LESS_THAN = "<";
var LESS_EQUAL_THAN = "<=";
var GREATER_THAN = ">";
var GREATER_EQUAL_THAN = ">=";
var IN = "in"
var NOT_IN = "not in"
var CONDITIONS_NOT_NEEDING_WIDGET = [ANY, IS_NULL, IS_NOT_NULL];
var BOOLEAN_CONDITIONS = [ANY, IS_NULL, IS_NOT_NULL, EQUAL, NOT_EQUAL];
var STRING_CONDITIONS = [ANY,
IS_NULL,
IS_NOT_NULL,
EQUAL,
NOT_EQUAL,
CONTAINS,
NOT_CONTAINS,
STARTS_WITH,
ENDS_WIDTH,
NOT_STARTS_WITH,
NOT_ENDS_WIDTH];
var NUMERIC_CONDITIONS = [ANY,
IS_NULL,
IS_NOT_NULL,
EQUAL,
NOT_EQUAL,
LESS_THAN,
LESS_EQUAL_THAN,
GREATER_THAN,
GREATER_EQUAL_THAN];
var ENUM_CONDITIONS = [ANY, IS_NULL, IS_NOT_NULL, IN, NOT_IN];
var DATE_CONDITIONS = [ANY,
IS_NULL,
IS_NOT_NULL,
EQUAL,
NOT_EQUAL,
LESS_THAN,
LESS_EQUAL_THAN,
GREATER_THAN,
GREATER_EQUAL_THAN];
var UNSUPPORTED_TYPES_CONDITIONS = [ANY, IS_NULL, IS_NOT_NULL];
var UNSUPPORTED_TYPES = ["Map", "Collection", "Set", "List"];
var sqlEscape = function (value)
{
return value.replace(/'/g, "'''");
}
var sqlLikeEscape = function (value)
{
return sqlEscape(value)
.replace(/%/g, "\\%")
.replace(/_/g, "\\_");
}
var sqlValue = function (value, type)
{
return (isNumericType(type) || type === "Boolean" || type === "Date") ? value : "'" + sqlEscape(value) + "'"
}
var isNumericType = function (type)
{
return (type === "Long" || type == "Integer" || type == "Double" || type == "Float" || type == "Short"
|| type == "Number"
);
}
var buildExpression = function (name, condition, value, type)
{
if (condition === ANY)
{
return undefined;
}
else if (condition === IS_NULL)
{
return name + " " + IS_NULL;
}
else if (condition === IS_NOT_NULL)
{
return name + " " + IS_NOT_NULL;
}
else if (condition === CONTAINS || condition === NOT_CONTAINS)
{
var exp = (condition === NOT_CONTAINS ? " " + NOT : "");
return name + exp + " like '%" + sqlLikeEscape(value) + "%' escape '\\'";
}
else if (condition === STARTS_WITH || condition === NOT_STARTS_WITH)
{
var exp = (condition === NOT_STARTS_WITH ? " " + NOT : "");
return name + exp + " like '" + sqlLikeEscape(value) + "%' escape '\\'";
}
else if (condition === ENDS_WIDTH || condition === NOT_ENDS_WIDTH)
{
var exp = (condition === NOT_ENDS_WIDTH ? " " + NOT : "");
return name + exp + " like '%" + sqlLikeEscape(value) + "' escape '\\'";
}
else if (condition === IN || condition === NOT_IN)
{
if (!value)
{
return undefined;
}
var exp = condition + " (";
if (lang.isArray(value))
{
if (!value.length)
{
return undefined;
}
for (var i = 0; i < value.length; i++)
{
if (i > 0)
{
exp = exp + ",";
}
exp = exp + sqlValue(value[i], type);
}
}
else
{
exp = exp + sqlValue(value, type);
}
exp = exp + " )";
return name + " " + exp;
}
else
{
return name + " " + condition + " " + sqlValue(value, type);
}
}
var getTypeConditions = function (type, validValues)
{
if (type === "Boolean")
{
return BOOLEAN_CONDITIONS;
}
if (type === "Date")
{
return DATE_CONDITIONS;
}
else if (isNumericType(type))
{
return NUMERIC_CONDITIONS;
}
else
{
if (UNSUPPORTED_TYPES.indexOf(type) != -1)
{
return UNSUPPORTED_TYPES_CONDITIONS;
}
if (validValues && validValues.length)
{
return ENUM_CONDITIONS;
}
else
{
return STRING_CONDITIONS;
}
}
}
var arrayToOptions = function (conditions, defaultValue)
{
var options = [];
for (var i = 0; i < conditions.length; i++)
{
var selected = conditions[i] == defaultValue;
options.push({
label: conditions[i],
value: conditions[i],
selected: selected
});
}
return options;
}
var conditionHasWidget = function (condition)
{
return array.indexOf(CONDITIONS_NOT_NEEDING_WIDGET, condition) == -1;
}
var isInt = function isInt(value)
{
return !isNaN(value) && parseInt(Number(value)) == value && !isNaN(parseInt(value, 10));
}
// dojo TimeTextBox has a bug in Firefox
// https://bugzilla.mozilla.org/show_bug.cgi?id=487897
// Custom TimePicker is implemented for Firefox
var TimePicker = declare("qpid.management.TimePicker", [dijit._WidgetBase, Evented], {
disabled: false,
intermediateChanges: false,
value: undefined,
buildRendering: function ()
{
var domNode = this.domNode = domConstruct.create("div", {className: "dijitReset dijitInline"});
domConstruct.create("span", {innerHTML: "T"}, domNode);
var timeNode = domConstruct.create("div", {}, domNode);
this.hoursEditor = new dijit.form.NumberSpinner({
name: "hours",
constraints: {
max: 23,
min: 0
},
style: {width: "4em"},
intermediateChanges: this.intermediateChanges,
title: "Hours in range 0-23"
}, timeNode);
this.hoursEditor.on("change", lang.hitch(this, this._setValue));
domConstruct.create("span", {innerHTML: ":"}, domNode);
var minutesNode = domConstruct.create("div", {}, domNode);
this.minutesEditor = new dijit.form.NumberSpinner({
name: "minutes",
constraints: {
max: 59,
min: 0
},
style: {width: "4em"},
intermediateChanges: this.intermediateChanges,
title: "Minutes in range 0-59"
}, minutesNode);
this.minutesEditor.on("change", lang.hitch(this, this._setValue));
domConstruct.create("span", {innerHTML: ":"}, domNode);
var secondsNode = domConstruct.create("div", {}, domNode);
this.secondsEditor = new dijit.form.NumberSpinner({
name: "seconds",
constraints: {
max: 59,
min: 0
},
style: {width: "4em"},
intermediateChanges: this.intermediateChanges,
title: "Seconds in range 0-59"
}, secondsNode);
this.secondsEditor.on("change", lang.hitch(this, this._setValue));
domConstruct.create("span", {innerHTML: "."}, domNode);
var millisecondsNode = domConstruct.create("div", {}, domNode);
this.millisecondsEditor = new dijit.form.NumberSpinner({
name: "milliseconds",
constraints: {
max: 999,
min: 0
},
style: {width: "5em"},
intermediateChanges: this.intermediateChanges,
title: "Milliseconds in range 0-999"
}, millisecondsNode);
this.millisecondsEditor.on("change", lang.hitch(this, this._setValue));
},
startup: function ()
{
this.inherited(arguments);
this.hoursEditor.startup();
this.minutesEditor.startup();
this.secondsEditor.startup();
this.millisecondsEditor.startup();
},
_setValue: function ()
{
var time = 0
if (isInt(this.hoursEditor.value))
{
time = time + this.hoursEditor.value * 60 * 60 * 1000;
}
if (isInt(this.minutesEditor.value))
{
time = time + this.minutesEditor.value * 60 * 1000;
}
if (isInt(this.secondsEditor.value))
{
time = time + this.secondsEditor.value * 1000;
}
if (isInt(this.millisecondsEditor.value))
{
time = time + this.millisecondsEditor.value;
}
this.value = new Date(time - new Date().getTimezoneOffset() * 60 * 1000);
this.emit("change", this.value);
},
_setDisabledAttr: function (value)
{
this.inherited(arguments);
this.disabled = value;
this.hoursEditor.set("disabled", value);
this.minutesEditor.set("disabled", value);
this.secondsEditor.set("disabled", value);
this.millisecondsEditor.set("disabled", value);
},
_setValueAttr: function (value)
{
if (value)
{
var date = value instanceof Date ? value : new Date(value);
this.hoursEditor.set("value", date.getHours());
this.minutesEditor.set("value", date.getMinutes());
this.secondsEditor.set("value", date.getSeconds());
this.millisecondsEditor.set("value", date.getMilliseconds());
}
this.value = date;
this.inherited(arguments);
},
_getValueAttr: function ()
{
return this.value;
}
});
var DateTimePicker = declare("qpid.management.DateTimePicker", [dijit._WidgetBase, Evented], {
disabled: false,
value: undefined,
buildRendering: function ()
{
var domNode = this.domNode = domConstruct.create("div", {className: "dijitReset dijitInline"});
var dateNode = domConstruct.create("div", {}, domNode);
this.dateEditor = new dijit.form.DateTextBox({
name: "date",
intermediateChanges: true,
constraints: {datePattern: "yyyy-MM-dd"}
}, dateNode);
this.dateEditor.on("change", lang.hitch(this, this._setValue));
var timeNode = domConstruct.create("div", {}, domNode);
if (has("ff"))
{
this.timeEditor = new TimePicker({
name: "time",
value: this.value,
intermediateChanges: true,
disabled: this.disabled
}, timeNode);
}
else
{
this.timeEditor = new dijit.form.TimeTextBox({
name: "time",
intermediateChanges: true,
value: this.value,
constraints: {
timePattern: 'HH:mm:ss.SSS',
clickableIncrement: 'T00:15:00',
visibleIncrement: 'T00:15:00',
visibleRange: 'T00:00:00'
}
}, timeNode);
}
this.timeEditor.on("change", lang.hitch(this, this._setValue));
},
startup: function ()
{
this.inherited(arguments);
this.dateEditor.startup();
this.timeEditor.startup();
},
_setValue: function ()
{
var date = this.dateEditor.get("value");
if (date)
{
var time = this.timeEditor.value;
var value = date.getTime() + (time ? time.getTime() + time.getTimezoneOffset() * 60 * 1000 : 0);
this.value = value;
this._buildExpression(value);
this.emit("change", this.value);
}
},
_buildExpression: function (value)
{
var formattedDate = this.userPreferences.formatDateTime(value, {
selector: "date",
datePattern: "yyyy-MM-dd"
});
var formattedTime = this.userPreferences.formatDateTime(value, {
selector: "time",
datePattern: "HH:mm:ss.SSS"
});
var timeZoneOffset = "";
var timeZone = this.userPreferences.getTimeZoneInfo();
if (timeZone)
{
var timeZoneOffsetInMinutes = timeZone.offset;
if (!timeZoneOffsetInMinutes)
{
timeZoneOffset = "Z";
}
else
{
if (timeZoneOffsetInMinutes > 0)
{
timeZoneOffset = "+";
}
timeZoneOffset =
timeZoneOffset + number.format(timeZoneOffsetInMinutes / 60, {pattern: "00"}) + ":"
+ number.format(timeZoneOffsetInMinutes % 60, {pattern: "00"});
}
}
this.expression = "to_date('" + formattedDate + "T" + formattedTime + timeZoneOffset + "')";
},
_getExpressionAttr: function ()
{
return this.expression;
},
_setDisabledAttr: function (value)
{
this.inherited(arguments);
this.disabled = value;
this.dateEditor.set("disabled", value);
this.timeEditor.set("disabled", value);
},
_setValueAttr: function (value)
{
var date;
if (value instanceof Date)
{
date = value;
}
else if (isInt(value))
{
date = new Date(value);
}
if (date)
{
this.dateEditor.set("value", date);
this.timeEditor.set("value", date);
this.value = date;
}
this.inherited(arguments);
},
_getValueAttr: function ()
{
return this.value;
},
isValid: function ()
{
return !!this.value;
}
});
return declare("qpid.management.query.CriteriaPane",
[dijit._WidgetBase, dijit._TemplatedMixin, dijit._WidgetsInTemplateMixin, Evented],
{
//Strip out the apache comment header from the template html as comments unsupported.
templateString: template.replace(/<!--[\s\S]*?-->/g, ""),
/**
* fields from template
*/
conditionExpression: null,
criteriaCondition: null,
criteriaValueInputContainer: null,
removeButton: null,
contentPane: null,
titleNode: null,
/**
* mandatory constructor arguments
*/
criteriaName: null,
typeName: null,
typeValidValues: null,
userPreferences: null,
/**
* auxiliary fields
*/
_removed: false,
_stored: false,
_lastCondition: undefined,
_savedValue: undefined,
_savedCondition: undefined,
postCreate: function ()
{
this.inherited(arguments);
this._postCreate();
},
_postCreate: function ()
{
var conditions = getTypeConditions(this.typeName, this.typeValidValues);
this.criteriaCondition.set("options", arrayToOptions(conditions, ANY));
this.criteriaCondition.on("change", lang.hitch(this, this._conditionChanged));
if (this.typeName === "Boolean")
{
this.typeValidValues = ['true', 'false'];
}
var domNode = domConstruct.create("div", {}, this.criteriaValueInputContainer);
if (this.typeValidValues && this.typeValidValues.length)
{
for (var i = 0; i < this.typeValidValues.length; i++)
{
var val = this.typeValidValues[i];
domConstruct.create("option", {
innerHTML: entities.encode(String(val)),
value: val
}, domNode);
}
this.valueEditor = new dijit.form.MultiSelect({
intermediateChanges: true,
multiple: true
}, domNode);
}
else if (this.typeName === "Date")
{
this.valueEditor = new DateTimePicker({
intermediateChanges: true,
userPreferences: this.userPreferences
}, domNode);
}
else
{
this.valueEditor = isNumericType(this.typeName) ? new dijit.form.NumberTextBox({
value: 0,
required: true,
intermediateChanges: true,
invalidMessage: 'Please enter a numeric value.'
}, domNode) : new dijit.form.ValidationTextBox({
value: '',
required: true,
intermediateChanges: true
}, domNode);
}
this.valueEditor.startup();
this.valueEditor.on("change", lang.hitch(this, this._conditionValueChanged));
this.removeCriteria.on("click", lang.hitch(this, this._removalRequested));
this._conditionChanged(ANY);
this._saveState()
},
_saveState: function ()
{
this._savedValue = this.valueEditor.value;
this._savedCondition = this.criteriaCondition.value;
},
_conditionChanged: function (newValue)
{
if (this.criteriaCondition.value != this._lastCondition)
{
var editable = conditionHasWidget(newValue);
this.valueEditor.set("disabled", !editable);
this._lastCondition = newValue;
this._conditionValueChanged();
}
},
_conditionValueChanged: function (newValue)
{
var expression;
var val = this._getConditionValue();
if (val)
{
expression =
buildExpression(this.criteriaName, this.criteriaCondition.value, val, this.typeName);
}
if (!expression)
{
expression = this.criteriaName + ":" + ANY;
}
this.conditionExpression.innerHTML = entities.encode(String(expression));
// notify listeners that criteria is changed
this.emit("change", this);
},
getExpression: function ()
{
if (this._removed)
{
return undefined;
}
return buildExpression(this.criteriaName,
this.criteriaCondition.value,
this._getConditionValue(),
this.typeName);
},
getCondition: function () {
if (this._removed)
{
return undefined;
}
return {
name: this.criteriaName,
value: this._getConditionValue(),
operator: this.criteriaCondition.value,
type: this.typeName
};
},
_getConditionValue: function ()
{
return this.valueEditor.expression || this.valueEditor.value;
},
_removalRequested: function ()
{
this._removed = true;
this.emit("change", this);
},
_getRemovedAttr: function ()
{
return this._removed;
},
cancelled: function ()
{
if (!this._stored)
{
this.destroyRecursive(false);
}
else
{
if (this._removed)
{
domStyle.set(this.domNode, "display", "");
this._removed = false;
}
this.valueEditor.set("value", this._savedValue);
this.criteriaCondition.set("value", this._savedCondition);
}
},
submitted: function ()
{
if (this._removed)
{
this.destroyRecursive(false);
}
else
{
this._saveState();
this._stored = true;
}
},
_setRemovableAttr: function (removable)
{
this._removable = removable
this.removeCriteria.set("disabled", !removable);
},
isValidCriteria: function ()
{
if (!this.valueEditor.get("disabled"))
{
if (this.valueEditor.isValid)
{
return this.valueEditor.isValid();
}
if (this.valueEditor instanceof dijit.form.MultiSelect)
{
var value = this.valueEditor.value;
return value && value.length;
}
}
return true;
}
});
});