xblox - port 1/3

This commit is contained in:
lovebird 2026-02-10 00:02:02 +01:00
parent 0fa17ead0c
commit 444f78ceb7
157 changed files with 45919 additions and 0 deletions

View File

@ -0,0 +1,37 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history

View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) 2017,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,2 @@
# xblox
xblox-core

View File

@ -0,0 +1,44 @@
{
"name": "@xblox/xblox",
"version": "0.0.1",
"license": "BSD",
"licenses": [
{
"type": "BSD",
"url": "https://github.com/xblox/xblox/blob/master/LICENSE"
}
],
"contributors": [
{
"name": "Günter Baumgart",
"url": "http://github.com/xblox"
}
],
"repository": {
"type": "git",
"url": "https://github.com/xblox/xblox.git"
},
"dependencies": {
"js-data": "^2.10.0"
},
"devDependencies": {
"dojo-typings": "^1.11.6",
"nodemon": "^1.11.0",
"ts-node": "^1.7.3",
"tsconfig-paths": "^2.1.1",
"tslint": "^4.3.1",
"typescript": "^2.2.1"
},
"optionalDependencies": {},
"main": "index",
"scripts": {
"test": "tsc && mocha build/test",
"build": "tsc",
"start": "node build/index.js",
"dev": "nodemon -w src -x ts-node -r ./tsconfig-paths/register src/index.ts",
"lint": "tslint --project=./tsconfig.json",
"install": "git-module init-modules"
},
"modules": [],
"readmeFilename": "Readme.md"
}

View File

@ -0,0 +1,291 @@
/**
* The block's capabilities. This will be evaluated in the interface but also
* by the run-time (speed ups).
*
*/
export enum BLOCK_CAPABILITIES {
/**
* No other block includes this one.
* @constant
* @type int
*/
TOPMOST = 0x00004000,
/**
* The block's execution context can be changed to another object.
* @constant
* @type int
*/
TARGET = 0x00040000,
/**
* The block may create additional input terminals ('reset', 'pause', ...).
* @constant
* @type int
*/
VARIABLE_INPUTS = 0x00000080,
/**
* The block may create additional output terminals ('onFinish', 'onError').
* @constant
* @type int
*/
VARIABLE_OUTPUTS = 0x00000100,
/**
* The block may create additional ouput parameters ('result', 'error',...).
* @constant
* @type int
*/
VARIABLE_OUTPUT_PARAMETERS = 0x00000200,
/**
* The block may create additional input parameters.
* @constant
* @type int
*/
VARIABLE_INPUT_PARAMETERS = 0x00000400,
/**
* The block can contain child blocks.
* @constant
* @type int
*/
CHILDREN = 0x00000020,
/**
* Block provides standard signals ('paused', 'error').
* @constant
* @type int
*/
SIGNALS = 0x00000080
}
/**
* Flags to describe a block's execution behavior.
*
* @enum {integer} module=xide/types/RUN_FLAGS
* @memberOf module=xide/types
*/
export enum RUN_FLAGS {
/**
* The block can execute child blocks.
* @constant
* @type int
*/
CHILDREN = 0x00000020,
/**
* Block is waiting for a message => EXECUTION_STATE==RUNNING
* @constant
* @type int
*/
WAIT = 0x000008000
};
/**
* Flags to describe a block's execution state.
*
* @enum {integer} module=xide/types/EXECUTION_STATE
* @memberOf module=xide/types
*/
export enum EXECUTION_STATE {
/**
* The block is doing nothing and also has done nothing. The is the default state
* @constant
* @type int
*/
NONE = 0x00000000,
/**
* The block is running.
* @constant
* @type int
*/
RUNNING = 0x00000001,
/**
* The block is an error state.
* @constant
* @type int
*/
ERROR = 0x00000002,
/**
* The block is in an paused state.
* @constant
* @type int
*/
PAUSED = 0x00000004,
/**
* The block is an finished state, ready to be cleared to "NONE" at the next frame.
* @constant
* @type int
*/
FINISH = 0x00000008,
/**
* The block is an stopped state, ready to be cleared to "NONE" at the next frame.
* @constant
* @type int
*/
STOPPED = 0x00000010,
/**
* The block has been launched once...
* @constant
* @type int
*/
ONCE = 0x80000000,
/**
* Block will be reseted next frame
* @constant
* @type int
*/
RESET_NEXT_FRAME = 0x00800000,
/**
* Block is locked and so no further inputs can be activated.
* @constant
* @type int
*/
LOCKED = 0x20000000 // Block is locked for utilisation in xblox
}
export enum BLOCK_MODE {
NORMAL = 0,
UPDATE_WIDGET_PROPERTY = 1
};
/**
* Flags to describe a block's belonging to a standard signal.
* @enum {integer} module=xblox/types/BLOCK_OUTLET
* @memberOf module=xblox/types
*/
export enum BLOCK_OUTLET {
NONE = 0x00000000,
PROGRESS = 0x00000001,
ERROR = 0x00000002,
PAUSED = 0x00000004,
FINISH = 0x00000008,
STOPPED = 0x00000010
};
/**
* Flags to describe flags of the inner state of a block which might change upon the optimization. It also
* contains some other settings which might be static, default or changed by the UI(debugger, etc...)
*
* @enum {integer} module:xide/types/BLOCK_FLAGS
* @memberOf module:xide/types
*/
export enum BLOCK_FLAGS {
NONE = 0x00000000, // Reserved for future use
ACTIVE = 0x00000001, // This behavior is active
SCRIPT = 0x00000002, // This behavior is a script
RESERVED1 = 0x00000004, // Reserved for internal use
USEFUNCTION = 0x00000008, // Block uses a function and not a graph
RESERVED2 = 0x00000010, // Reserved for internal use
SINGLE = 0x00000020, // Only this block will excecuted, child blocks not.
WAITSFORMESSAGE = 0x00000040, // Block is waiting for a message to activate one of its outputs
VARIABLEINPUTS = 0x00000080, // Block may have its inputs changed by editing them
VARIABLEOUTPUTS = 0x00000100, // Block may have its outputs changed by editing them
VARIABLEPARAMETERINPUTS = 0x00000200, // Block may have its number of input parameters changed by editing them
VARIABLEPARAMETEROUTPUTS = 0x00000400, // Block may have its number of output parameters changed by editing them
TOPMOST = 0x00004000, // No other Block includes this one
BUILDINGBLOCK = 0x00008000, // This Block is a building block (eg= not a transformer of parameter operation)
MESSAGESENDER = 0x00010000, // Block may send messages during its execution
MESSAGERECEIVER = 0x00020000, // Block may check messages during its execution
TARGETABLE = 0x00040000, // Block may be owned by a different object that the one to which its execution will apply
CUSTOMEDITDIALOG = 0x00080000, // This Block have a custom Dialog Box for parameters edition .
RESERVED0 = 0x00100000, // Reserved for internal use.
EXECUTEDLASTFRAME = 0x00200000, // This behavior has been executed during last process. (Available only in profile mode )
DEACTIVATENEXTFRAME = 0x00400000, // Block will be deactivated next frame
RESETNEXTFRAME = 0x00800000, // Block will be reseted next frame
INTERNALLYCREATEDINPUTS = 0x01000000, // Block execution may create/delete inputs
INTERNALLYCREATEDOUTPUTS = 0x02000000, // Block execution may create/delete outputs
INTERNALLYCREATEDINPUTPARAMS = 0x04000000, // Block execution may create/delete input parameters or change their type
INTERNALLYCREATEDOUTPUTPARAMS = 0x08000000, // Block execution may create/delete output parameters or change their type
INTERNALLYCREATEDLOCALPARAMS = 0x40000000, // Block execution may create/delete local parameters or change their type
ACTIVATENEXTFRAME = 0x10000000, // Block will be activated next frame
LOCKED = 0x20000000, // Block is locked for utilisation in xblox
LAUNCHEDONCE = 0x80000000 // Block has not yet been launched...
}
/**
* Mask for the messages the callback function of a block should be aware of. This goes directly in
* the EventedMixin as part of the 'emits' chain (@TODO)
*
* @enum module:xide/types/BLOCK_CALLBACKMASK
* @memberOf module:xide/types
*/
export enum BLOCK_CALLBACKMASK {
PRESAVE = 0x00000001, // Emits PRESAVE messages
DELETE = 0x00000002, // Emits DELETE messages
ATTACH = 0x00000004, // Emits ATTACH messages
DETACH = 0x00000008, // Emits DETACH messages
PAUSE = 0x00000010, // Emits PAUSE messages
RESUME = 0x00000020, // Emits RESUME messages
CREATE = 0x00000040, // Emits CREATE messages
RESET = 0x00001000, // Emits RESET messages
POSTSAVE = 0x00000100, // Emits POSTSAVE messages
LOAD = 0x00000200, // Emits LOAD messages
EDITED = 0x00000400, // Emits EDITED messages
SETTINGSEDITED = 0x00000800, // Emits SETTINGSEDITED messages
READSTATE = 0x00001000, // Emits READSTATE messages
NEWSCENE = 0x00002000, // Emits NEWSCENE messages
ACTIVATESCRIPT = 0x00004000, // Emits ACTIVATESCRIPT messages
DEACTIVATESCRIPT = 0x00008000, // Emits DEACTIVATESCRIPT messages
RESETINBREAKPOINT = 0x00010000, // Emits RESETINBREAKPOINT messages
RENAME = 0x00020000, // Emits RENAME messages
BASE = 0x0000000E, // Base flags =attach /detach /delete
SAVELOAD = 0x00000301, // Base flags for load and save
PPR = 0x00000130, // Base flags for play/pause/reset
EDITIONS = 0x00000C00, // Base flags for editions of settings or parameters
ALL = 0xFFFFFFFF // All flags
}
export enum EVENTS {
ON_RUN_BLOCK = <any>'onRunBlock',
ON_RUN_BLOCK_FAILED = <any>'onRunBlockFailed',
ON_RUN_BLOCK_SUCCESS = <any>'onRunBlockSuccess',
ON_BLOCK_SELECTED = <any>'onItemSelected',
ON_BLOCK_UNSELECTED = <any>'onBlockUnSelected',
ON_BLOCK_EXPRESSION_FAILED = <any>'onExpressionFailed',
ON_BUILD_BLOCK_INFO_LIST = <any>'onBuildBlockInfoList',
ON_BUILD_BLOCK_INFO_LIST_END = <any>'onBuildBlockInfoListEnd',
ON_BLOCK_PROPERTY_CHANGED = <any>'onBlockPropertyChanged',
ON_SCOPE_CREATED = <any>'onScopeCreated',
ON_VARIABLE_CHANGED = <any>'onVariableChanged',
ON_CREATE_VARIABLE_CI = <any>'onCreateVariableCI'
}
export enum Type {
AssignmentExpression = <any>'AssignmentExpression',
ArrayExpression = <any>'ArrayExpression',
BlockStatement = <any>'BlockStatement',
BinaryExpression = <any>'BinaryExpression',
BreakStatement = <any>'BreakStatement',
CallExpression = <any>'CallExpression',
CatchClause = <any>'CatchClause',
ConditionalExpression = <any>'ConditionalExpression',
ContinueStatement = <any>'ContinueStatement',
DoWhileStatement = <any>'DoWhileStatement',
DebuggerStatement = <any>'DebuggerStatement',
EmptyStatement = <any>'EmptyStatement',
ExpressionStatement = <any>'ExpressionStatement',
ForStatement = <any>'ForStatement',
ForInStatement = <any>'ForInStatement',
FunctionDeclaration = <any>'FunctionDeclaration',
FunctionExpression = <any>'FunctionExpression',
Identifier = <any>'Identifier',
IfStatement = <any>'IfStatement',
Literal = <any>'Literal',
LabeledStatement = <any>'LabeledStatement',
LogicalExpression = <any>'LogicalExpression',
MemberExpression = <any>'MemberExpression',
NewExpression = <any>'NewExpression',
ObjectExpression = <any>'ObjectExpression',
Program = <any>'Program',
Property = <any>'Property',
ReturnStatement = <any>'ReturnStatement',
SequenceExpression = <any>'SequenceExpression',
SwitchStatement = <any>'SwitchStatement',
SwitchCase = <any>'SwitchCase',
ThisExpression = <any>'ThisExpression',
ThrowStatement = <any>'ThrowStatement',
TryStatement = <any>'TryStatement',
UnaryExpression = <any>'UnaryExpression',
UpdateExpression = <any>'UpdateExpression',
VariableDeclaration = <any>'VariableDeclaration',
VariableDeclarator = <any>'VariableDeclarator',
WhileStatement = <any>'WhileStatement',
WithStatement = <any>'WithStatement'
};

View File

@ -0,0 +1,35 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"allowJs": false,
"noImplicitAny": false,
"sourceMap": true,
"outDir": "./build/node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"pretty": true,
"baseUrl": "./src/",
"rootDir": "../",
"paths": {
"@xblox/core/*": [
"../../core-ts/src/*",
"../../../core-ts/src/*"
]
}
},
"compileOnSave": false,
"filesGlob": [
"./src/**/*.ts"
],
"exclude": [],
"atom": {
"rewriteTsconfig": true
},
"files": [
"./src/index.ts",
"./src/enums.ts",
"./src/model/Block.ts",
"./typings/index.d.ts"
]
}

View File

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,140 @@
define([
"dcl/dcl",
"delite/register",
"delite/CustomElement",
'xblox/_State',
'xide/utils',
'xdojo/has'
], function (dcl, register, CustomElement, _State, utils, has) {
var extraRules = [],
extraSheet,
removeMethod,
rulesProperty,
invalidCssChars = /([^A-Za-z0-9_\u00A0-\uFFFF-])/g;
has.add('dom-contains', function (global, doc, element) {
return !!element.contains; // not supported by FF < 9
});
function removeRule(index) {
// Function called by the remove method on objects returned by addCssRule.
var realIndex = extraRules[index],
i, l;
if (realIndex === undefined) {
return; // already removed
}
// remove rule indicated in internal array at index
extraSheet[removeMethod](realIndex);
// Clear internal array item representing rule that was just deleted.
// NOTE: we do NOT splice, since the point of this array is specifically
// to negotiate the splicing that occurs in the stylesheet itself!
extraRules[index] = undefined;
// Then update array items as necessary to downshift remaining rule indices.
// Can start at index + 1, since array is sparse but strictly increasing.
for (i = index + 1, l = extraRules.length; i < l; i++) {
if (extraRules[i] > realIndex) {
extraRules[i]--;
}
}
}
var Impl = {
_lastState: null,
declaredClass: 'xblox/CSSState',
cssClass: "",
addCssRule: function (selector, css) {
// summary:
// Dynamically adds a style rule to the document. Returns an object
// with a remove method which can be called to later remove the rule.
if (!extraSheet) {
// First time, create an extra stylesheet for adding rules
extraSheet = document.createElement('style');
document.getElementsByTagName('head')[0].appendChild(extraSheet);
// Keep reference to actual StyleSheet object (`styleSheet` for IE < 9)
extraSheet = extraSheet.sheet || extraSheet.styleSheet;
// Store name of method used to remove rules (`removeRule` for IE < 9)
removeMethod = extraSheet.deleteRule ? 'deleteRule' : 'removeRule';
// Store name of property used to access rules (`rules` for IE < 9)
rulesProperty = extraSheet.cssRules ? 'cssRules' : 'rules';
}
var index = extraRules.length;
extraRules[index] = (extraSheet.cssRules || extraSheet.rules).length;
extraSheet.addRule ?
extraSheet.addRule(selector, css) :
extraSheet.insertRule(selector + '{' + css + '}', extraRules[index]);
return {
get: function (prop) {
return extraSheet[rulesProperty][extraRules[index]].style[prop];
},
set: function (prop, value) {
if (typeof extraRules[index] !== 'undefined') {
extraSheet[rulesProperty][extraRules[index]].style[prop] = value;
}
},
remove: function () {
removeRule(index);
},
sheet: extraSheet
};
},
escapeCssIdentifier: function (id, replace) {
return typeof id === 'string' ? id.replace(invalidCssChars, replace || '\\$1') : id;
},
detachedCallback: function () {
this._styles && _.each(this._styles, function (style) {
style.remove();
});
delete this._styles;
},
applyTo: function (widget, name) {
if (this._lastState) {
this._lastState.remove();
}
delete this._lastStateName;
this._lastStateName = name;
if (!this._attached) {
return;
}
var cssClass = this.cssClass;
var isCSSClass = cssClass.length > 0;
var id = widget.id || utils.createUUID();
var _uniqueId = widget.tagName.replace(/\./g, "_") + '_' + id;
var css = '' + this.innerHTML;
css = css.replace('.style', '');
css = css.replace(/<(?:.|\n)*?>/gm, '');
css = css.replace('{', '');
css = css.replace('}', '');
css = css.replace(/(\r\n|\n|\r|\t)/gm, "");
_uniqueId += '_state_' + name;
$(widget).removeClass($(widget).data('_lastCSSState'));
$(widget).removeClass($(widget).data('_lastCSSClass'));
$(widget).removeClass(cssClass);
if (!cssClass) {
$(widget).addClass(_uniqueId);
$(widget).data('_lastCSSState', _uniqueId);
var selectorPrefix = '.' + this.escapeCssIdentifier(_uniqueId);
if (!this._styles) {
this._styles = [];
}
var style = this.addCssRule(selectorPrefix, css);
this._styles.push(style);
} else {
$(widget).addClass(cssClass);
$(widget).data('_lastCSSClass', cssClass);
}
}
};
var _class = dcl([_State], Impl);
//static access to Impl.
_class.Impl = Impl;
return register("d-xstate-css", [HTMLElement, CustomElement, _class]);
});

View File

@ -0,0 +1,189 @@
/*global module */
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON("package.json"),
jshint: {
src: [
"**/*.js",
"!./app.profile.js",
"!{node_modules}/**/*.js"
],
options: {
jshintrc: ".jshintrc"
}
},
// Task for compiling less files into CSS files
less : {
// Compile theme independent files
transitions: {
expand: true,
cwd: "themes/common/transitions",
src: ["*.less"],
dest: "themes/common/transitions",
ext: ".css"
},
// Infrastructure per-theme files
common : {
files: [
{
expand: true,
src: ["themes/*/*.less", "!themes/common/*.less", "!**/variables.less", "!**/common.less"],
ext: ".css"
}
]
},
// Compile less code for each widget
widgets : {
files: [
{
expand: true,
src: [
"*/themes/*/*.less",
"samples/ExampleWidget/themes/*/*.less",
"!{dijit,mobile}/themes/*/*.less"
],
ext: ".css"
}
]
}
},
// convert CSS files to JS files
cssToJs : {
// conversions removing the CSS files
replace: {
src: [
// infrastructure
"themes/*/*.css",
"!themes/common/*.css",
"themes/common/transitions/*.css",
// widgets
"*/themes/*/*.css",
"samples/ExampleWidget/themes/*/*.css",
"!{dijit,mobile}/themes/*/*.css"
],
options: {
remove: true
}
},
// conversions keeping the CSS files
keep: {
src: [
// some apps may want to load defaultapp.css as a JS file rather than a CSS file.
"themes/defaultapp.css",
// files originally authored as CSS
"tests/unit/css/*.css"
]
}
},
intern: {
local: {
options: {
runType: 'runner',
config: 'test/intern/intern.local'
}
},
remote: {
options: {
runType: 'runner',
config: 'test/intern/intern'
}
}
},
"jsdoc-amddcl": {
"plugins": [
"plugins/markdown"
],
docs: {
files: [
{
src: [
"./views/Grid.js",
"xblox/views/BlockGrid.js",
"./BlockActions.js",
"../xide/types/Types.js",
"./types/Types.js",
"!./node_modules"
]
}
]
},
'export': {
files: [
{
args: [
"-X"
],
src: [
".",
"../xide/types/Types.js",
"./README.md",
"./package.json"
],
dest: "/tmp/doclets.json"
}
]
}
}
});
// Load plugins
grunt.loadNpmTasks("intern");
grunt.loadNpmTasks("grunt-contrib-jshint");
grunt.loadNpmTasks("grunt-contrib-less");
grunt.loadNpmTasks("grunt-contrib-uglify");
grunt.loadNpmTasks("jsdoc-amddcl");
grunt.loadNpmTasks('intern-geezer');
// Aliases
//grunt.registerTask("css", ["less", "cssToJs"]);
grunt.registerTask("jsdoc", "jsdoc-amddcl");
// Testing.
// Always specify the target e.g. grunt test:remote, grunt test:remote
// then add on any other flags afterwards e.g. console, lcovhtml.
var testTaskDescription = "Run this task instead of the intern task directly! \n" +
"Always specify the test target e.g. \n" +
"grunt test:local\n" +
"grunt test:remote\n\n" +
"Add any optional reporters via a flag e.g. \n" +
"grunt test:local:console\n" +
"grunt test:local:lcovhtml\n" +
"grunt test:local:console:lcovhtml";
/*
grunt.registerTask("test", testTaskDescription, function (target) {
function addReporter(reporter) {
var property = "intern." + target + ".options.reporters",
value = grunt.config.get(property);
if (value.indexOf(reporter) !== -1) {
return;
}
value.push(reporter);
grunt.config.set(property, value);
}
if (this.flags.lcovhtml) {
addReporter("lcovhtml");
}
if (this.flags.console) {
addReporter("console");
}
grunt.task.run("intern:" + target);
});*/
grunt.registerTask('test', [ 'intern:local' ]);
};

View File

@ -0,0 +1,564 @@
define([
"dojo/_base/lang",
"dojo/on",
"dcl/dcl",//make sure
"delite/register",
"delite/CustomElement",
//explicit because a bootstrap might not be loaded at some point
"xide/factory/Events",
//explicit because a bootstrap might not be loaded at some point
'xide/utils/StringUtils',
'xide/types/Types',
'xblox/model/Referenced',
'xide/mixins/EventedMixin',
'xide/mixins/ReloadMixin',
/** 2way binding dependencies **/
'xwire/Binding',
'xwire/EventSource',
'xwire/WidgetTarget'
], function (lang, on, dcl, register, CustomElement, Events, utils, Types, Referenced, EventedMixin, ReloadMixin, Binding, EventSource, WidgetTarget, registry) {
var debugWidgets = false;
var debugApp = false;
var debugAttach = false;
var debugCreated = false;
var debugBinding = false;
var debugRun = false;
/**
* Proxy widget to run a selected blox script on the parent widget/node.
*
* @class xblox/RunScript
*/
var Impl = {
declaredClass: 'xblox/RunScript',
targetevent: '',
sourceevent: "",
sourceeventvaluepath: "",
sourceeventnamepath: "",
targetproperty: "",
targetvariable: "",
targetfilter: "",
script: "",
bidirectional: false,
blockGroup: '',
block: '',
_targetBlock: null,
_targetReference: null,
_appContext: null,
_complete: false,
enabled: true,
stop: false,
_events: [],
context: null,
accept: '',
transform: '',
mode: 0,
_2wayHandle: null,//the handle
binding: null,//the binding
/**
* soft destroy
*/
reset: function () {
this._destroyHandles();
if (this._2wayHandle) {
this._2wayHandle.remove();
}
if (this.binding) {
this.binding.destroy();
}
delete this.binding;
this._appContext = null;
this._targetReference = null;
this._targetBlock = null;
},
/**
*
* @param newSettings
*/
onSettingsChanged: function () {
this.reset();
if (!this.enabled) {
return;
}
this.onAppReady(null);
},
getChildren: function () {
return [];
},
/**
* @inheritDoc
*/
destroy: function () {
this.onDestroy && this.onDestroy();
this.reset();
delete this.binding;
delete this.context;
},
/**
* The final execution when 'target event' has been triggered. This
* will run the select block.
* @param event
* @param val
*/
run: function (event, val) {
if (!this.enabled) {
return;
}
var settings = {};
//filter, in design mode, we ain't do anything
if (this.context && this.context.delegate) {
if (this.context.delegate.isDesignMode && this.context.delegate.isDesignMode()) {
return;
}
if (this.context.delegate.getBlockSettings) {
settings = this.context.delegate.getBlockSettings();
}
}
//setup variables
var block = this._targetBlock,
context = this._targetReference,
result;
if (block && context) {
block.context = context;
block._targetReference = context;
if (this.targetvariable && this.targetvariable.length && val != null) {
block.override = {
variables: {}
};
block.override.variables[this.targetvariable] = val;
}
result = block.solve(block.scope, settings);
debugRun && console.log('run ' + block.name + ' for even ' + event, result + ' for ' + this.id, this._targetReference);
}
},
/**
* Callback when the minimum parameters are given: targetReference & targetBlock
*/
onReady: function () {
if (!this._targetReference) {
this._setupTargetReference();
}
//resolve 2way binding
if (this._targetReference && this['bidirectional'] === true && this.sourceevent && this.sourceevent.length && !this.binding) {
this._setup2WayBinding();
}
if (this._complete) {
return;
}
if (!this._targetReference) {
console.error('have no target reference');
}
if (!this._targetBlock) {
console.error('have no target block');
}
if (this._targetReference && this._targetBlock) {
//we trigger on events
if (this.targetevent) {
this._complete = true;
//patch the target
utils.mixin(this._targetReference, EventedMixin.prototype);
var _target = this._targetReference.domNode || this._targetReference,
_event = this.targetevent,
_isWidget = this._targetReference.declaredClass || this._targetReference.startup,
_hasWidgetCallback = this._targetReference.on != null && this._targetReference['on' + utils.capitalize(_event)] != null,
_handle = null,
_isDelite = _target.render != null && _target.on != null,
thiz = this;
if (_isWidget && (this._targetReference.baseClass && this._targetReference.baseClass.indexOf('dijitContentPane') != -1) || this._targetReference.render != null || this._targetReference.on != null) {
_isWidget = false;//use on
}
if (_target) {
debugBinding && console.log('wire success ' + this.id + ' for ' + this.targetevent);
if (!_isDelite && (!_hasWidgetCallback || !_isWidget)) {
_handle = on(_target, this.targetevent, function (evt) {
this.run(this.targetevent);
}.bind(this));
} else {
_target = this._targetReference;
var useOn = true;
if (useOn) {
if (!_isDelite) {
var _e = 'on' + utils.capitalize(_event);
this._targetReference[_e] = function (val, nada) {
if (_target.ignore !== true) {
thiz.run(thiz.targetevent, val);
}
};
} else {
_handle = _target.on(this.targetevent, function (evt) {
if (this.stop) {
evt.preventDefault();
evt.stopImmediatePropagation();
}
this.run(this.targetevent, evt.currentTarget.value);
}.bind(this));
}
} else {
this._targetReference['on' + utils.capitalize(_event)] = function (val) {
if (_target.ignore !== true) {
thiz.run(thiz.targetevent, val);
}
};
}
}
_handle && this._events.push(_handle);
} else {
console.error('have no target to wire');
}
}
} else {
console.error('invalid params, abort', this);
}
if (this.binding) {
this.binding.start();
}
},
resolveBlock: function (block) {
var ctx = this._appContext;
var deviceManager = ctx.getDeviceManager();
if (block.indexOf('://') !== -1) {
if (!deviceManager) {
return;
}
var _block = deviceManager.getBlock(this.block);
if (_block) {
return _block;
}
}
},
/**
*
* @param ctx
* @private
*/
_setBlock: function (ctx) {
ctx = ctx || window['appContext'];
if (!ctx || !ctx.getBlockManager) {
debugApp && console.warn('have no context or block manager');
return;
}
this._appContext = ctx;
var blockManager = ctx.getBlockManager(),
deviceManager = ctx.getDeviceManager(),
thiz = this;
if (!blockManager) {
return;
}
var _block = this.block ? this.block : this.getAttribute('block');
if (_block && _block.length > 0) {
var parts = utils.parse_url(_block);
if (_block.indexOf('://') !== -1) {
if (!deviceManager) {
debugApp && console.warn('xScript::_setBlock : have no device manager');
return;
}
var _block2 = deviceManager.getBlock(_block);
if (_block2) {
thiz._targetBlock = _block2;
thiz.onReady();
} else {
debugBinding && console.warn('cant get block : ' + _block);
}
} else {
blockManager.load(parts.scheme, parts.host).then(function (scope) {
var block = scope.getBlockById(thiz.blockid);
if (block) {
thiz._targetBlock = block;
thiz.onReady();
}
});
}
} else if (this.scopeid) {
var scope = blockManager.hasScope(thiz.scopeid);
if (scope) {
var block = scope.getBlockById(thiz.blockid);
if (block) {
thiz._targetBlock = block;
thiz.onReady();
} else {
block = scope.getVariableById(thiz.blockid);
if (block) {
thiz._targetBlock = block;
thiz.onReady();
}
}
} else {
console.error('have no scope!');
}
}
},
initWithReference: function (ref) {
if (ref.nodeType !== 1) {
return;
}
this._targetReference = ref;
this._setBlock(null);
},
resolveFilter: function (expression, value, widget) {
if (this._targetBlock) {
var expressionModel = this._targetBlock.scope.expressionModel;
value = expressionModel.parseVariable(this._targetBlock.scope, {
value: expression
}, '', false, false, widget, [value]);
}
return value;
},
/**
* setup outbound wire, assumes all parameters are checked
* @private
*/
_setup1WayBinding: function () {
debugBinding && console.log('setup 1 way binding');
//destroy old handle
if (this._2wayHandle) {
this._2wayHandle.remove();
}
if (!this._targetBlock) {
console.error('invalid params for one way binding');
return;
}
var sourceVariableTitle = this._targetBlock.name;
//wire to system event
var bindingSource = new EventSource({
//listen to variable changes
trigger: this.sourceevent,
//the path to value, ie: 'item.value'
path: this.sourceeventvaluepath,
//add an event filter
filters: [{
// variable title must match,ie: 'item.title'
path: this.sourceeventnamepath,
// the name of the variable, ie: 'Volume'
value: sourceVariableTitle
}]
});
//now map the event source to a widget
var bindingTarget = new WidgetTarget({
//the path to value
path: this.targetproperty,
object: this._targetReference,
targetFilter: this.targetfilter,
delegate: this
});
var accept = this._findbyTagAndName('D-SCRIPT', 'accept');
var transform = this._findbyTagAndName('D-SCRIPT', 'transform');
//construct the binding
var binding = new Binding({
source: bindingSource,
target: bindingTarget,
accept: this._findbyTagAndName('D-SCRIPT', 'accept'),
transform: this._findbyTagAndName('D-SCRIPT', 'transform')
});
this.binding = binding;
binding.start();
},
_findbyTagAndName: function (tag, name) {
var scripts = $(this).find(tag);
for (var i = 0; i < scripts.length; i++) {
var script = scripts[i];
if ($(script).attr('name') === name) {
return script;
}
}
return null;
},
/**
* setup inbound wire, assumes all parameters are checked
* @private
*/
_setup2WayBinding: function () {
if (this.binding) {
return;
}
debugBinding && console.log('setup 2 way binding');
//destroy old handle
if (this._2wayHandle) {
this._2wayHandle.remove();
}
//wire to system event
var bindingSource = new EventSource({
//listen to variable changes
trigger: this.sourceevent,
//the path to value, ie: 'item.value'
path: this.sourceeventvaluepath,
//add an event filter
filters: [{
// variable title must match,ie: 'item.title'
path: this.sourceeventnamepath,
// the name of the variable, ie: 'Volume'
value: this.targetvariable
}]
});
//now map the event source to a widget
var bindingTarget = new WidgetTarget({
//the path to value
path: 'value',
object: this._targetReference
});
this.binding = new Binding({
source: bindingSource,
target: bindingTarget
});
this.binding.start();
},
/**
* Returns the widget whose DOM tree contains the specified DOMNode, or null if
* the node is not contained within the DOM tree of any widget
* @param {Element} node
*/
getEnclosingWidget: function (node) {
if (node) {
do {
if (node.nodeType === 1 && node.render) {
return node;
}
} while ((node = node.parentNode));
}
return null;
},
/**
* Function to setup the target reference
* on the surrounding widget!
*
*/
_setupTargetReference: function () {
var i = 0,
element = this,
widget = null;
while (i < 2 && !widget) {
if (element) {
element = element.parentNode;
widget = this.getEnclosingWidget(element, "widgetId");
if (!widget) {
widget = this.getEnclosingWidget(element, "widgetid");
}
}
i++;
}
if (widget) {
debugWidgets && console.info('have widget reference' + ' : ', widget);
this.initWithReference(widget);
} else {
if (this.domNode && this.domNode.parentNode) {
this.initWithReference(this.domNode.parentNode);
debugWidgets && console.error('cant find widget reference, using parent node', this._targetReference);
} else {
if (this.parentNode) {
this.initWithReference(this.parentNode);
}
debugWidgets && console.error('cant find widget reference', this);
}
}
},
/**
* Required in case of dojoConfig.parseOnLoad
* @param evt
*/
onAppReady: function (evt) {
debugApp && console.log('-ready');
if (this._targetBlock && this._targetBlock.scope && !this._targetBlock.scope.device) {
this.reset();
this._targetBlock = null;
}
//resolve target reference
if (!this._targetReference) {
this._setupTargetReference();
}
//resolve target block
if (!this._targetBlock) {
this._setBlock(evt ? evt.context : null);
}
this.mode = this['bidirectional'] === true ? 0 : 1;
//normal mode, allows 2-way binding
if (this.mode === 0) {
//resolve 2way binding
if (this._targetBlock && this._targetReference && this['bidirectional'] === true && this.sourceevent && this.sourceevent.length) {
this._setup2WayBinding();
}
//if both are valid, run the the init procedure
if (this._targetReference && this._targetBlock) {
this.onReady();
}
} else if (this.mode === 1 && this._targetBlock) {
if (this._targetReference && this.sourceevent && this.sourceevent.length && this.targetproperty && this.targetproperty.length) {
this._setup1WayBinding();
if (this.binding) {
this.binding.start();
}
}
}
//track context {xapp/manager/Context}
if (evt && evt.context) {
this.context = evt.context;
}
},
detachedCallback: function () {
debugAttach && console.info('detachedCallback', this);
if (this._appContext) {
this.destroy();
}
},
/**
* Delite created callback
*/
createdCallback: function () {
debugCreated && console.info('createdCallback', this);
},
/**
* Delite attached callback
*/
attachedCallback: function () {
debugAttach && console.info('attachedCallback', this);
if (this._started) {
return;
}
this.initReload();
this.subscribe(Types.EVENTS.ON_APP_READY);
this._started = true;
},
detachCallback: function () {
},
render: function () {
},
postRender: function () {
},
startup: function () {
debugAttach && console.log('startup');
this.inherited(arguments);
this.onAppReady();
this.initReload();
this.subscribe(Types.EVENTS.ON_APP_READY);
}
};
//package and declare via dcl
var _class = dcl([EventedMixin.dcl, ReloadMixin.dcl, Referenced.dcl], Impl);
//static access to Impl.
_class.Impl = Impl;
return register("d-xscript", [HTMLElement, CustomElement, _class]);
});

View File

@ -0,0 +1,83 @@
define([
'dcl/dcl',
'delite/register',
'delite/CustomElement',
'xide/factory/Events',
'xide/utils/StringUtils',
'xide/types/Types',
'xblox/_State'
], function (dcl,register, CustomElement, Events, utils, Types,_State) {
var Impl = {
declaredClass: 'xblox/StyleState',
_targetReference: null,
name:"Default",
_widget:null,
/**
* Convert Style String to an object array, eg: { color:value,.... }
* @param styleString
* @returns {{}}
* @private
*/
_toObject:function(styleString){
if(!styleString){
return {};
}
var _result = {};
var _values = styleString.split(';');
for (var i = 0; i < _values.length; i++) {
var obj = _values[i];
if(!obj || obj.length==0 || !obj.split){
continue;
}
var keyVal = obj.split(':');
if(!keyVal || !keyVal.length){
continue;
}
var key = obj.substring(0,obj.indexOf(':')).trim();
var value = obj.substring(obj.indexOf(':')+1,obj.length).trim();
_result[key]=value;
}
return _result;
},
_toStyleString:function(values){
var _values = [];
for(var prop in values){
_values.push( prop + ':' + values[prop]);
}
return _values.join(';') + ';';
},
onChanged:function () {
this.applyTo(this._widget);
},
attachedCallback: function () {
//if($(this).attr('style').indexOf('display')==-1){
// this.style.display = 'none';
//}
//
/*
console.log('attached ' + has('ide'));
if(!has('ide')){
var style = $(this).attr('style');
var background = utils.getBackgroundUrl(style);
console.log('style : '+background,this);
}
*/
},
_lastStyle:'',
applyTo:function(widget){
$(widget).removeClass($(widget).data('_lastCSSState'));
$(widget).removeClass($(widget).data('_lastCSSClass'));
if(widget && widget._attached){
this._widget = widget;
var _cssWidget = this._toObject($(widget).attr('style'));
var _cssThis = this._toObject($(this).attr('style'));
this._lastStyle = _cssThis;
widget._lastStyle = _cssThis;
var styleOut = utils.mixin(_cssWidget,_cssThis);
$(widget).attr('style',this._toStyleString(styleOut));
}
}
};
var _class = dcl(_State, Impl);
return register("d-xstate-style", [HTMLElement, CustomElement, _class]);
});

View File

@ -0,0 +1,182 @@
define([
"dojo/_base/lang",
"dojo/on",
"dcl/dcl",//make sure
"delite/register",
"delite/CustomElement",
//explicit because a bootstrap might not be loaded at some point
"xide/factory/Events",
//explicit because a bootstrap might not be loaded at some point
'xide/utils/StringUtils',
'xide/types/Types',
'xblox/model/Referenced',
'xide/mixins/EventedMixin',
'xide/mixins/ReloadMixin',
'xwire/Binding',
'xwire/EventSource',
'xwire/WidgetTarget'
], function (lang, on, dcl,register, CustomElement, Events, utils, Types, Referenced, EventedMixin, ReloadMixin, Binding, EventSource, WidgetTarget) {
var debugWidgets = false;
var debugApp = false;
var debugAttach = false;
var debugCreated = false;
var debugBinding = false;
var debugRun = false;
/**
* Proxy widget to run a selected blox script on the parent widget/node.
*
* @class xblox/RunScript
*/
var Impl = {
declaredClass: 'xblox/_State',
script:"",
bidirectional: false,
_targetBlock: null,
_targetReference: null,
_complete: false,
enabled: true,
stop: false,
_events: [],
context: null,
name:"Default",
isState:true,
_isState:function(){
return true;
},
/**
* soft destroy
*/
reset:function(){
},
getChildren: function () {
return [];
},
/**
* @inheritDoc
*/
destroy: function () {
this.onDestroy && this.onDestroy();
this.reset();
},
/**
* The final execution when 'target event' has been triggered. This
* will run the select block.
* @param event
* @param val
*/
run: function (event, val) {
if (!this.enabled) {
return;
}
},
/**
* Callback when the minimum parameters are given: targetReference & targetBlock
*/
onReady: function () {
},
getEnclosingWidget: function (node) {
if(node) {
do {
if (node.nodeType === 1 && node.render) {
return node;
}
} while ((node = node.parentNode));
}
return null;
},
initWithReference: function (ref) {
//target node or widget
if(ref.nodeType!==1){
return;
}
this._targetReference = ref;
},
/**
* Function to setup the target reference
* on the surrounding widget!
*
*/
_setupTargetReference: function () {
var i = 0,
element = this,
widget = null;
while (i < 2 && !widget) {
if (element) {
element = element.parentNode;
widget = this.getEnclosingWidget(element, "widgetId");
if (!widget) {
widget = this.getEnclosingWidget(element, "widgetid");
}
}
i++;
}
if (widget) {
debugWidgets && console.info('have widget reference' + ' : ', [widget,this]);
this.initWithReference(widget);
if(widget._attached && widget.stateReady){
widget.stateReady(this);
}
} else {
if (this.domNode && this.domNode.parentNode) {
this.initWithReference(this.domNode.parentNode);
debugWidgets && console.error('cant find widget reference, using parent node', this._targetReference);
} else {
if(this.parentNode){
this.initWithReference(this.parentNode);
}
debugWidgets && console.error('cant find widget reference', this);
}
}
},
onAppReady: function (evt) {
debugApp && console.log('-ready');
//resolve target reference
//if (!this._targetReference) {
this._setupTargetReference();
//}
//track context {xapp/manager/Context}
if (evt && evt.context) {
this.context = evt.context;
}
},
detachedCallback:function(){
debugAttach && console.info('detachedCallback', this);
if(this._appContext){
this.destroy();
}
},
applyTo:function(widget){
},
/**
* Delite created callback
*/
createdCallback: function () {
debugCreated && console.info('createdCallback', this);
if (!this._targetReference) {
this._setupTargetReference();
if(this._targetReference && this._targetReference.stateReady){
this._targetReference.stateReady(this);
}
}
},
attachedCallback: function () {
debugAttach && console.info('attachedCallback', this);
if (this._started) {
return;
}
this.onAppReady();//emulates
this.subscribe(Types.EVENTS.ON_APP_READY);
this._started = true;
}
};
//package and declare via dcl
var _class = dcl([EventedMixin.dcl,Referenced.dcl], Impl);
return _class;
});

View File

@ -0,0 +1,78 @@
/** @module delite/_Stated */
define([
"dcl/dcl",
"requirejs-dplugins/has",
"xide/utils/CSSUtils"
], function (dcl,has,utils) {
return dcl(null,{
state:'',
/**
* Returns all direct children of this widget, i.e. all widgets or DOM nodes underneath
* `this.containerNode`. Note that it does not return all
* descendants, but rather just direct children.
*
* The result intentionally excludes element outside off `this.containerNode`. So, it is different than
* accessing the `children` or `childNode` properties.
*
* @returns {Element[]}
*/
_getChildren: function () {
// use Array.prototype.slice to transform the live HTMLCollection into an Array
return Array.prototype.slice.call(this.children);
},
_states:null,
setState:function(stateName){
//can be integer or anything non string
var stateName = "" + stateName;
var state = _.find(this.getStates(),{
name:stateName
});
state && state.applyTo(this,stateName);
},
getState:function(_stateName){
//can be integer or anything non string
var stateName = "" + _stateName;
return _.find(this.getStates(),{
name:stateName
});
},
attachedCallback: function () {
/*
console.log('attached ' + has('ide'));
if(!has('ide')){
var style = $(this).attr('style');
var background = utils.getBackgroundUrl(style);
console.log('style : '+background,this);
}
*/
},
addState:function(state){
if(!this._states){
this._states = [];
}
if(this._states.indexOf(state)==-1){
this._states.push(state);
}
},
removeState:function(state){
if(!this._states){
this._states = [];
}
if(this._states.indexOf(state)==-1){
this._states.splice(this._states.indexOf(state),1);
}
},
stateReady:function(state){
if(state.name ===this.state){
state.applyTo(this,state.name);
}
this.addState(state);
},
getStates:function(){
return this._states || [];
}
})
});

View File

@ -0,0 +1,759 @@
define([
'dojo/_base/declare',
'dojo/_base/lang',
'xide/views/BeanView',
'xide/views/BeanTreeView',
'xide/factory',
'xide/utils',
"xide/views/_EditorMixin",
"xide/layout/ContentPane",
'xblox/views/GroupedBlockView',
'xblox/views/BlocksGridViewDefault',
'xblox/model/variables/Variable',
'dojo/Deferred',
'xide/layout/TabContainer',
'xide/views/CIActionDialog',
'xide/types',
'xide/form/FilterSelect'
], function (declare, lang, BeanView, BeanTreeView, factory, utils, _EditorMixin, ContentPane, GroupedBlockView, BlocksGridViewDefault, Variable, Deferred,TabContainer,CIActionDialog,types,FilterSelect) {
return declare("xblox.views.BlocksFileEditor", [BeanView, BeanTreeView, _EditorMixin], {
//////////////////////////////////////////////////////////
//
// object instances
//
/**
* xFile item, tracked in ::openItem
*/
_item: null,
cssClass: 'bloxEditor',
blockManager: null,
blockManagerClass: 'xblox.manager.BlockManager',
model: null,
store: null,
tree: null,
currentItem: null,
didLoad: false,
selectable: false,
beanType: 'BLOCK',
newGroupPrefix:'',
_debug:false,
clearGroupViews:function(all){
this.destroyWidgets();
return;
var container = this.getGroupContainer(),
thiz = this;
var panes = container.getChildren();
for (var i = 0; i < panes.length; i++) {
if(panes[i].isNewTab){
container.removeChild(panes[i]);
}
}
for (var i = 0; i < panes.length; i++) {
var pane = panes[i];
/*
if(pane.title=='Variables' && all!==true){
continue;
}*/
container.removeChild(pane);
}
this.createNewTab();
},
getContainerLabel:function(group){
var title = '' + group;
console.log('add new block group ' + group);
if(utils.isNativeEvent(group)){
title = title.replace('on','');
}
//device variable changed: onDriverVariableChanged__deviceId__dd2985b9-9071-1682-226c-70b84b481117/9ab3eabe-ef9a-7613-c3c8-099cde54ef39
if(group.indexOf(types.EVENTS.ON_DRIVER_VARIABLE_CHANGED)!==-1){
var deviceManager = this.ctx.getDeviceManager();
var parts = group.split('__');
var event = parts[0];
var deviceId = parts[1];
var driverId = parts[2];
var variableId = parts[3];
var device = deviceManager.getDeviceById(deviceId);
var driverScope = device ? device.driver : null;
//not initiated driver!
if(driverScope && driverScope.blockScope){
driverScope=driverScope.blockScope;
}
if(!driverScope){
console.error('have no driver, use driver from DB',group);
if(device) {
var driverId = deviceManager.getMetaValue(device, types.DEVICE_PROPERTY.CF_DEVICE_DRIVER);
//var driverManager = this.ctx.getDriverManager();
driverScope = this.ctx.getBlockManager().getScope(driverId);
}
}
var deviceTitle = device ? deviceManager.getMetaValue(device, types.DEVICE_PROPERTY.CF_DEVICE_TITLE) : 'Invalid Device';
var variable=driverScope ? driverScope.getVariableById(driverId + '/' + variableId) : 'Invalid Variable(Have no driver)';
title = 'Variable Changed('+deviceTitle+'/'+ (variable ? variable.title : 'Unknown Variable') +')';
}
return title;
},
addNewBlockGroup:function(group){
if(!group){
return;
}
var blockScope = this.blockScope;
var container = this.getGroupContainer();
var title = this.getContainerLabel(group);
var contentPane = this.createGroupView(container, title, blockScope,true,'fa-bell');
var gridViewConstructurArgs = {};
var newGroup = this.newGroupPrefix + group;
gridViewConstructurArgs.newRootItemGroup = newGroup;
if(this._debug) {
console.log('add new group:' + newGroup);
}
var view = this.createGroupedBlockView(contentPane.containerNode, newGroup, blockScope, gridViewConstructurArgs);
container.selectChild(contentPane);
},
createGroupedBlockView: function (container, group, scope, extra,gridBaseClass) {
var thiz = this;
var args = {
attachTo: container,
beanContextName: this.beanContextName || scope.id,
blockGroup: group,
title: group,
gridViewProto: BlocksGridViewDefault,
blockScope: scope,
ctx: this.ctx,
delegate: this,
showAllBlocks: true,
open: true,
lazy: true,
titlePane: false,
canToggle: false,
gridParams: {
cssClass: 'bloxGridView'
},
gridBaseClass:gridBaseClass
};
if (extra) {
args = lang.mixin(args, extra);
}
var view = new GroupedBlockView(args);
view.startup();
return view;
},
getDeviceVariablesAsEventOptions:function(startIntend){
var options = [];
var _item = function(label,value,intend,selected,displayValue){
var string="<span style=''>" +label + "</span>";
var pre = "";
if(intend>0){
for (var i = 0; i < intend; i++) {
pre+="&nbsp;";
pre+="&nbsp;";
pre+="&nbsp;";
}
}
var _final = pre + string;
return {
label:_final,
label2:displayValue,
value:value
};
};
var deviceManager = this.ctx.getDeviceManager();
var items = deviceManager.getDevices(false,true);
for (var i = 0; i < items.length; i++) {
var device = items[i];
var driver = device.driver;
if(!driver){
continue;
}
var title = deviceManager.getMetaValue(device, types.DEVICE_PROPERTY.CF_DEVICE_TITLE);
var id = deviceManager.getMetaValue(device, types.DEVICE_PROPERTY.CF_DEVICE_ID);
options.push(_item(title,driver.id+'/' +driver.id,startIntend,false));
var blockScope = driver.blockScope;
var variables = blockScope.getVariables();
console.log(device);
for (var j = 0; j < variables.length; j++) {
var variable = variables[j];
var value = types.EVENTS.ON_DRIVER_VARIABLE_CHANGED+ '__' + id+'__'+driver.id + '__'+ variable.id;
var selected = false;
options.push(_item(variable.title,value,startIntend + 1,selected,title + '/' + variable.title));
}
}
return options;
},
openNewGroupDialog:function(){
//var options = utils.getEventsAsOptions();
var options = [];
var _item = function(label,value,intend){
var string="<span style=''>" +label + "</span>";
var pre = "";
if(intend>0){
for (var i = 0; i < intend; i++) {
pre+="&nbsp;";
pre+="&nbsp;";
pre+="&nbsp;";
}
}
var _final = pre + string;
return {
label:_final,
value:value,
label2:label
};
};
options.push( _item('HTML','',0));
options = options.concat([
_item('onclick','click',1),
_item('ondblclick','dblclick',1),
_item('onmousedown','mousedown',1),
_item('onmouseup','mouseup',1),
_item('onmouseover','mouseover',1),
_item('onmousemove','mousemove',1),
_item('onmouseout','mouseout',1),
_item('onkeypress','keypress',1),
_item('onkeydown','keydown',1),
_item('onkeyup','keyup',1),
_item('onfocus','focus',1),
_item('onblur','blur',1),
_item('load','load',1)
]);
options.push( _item('Device Variable Changed','',0));
options = options.concat(this.getDeviceVariablesAsEventOptions(1));
var thiz = this;
var actionDialog = new CIActionDialog({
title: 'Create a new block group',
style: 'width:500px;min-height:200px;',
resizeable: true,
delegate: {
onOk: function (dlg, data) {
var event = data[0].value;
thiz.addNewBlockGroup(event);
}
},
cis: [
utils.createCI('Event', types.ECIType.ENUMERATION, '',
{
group: 'Event',
delegate: null,
options:options,
value:'HTML',
widget:{
"class":"xide.form.FilterSelect"
}
}
)
],
viewArgs:{
inserts: [{
query: '.dijitDialogPaneContent',
insert: '<div><span class="simpleText">Select the event for the new group:</span></div>',
place: 'first'
}]
}
});
actionDialog.show();
},
createNewTab:function(){
var thiz=this;
utils.addWidget(ContentPane,{
iconClass:'fa-magic',
canSelect:false,
isNewTab:true,
onSelect:function(){
thiz.openNewGroupDialog();
}
},this,this.getGroupContainer(),true,'',null,false);
},
onGroupsCreated:function(){
if(this.canAddGroups){
this.createNewTab();
}
},
/**
* Current Blox Scope {xblox.model.Scope}
*/
blockScope: null,
//////////////////////////////////////////////////////////
//
// Widget Instances
//
groupContainer: null,
getTabContainer:function(){
return this.groupContainer;
},
canAddGroups:true,
////////////////////////////////////////////////////////////////
//
// item bean protocol
//
///////////////////////////////////////////////////////////////
hasItemActions: function () {
return true;
},
/**
* Returns item actions
* @returns {Array}
*/
getItemActions: function () {
return this.inherited(arguments);//done in xide/bean/Grouped
},
//////////////////////////////////////////////////////////
//
// Public API
//
onReloaded: function () {
this.destroyWidgets();
this.openItem(this._item);
},
/**
* default entry when opening a file item through xfile
* @param item
*/
openItem: function (item) {
var dfd = new Deferred();
this._item = item;
var thiz = this;
var blockManager = this.getBlockManager();
blockManager.load(item.mount, item.path).then(function (scope) {
thiz.initWithScope(scope);
dfd.resolve(thiz);
});
return dfd;
},
/**
* Init with serialized string, forward to
* @param content
*/
initWithContent: function (content) {
var data = null;
try {
data = utils.getJson(content);
} catch (e) {
console.error('invalid block data');
}
if (data) {
this.initWithData(data);
}
},
/**
* Entry point when a blox scope is fully parsed
* @param blockScope
*/
initWithScope: function (blockScope) {
this.blockScope = blockScope;
var allBlockGroups = blockScope.allGroups(),
thiz = this;
//console.log(' block groups', allBlockGroups);
if (allBlockGroups.indexOf('Variables') == -1) {
allBlockGroups.push('Variables');
}
if (allBlockGroups.indexOf('Events') == -1) {
allBlockGroups.push('Events');
}
if (allBlockGroups.indexOf('On Load') == -1) {
allBlockGroups.push('On Load');
}
thiz.renderGroups(allBlockGroups, blockScope);
this.onLoaded();
},
getScopeUserData: function () {
return {
owner: this
};
},
initWithData: function (data) {
if(this._debug) {
console.log('init with data', data);
}
this.onLoaded();
var scopeId = utils.createUUID(),
blockInData = data,
variableInData = data;
//check structure
if (lang.isArray(data)) {// a flat list of blocks
} else if (lang.isObject(data)) {
scopeId = data.scopeId || scopeId;
blockInData = data.blocks || [];
variableInData = data.variables || [];
}
var blockManager = this.getBlockManager();
this.blockManager = blockManager;
var scopeUserData = this.getScopeUserData();
var blockScope = blockManager.getScope(scopeId, scopeUserData, true);
var allBlocks = blockScope.blocksFromJson(blockInData);
for (var i = 0; i < allBlocks.length; i++) {
var obj = allBlocks[i];
obj._lastRunSettings = {
force: false,
highlight: true
}
}
var allVariables = blockScope.variablesFromJson(variableInData);
blockManager.onBlocksReady(blockScope);
if(this._debug) {
console.log(' got blocks', allBlocks);
console.log(' got variables', allVariables);
}
if (allBlocks) {
return this.initWithScope(blockScope);
}
/**
* a blocks file must be in that structure :
*/
},
//////////////////////////////////////////////////////////
//
// utils
//
destroyWidgets: function () {
utils.destroyWidget(this.groupContainer);
if (this.blockManager && this.blockScope) {
this.blockManager.removeScope(this.blockScope.id);
}
this.groupContainer = null;
this.blockManager = null;
},
destroy: function () {
this.destroyWidgets();
this.inherited(arguments);
},
/**
* Get/Create a block manager implicit
* @returns {xblox.manager.BlockManager}
*/
getBlockManager: function () {
if (!this.blockManager) {
if (this.ctx.blockManager) {
return this.ctx.blockManager;
}
this.blockManager = factory.createInstance(this.blockManagerClass, {
ctx: this.ctx
});
if(this._debug) {
console.log('_createBlockManager ', this.blockManager);
}
}
return this.blockManager;
},
//////////////////////////////////////////////////////////
//
// UI - Factory
//
createGroupContainer: function () {
var tabContainer = utils.addWidget(TabContainer, {
tabStrip: true,
tabPosition: "top",
/*splitter: true,*/
style: "min-width:450px;min-height:400px;height:inherit;padding:0px;"
}, this, this.containerNode, true,'ui-widget-content');
return tabContainer;
},
getGroupContainer: function () {
if (this.groupContainer) {
return this.groupContainer;
}
this.groupContainer = this.createGroupContainer();
return this.groupContainer;
},
createGroupView: function (groupContainer, group,scope,closable,iconClass,select) {
return utils.addWidget(ContentPane, {
delegate: this,
iconClass:iconClass || '',
title: group,
allowSplit:true,
closable:closable,
style: 'padding:0px',
cssClass: 'blocksEditorPane',
blockView: null,
onSelect: function () {
if (this.blockView) {
this.blockView.onShow();
}
}
}, this, groupContainer, true,null,null,select,null);
},
renderGroup:function(group,blockScope,gridBaseClass){
blockScope = blockScope || this.blockScope;
var groupContainer = this.getGroupContainer(),
thiz = this;
var title = group.replace(this.newGroupPrefix,'');
if(utils.isNativeEvent(group)){
title = title.replace('on','');
}
title = this.getContainerLabel(group.replace(this.newGroupPrefix,''));
if(this._debug) {
console.log('render group : ' + title);
}
var isVariableView = group === 'Variables';
var contentPane = this.createGroupView(groupContainer, title, blockScope,!isVariableView,isVariableView ? ' fa-info-circle' : 'fa-bell',!isVariableView);
var gridViewConstructurArgs = {};
if (group === 'Variables') {
gridViewConstructurArgs.newRootItemFunction = function () {
try {
var newItem = new Variable({
title: 'No-Title-Yet',
type: 13,
value: 'No Value',
enumType: 'VariableType',
save: false,
initialize: '',
group: 'Variables',
id: utils.createUUID(),
scope: blockScope
});
} catch (e) {
debugger;
}
};
gridViewConstructurArgs.onGridDataChanged = function (evt) {
var item = evt.item;
if (item) {
item[evt.field] = evt.newValue;
}
thiz.save();
};
gridViewConstructurArgs.showAllBlocks = false;
gridViewConstructurArgs.newRootItemLabel = 'New Variable';
gridViewConstructurArgs.newRootItemIcon = 'fa-code';
gridViewConstructurArgs.storeField = 'variableStore';
gridViewConstructurArgs.gridParams ={
cssClass: 'bloxGridView',
getColumns:function(){
return [
{
label: "Name",
field: "title",
sortable: true
},
{
label: "Value",
field: "value",
sortable: false
}
]
}
};
}
gridViewConstructurArgs.newRootItemGroup = group;
var view = this.createGroupedBlockView(contentPane.containerNode, group, blockScope, gridViewConstructurArgs,gridBaseClass);
contentPane.blockView = view;
view.parentContainer = contentPane;
return view;
},
renderGroups: function (_array, blockScope) {
var groupContainer = this.getGroupContainer();
var lastChild = null, thiz = this;
for (var i = 0; i < _array.length; i++) {
var group = _array[i];
if(this.newGroupPrefix=='' && group.indexOf('__')!==-1){
continue;
}
try {
var title = group.replace(this.newGroupPrefix,'');
var groupBlocks = blockScope.getBlocks({
group: group
});
if (group !== 'Variables' && (!groupBlocks || !groupBlocks.length)) {//skip empty
continue;
}
this.renderGroup(group,blockScope);
} catch (e) {
debugger;
}
}
groupContainer.resize();
setTimeout(function () {
if (thiz.parentContainer) {
thiz.parentContainer.resize();
}
}, 500);
this.onGroupsCreated();
},
onSave: function (groupedBlockView) {
this.save();
},
//////////////////////////////////////////////////////////
//
// Editor related
//
save: function () {
if (this.blockScope) {
var all = {
blocks: null,
variables: null
};
var blocks = this.blockScope.blocksToJson();
try {
//test integrity
dojo.fromJson(JSON.stringify(blocks));
} catch (e) {
console.error('invalid data');
return;
}
var _onSaved = function () {};
var variables = this.blockScope.variablesToJson();
try {
//test integrity
dojo.fromJson(JSON.stringify(variables));
} catch (e) {
console.error('invalid data');
return;
}
all.blocks = blocks;
all.variables = variables;
this.saveContent(JSON.stringify(all, null, 2), this._item, _onSaved);
}
},
onGridKeyEnter: function () {
this.editBlock(this.getItem());
},
onGridMouseDoubleClick: function () {
this.editBlock(this.getItem());
}
});
});

View File

@ -0,0 +1,861 @@
define([
'dcl/dcl',
'dojo/_base/lang',
'dojo/_base/declare',
'xide/factory',
'xide/utils',
'xblox/model/variables/Variable',
'dojo/Deferred',
'xide/views/_CIDialog',
'xide/types',
'xide/views/_LayoutMixin',
'xblox/views/BlockGrid',
'xaction/ActionProvider',
'xide/layout/_TabContainer',
'xide/layout/Container'
], function (dcl,lang,declare, factory, utils, Variable, Deferred,_CIDialog,types,_LayoutMixin,BlockGrid,ActionProvider,_TabContainer,Container) {
var GridClass = declare('BlockGrid',BlockGrid,{
_refresh:function(args){
//var res = this.inherited(arguments);
var history = this.getHistory();
var now = history.getNow();
if(now){
//debugger;
this.setParentBlock(now);
}
return res;
},
editBlock:function(_item,changedCB,select) {
var selection = this.getSelection(),
item = selection[0] || _item;
if (!item) {
return;
}
var head = new Deferred(),
children = item.getChildren();
if(children && children.length){
console.log('--p');
var history = this.getHistory(),
self = this,
select = true;
this.setParentBlock(item);
var isBack = false;
head.resolve({
select: select !== false ? ( isBack ? item : self.getRows()[0]) : null,
focus: true,
append: false,
delay: 200
});
return head;
}
},
__select:function(items){
var item = items[0] || {};
return this.inherited(arguments);
},
/**
* Step/Move Down & Step/Move Up action
* @param dir
*/
move: function (dir) {
var items =this.getSelection();
console.log('move ' + dir,_.pluck(items,'id'));
if (!items || !items.length /*|| !item.parentId*/) {
console.log('cant move, no selection or parentId', items);
return;
}
var thiz = this;
if(dir===1){
//items.reverse();
}
_.each(items,function(item){
item.move(item, dir);
});
thiz.refreshRoot();
thiz.refreshCurrent();
this.select(items,null,true,{
focus:true,
delay:10
}).then(function(){
thiz.refreshActions();
});
},
reParentBlock:function(dir){
var item = this.getSelection()[0];
if (!item) {
console.log('cant move, no selection or parentId', item);
return false;
}
var thiz = this;
if(dir==-1) {
item.unparent(thiz.blockGroup);
}else{
item.reparent();
}
thiz.deselectAll();
thiz.refreshRoot();
this.refreshCurrent();
var dfd = new Deferred();
var defaultSelectArgs = {
focus: true,
append: false,
delay: 100,
select:item,
expand:true
};
dfd.resolve(defaultSelectArgs);
return dfd;
},
defaultActionResult:function(items){
var dfd = new Deferred();
var defaultSelectArgs = {
focus: true,
append: false,
delay: 0,
select:items,
expand:true
};
dfd.resolve(defaultSelectArgs);
return dfd;
},
runAction: function (action) {
var thiz = this;
var sel = this.getSelection();
var selection = this.getSelection(),
item = selection[0];
switch (action.command){
/*
case 'Step/Move Left':
{
return this.reParentBlock(-1);
}
case 'Step/Move Right':
{
return this.reParentBlock(1);
}
*/
}
if(action) {
//console.error('run action ' + action.command);
}
return this.inherited(arguments);
}
});
var Module = dcl([Container,_LayoutMixin.dcl,ActionProvider.dcl],{
declaredClass:"xblox.views.BlocksFileEditor",
registerView:false,
_item: null,
cssClass: 'bloxEditor',
blockManager: null,
blockManagerClass: 'xblox.manager.BlockManager',
model: null,
store: null,
tree: null,
currentItem: null,
didLoad: false,
selectable: false,
beanType: 'BLOCK',
newGroupPrefix:'',
_debug:false,
blockScope: null,
groupContainer: null,
canAddGroups:true,
gridClass:GridClass,
activeGrid:null,
activeTab:null,
constructor:function(options,container){
utils.mixin(this,options);
},
onGridAction:function(evt){
var action = evt.action,
command = action.command,
result = evt.result,
ACTION = types.ACTION;
switch (command){
case ACTION.SAVE:{
return this.save();
}
}
//console.log('on after action '+evt.action.command);
},
clearGroupViews:function(all){
var container = this.getGroupContainer(),
thiz = this;
container.empty();
this.destroyWidgets();
return;
var container = this.getGroupContainer(),
thiz = this;
var panes = container.getChildren();
for (var i = 0; i < panes.length; i++) {
if(panes[i].isNewTab){
container.removeChild(panes[i]);
}
}
for (var i = 0; i < panes.length; i++) {
var pane = panes[i];
/*
if(pane.title=='Variables' && all!==true){
continue;
}*/
container.removeChild(pane);
}
this.createNewTab();
},
getContainerLabel:function(group){
var title = '' + group;
if(utils.isNativeEvent(group)){
title = title.replace('on','');
}
//device variable changed: onDriverVariableChanged__deviceId__dd2985b9-9071-1682-226c-70b84b481117/9ab3eabe-ef9a-7613-c3c8-099cde54ef39
if(group.indexOf(types.EVENTS.ON_DRIVER_VARIABLE_CHANGED)!==-1){
var deviceManager = this.ctx.getDeviceManager();
var parts = group.split('__');
var deviceId = parts[1];
var driverId = parts[2];
var variableId = parts[3];
var device = deviceManager.getDeviceById(deviceId);
var driverScope = device ? device.driver : null;
//not initiated driver!
if(driverScope && driverScope.blockScope){
driverScope=driverScope.blockScope;
}
if(!driverScope){
console.error('have no driver, use driver from DB',group);
if(device) {
var driverId = deviceManager.getMetaValue(device, types.DEVICE_PROPERTY.CF_DEVICE_DRIVER);
//var driverManager = this.ctx.getDriverManager();
driverScope = this.ctx.getBlockManager().getScope(driverId);
}
}
var deviceTitle = device ? deviceManager.getMetaValue(device, types.DEVICE_PROPERTY.CF_DEVICE_TITLE) : 'Invalid Device';
var variable=driverScope ? driverScope.getVariableById(driverId + '/' + variableId) : 'Invalid Variable(Have no driver)';
title = 'Variable Changed('+deviceTitle+'/'+ (variable ? variable.title : 'Unknown Variable') +')';
}
return title;
},
addNewBlockGroup:function(group){
if(!group){
return;
}
var blockScope = this.blockScope;
var container = this.getGroupContainer();
var title = this.getContainerLabel(group);
var contentPane = this.createGroupView(container, title, blockScope,true,'fa-bell');
var gridViewConstructurArgs = {};
var newGroup = this.newGroupPrefix + group;
gridViewConstructurArgs.newRootItemGroup = newGroup;
var view = this.createGroupedBlockView(contentPane.containerNode, newGroup, blockScope, gridViewConstructurArgs);
contentPane.grid=view;
container.selectChild(contentPane);
view.resize();
},
createGroupedBlockView: function (container, group, scope, extra,gridBaseClass){
var thiz = this;
gridBaseClass = gridBaseClass || this.gridClass;
var store = scope.blockStore;
var gridArgs = {
_docker:this.getDocker(),
__right:this.getRightPanel(),
ctx:this.ctx,
blockScope: scope,
blockGroup: group,
attachDirect:true,
resizeToParent:true,
collection: store.filter({
group: group
}),
_parent:container
};
extra && lang.mixin(gridArgs, extra);
var view = utils.addWidget(gridBaseClass,gridArgs,null,container,false);
if(!view.__editorActions){
view.__editorActions=true;
this.addGridActions(view,view);
}
//view.startup();
container.grid = view;
view._on('selectionChanged',function(evt){
thiz._emit('selectionChanged',evt);
});
view._on('onAfterAction',function(e){
thiz.onGridAction(e);
});
if(this.registerView && this.ctx){
}
//this.ctx.getWindowManager().registerView(view,true);
if(!container._widgets){
container._widgets=[];
}
container._widgets.push(view);
//utils.resizeTo(container,view,true,true);
//view.showToolbar(true);
return view;
},
getDeviceVariablesAsEventOptions:function(startIntend){
var options = [];
var _item = function(label,value,intend,selected,displayValue){
var string="<span style=''>" +label + "</span>";
var pre = "";
if(intend>0){
for (var i = 0; i < intend; i++) {
pre+="&nbsp;";
pre+="&nbsp;";
pre+="&nbsp;";
}
}
var _final = pre + string;
return {
label:_final,
label2:displayValue,
value:value
};
};
var deviceManager = this.ctx.getDeviceManager();
var items = deviceManager.getDevices(false,true);
for (var i = 0; i < items.length; i++) {
var device = items[i];
var driver = device.driver;
if(!driver){
continue;
}
var title = deviceManager.getMetaValue(device, types.DEVICE_PROPERTY.CF_DEVICE_TITLE);
var id = deviceManager.getMetaValue(device, types.DEVICE_PROPERTY.CF_DEVICE_ID);
options.push(_item(title,driver.id+'/' +driver.id,startIntend,false));
var blockScope = driver.blockScope;
var variables = blockScope.getVariables();
for (var j = 0; j < variables.length; j++) {
var variable = variables[j];
var value = types.EVENTS.ON_DRIVER_VARIABLE_CHANGED+ '__' + id+'__'+driver.id + '__'+ variable.id;
var selected = false;
options.push(_item(variable.name,value,startIntend + 1,selected,title + '/' + variable.name));
}
}
return options;
},
openNewGroupDialog:function(){
var options = [];
var _item = function(label,value,intend,isHTML){
var string= isHTML !==true ? "<span style=''>" +label + "</span>" : label;
var pre = "";
if(intend>0){
for (var i = 0; i < intend; i++) {
pre+="&nbsp;";
pre+="&nbsp;";
pre+="&nbsp;";
}
}
var _final = pre + string;
return {
label:_final,
value:value,
label2:label
};
};
options.push( _item('HTML','',0));
options = options.concat([
_item('onclick','click',1),
_item('ondblclick','dblclick',1),
_item('onmousedown','mousedown',1),
_item('onmouseup','mouseup',1),
_item('onmouseover','mouseover',1),
_item('onmousemove','mousemove',1),
_item('onmouseout','mouseout',1),
_item('onkeypress','keypress',1),
_item('onkeydown','keydown',1),
_item('onkeyup','keyup',1),
_item('onfocus','focus',1),
_item('onblur','blur',1),
_item('load','load',1)
]);
options.push( _item('<span class="text-default">Device Variable Changed</span>','',0,true));
options = options.concat(this.getDeviceVariablesAsEventOptions(1));
var thiz = this;
var actionDialog = new _CIDialog({
title: 'Create a new block group',
style: 'width:500px;min-height:200px;',
size: types.DIALOG_SIZE.SIZE_NORMAL,
resizeable: true,
onOk: function () {
thiz.addNewBlockGroup(this.getField('Event'));
},
cis: [
utils.createCI('Event', types.ECIType.ENUMERATION, '', {
group: 'Event',
delegate: null,
options:options,
value:'HTML',
title:'Select an event &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;',
widget:{
search:true,
"class":"xide.form.FilterSelect"
}
}
)
]
});
this.add(actionDialog,null,false);
return actionDialog.show();
},
onGroupsCreated:function(){
//this.ctx.getWindowManager().registerView(this,true);
},
getActionStore:function(){
if(this.activeGrid){
return this.activeGrid.getActionStore();
}
return null;
},
getTabContainer:function(){
return this.groupContainer;
},
onReloaded: function () {
this.destroyWidgets();
this.openItem(this._item);
},
/**
* default entry when opening a file item through xfile
* @param item
*/
openItem: function (item) {
var dfd = new Deferred();
this._item = item;
var thiz = this;
var blockManager = this.getBlockManager();
blockManager.load(item.mount, item.path).then(function (scope) {
thiz.initWithScope(scope);
dfd.resolve(thiz);
});
return dfd;
},
/**
* Init with serialized string, forward to
* @param content
*/
initWithContent: function (content) {
var data = null;
try {
data = utils.getJson(content);
} catch (e) {
console.error('invalid block data');
}
if (data) {
this.initWithData(data);
}
},
/**
* Entry point when a blox scope is fully parsed
* @param blockScope
*/
initWithScope: function (blockScope) {
this.blockScope = blockScope;
var allBlockGroups = blockScope.allGroups(),
thiz = this;
if (allBlockGroups.indexOf('Variables') == -1) {
allBlockGroups.push('Variables');
}
if (allBlockGroups.indexOf('Events') == -1) {
allBlockGroups.push('Events');
}
if (allBlockGroups.indexOf('On Load') == -1) {
allBlockGroups.push('On Load');
}
thiz.renderGroups(allBlockGroups, blockScope);
},
getScopeUserData: function () {
return {
owner: this
};
},
initWithData: function (data) {
if(this._debug) {
console.log('init with data', data);
}
this.onLoaded();
var scopeId = utils.createUUID(),
blockInData = data,
variableInData = data;
//check structure
if (lang.isArray(data)) {// a flat list of blocks
} else if (lang.isObject(data)) {
scopeId = data.scopeId || scopeId;
blockInData = data.blocks || [];
variableInData = data.variables || [];
}
var blockManager = this.getBlockManager();
this.blockManager = blockManager;
var scopeUserData = this.getScopeUserData();
var blockScope = blockManager.getScope(scopeId, scopeUserData, true);
var allBlocks = blockScope.blocksFromJson(blockInData);
for (var i = 0; i < allBlocks.length; i++) {
var obj = allBlocks[i];
obj._lastRunSettings = {
force: false,
highlight: true
}
}
var allVariables = blockScope.variablesFromJson(variableInData);
blockManager.onBlocksReady(blockScope);
if(this._debug) {
console.log(' got blocks', allBlocks);
console.log(' got variables', allVariables);
}
if (allBlocks) {
return this.initWithScope(blockScope);
}
/**
* a blocks file must be in that structure :
*/
},
destroyWidgets: function () {
if (this.blockManager && this.blockScope) {
this.blockManager.removeScope(this.blockScope.id);
}
//this.groupContainer = null;
this.blockManager = null;
},
destroy: function () {
this.destroyWidgets();
},
getBlockManager: function () {
if (!this.blockManager) {
if (this.ctx.blockManager) {
return this.ctx.blockManager;
}
this.blockManager = factory.createInstance(this.blockManagerClass, {
ctx: this.ctx
});
if(this._debug) {
console.log('_createBlockManager ', this.blockManager);
}
}
return this.blockManager;
},
createGroupContainer: function () {
var tabContainer = utils.addWidget(_TabContainer, {
tabStrip: true,
tabPosition: "top",
direction:'below',
style: "min-width:450px;min-height:400px;padding:0px;",
resizeToParent:true
},this,this.containerNode,true);
this.add(tabContainer,null,false);
return tabContainer;
},
getGroupContainer: function () {
if (this.groupContainer) {
return this.groupContainer;
}
this.groupContainer = this.createGroupContainer();
return this.groupContainer;
},
getActiveGrid:function(){
return this.activeGrid;
},
runAction:function(action){
if(action.command=='File/New Group'){
this.openNewGroupDialog();
return null;
}
return this.getActiveGrid().runAction(arguments);
},
addGridActions:function(grid,who){
var result = [];
var thiz = this;
var defaultMixin = { addPermission: true };
result.push(thiz.createAction({
label: 'New Group',
command: 'File/New Group',
icon: 'fa-magic',
tab: 'Home',
group: 'File',
keycombo: ['f7'],
mixin: utils.mixin({quick:true},defaultMixin)
}));
result.push(thiz.createAction({
label: 'Delete Group',
command: 'File/Delete Group',
icon: 'fa-remove',
tab: 'Home',
group: 'File',
keycombo: ['ctrl f7'],
mixin: utils.mixin({quick:true},defaultMixin)
}));
(who || this).addActions(result);
},
onShowGrid:function(grid){
if(this._isCreating){
return;
}
this.ctx.getWindowManager().clearView(null);
if(!grid.__editorActions){
grid.__editorActions=true;
this.addGridActions(grid);
}
//this.groupContainer.resize();
//grid.resize();
setTimeout(function(){
grid.resize();
},100);
this.ctx.getWindowManager().registerView(grid);
},
createGroupView: function (groupContainer, group,scope,closable,iconClass,selected) {
var tab = groupContainer.createTab(group,iconClass,selected,_TabContainer.tabClass,{
delegate: this,
iconClass:iconClass || '',
title: group,
allowSplit:true,
closable:closable,
style: 'padding:0px',
cssClass: 'blocksEditorPane',
blockView: null
});
tab._on('show',function(tab){
if(tab.grid) {
this.activeTab = tab;
this.activeGrid = tab.grid;
this.onShowGrid(tab.grid); }
},this);
return tab;
},
renderGroup:function(group,blockScope,gridBaseClass){
blockScope = blockScope || this.blockScope;
var groupContainer = this.getGroupContainer(),
thiz = this;
var title = group.replace(this.newGroupPrefix,'');
if(utils.isNativeEvent(group)){
title = title.replace('on','');
}
title = this.getContainerLabel(group.replace(this.newGroupPrefix,''));
var isVariableView = group === 'Variables';
var contentPane = this.createGroupView(groupContainer, title, blockScope,!isVariableView,isVariableView ? ' fa-info-circle' : 'fa-bell',!isVariableView);
//groupContainer.resize();
var gridViewConstructurArgs = {};
if (group === 'Variables') {
gridViewConstructurArgs.newRootItemFunction = function () {
try {
var newItem = new Variable({
title: 'No-Title-Yet',
type: 13,
value: 'No Value',
enumType: 'VariableType',
save: false,
initialize: '',
group: 'Variables',
id: utils.createUUID(),
scope: blockScope
});
} catch (e) {
debugger;
}
};
gridViewConstructurArgs.onGridDataChanged = function (evt) {
var item = evt.item;
if (item) {
item[evt.field] = evt.newValue;
}
thiz.save();
};
gridViewConstructurArgs.showAllBlocks = false;
gridViewConstructurArgs.newRootItemLabel = 'New Variable';
gridViewConstructurArgs.newRootItemIcon = 'fa-code';
gridViewConstructurArgs.storeField = 'variableStore';
gridViewConstructurArgs.gridParams ={
cssClass: 'bloxGridView',
getColumns:function(){
return [
{
label: "Name",
field: "title",
sortable: true
},
{
label: "Value",
field: "value",
sortable: false
}
]
}
};
}
gridViewConstructurArgs.newRootItemGroup = group;
var view = this.createGroupedBlockView(contentPane, group, blockScope, gridViewConstructurArgs,gridBaseClass);
contentPane.blockView = view;
view.parentContainer = contentPane;
!this.activeGrid && (this.activeGrid=view);
//groupContainer.resize();
//contentPane.resize();
setTimeout(function(){
//view.showToolbar(true);
},200);
//view.resize();
//contentPane.resize();
//console.log('render group '+group);
return view;
},
renderGroups: function (_array, blockScope) {
this._isCreating = true;
this.activeGrid = null;
var groupContainer = this.getGroupContainer();
for (var i = 0; i < _array.length; i++) {
var group = _array[i];
if(this.newGroupPrefix=='' && group.indexOf('__')!==-1){
continue;
}
try {
var groupBlocks = blockScope.getBlocks({
group: group
});
if (group !== 'Variables' && (!groupBlocks || !groupBlocks.length)) {//skip empty
continue;
}
this.renderGroup(group,blockScope);
} catch (e) {
logError(e);
}
}
var firstTab = groupContainer.selectChild(0);
this.onGroupsCreated();
//groupContainer.resize();
//groupContainer.resize();
var thiz = this;
//firstTab.resize();
setTimeout(function(){
thiz._isCreating = false;
},1000);
},
onSave: function (groupedBlockView) {
this.save();
},
save: function () {
if (this.blockScope) {
//this.saveContent(this.blockScope.toString());
var fileManager = this.ctx.getFileManager(),
item = this.item;
fileManager.setContent(item.mount,item.path,this.blockScope.toString(),function(){
console.log('saved blocks! to ' + item.path);
});
}else{
console.warn('BlocksFileEditor::save : have no block scope');
}
}
});
return Module;
});

View File

@ -0,0 +1,68 @@
define([
"dcl/dcl",
"xdojo/has",
"xide/model/Component"
], function (dcl,has,Component) {
/**
* @class xblox.component
* @inheritDoc
*/
return dcl(Component, {
/**
* @inheritDoc
*/
beanType:'BLOCK',
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Implement base interface
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
_load:function(){
},
hasEditors:function(){
return ['xblox'];
},
getDependencies:function(){
if(has('xblox-ui')) {
return [
'xide/xide',
'xblox/types/Types',
'xblox/manager/BlockManager',
'xblox/manager/BlockManagerUI',
'xblox/embedded_ui',
'xblox/views/BlockGridPalette',
'xide/widgets/ExpressionJavaScript',
'xide/widgets/Expression',
'xide/widgets/RichTextWidget',
'xide/widgets/ExpressionEditor',
'xide/widgets/WidgetReference'
//'xide/widgets/DomStyleProperties',
//'xblox/views/BlocksFileEditor'
//'xide/widgets/BlockPickerWidget',
//'xide/widgets/BlockSettingsWidget'
];
}else{
return [
'xide/xide',
'xblox/types/Types',
'xblox/manager/BlockManager',
'xblox/embedded'
];
}
},
/**
* @inheritDoc
*/
getLabel: function () {
return 'xblox';
},
/**
* @inheritDoc
*/
getBeanType:function(){
return this.beanType;
}
});
});

View File

@ -0,0 +1,72 @@
/** @module xblox/data/Store **/
define([
"dojo/_base/declare",
'xide/data/TreeMemory',
'xide/data/ObservableStore',
'dstore/Trackable',
'dojo/Deferred'
], function (declare, TreeMemory, ObservableStore, Trackable, Deferred) {
return declare("xblox.data.Store", [TreeMemory, Trackable, ObservableStore], {
idProperty: 'id',
parentField: 'parentId',
parentProperty: 'parentId',
filter: function (data) {
var _res = this.inherited(arguments);
delete this._state.filter;
this._state.filter = data;
return _res;
},
getRootItem:function(){
return {
canAdd:function(){
return true
},
id:this.id +'_root',
group:null,
name:'root',
isRoot:true,
parentId:null
}
},
getChildren: function (object) {
return this.root.filter({parentId: this.getIdentity(object)});
},
_fetchRange: function (kwArgs) {
var deferred = new Deferred();
var _res = this.fetchRangeSync(kwArgs);
var _items;
if (this._state.filter) {
//the parent query
if (this._state.filter['parentId']) {
var _item = this.getSync(this._state.filter.parentId);
if (_item) {
this.reset();
_items = _item.items;
if (_item.getChildren) {
_items = _item.getChildren();
}
deferred.resolve(_items);
_res = _items;
}
}
//the group query
if (this._state && this._state.filter && this._state.filter['group']) {
_items = this.getSync(this._state.filter.parent);
if (_item) {
this.reset();
_res = _item.items;
}
}
}
deferred.resolve(_res);
return deferred;
},
mayHaveChildren: function (parent) {
if (parent.mayHaveChildren) {
return parent.mayHaveChildren(parent);
}
return parent.items != null && parent.items.length > 0;
}
});
});

View File

@ -0,0 +1,76 @@
## Description
The command block is meant for holding a string to be send to a device.
## Parameters
**Enabled**
Field Name : "enabled"
Type : Boolean
Remarks:
- If disabled at run-time, it will stop the block and cancels also the interval timer.
<hr/>
**Send on Startup**
If true, this block will be executed when the device is connected.
Field Name: "startup"
Type: Boolean
<hr/>
**Interval**
If greater than 0, the block will be looped by that interval in ms.
Field Name: "interval"
Type: Boolean
<hr/>
**Has Response**
Some protocols like the built-in SSH return standard execution marks, like std-error, std-data.
When this setting is on, the system will mark the command as running but not finished.
As soon such command receives and "end" from the server, it will mark the command as finished and proceeds
with sub blocks.
Field Name: "waitForResponse"
Type: Boolean
<hr/>
**Flags**
*"Dont parse"* : will send the string in "Send" as is.
*"Expression"* : This flag is on by default! When on, the string in "Send" will be treated and evaluated as Javascript.
Field Name: "flags"
Type: integer
*Remarks*:
-When "Expression" is on, it will replace variables in "Send"
-When "Expression" is on, it will add "return" in front of what's in "Send" if missing
<hr/>

View File

@ -0,0 +1,106 @@
## Description
This block enables to subscribe to system events.
## Parameters
**Enabled**
Field Name : "enabled"
Type : Boolean
<hr/>
**Filter Path**
Every event comes with its own payload object and its own structure differs per event.
This field enables to specify the path to a value inside that payload. This needs be set only if the
payload is an object, and not already primitive.
So this field enables you to filter events and the block will only be triggered if *Filter Value* matches the value inside the payload path.
**Example**
We want to subscribe to "Driver Variable Changed", but only for a certain variable: "Volume".
The event payload object for "onDriverVariableChanged" looks like this:
```
{
item:variable
}
```
where the variable object looks like this:
```
{
value:"variable value",
name:"Volume"
}
```
To trigger this block only for "Volume", this field needs to be set to: item.name
<hr/>
**Filter Value**
Needs to be set when the block should only trigger if the event payload contains a certain value, specified by "Filter Path"
**Example**
We want to subscribe to "Driver Variable Changed", but only for a certain variable: "Volume".
The event payload object for "onDriverVariableChanged" looks like this:
```
{
item:variable
}
```
where the variable object looks like this:
```
{
value:"variable value",
name:"Volume"
}
```
To trigger this block only for "Volume", this field needs to be set to: "Volume", and the "Filter Path" needs
to be set to "item.name"
<hr/>
**Value Path**
The path to the value to be forward to sub blocks within the payload.
**Example**
If we want to forward the variable's value to sub blocks, this field needs to be set to "item.value"
For instance, you can access this value in sub blocks with arguments[0];
<hr/>
### Tips:
Attach a Run Script block with this code:
```
console.log(arguments);
```
to see the event payload in the Dev-Tools console

View File

@ -0,0 +1,63 @@
## Description
The block reads JSON data from a file
## Parameters
**Enabled**
Field Name : "enabled"
Type : Boolean
Remarks:
- If disabled at run-time, it will stop the block and cancels also the interval timer.
<hr/>
**Path**
Field Name : "path"
Type : String
Description : An absolute path. This path must exist on the device server.
<hr/>
**Select**
Field Name : "jsonPath"
Type : String
Description : A path within the data. If set, the selected data will be forwarded to child blocks.
For instance, if the data is:
```json
{
"boolean":true,
"number":10.0,
"array":[1,2,3],
"myField":"my field value"
}
```
You can select the value of "myField" by using 'myField' which returns my field value.
You can select the second field of "array" by using 'array.2' which returns 2.
<hr/>
**Tips**:
In child blocks you can retrieve the data by using "return arguments[0]".

View File

@ -0,0 +1,5 @@
### XBlocks Reference
### Driver related
[Command](Blocks/Command)

View File

@ -0,0 +1,74 @@
define([
'dojo/_base/declare',
'xide/types',
'xblox/types/Types',
'xide/factory',
'xide/utils',
'xide/mixins/ReloadMixin',
'xide/mixins/EventedMixin',
"xblox/model/logic/CaseBlock",
"xblox/model/Block",
"xblox/model/functions/CallBlock",
"xblox/model/code/CallMethod",
"xblox/model/code/RunScript",
"xblox/model/code/RunBlock",
"xblox/model/loops/ForBlock",
"xblox/model/loops/WhileBlock",
"xblox/model/variables/VariableAssignmentBlock",
"xblox/model/logic/IfBlock",
"xblox/model/logic/ElseIfBlock",
"xblox/model/logic/SwitchBlock",
"xblox/model/variables/VariableSwitch",
"xblox/model/events/OnEvent",
"xblox/model/events/OnKey",
"xblox/model/logging/Log",
"xblox/model/html/SetStyle",
"xblox/model/html/SetCSS",
"xblox/model/html/SetStyle",
"xblox/manager/BlockManager",
"xblox/factory/Blocks",
"xdojo/has!xblox-ui?xblox/model/Block_UI"
], function () {
if(!Array.prototype.remove){
Array.prototype.remove= function(){
var what, a= arguments, L= a.length, ax;
while(L && this.length){
what= a[--L];
if(this.indexOf==null){
break;
}
while((ax= this.indexOf(what))!= -1){
this.splice(ax, 1);
}
}
return this;
};
}
if(!Array.prototype.swap){
Array.prototype.swap = function (x,y) {
var b = this[x];
this[x] = this[y];
this[y] = b;
return this;
};
}
if ( typeof String.prototype.startsWith != 'function' ) {
String.prototype.startsWith = function( str ) {
return this.substring( 0, str.length ) === str;
};
}
if ( typeof String.prototype.endsWith != 'function' ) {
String.prototype.endsWith = function( str ) {
return this.substring( this.length - str.length, this.length ) === str;
};
}
if(!Function.prototype.bind) {
// Cheap polyfill to approximate bind(), make Safari happy
Function.prototype.bind = Function.prototype.bind || function (that) {
return dojo.hitch(that, this);
};
}
});

View File

@ -0,0 +1,6 @@
define([
'xblox/embedded',
'xblox/factory/Blocks',
'xide/widgets/ScriptWidget'
], function () {
});

View File

@ -0,0 +1,542 @@
define([
'xide/factory',
'xide/utils',
'xide/types',
'xide/mixins/ReloadMixin',
'xide/mixins/EventedMixin',
"xblox/model/logic/CaseBlock",
"xblox/model/Block",
"xblox/model/functions/CallBlock",
"xblox/model/functions/StopBlock",
"xblox/model/functions/PauseBlock",
"xblox/model/functions/SetProperties",
"xblox/model/code/CallMethod",
"xblox/model/code/RunScript",
"xblox/model/loops/ForBlock",
"xblox/model/loops/WhileBlock",
"xblox/model/variables/VariableAssignmentBlock",
"xblox/model/logic/IfBlock",
"xblox/model/logic/ElseIfBlock",
"xblox/model/logic/SwitchBlock",
"xblox/model/variables/VariableSwitch",
"xblox/model/logging/Log",
"xblox/model/server/RunServerMethod",
"xblox/model/server/Shell",
"xblox/model/code/RunBlock",
"xblox/model/events/OnEvent",
"xblox/model/events/OnKey",
"xblox/model/mqtt/Subscribe",
"xblox/model/mqtt/Publish",
"xblox/model/File/ReadJSON",
"xcf/factory/Blocks"
], function (factory,
utils,
types,
ReloadMixin, EventedMixin,
CaseBlock,
Block,
CallBlock,
StopBlock,
PauseBlock,
SetProperties,
CallMethod,
RunScript,
ForBlock,
WhileBlock,
VariableAssignmentBlock,
IfBlock,
ElseIfBlock,
SwitchBlock,
VariableSwitch,
Log,
RunServerMethod,
Shell,
RunBlock,
OnEvent,
OnKey,
Subscribe,
Publish,
ReadJSON) {
var cachedAll = null;
/***
*
* @param mixed String|Prototype
* @param ctorArgs
* @param baseClasses
* @param publish
*/
factory.createBlock = function (mixed, ctorArgs, baseClasses, publish) {
//complete missing arguments:
Block.prototype.prepareArgs(ctorArgs);
var block = factory.createInstance(mixed, ctorArgs, baseClasses);
block.ctrArgs = null;
var newBlock;
try {
if (block && block.init) {
block.init();
}
//add to scope
if (block.scope) {
newBlock = block.scope.registerBlock(block, publish);
}
if (block.initReload) {
block.initReload();
}
} catch (e) {
logError(e, 'create block');
}
return newBlock || block;
};
factory.clearVariables = function () {
};
factory.getAllBlocks = function (scope, owner, target, group, allowCache) {
if (allowCache !== false && cachedAll != null) {
return cachedAll;
} else if (allowCache == false) {
cachedAll = null;
}
var items = factory._getFlowBlocks(scope, owner, target, group);
items = items.concat(factory._getLoopBlocks(scope, owner, target, group));
items = items.concat(factory._getCommandBlocks(scope, owner, target, group));
items = items.concat(factory._getCodeBlocks(scope, owner, target, group));
items = items.concat(factory._getEventBlocks(scope, owner, target, group));
items = items.concat(factory._getLoggingBlocks(scope, owner, target, group));
items = items.concat(factory._getServerBlocks(scope, owner, target, group));
items = items.concat(factory._getMQTTBlocks(scope, owner, target, group));
items = items.concat(factory._getFileBlocks(scope, owner, target, group));
cachedAll = items;
return items;
};
factory._getMQTTBlocks = function (scope, owner, target, group) {
var items = [];
items.push({
name: 'MQTT',
iconClass: 'fa-cloud',
items: [
{
name: 'Subscribe',
owner: owner,
iconClass: 'fa-bell',
proto: Subscribe,
target: target,
ctrArgs: {
scope: scope,
group: group
}
},
{
name: 'Publish',
owner: owner,
iconClass: 'fa-send',
proto: Publish,
target: target,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
//tell everyone
factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST, {
items: items,
group: 'MQTT'
});
return items;
};
factory._getFileBlocks = function (scope, owner, target, group) {
var items = [];
items.push({
name: 'File',
iconClass: 'fa-file',
items: [
{
name: '%%Read JSON',
owner: owner,
iconClass: 'fa-file',
proto: ReadJSON,
target: target,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
//tell everyone
factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST, {
items: items,
group: 'File'
});
return items;
};
factory._getServerBlocks = function (scope, owner, target, group) {
var items = [];
items.push({
name: 'Server',
iconClass: 'el-icon-repeat',
items: [
{
name: 'Run Server Method',
owner: owner,
iconClass: 'fa-plug',
proto: RunServerMethod,
target: target,
ctrArgs: {
scope: scope,
group: group
}
},
{
name: 'Shell',
owner: owner,
iconClass: 'fa-code',
proto: Shell,
target: target,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
//tell everyone
factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST, {
items: items,
group: 'Server'
});
return items;
};
factory._getVariableBlocks = function (scope, owner, target, group) {
var items = [];
items.push({
name: 'Flow',
iconClass: 'el-icon-random',
items: [
{
name: 'If...Else',
owner: owner,
iconClass: 'el-icon-fork',
proto: IfBlock,
target: target,
ctrArgs: {
scope: scope,
group: group,
condition: "[value]=='PW'"
}
}/*,
{
name:'Switch',
owner:owner,
iconClass:'el-icon-fork',
proto:SwitchBlock,
target:target,
ctrArgs:{
scope:scope,
group:group
}
}
*/
]
});
return items;
};
factory._getEventBlocks = function (scope, owner, target, group) {
var items = [];
items.push({
name: 'Events',
iconClass: 'fa-bell',
items: [
{
name: 'On Event',
owner: owner,
iconClass: 'fa-bell',
proto: OnEvent,
target: target,
ctrArgs: {
scope: scope,
group: group
}
},
{
name: 'On Key',
owner: owner,
iconClass: 'fa-keyboard-o',
proto: OnKey,
target: target,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
//tell everyone
factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST, {
items: items,
group: 'Events'
});
return items;
};
factory._getLoggingBlocks = function (scope, owner, target, group) {
var items = [];
items.push({
name: 'Logging',
iconClass: 'fa-bug',
items: [
{
name: 'Log',
owner: owner,
iconClass: 'fa-bug',
proto: Log,
target: target,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
//tell everyone
factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST, {
items: items,
group: 'Logging'
});
return items;
};
factory._getCodeBlocks = function (scope, owner, target, group) {
var items = [];
items.push({
name: 'Code',
iconClass: 'fa-code',
items: [
{
name: 'Call Method',
owner: owner,
iconClass: 'el-icon-video',
proto: CallMethod,
target: target,
ctrArgs: {
scope: scope,
group: group
}
},
{
name: 'Run Script',
owner: owner,
iconClass: 'fa-code',
proto: RunScript,
target: target,
ctrArgs: {
scope: scope,
group: group
}
},
{
name: 'Run Block',
owner: owner,
iconClass: 'fa-code',
proto: RunBlock,
target: target,
ctrArgs: {
scope: scope,
group: group
}
},
{
name: 'Set Properties',
owner: owner,
iconClass: 'fa-code',
proto: SetProperties,
target: target,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
//tell everyone
factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST, {
items: items,
group: 'Code'
});
return items;
};
factory._getFlowBlocks = function (scope, owner, target, group) {
var items = [];
items.push({
name: 'Flow',
iconClass: 'el-icon-random',
items: [
{
name: 'If...Else',
owner: owner,
iconClass: 'el-icon-fork',
proto: IfBlock,
target: target,
ctrArgs: {
scope: scope,
group: group,
condition: "[value]=='PW'"
}
},
{
name: 'Switch',
owner: owner,
iconClass: 'el-icon-fork',
proto: SwitchBlock,
target: target,
ctrArgs: {
scope: scope,
group: group
}
},
{
name: 'Variable Switch',
owner: owner,
iconClass: 'el-icon-fork',
proto: VariableSwitch,
target: target,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
//tell everyone
factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST, {
items: items,
group: 'Flow'
});
return items;
};
factory._getLoopBlocks = function (scope, owner, target, group) {
var items = [];
items.push({
name: 'Loops',
iconClass: 'el-icon-repeat',
items: [
{
name: 'While',
owner: owner,
iconClass: 'el-icon-repeat',
proto: WhileBlock,
target: target,
ctrArgs: {
scope: scope,
group: group,
condition: "[Volume]<=100"
}
},
{
name: 'For',
owner: owner,
iconClass: 'el-icon-repeat',
proto: ForBlock,
target: target,
ctrArgs: {
scope: scope,
group: group,
initial: '1',
comparator: "<=",
"final": '5',
modifier: '+1',
counterName: 'value'
}
}
]
});
//tell everyone
factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST, {
items: items,
group: 'Loops'
});
return items;
};
factory._getMathBlocks = function (scope, owner, dstItem, group) {
var items = [];
items.push({
name: 'Math',
owner: this,
iconClass: 'el-icon-qrcode',
dstItem: dstItem,
items: [
{
name: 'If...Else',
owner: dstItem,
iconClass: 'el-icon-compass',
proto: IfBlock,
item: dstItem,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
return items;
};
factory._getTimeBlocks = function (scope, owner, dstItem, group) {
var items = [];
items.push({
name: 'Time',
owner: this,
iconClass: 'el-icon-qrcode',
dstItem: dstItem,
items: [
{
name: 'If...Else',
owner: dstItem,
iconClass: 'el-icon-time',
proto: IfBlock,
item: dstItem,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
return items;
};
factory._getTransformBlocks = function (scope, owner, dstItem, group) {
var items = [];
items.push({
name: 'Time',
owner: this,
iconClass: 'el-icon-magic',
dstItem: dstItem,
items: [
{
name: 'If...Else',
owner: dstItem,
iconClass: 'el-icon-time',
proto: IfBlock,
item: dstItem,
ctrArgs: {
scope: scope,
group: group
}
}
]
});
return items;
};
return factory;
});

View File

@ -0,0 +1,88 @@
define([
'dojo/_base/declare',
'dojo/_base/array',
'./_Base'
],function(declare,array,_Base)
{
/**
* Javascript code generator
*/
return declare("xblox.generator.Javascript",_Base,
{
/***
* Print a set of variables as Javascript
*
* Supported modes:
*
* 1. output variables straight forward as they are, no evaluation
* 2. evaluated : means it actually evaluates the variables and then prints the result!
* this for now!
*
* @param scope {xblox.model.Scope}
* @param variables {Array}
* @param resolve {Boolean}
* @returns {string}
*/
printVariables:function(scope,variables,resolve){
var result='';
for(var i = 0 ; i < variables.length ; i++){
var _var = variables[i];
var _varVal = ''+_var.value;
if(_varVal.length==0){
continue;
}
if(resolve===true){
_varVal = scope.expressionModel.parseVariable(scope,_var);//
}
result+="var " + _var.title + " = " + _varVal + ";";
result+="\n";
}
return result;
},
/***
* Print a set of blocks, in the order as in the array
*
* Supported modes:
* 1. output variables straight forward as they are, no evaluation
*
* @param scope {xblox.model.Scope}
* @param blocks {Array}
* @returns {string}
*/
printBlocks:function(scope,blocks){
var result='';
for(var i = 0 ; i < blocks.length ; i++){
var block = blocks[i];
/***
* Variant 1: 'Create the Code here'
*/
//simple example : if block
if(block.declaredClass=='xblox.model.logic.IfBlock'){
//start
result+='if(' + block.condition + '){';
//iterate over blocks in 'consequent'
array.forEach(block.consequent,function(item){
});
}
/***
* Variant 2: 'Let the block create the code
*/
result+=block.toCode('Javascript');
}
return result;
}
});
});

View File

@ -0,0 +1,5 @@
define([
'dojo/_base/declare'
],function(declare){
return declare("xblox.generator._Base", null,{});
});

View File

@ -0,0 +1,163 @@
/**
* This is a new Dojo 1.7 style build profile. Look at util/build/buildControlDefault.js if you want to explore the
* default Dojo build profile options.
*/
// This function is used to determine whether or not a resource should be tagged as copy-only. See the resourceTags
// property below for more information.
function copyOnly(mid) {
return mid in {
// There are no modules right now in dojo boilerplate that are copy-only. If you have some, though, just add
// them here like this:
};
}
var profile = {
packages: [
{
name:"xblox",
location:"xblox"
}
],
// basePath is relative to the directory containing this profile file; in this case, it is being set to the
// src/ directory, which is the same place as the baseUrl directory in the loader configuration. (If you change
// this, you will also need to update run.js).
basePath: '../',
// This is the directory within the release directory where built packages will be placed. The release directory
// itself is defined by build.sh. You really probably should not use this; it is a legacy option from very old
// versions of Dojo (like, version 0.1). If you do use it, you will need to update build.sh, too.
// releaseName: '',
// Builds a new release.
action: 'release',
// Strips all comments from CSS files.
cssOptimize: 'comments',
// Excludes tests, demos, and original template files from being included in the built version.
mini: true,
// Uses Closure Compiler as the JavaScript minifier. This can also be set to "shrinksafe" to use ShrinkSafe.
// Note that you will probably get some “errors” with CC; these are generally safe to ignore, and will be
// fixed in a later version of Dojo. This defaults to "" (no compression) if not provided.
optimize: 'closure',
// Were building layers, so we need to set the minifier to use for those, too. This defaults to "shrinksafe" if
// it is not provided.
layerOptimize: 'closure',
// Strips all calls to console functions within the code. You can also set this to "warn" to strip everything
// but console.error, and any other truthy value to strip everything but console.warn and console.error.
stripConsole: 'warn',
// The default selector engine is not included by default in a dojo.js build in order to make mobile builds
// smaller. We add it back here to avoid that extra HTTP request. There is also a "lite" selector available; if
// you use that, youll need to set selectorEngine in app/run.js too. (The "lite" engine is only suitable if you
// are not supporting IE7 and earlier.)
selectorEngine: 'lite',
// Builds can be split into multiple different JavaScript files called “layers”. This allows applications to
// defer loading large sections of code until they are actually required while still allowing multiple modules to
// be compiled into a single file.
layers: {
// This is the main loader module. It is a little special because it is treated like an AMD module even though
// it is actually just plain JavaScript. There is some extra magic in the build system specifically for this
// module ID.
'xblox/xblox': {
exclude:[
"dojo/dojo",
"xblox/build"
],
// In addition to the loader (dojo/dojo) and the loader configuration file (app/run), were also including
// the main application (app/main) and the dojo/i18n and dojo/domReady modules because they are one of the
// conditional dependencies in app/main (the other being app/Dialog) but we dont want to have to make
// extra HTTP requests for such tiny files.
// By default, the build system will try to include dojo/main in the built dojo/dojo layer, which adds a
// bunch of stuff we dont want or need. We want the initial script load to be as small and quick as
// possible, so we configure it as a custom, bootable base.
include: [
'xblox/main',
'xblox/component',
'xblox/types/Types',
'xblox/embedded',
'xblox/embedded_ui',
'xblox/views/BlockGrid',
'xblox/manager/BlockManagerUI',
//'xblox/views/BlockEditDialog',
//'xblox/views/BlockTreeView',
'xblox/views/BlocksFileEditor',
'xblox/model/Block_UI',
'xblox/manager/BlockManagerUI',
'xblox/RunScript',
'xblox/CSSState',
'xblox/model/html/SetState',
'xblox/views/BlockGridPalette'
],
boot: false,
customBase: false
}
},
// Providing hints to the build system allows code to be conditionally removed on a more granular level than
// simple module dependencies can allow. This is especially useful for creating tiny mobile builds.
// Keep in mind that dead code removal only happens in minifiers that support it! Currently, ShrinkSafe does not
// support dead code removal; Closure Compiler and UglifyJS do.
staticHasFeatures: {
// The trace & log APIs are used for debugging the loader, so we dont need them in the build
'dojo-trace-api':0,
'dojo-log-api':0,
// This causes normally private loader data to be exposed for debugging, so we dont need that either
'dojo-publish-privates':0,
// Were fully async, so get rid of the legacy loader
'dojo-sync-loader':0,
// dojo-xhr-factory relies on dojo-sync-loader
'dojo-xhr-factory':0,
// We arent loading tests in production
'dojo-test-sniff':0,
'xblox-ui':1,
'xblox':1,
'xreload':1,
'xideve':1,
'xnode':1
},
// Resource tags are functions that provide hints to the compiler about a given file. The first argument is the
// filename of the file, and the second argument is the module ID for the file.
resourceTags: {
// Files that contain test code.
test: function (filename, mid) {
if( filename.indexOf('/test')!=-1 ||
filename.indexOf('/tests')!=-1 ||
filename.indexOf('/out')!=-1 ||
filename.indexOf('/bak/')!=-1 ||
filename.indexOf('/build')!=-1 ||
filename.indexOf('/xblox/bak')!=-1 ||
filename.indexOf('/Gruntfile')!=-1
)
{
return true;
}
return false;
},
// Files that should be copied as-is without being modified by the build system.
copyOnly: function (filename, mid) {
return copyOnly(mid);
},
// Files that are AMD modules.
amd: function (filename, mid) {
return !copyOnly(mid) && /\.js$/.test(filename);
},
// Files that should not be copied when the “mini” compiler flag is set to true.
miniExclude: function (filename, mid) {
return mid in {
'XPLUGIN/profile': 1
};
}
}
};

View File

@ -0,0 +1,5 @@
define([
"dojo/_base/kernel"
], function(){
return dojo.xblox;
});

View File

@ -0,0 +1,30 @@
define([
"xblox/component",
"xblox/embedded",
"xblox/model/Block",
"xblox/model/logic/CaseBlock",
"xblox/model/functions/CallBlock",
"xblox/model/code/CallMethod",
"xblox/model/code/RunScript",
"xblox/model/code/RunBlock",
"xblox/model/loops/ForBlock",
"xblox/model/loops/WhileBlock",
"xblox/model/variables/VariableAssignmentBlock",
"xblox/model/logic/IfBlock",
"xblox/model/logic/ElseIfBlock",
"xblox/model/logic/SwitchBlock",
"xblox/model/variables/VariableSwitch",
"xblox/model/events/OnEvent",
"xblox/model/events/OnKey",
"xblox/model/logging/Log",
"xblox/model/html/SetStyle",
"xblox/model/html/SetCSS",
"xblox/model/html/SetState",
"xblox/manager/BlockManager",
"xblox/factory/Blocks",
"xblox/model/Referenced",
"xblox/types/Types",
"xblox/RunScript",
"xblox/CSSState",
"xblox/StyleState"
], function(){});

View File

@ -0,0 +1,330 @@
define([
'dcl/dcl',
'dojo/has',
'dojo/Deferred',
'dojo/promise/all',
'xide/types',
'xide/utils',
'xide/factory',
'xblox/model/ModelBase',
'xblox/model/Scope',
'xblox/model/BlockModel',
'xide/mixins/ReloadMixin',
'xide/manager/ManagerBase',
'xblox/data/Store',
"xdojo/has!xblox-ui?xblox/manager/BlockManagerUI",
"xide/lodash"
],function (dcl,has,Deferred,all,types,utils,factory,ModelBase,Scope,BlockModel,ReloadMixin,ManagerBase,Store,BlockManagerUI,_){
var bases = has('host-browser') && has("xblox-ui") ? [BlockManagerUI,ManagerBase,ReloadMixin.dcl] : [ManagerBase,ReloadMixin.dcl];
var debug = false;
return dcl(bases,{
declaredClass:"xblox/manager/BlockManager",
serviceObject:null,
loaded:{},
test:function(){
},
/***
* scope: storage for all registered variables / commands
*/
scope:null,
scopes:null,
//track original create block function
_createBlock:null,
_registerActions:function(){},
toScope:function(data){
try {
data = utils.getJson(data);
} catch (e) {
console.error('BlockManager::toScope: failed,err='+e);
return null;
}
if(!data){
console.error('correct data');
data = {
"blocks": [
],
"variables": []
};
}
var scopeId = utils.createUUID();
var blockInData = data;
//check structure
if (_.isArray(data)) {// a flat list of blocks
} else if (_.isObject(data)) {
scopeId = data.scopeId || scopeId;
blockInData = data.blocks || [];
}
var scopeUserData = {
owner:this
};
var blockScope = this.getScope(scopeId, scopeUserData, true);
var allBlocks = blockScope.blocksFromJson(blockInData);
for (var i = 0; i < allBlocks.length; i++) {
var obj = allBlocks[i];
obj._lastRunSettings = {
force: false,
highlight: true
};
}
blockScope.serviceObject = this.serviceObject;
return blockScope;
},
/**
*
* @param files{Object[]} array of items to load in this format
* @example:
* @returns {Deferred.promise}
*/
loadFiles:function(files){
var thiz=this,
_createDfd = function(mount,path,force,publish)
{
return thiz.load(mount,path,force);
},
_promises = [],
dfd = new Deferred();
//build promise chain for 'all'
for (var i = 0; i < files.length; i++) {
var item = files[i];
_promises.push(_createDfd(item.mount, item.path, item.force, item.publish));
}
//run and resolve head
all(_promises).then(function(results){
debug && console.log('got all block files ',results);
dfd.resolve(results);
});
return dfd.promise;
},
load:function(mount,path,forceReload){
var dfd = new Deferred(),
thiz = this,
_mount = utils.replaceAll('/','',mount),
_path = utils.replaceAll('./','',path);
var full = _mount + _path;
full = full.trim();
if(this.loaded[full] && forceReload===true){
this.removeScope(this.loaded[full].id);
this.loaded[full]=null;
}
if(forceReload !==true && this.loaded[full]){
dfd.resolve(this.loaded[full]);
return dfd.promise;
}
var _ready = function(data){
var scope = thiz.toScope(data);
if(scope){
thiz.loaded[full] = scope;
scope.mount = mount;//track file info
scope.path = path;
}
dfd.resolve(scope);
};
this.ctx.getFileManager().getContent(_mount,_path,_ready,false);
return dfd.promise;
},
onBlocksReady:function(scope){
var blocks = scope.allBlocks();
for (var i = 0; i < blocks.length; i++) {
var obj = blocks[i];
this.setScriptFunctions(obj,scope,this);
}
/**
* pick 'On Load' blocks
*/
var loadBlocks = scope.getBlocks({
group:'On Load'
});
if(loadBlocks && loadBlocks.length>0){
for (var i = 0; i < loadBlocks.length; i++) {
var loadBlock = loadBlocks[i];
if(loadBlock.onLoad){
loadBlock.onLoad();
}
}
}
},
getBlock:function(){
},
setScriptFunctions:function(obj,scope,owner){
var thiz=owner;
//scope.context = obj;//set the context of the blox scope
if(!obj.blockScope) {
obj.blockScope = scope;
}
debug && console.log('set script functions ' + scope.id,obj);
scope.serviceObject = this.serviceObject;
///////////////////////////////////////////////////////////////////////////////
//
// Variables
//
///////////////////////////////////////////////////////////////////////////////
/**
* Add 'setVariable'
* @param title
* @param value
*/
if(!obj.setVariable) {
obj.setVariable = function (title, value, save, publish, source) {
var _scope = this.blockScope;
var _variable = _scope.getVariable(title);
if (_variable) {
_variable.value = value;
debug && console.log('setting variable '+title + ' to ' + value);
} else {
debug && console.log('no such variable : ' + title);
return;
}
if (publish !== false) {
thiz.publish(types.EVENTS.ON_VARIABLE_CHANGED, {
item: _variable,
scope: scope,
driver: obj,
owner: thiz,
save: save === true,
source: source || types.MESSAGE_SOURCE.BLOX //for prioritizing
});
}
};
}
/**
* Add getVariable
* @param title
*/
if(!obj.getVariable) {
obj.getVariable = function (title) {
var _scope = this.blockScope;
var _variable = _scope.getVariable(title);
if (_variable) {
return _variable.value;
}
return '';
};
}
},
hasScope:function(id) {
if (!this.scopes) {
this.scopes = {};
}
if (this.scopes[id]) {
return this.scopes[id];
}
return null;
},
getScope:function(id,userData,publish){
if(!this.scopes){
this.scopes={};
}
if(!this.scopes[id]){
this.scopes[id]=this.createScope({
id:id,
ctx:this.ctx
});
this.scopes[id].userData=userData;
if(publish!==false){
try{
factory.publish(types.EVENTS.ON_SCOPE_CREATED,this.scopes[id]);
}catch(e){
console.error('bad, scope creation failed ' +e ,e);
}
}
}
return this.scopes[id];
},
/**
*
* @param id
* @returns {null}
*/
removeScope:function(id){
if(!this.scopes){
this.scopes={};
}
for (var scopeId in this.loaded){
if(this.loaded[scopeId].id==id){
delete this.loaded[scopeId];
}
}
if (this.scopes[id]) {
this.scopes[id]._destroy();
delete this.scopes[id];
}
return null;
},
/**
*
* @param mixed
* @param data
* @returns {*}
*/
createScope:function(mixed,data,errorCB){
data = data || [];
var blockStore = new Store({
data: [],
Model:BlockModel,
id:utils.createUUID(),
__events:{
},
observedProperties:[
"name",
"enabled",
"value"
],
getLabel:function(item){
return item.name;
},
labelAttr:'name'
});
blockStore.reset();
blockStore.setData([]);
var args = {
owner:this,
blockStore:blockStore,
serviceObject:this.serviceObject,
config:this.config
};
utils.mixin(args,mixed);
try {
var scope = new Scope(args);
data && scope.initWithData(data,errorCB);
scope.init();
}catch(e){
logError(e,'error creating scope, data:',mixed);
}
return scope;
},
onReloaded:function(){
debug && console.log('on reloaded');
},
init:function() {
this.scope = {
variables:[],
blocks: []
};
ModelBase.prototype.types=types;
ModelBase.prototype.factory=factory;
if(this.onReady){
this.onReady();
}
}
});
});

View File

@ -0,0 +1,14 @@
/** module xblox/manager/BlockManagerUI **/
define([
'dcl/dcl',
"xide/manager/BeanManager"
], function (dcl, BeanManager) {
/**
* @mixin module:xblox/manager/BlockManagerUI
* @extends {module:xide/manager/BeanManager}
*/
return dcl(BeanManager, {
declaredClass: "xblox/manager/BlockManagerUI",
init: function () {}
});
});

View File

@ -0,0 +1,70 @@
define([
"dojo/_base/declare",
"dojo/has",
"xide/model/Component"
], function (declare,has,Component) {
/**
* @class xblox.component
* @inheritDoc
*/
return declare([Component], {
/**
* @inheritDoc
*/
beanType:'BLOCK',
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Implement base interface
//
//////////////////////////////////////////////////////////////////////////////////////////////////////
_load:function(){
},
hasEditors:function(){
return ['xblox'];
},
getDependencies:function(){
if(has('xblox-ui')) {
return [
'xide/xide',
'xblox/types/Types',
'xblox/manager/BlockManager',
'xblox/manager/BlockManagerUI',
'xblox/embedded_ui',
'xide/widgets/ExpressionJavaScript',
'xide/widgets/ImageWidget',
'xide/widgets/Expression',
'xide/widgets/ArgumentsWidget',
'xide/widgets/RichTextWidget',
'xide/widgets/JSONEditorWidget',
'xide/widgets/ExpressionEditor',
'xide/widgets/WidgetReference',
'xide/widgets/DomStyleProperties',
'xblox/views/BlocksFileEditor'
//'xide/widgets/BlockPickerWidget',
//'xide/widgets/BlockSettingsWidget'
];
}else{
return [
'xide/xide',
'xblox/types/Types',
'xblox/manager/BlockManager',
'xblox/embedded'
];
}
},
/**
* @inheritDoc
*/
getLabel: function () {
return 'xblox';
},
/**
* @inheritDoc
*/
getBeanType:function(){
return this.beanType;
}
});
});

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,33 @@
define([
'dcl/dcl',
'xdojo/declare',
'xide/data/Model',
'xide/data/Source'
], function(dcl,declare,Model,Source){
/**
* Contains provides implements functions to deal with sub blocks.
*/
return declare('xblox.model.BlockModel',[Model,Source],{
declaredClass:'xblox.model.BlockModel',
icon:'fa-play',
/**
* Store function override
* @param parent
* @returns {boolean}
*/
mayHaveChildren: function (parent) {
return this.items != null && this.items.length > 0;
},
/**
* Store function override
* @param parent
* @returns {Array}
*/
getChildren: function (parent) {
return this.items;
},
getBlockIcon:function(){
return '<span class="' +this.icon + '"></span>';
}
});
});

View File

@ -0,0 +1,424 @@
/** @module xblox/model/Block_UI **/
define([
'dcl/dcl',
"xide/utils",
"xide/types",
"xide/data/Source",
"xaction/ActionProvider"
], function (dcl,utils, types,Source,ActionProvider) {
/**
* All ui - related addons/interfaces for blocks
* Please read {@link module:xide/types}
* @class module:xblox/model/Block_UI
* @lends module:xblox/model/Block
*/
return dcl([Source.dcl,ActionProvider.dcl],{
declaredClass:"xblox.model.Block_UI",
_statusIcon:null,
/**
* hash-map per scope - id
*/
_statusClass:null,
/**
* The blocks internal user description
* Description is used for the interface. This should be short and expressive and supports plain and html text.
*
* @todo: same as name, move that in user space, combine that with a template system, so any block ui parts gets off from here!
* @type {string}
* @default 'No Description'
* @required true
*/
description: 'No Description',
/**
* UI flag, prevents that a block can be deleted.
* @todo, move to block flags
* @type {boolean}
* @default true
*/
canDelete: true,
renderBlockIcon: true,
/**
* Return current status icon
* @returns {string|null}
*/
getStatusIcon:function(){
return this._statusIcon;
},
/**
* Returns the stored highlight class per scope-id
* @param scopeId {string}
* @returns {object|null}
*/
getStatusClass:function(scopeId){
if(this._statusClass){
return this._statusClass[scopeId];
}
return null;
},
/**
* Store the highlight class per scope id
* @param scopeId
* @param statusClass
*/
setStatusClass:function(scopeId,statusClass){
if(!this._statusClass){
this._statusClass = {};
}
delete this._statusClass[scopeId];
statusClass && (this._statusClass[scopeId]=statusClass);
},
_getText: function (url) {
var result;
dojo.xhrGet({
url: url,
sync: true,
handleAs: 'text',
load: function (text) {
result = text;
}
});
return '' + result + '';
},
/**
* implements
*/
getHelp:function(){
},
/**
* provides
* @param which
* @param style
* @param after
* @returns {string}
*/
getIcon:function(which,style,after){
return '<span style="' + (style||"")+'" class="'+which+'"></span> ' + (after || "");
},
/**
*
* @param field
* @param pos
* @param type
* @param title
* @param mode: inline | popup
* @returns {string}
*/
makeEditable2:function(field,pos,type,title,mode){
return "<a tabIndex=\"-1\" pos='" + pos +"' display-mode='" + (mode||'popup') + "' display-type='" + (type || 'text') +"' data-prop='" + field + "' data-title='" + title + "' class='editable' href='#'>" + this[field] +"</a>";
},
makeEditable:function(field,pos,type,title,mode){
var editableClass = this.canEdit() ? 'editable' : 'disabled';
return "<a data-value='" + this[field] + "' tabIndex='-1' pos='" + pos +"' display-mode='" + (mode||'popup') + "' display-type='" + (type || 'text') +"' data-prop='" + field + "' data-title='" + title + "' class='" +editableClass + "' href='#'>" + this[field] +"</a>";
},
/**
* Called by UI, determines whether a block can be moved up or down
* @param item
* @param dir
* @returns {boolean}
*/
canMove: function (item, dir) {
item = item || this;
if (!item) {
return false;
}
var parent = item.getParent(),
items = null;
if (parent) {
items = parent[parent._getContainer(item)];
if (!items || items.length < 2 || !this.containsItem(items, item)) {
return false;
}
var cIndex = this.indexOf(items, item);
if (cIndex + (dir) < 0) {
return false;
}
var upperItem = items[cIndex + (dir)];
if (!upperItem) {
return false;
}
}else{
var store = this._store;
items = store.storage.fullData;
var _next = this.next(items,dir);
return _next!=null;
}
return true;
},
/**
* Moves an item up/down in the container list
* @param item
* @param dir
* @returns {boolean}
*/
move: function (item, dir) {
item = item || this;
if (!item) {
return false;
}
var parent = item.getParent();
var items = null;
var store = item._store;
if(parent) {
items = parent[parent._getContainer(item)];
if (!items || items.length < 2 || !this.containsItem(items, item)) {
return false;
}
var cIndex = this.indexOf(items, item);
if (cIndex + (dir) < 0) {
return false;
}
var upperItem = items[cIndex + (dir)];
if (!upperItem) {
return false;
}
items[cIndex + (dir)] = item;
items[cIndex] = upperItem;
return true;
}else{
if(store && item.group){
items = store.storage.fullData;
}
var _dstIndex = 0;
var step = 1;
function _next(item,items,dir){
var cIndex = item.indexOf(items, item);
var upperItem = items[cIndex + (dir * step)];
if(upperItem){
if(!upperItem.parentId && upperItem.group && upperItem.group===item.group){
_dstIndex = cIndex + (dir * step);
return upperItem;
}else{
step++;
return _next(item,items,dir);
}
}
return null;
}
var cIndex = this.indexOf(items, item);
if (cIndex + (dir) < 0) {
return false;
}
var next = _next(item,items,dir);
if (!next) {
return false;
}
items[_dstIndex]=item;
items[cIndex] = next;
store._reindex();
return true;
}
},
/**
* provides
* @param block
* @param settings
* @param event
* @param args
*/
onActivity: function (block, settings, event,args) {
args = args || {};
args.target = block;
this._emit(event, args, block);
this.publish(event,args,block);
},
/**
* provides
* @param block
* @param settings
*/
onRun: function (block, settings,args) {
var highlight = settings && settings.highlight;
if (block && highlight) {
this.onActivity(block, settings, types.EVENTS.ON_RUN_BLOCK,args);
}
this._statusIcon = 'text-info fa-spinner fa-spin';
},
/**
* provides
* @param block
* @param settings
* @param args
*/
onFailed: function (block, settings,args) {
var highlight = settings && settings.highlight;
if (block && highlight) {
this.onActivity(block, settings, types.EVENTS.ON_RUN_BLOCK_FAILED,args);
}
this._statusIcon = 'text-danger fa-exclamation';
},
/**
* provides
* @param block
* @param settings
* @param args
*/
onSuccess: function (block, settings,args) {
var highlight = settings && settings.highlight;
if (block && highlight) {
this.onActivity(block, settings, types.EVENTS.ON_RUN_BLOCK_SUCCESS,args);
}
this._statusIcon = 'text-success fa-check';
},
/**
* implements
* @returns {boolean}
*/
canDisable: function () {
return true;
},
/**
* provides defaults
* @param icon {boolean=true}
* @param share {boolean=true}
* @param outlets {boolean=true}
* @returns {Array}
*/
getDefaultFields: function (icon,share,outlets) {
var fields = [];
if (this.canDisable && this.canDisable() !== false) {
fields.push(
utils.createCI('enabled', 0, this.enabled, {
group: 'General',
title: 'Enabled',
dst: 'enabled',
actionTarget:'value',
order:210
})
);
}
fields.push(utils.createCI('description', 26, this.description, {
group: 'Description',
title: 'Description',
dst: 'description',
useACE: false
}));
icon!==false && fields.push(utils.createCI('icon', 17, this.icon, {
group: 'General',
title: 'Icon',
dst: 'icon',
useACE: false,
order:206
}));
outlets!==false && fields.push(utils.createCI('outlet', 5, this.outlet, {
group: 'Special',
title: 'Type',
dst: 'outlet',
order:205,
data:[
{
value: 0x00000001,
label: 'Progress',
title: 'Executed when progress'
},
{
value: 0x00000002,
label: 'Error',
title: "Executed when errors"
},
{
value: 0x00000004,
label: 'Paused',
title: "Executed when paused"
},
{
value: 0x00000008,
label: 'Finish',
title: "Executed when finish"
},
{
value: 0x00000010,
label: 'Stopped',
title: "Executed when stopped"
}
],
widget:{
hex:true
}
}));
if (this.sharable) {
fields.push(
utils.createCI('enabled', 13, this.shareTitle, {
group: 'Share',
title: 'Title',
dst: 'shareTitle',
toolTip: 'Enter an unique name to share this block!'
})
);
}
return fields;
},
/**
* implements
* @returns {*|Array}
*/
getFields: function () {
return this.getDefaultFields();
},
/**
* util
* @param str
* @returns {*}
*/
toFriendlyName: function (str) {
var special = ["[", "]", "(", ")", "{", "}"];
for (var n = 0; n < special.length; n++) {
str = str.replace(special[n], '');
}
str = utils.replaceAll('==', ' equals ', str);
str = utils.replaceAll('<', ' is less than ', str);
str = utils.replaceAll('=<', ' is less than ', str);
str = utils.replaceAll('>', ' is greater than ', str);
str = utils.replaceAll('>=', ' is greater than ', str);
str = utils.replaceAll("'", '', str);
return str;
},
/**
* implements
* @returns {string}
*/
getIconClass: function () {
return this.icon;
},
/**
* implements
* @param symbol
* @returns {string}
*/
getBlockIcon: function (symbol) {
symbol = symbol || '';
return this.renderBlockIcon == true ? '<span class="xBloxIcon ' + this.icon + '">' + symbol + '</span>' : '';
},
/**
* provides
* @param block
* @param cis
*/
onFieldsRendered: function (block, cis) {
},
/**
* inherited
* @param field
* @param newValue
*/
onChangeField: function (field, newValue) {
if (field == 'enabled') {
if (newValue == true) {
this.activate();
} else {
this.deactivate();
}
}
},
/**
* implements
*/
destroy:function(){
this.setStatusClass(this.getScope().id,null);
}
});
});

View File

@ -0,0 +1,107 @@
define([
'dcl/dcl',
"dojo/promise/all",
"xide/types"
], function (dcl, all, types) {
/**
* Contains provides implements functions to deal with sub blocks.
*/
return dcl(null, {
declaredClass: 'xblox.model.Contains',
runByType: function (outletType, settings) {
var items = this.getItemsByType(outletType);
if (items.length) {
this.runFrom(items, 0, settings);
}
},
getItemsByType: function (outletType) {
var items = this.items;
if (!outletType) {
return items;
}
var result = [];
_.each(items, function (item) {
if (item.outlet & outletType) {
result.push(item);
}
});
return result;
},
getContainer: function () {
return this[this._getContainer()];
},
/**
* Store is asking this!
* @param parent
* @returns {boolean}
*/
mayHaveChildren: function (parent) {
var items = this[this._getContainer()];
return items != null && items.length > 0;
},
/**
* Store function
* @param parent
* @returns {Array}
*/
getChildren: function (parent) {
return this[this._getContainer()];
},
// standard call from interface
canAdd: function () {
return [];
},
/***
* Generic: run sub blocks
* @param scope
* @param settings
* @param run
* @param error
* @returns {Array}
*/
_solve: function (scope, settings, run, error) {
if (!this._lastRunSettings && settings) {
this._lastRunSettings = settings;
}
settings = this._lastRunSettings || settings;
this._currentIndex = 0;
this._return = [];
var ret = [], items = this[this._getContainer()];
if (items.length) {
var res = this.runFrom(items, 0, settings);
this.onSuccess(this, settings);
return res;
} else {
this.onSuccess(this, settings);
}
return ret;
},
onDidRunItem: function (dfd, result) {
this._emit(types.EVENTS.ON_RUN_BLOCK_SUCCESS, this);
dfd.resolve(result);
},
onDidRunItemError: function (dfd, result) {
dfd.reject(result);
},
onRunThis: function () {
this._emit(types.EVENTS.ON_RUN_BLOCK, this);
},
onDidRunThis: function (dfd, result, items, settings) {
var thiz = this;
//more blocks?
if (items && items.length) {
var subDfds = thiz.runFrom(items, 0, settings);
all(subDfds).then(function () {
thiz.onDidRunItem(dfd, result, settings);
}, function (err) {
thiz.onDidRunItem(dfd, err, settings);
});
} else {
thiz.onDidRunItem(dfd, result, settings);
}
},
___solve: function (scope, settings, run, error) {
}
});
});

View File

@ -0,0 +1,251 @@
/** @module xblox/model/Expression */
define([
"xdojo/declare",
"xdojo/has",
"xide/utils",
"xide/types",
"xblox/model/ModelBase"
], function (declare, has, utils, types, ModelBase, tracer, _console) {
'use strict';
var isServer = has('host-node');
var console = typeof window !== 'undefined' ? window.console : global.console;
if (isServer && tracer && console && console.error) {
console = _console;
}
var _debug = false;
/**
* The expression
* @class module:xblox.model.Expression
* @extends module:xblox/model/ModelBase
*/
return declare("xblox.model.Expression", [ModelBase], {
id: null,
context: null,
// Constants
variableDelimiters: {
begin: "[",
end: "]"
},
blockCallDelimiters: {
begin: "{",
end: "}"
},
expressionCache: null,
variableFuncCache: null,
constructor: function () {
this.reset();
},
reset: function () {
this.expressionCache = {};
this.variableFuncCache = {};
},
/**
* Replace variable calls width variable values
* @param scope
* @param expression
* @param _evaluate
* @param _escape
* @param variableOverrides
* @returns {*}
*/
replaceVariables: function (scope, expression, _evaluate, _escape, variableOverrides, useVariableGetter, variableDelimiters, flags) {
var FLAG = types.CIFLAG;
variableDelimiters = variableDelimiters || this.variableDelimiters;
flags = flags || FLAG.NONE;
if (flags & FLAG.DONT_ESCAPE) {
_escape = false;
}
if (flags & FLAG.DONT_PARSE) {
_evaluate = false;
}
var occurrence = this.findOccurrences(expression, variableDelimiters);
if (occurrence) {
for (var n = 0; n < occurrence.length; n++) {
// Replace each variable call width the variable value
var oc = occurrence[n];
oc = oc.replace(variableDelimiters.begin, '');
oc = oc.replace(variableDelimiters.end, '');
var _var = this._getVar(scope, oc);
if (_var && _var.flags & FLAG.DONT_PARSE) {
_evaluate = false;
}
var value = null;
if (_var) {
if (useVariableGetter) {
expression = expression.replace(occurrence[n], 'this.getVariable(\'' + _var.name + '\')');
continue;
}
value = this.getValue(_var.value);
if (variableOverrides && _var.name in variableOverrides) {
value = variableOverrides[_var.name];
}
if (this.isScript(value) && _evaluate !== false) {
try {
//put other variables on the stack: should be avoided
var _otherVariables = scope.variablesToJavascript(_var, true);
if (_otherVariables) {
value = _otherVariables + value;
}
var _parsed = (new Function("{\n" + value + "\n}")).call(scope.context || {});
//wasnt a script
if (_parsed === 'undefined' || typeof _parsed === 'undefined') {
value = '' + _var.value;
} else {
value = _parsed;
!(flags & FLAG.DONT_ESCAPE) && (value = "'" + value + "'");
}
} catch (e) {
console.log(' parsed variable expression failed \n' + value, e);
}
} else {
if (!this.isNumber(value)) {
if (_escape !== false) {
value = "'" + value + "'";
}
}
}
} else {
_debug && console.log(' expression failed, no such variable :' + occurrence[n] + ' ! setting to default ' + '');
value = occurrence[n];
}
expression = expression.replace(occurrence[n], value);
}
}
return expression;
},
/**
*
* @param scope
* @param expression
* @param addVariables
* @param runCallback
* @param errorCallback
* @param context
* @param variableOverrides
* @param args {[*]}
* @param flags {CIFLAGS}
* @returns {*}
*/
parse: function (scope, expression, addVariables, runCallback, errorCallback, context, variableOverrides, args, flags) {
expression = this.replaceAll("''", "'", expression);
var expressionContext = context || scope.context || scope.getContext() || {};
var useVariableGetter = expressionContext['getVariable'] != null;
expression = this.replaceVariables(scope, expression, null, null, variableOverrides, useVariableGetter, null, flags);
var isExpression = this.isScript(expression);
if (!isExpression && (this.isString(expression) || this.isNumber(expression))) {
if (runCallback) {
runCallback('Expression ' + expression + ' evaluates to ' + expression);
}
return expression;
}
if (expression.indexOf('return') == -1 && isExpression) {
expression = 'return ' + expression;
}
addVariables = false;
if (addVariables === true) {
var _otherVariables = scope.variablesToJavascript(null, expression);
if (_otherVariables) {
expression = _otherVariables + expression;
expression = this.replaceAll("''", "'", expression);//weird!
}
}
var parsed = this;
try {
expression = this.replaceAll("''", "'", expression);
var _function = this.expressionCache[expression];
if (!_function) {
_debug && console.log('create function ' + expression);
_function = new Function("{" + expression + "; }");
this.expressionCache[expression] = _function;
} else {
}
parsed = _function.apply(expressionContext, args);
} catch (e) {
console.error('invalid expression : \n' + expression, e);
if (errorCallback) {
errorCallback('invalid expression : \n' + expression + ': ' + e, e);
}
parsed = '' + expression;
return parsed;
}
if (parsed === true) {
_debug && console.log('expression return true! : ' + expression);
}
if (runCallback) {
runCallback('Expression ' + expression + ' evaluates to ' + parsed);
}
return parsed;
},
parseVariable: function (scope, _var, _prefix, escape, allowCache, context, args) {
var value = '' + _var.value;
_prefix = _prefix || '';
if (allowCache !== false) {
var _function = this.variableFuncCache[scope.id + '|' + _var.title];
if (!_function) {
_function = new Function("{" + _prefix + value + "}");
this.variableFuncCache[scope.id + '|' + _var.title] = _function;
}
} else {
_function = new Function("{" + _prefix + value + "}");
}
var _parsed = _function.apply(context || scope.context || {}, args || []);
if (_parsed === 'undefined' || typeof _parsed === 'undefined') {
value = '' + _var.value;
} else {
if (!this.isNumber(_parsed) && escape !== false) {
value = '' + _parsed;
value = "'" + value + "'";
} else {
value = _parsed;
}
}
return value;
},
// Replace block call with block result
replaceBlockCalls: function (scope, expression) {
var occurrences = this.findOccurrences(expression, this.blockCallDelimiters);
if (occurrences) {
for (var n = 0; n < occurrences.length; n++) {
// Replace each block call with block result
var blockName = this._removeDelimiters(occurrences[n], this.blockCallDelimiters);
var blockResult = scope.solveBlock(blockName).join("\n");
expression = expression.replace(occurrences[n], blockResult);
}
}
return expression;
},
// gets a variable from the scope using text [variableName]
_getVar: function (scope, string) {
return scope.getVariable(this._getVarName(string));
},
_getVarName: function (string) {
return this._removeDelimiters(string, this.variableDelimiters);
},
_removeDelimiters: function (text, delimiters) {
return text.replace(delimiters.begin, '').replace(delimiters.end, '');
},
// escape regular expressions special chars
_escapeRegExp: function (string) {
var special = ["[", "]", "(", ")", "{", "}", "*", "+", "."];
for (var n = 0; n < special.length; n++) {
string = string.replace(special[n], "\\" + special[n]);
}
return string;
},
/**
* Finds a term in an expression by start and end delimiters
* @param expression
* @param delimiters
* @private
*/
findOccurrences: function (expression, delimiters) {
var d = {
begin: this._escapeRegExp(delimiters.begin),
end: this._escapeRegExp(delimiters.end)
};
return expression.match(new RegExp(d.begin + "(" + "[^" + d.end + "]*" + ")" + d.end, 'g'));
}
});
});

View File

@ -0,0 +1,177 @@
/** @module xblox/model/File/ReadJSON **/
define([
'dcl/dcl',
"dojo/Deferred",
"xblox/model/Block",
'xide/utils',
'xblox/model/Contains',
'xide/types',
"xdojo/has!xblox-ui?xfile/data/DriverStore",
'xdojo/has!xblox-ui?xfile/views/FileGridLight'
], function (dcl, Deferred, Block, utils, Contains, types, DriverStore, FileGridLight) {
/**
*
* @param ext
* @param config
* @param options
* @param fileServer
* @returns {*}
*/
function createStore(ext,options,fileServer) {
return new DriverStore({
data: [],
config: {},
mount: 'none',
options: options,
driver: fileServer,
micromatch: "(*.json)|!(*.*)", // Only folders and json files
//micromatch: "(*.mp3)|(*.wav)|(*.webm)|!(*.*)", // Only folders and json files
glob: ext
});
}
/**
*
* @class module:xblox/model/code/RunScript
* @extends module:xblox/model/Block
* @augments module:xblox/model/Block_UI
*/
return dcl([Block, Contains], {
declaredClass: "xblox.model.File.ReadJSON",
name: 'Read JSON',
deferred: false,
sharable: false,
context: null,
icon: 'fa-file',
observed: [
'path'
],
getContext: function () {
return this.context || (this.scope.getContext ? this.scope.getContext() : this);
},
getFileContent: function (path) {
var scope = this.getScope();
var ctx = scope.ctx;
var deviceManager = ctx.getDeviceManager();
var fileServer = deviceManager.getInstanceByName('File-Server');
return fileServer.callCommand('GetProg', {
override: {
args: [path]
}
});
},
processJSON: function (data, settings) {
var path = this.jsonPath;
if (path) {
this._lastResult = utils.getAt(data, path);
} else {
this._lastResult = data;
}
this.onSuccess(this, settings);
this.runByType(types.BLOCK_OUTLET.FINISH, settings);
},
/**
*
* @param scope
* @param settings
* @param isInterface
* @param run
* @param error
* @returns {Deferred}
*/
solve: function (scope, settings, isInterface, run, error) {
this._currentIndex = 0;
this._return = [];
settings = this._lastSettings = settings || this._lastSettings || {};
var _script = ('' + this._get('path')),
thiz = this,
dfd = new Deferred(),
self = this;
this.onRunThis(settings);
var expression = scope.expressionModel.replaceVariables(scope, _script, null, null);
var getDfd = this.getFileContent(expression);
getDfd.then(function (data) {
var content = data.content;
if (content) {
content = utils.getJson(content, true);
if (content) {
self.processJSON(content, settings);
}
}
}.bind(this));
try {
if (run) {
run('Expression ' + _script + ' evaluates to ' + expression);
}
} catch (e) {
thiz.onDidRunItemError(dfd, e, settings);
thiz.onFailed(thiz, settings);
if (error) {
error('invalid expression : \n' + _script + ': ' + e);
}
}
return dfd;
},
/////////////////////////////////////////////////////////////////////////////////////
//
// UI impl.
//
/////////////////////////////////////////////////////////////////////////////////////
toText: function () {
var result = '<span style="">' + this.getBlockIcon() + ' ' + this.name + ' :: ' + '</span>';
if (this.path) {
result += this.path.substr(0, 50);
}
return result;
},
// standard call from interface
canAdd: function () {
return [];
},
// standard call for editing
getFields: function () {
var fields = this.inherited(arguments) || this.getDefaultFields();
var scope = this.getScope();
var ctx = scope.ctx;
var deviceManager = ctx.getDeviceManager();
var fileServer = deviceManager.getInstanceByName('File-Server');//system's default
var permissions = utils.clone(types.DEFAULT_FILE_GRID_PERMISSIONS);
if (fileServer && DriverStore) {
var FilePickerOptions = {
ctx: ctx,
owner: this,
selection: '/',
resizeToParent: true,
Module: FileGridLight,
permissions: permissions
},
options = {
fields: types.FIELDS.SHOW_ISDIR | types.FIELDS.SHOW_OWNER | types.FIELDS.SHOW_SIZE |
types.FIELDS.SHOW_FOLDER_SIZE |
types.FIELDS.SHOW_MIME |
types.FIELDS.SHOW_PERMISSIONS |
types.FIELDS.SHOW_TIME |
types.FIELDS.SHOW_MEDIA_INFO
};
FilePickerOptions.leftStore = createStore("/*",options,fileServer);
FilePickerOptions.rightStore = createStore("/*",options,fileServer);
fields.push(utils.createCI('path', 4, this.path, {
group: 'General',
title: 'Path',
dst: 'path',
filePickerOptions: FilePickerOptions,
widget: {
item: this
}
}));
}
fields.push(utils.createCI('jsonPath', 13, this.jsonPath, {
group: 'General',
title: 'Select',
dst: 'jsonPath'
}));
return fields;
}
});
});

View File

@ -0,0 +1,168 @@
/** @module xblox/model/ModelBase
* @description The base for block related classes, this must be kept small and light as possible
*/
define([
'dcl/dcl',
"xide/utils",
"xide/types",
"xide/mixins/EventedMixin",
"xide/lodash"
], function(dcl,utils,types,EventedMixin,_){
/**
* The model mixin for a block
* @class module:xblox.model.ModelBase
*/
var Module = dcl(EventedMixin.dcl,{
declaredClass:'xblox.model.ModelBase',
id:null,
description:'',
parent:null,
parentId:null,
group:null,
order:0,
_store:null,
////////////////////////////////////////////////////////////
//
// Functions to expose out & in - lets
//
////////////////////////////////////////////////////////////
/**
*
* Implmented by the subclass. Each block must provide an output signature.
* The format is currently the same as Dojo SMD
*
* @returns {Array}
*/
outputs:function(){
return [];
},
/**
* Implemented by the subclass. Each block must provide an input signature.
* The format is currently the same as Dojo SMD
* @returns {Array}
*/
takes:function(){
return [];
},
/**
* Implemented by the subclass. Each block must provide an needed input signature.
* The format is currently the same as Dojo SMD. This is a filtered version of
* 'takes'
*
* @returns {Array}
*/
needs:function(){
return [];
},
////////////////////////////////////////////////////////////
//
// Functions to expose outlets
//
////////////////////////////////////////////////////////////
/***
* Standard constructor for all sub classing blocks
* @param {array} args
*/
constructor: function(args){
//simple mixin of constructor arguments
for (var prop in args) {
if (args.hasOwnProperty(prop)) {
this[prop] = args[prop];
}
}
if(!this.id){
this.id = this.createUUID();
}
//short cuts
this.utils=utils;
this.types=types;
},
////////////////////////////////////////////////////////////
//
// Standard tools
//
////////////////////////////////////////////////////////////
keys: function (a) {
var b = [];
for (var c in a) {
b.push(c);
}
return b;
},
values: function (b) {
var a = [];
for (var c in b) {
a.push(b[c]);
}
return a;
},
toArray: function () {
return this.map();
},
size: function () {
return this.toArray().length;
},
createUUID:utils.createUUID,
canEdit:function(){
return true;
},
getFields:function(){
return null;
},
isString: function (a) {
return typeof a == "string"
},
isNumber: function (a) {
return typeof a == "number"
},
isBoolean: function (a) {
return typeof a == "boolean"
},
isObject:_.isObject,
isArray:_.isArray,
getValue:function(val){
var _float = parseFloat(val);
if(!isNaN(_float)){
return _float;
}
if(val==='true' || val===true){
return true;
}
if(val==='false' || val===false){
return false;
}
return val;
},
isScript:function(val){
return this.isString(val) &&(
val.indexOf('return')!=-1||
val.indexOf(';')!=-1||
val.indexOf('(')!=-1||
val.indexOf('+')!=-1||
val.indexOf('-')!=-1||
val.indexOf('<')!=-1||
val.indexOf('*')!=-1||
val.indexOf('/')!=-1||
val.indexOf('%')!=-1||
val.indexOf('=')!=-1||
val.indexOf('==')!=-1||
val.indexOf('>')!=-1||
val.indexOf('[')!=-1||
val.indexOf('{')!=-1||
val.indexOf('}')!=-1
);
},
replaceAll:function(find, replace, str) {
if(this.isString(str)){
return str.split(find).join(replace);
}
return str;
},
isInValidState:function(){
return true;
},
destroy:function(){}
});
dcl.chainAfter(Module,'destroy');
return Module;
});

View File

@ -0,0 +1,11 @@
define([
"dojo/_base/declare",
"xide/utils"
], function(declare,utils){
/**
* Holds information to locate an object by string or direct reference!
*/
return declare('xblox.model.Reference',null,{
reference:null
});
});

View File

@ -0,0 +1,35 @@
define([
'dcl/dcl',
"dojo/_base/declare",
"xide/mixins/ReferenceMixin",
"xide/utils"
], function (dcl,declare, ReferenceMixin,utils) {
var Implementation = {
/**
* JSON String in that format : reference(string) | mode (string)
*/
reference: null,
/**
* 'reference' is a JSON structure
* @param value
* @returns {*}
*/
deserialize: function (value) {
if (!value || value.length == 0) {
return {};
}
try {
return utils.fromJson(value);
} catch (e) {
return {};
}
}
};
/**
* Holds information to locate an object by string or direct reference.
* This must be used as mixin rather as base class!
*/
var Module = declare('xblox.model.Referenced', [ReferenceMixin],Implementation);
Module.dcl = dcl(ReferenceMixin.dcl,Implementation);
return Module;
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
define([
"dcl/dcl",
"xblox/model/Block"
], function(dcl,Block){
// summary:
// The statement block is only a wrapper for items like in 'else'
// module:
// xblox.model.Statement
return dcl(Block,{
declaredClass:"xblox.model.Statement",
/**
* Return block name
* @returns {name|*}
*/
toText:function(){
return this.name;
},
/**
*
* @returns {items|*}
*/
getChildren:function(){
return this.items;
}
});
});

View File

@ -0,0 +1,12 @@
define([
"dojo/_base/declare",
"./Referenced"
], function(declare,Referenced){
/**
* Targeted provides functions to get an object through various ways
*/
return declare('xblox.model.Targeted',[Referenced],{
});
});

View File

@ -0,0 +1,94 @@
define([
'dcl/dcl',
"xblox/model/Block",
'xide/utils'
], function(dcl,Block,utils){
// summary:
// The Call Block model.
// This block makes calls to another blocks in the same scope by action name
// module:
// xblox.model.code.CallMethod
return dcl(Block,{
declaredClass:"xblox.model.code.CallMethod",
//method: (String)
// block action name
name:'Call Method',
//method: (String)
// block action name
method:'',
args:'',
sharable:true,
/***
* Returns the block run result
* @param scope
*/
solve:function(scope,settings) {
var context = this.getContext();
if (context && context[this.method]!=null)
{
var res = [];
var _fn = context[this.method];
try{
var _args = this.getArgs(settings);
console.log('args',_args);
var _res = _fn.apply(context,_args||[]);
res = _res;
this.onSuccess(this,settings);
return res;
}catch(e){
console.error('call method ' + this.method + ' failed: '+e);
logError(e);
this.onFailed(this,settings);
}
}else{
this.onFailed(this,settings);
return [];
}
return [];
},
toText:function(){
var result = this.getBlockIcon() + ' ' + this.name + ' ';
if(this.method){
result+= this.makeEditable('method','bottom','text','Enter a driver method','inline');
}
return result;
},
// standard call for editing
getFields:function(){
var fields = this.getDefaultFields();
var context = this.getContext();
/*
console.log('call method ', this.getScope().getContext());
console.log('call method ', context);*/
fields.push(utils.createCI('value',13,this.method,{
group:'General',
title:'Method',
dst:'method'
}));
fields.push(utils.createCI('value',27,this.args,{
group:'Arguments',
dst:'args',
widget:{
title:''
}
}));
return fields;
},
getBlockIcon:function(){
return '<span class="fa-caret-square-o-right"></span>';
}
});
});

View File

@ -0,0 +1,151 @@
define([
'dcl/dcl',
"xblox/model/Block",
'xide/types',
'xide/utils'
], function(dcl,Block,types,utils){
// summary:
// The Call Block model.
// This block makes calls to another blocks in the same scope by action name
// module:
// xblox.model.code.CallMethod
return dcl(Block,{
declaredClass:"xblox.model.code.RunBlock",
//method: (String)
// block action name
name:'Run Block',
file:'',
//method: (String)
// block action name
method:'',
args:'',
sharable:true,
block:'',
description:"Runs another Block",
/***
* Returns the block run result
* @param scope
*/
solve:function(scope,settings) {
var context = this.getContext();
if (context && context[this.method]!=null)
{
var res = [];
var _fn = context[this.method];
try{
var _args = this._getArgs();
var _res = _fn.apply(context,_args||[]);
res = _res;
this.onSuccess(this,settings);
return res;
}catch(e){
console.error('call method failed');
this.onFailed(this,settings);
}
}else{
this.onFailed(this,settings);
return [];
}
return [];
},
toText:function(){
var result = this.getBlockIcon() + ' ' + this.name + ' ';
if(this.method){
result+= this.method.substr(0,20);
}
return result;
},
// standard call for editing
getFields:function(){
var fields = this.getDefaultFields();
fields.push(utils.createCI('Block', types.ECIType.BLOCK_REFERENCE, this.block, {
toolTip:'Enter block, you can use also the block\'s share title',
group: 'General',
dst: 'block',
value: this.block,
title:'Block',
scope:this.scope
}));
fields.push(utils.createCI('File', types.ECIType.FILE, this.file, {
toolTip:'Leave empty to auto-select this file',
group: 'General',
dst: 'file',
value: this.file,
intermediateChanges: false,
acceptFolders: false,
acceptFiles: true,
encodeFilePath: false,
buildFullPath: true,
filePickerOptions: {
dialogTitle: 'Select Block File',
filePickerMixin: {
beanContextName: this.id,
persistent: false,
globalPanelMixin: {
allowLayoutCookies: false
}
},
configMixin: {
beanContextName: this.id,
LAYOUT_PRESET: types.LAYOUT_PRESET.SINGLE,
PANEL_OPTIONS:{
ALLOW_MAIN_MENU:false,
ALLOW_NEW_TABS:true,
ALLOW_MULTI_TAB:false,
ALLOW_INFO_VIEW:true,
ALLOW_LOG_VIEW:false,
ALLOW_CONTEXT_MENU:true,
ALLOW_LAYOUT_SELECTOR:true,
ALLOW_SOURCE_SELECTOR:true,
ALLOW_COLUMN_RESIZE:true,
ALLOW_COLUMN_REORDER:true,
ALLOW_COLUMN_HIDE:true,
ALLOW_ACTION_TOOLBAR:true,
ALLOW_BREADCRUMBS:false
}
},
defaultStoreOptions: {
"fields": 1663,
"includeList": "xblox",
"excludeList": "*"
},
startPath: this.file
}
}));
return fields;
/*
fields.push(utils.createCI('value',27,this.args,{
group:'General',
title:'Arguments',
dst:'args'
}));
return fields;
*/
},
getBlockIcon:function(){
return '<span class="el-icon-share-alt"></span>';
}
});
});

View File

@ -0,0 +1,10 @@
Runs an expression.<br/>
<b>Behaviour</b>
<pre>
//to abort execution (child blocks), return something negative as -1 or false.
return false;
</pre>

View File

@ -0,0 +1,300 @@
/** @module xblox/model/code/RunScript **/
define([
'dcl/dcl',
'xdojo/has',
"dojo/Deferred",
"xblox/model/Block",
'xide/utils',
'xblox/model/Contains',
'dojo/promise/all',
'xide/types',
'module'
//'xdojo/has!host-node?dojo/node!tracer',
//'xdojo/has!host-node?nxapp/utils/_console'
//"xdojo/has!xblox-ui?dojo/text!./RunScript.html"
//"xdojo/has!xblox-ui?dojo/text!xblox/docs/code/RunScript.md"
], function(dcl,has,Deferred,Block,utils,Contains,all,types,module,tracer,_console,Description,Help){
var isServer = has('host-node');
var console = typeof window !== 'undefined' ? window.console : global.console;
if(isServer && tracer && console && console.error){
console = _console;
}
/**
*
* @class module:xblox/model/code/RunScript
* @extends module:xblox/model/Block
*/
return dcl([Block,Contains],{
declaredClass:"xblox.model.code.RunScript",
name:'Run Script',
method:'',
args:'',
deferred:false,
sharable:true,
context:null,
icon:'fa-code',
observed:[
'method'
],
getContext:function(){
return this.context || (this.scope.getContext ? this.scope.getContext() : this);
return this.context || this;
},
/***
* Returns the block run result
* @param scope
* @param settings
* @param run
* @param error
* @returns {Array}
*/
solve2:function(scope,settings,run,error) {
this._currentIndex = 0;
this._return=[];
var _script = '' + this._get('method');
var thiz=this,
ctx = this.getContext();
if(_script && _script.length) {
var runScript = function() {
var _function = new Function("{" + _script + "}");
var _args = thiz.getArgs() || [];
try {
var _parsed = _function.apply(ctx, _args || {});
thiz._lastResult = _parsed;
if (run) {
run('Expression ' + _script + ' evaluates to ' + _parsed);
}
if (_parsed !== 'false' && _parsed !== false) {
thiz.onSuccess(thiz, settings,{
result:_parsed
});
} else {
thiz.onFailed(thiz, settings);
return [];
}
} catch (e) {
if (error) {
error('invalid expression : \n' + _script + ': ' + e);
}
thiz.onFailed(thiz, settings);
return [];
}
};
if(scope.global){
(function() {
window = scope.global;
var _args = thiz.getArgs() || [];
try {
var _parsed = null;
if(!ctx.runExpression) {
var _function = new Function("{" + _script + "}").bind(this);
_parsed = _function.apply(ctx, _args || {});
}else{
_parsed = ctx.runExpression(_script,null,_args);
}
thiz._lastResult = _parsed;
if (run) {
run('Expression ' + _script + ' evaluates to ' + _parsed);
}
if (_parsed !== 'false' && _parsed !== false) {
thiz.onSuccess(thiz, settings);
} else {
thiz.onFailed(thiz, settings);
return [];
}
} catch (e) {
thiz._lastResult = null;
if (error) {
error('invalid expression : \n' + _script + ': ' + e);
}
thiz.onFailed(thiz, settings);
return [];
}
}).call(scope.global);
}else{
return runScript();
}
}else{
console.error('have no script');
}
var ret=[], items = this[this._getContainer()];
if(items.length) {
this.runFrom(items,0,settings);
}else{
this.onSuccess(this, settings);
}
this.onDidRun();
return ret;
},
/**
*
* @param scope
* @param settings
* @param run
* @param error
*/
solve:function(scope,settings,isInterface,send,run,error){
this._currentIndex = 0;
this._return=[];
settings = settings || {};
var _script = send || (this._get('method') ? this._get('method') : this.method);
if(!scope.expressionModel){
//console.error('mar',scope);
throw new Error('na');
return;
}
var thiz=this,
ctx = this.getContext(),
items = this[this._getContainer()],
//outer
dfd = new Deferred,
listener = settings.listener,
isDfd = thiz.deferred,
expressionModel = scope.getExpressionModel();
this.onRunThis(settings);
function globalEval(text) {
var ret;
// Properly escape \, " and ' in the input, normalize \r\n to an escaped \n
text = text.replace(/["'\\]/g, "\\$&").replace(/\r\n/g, "\\n");
// You have to use eval() because not every expression can be used with an assignment operator
var where = typeof window!=='undefined' ? window : global;
where.execScript("globalEval.____lastInputResult____ = eval('" + text + "');} }");
// Store the result and delete the property
ret = globalEval.____lastInputResult____;
delete globalEval.____lastInputResult____;
return ret;
}
if(!expressionModel){
console.error('scope has no expression model');
return false;
}
var expression = expressionModel.replaceVariables(scope,_script,null,null);
var _function = expressionModel.expressionCache[expression];
if(!_function){
_function = expressionModel.expressionCache[expression] = new Function("{" + expression + "}");
}
var _args = thiz.getArgs(settings) || [];
try {
if(isDfd){
ctx.resolve=function(result){
if(thiz._deferredObject) {
thiz._deferredObject.resolve();
}
thiz.onDidRunThis(dfd,result,items,settings);
}
}
var _parsed = _function.apply(ctx, _args || {});
thiz._lastResult = _parsed;
if (run) {
run('Expression ' + _script + ' evaluates to ' + _parsed);
}
if(!isDfd) {
thiz.onDidRunThis(dfd,_parsed,items,settings);
}
if (_parsed !== 'false' && _parsed !== false) {
thiz.onSuccess(thiz, settings);
} else {
thiz.onFailed(thiz, settings);
}
} catch (e) {
e=e ||{};
thiz.onDidRunItemError(dfd,e,settings);
thiz.onFailed(thiz,settings);
if (error) {
error('invalid expression : \n' + _script + ': ' + e);
}
}
return dfd;
},
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText:function(){
var result = '<span style="">' + this.getBlockIcon() + ' ' + this.name + ' :: '+'</span>';
if(this.method){
result+= this.method.substr(0,50);
}
return result;
},
canAdd:function(){
return true;
},
getFields:function(){
if(this.description === 'No Description'){
this.description = Description;
}
var fields = this.inherited(arguments) || this.getDefaultFields();
var thiz=this;
fields.push(
utils.createCI('name',13,this.name,{
group:'General',
title:'Name',
dst:'name'
})
);
fields.push(
utils.createCI('deferred',0,this.deferred,{
group:'General',
title:'Deferred',
dst:'deferred'
})
);
fields.push(utils.createCI('arguments',27,this.args,{
group:'Arguments',
title:'Arguments',
dst:'args'
}));
fields.push(
utils.createCI('value',types.ECIType.EXPRESSION_EDITOR,this.method,{
group:'Script',
title:'Script',
dst:'method',
select:true,
widget:{
allowACECache:true,
showBrowser:false,
showSaveButton:true,
editorOptions:{
showGutter:true,
autoFocus:false
},
item:this
},
delegate:{
runExpression:function(val,run,error){
var old = thiz.method;
thiz.method=val;
var _res = thiz.solve(thiz.scope,null,run,error);
}
}
}));
return fields;
}
});
});

View File

@ -0,0 +1,393 @@
define([
'dcl/dcl',
"dojo/_base/lang",
"dojo/Deferred",
"xblox/model/Block",
'xide/utils',
'xide/types',
'xide/mixins/EventedMixin',
'xblox/model/Referenced',
'xide/registry',
'dojo/on',
'xwire/_Base'
], function(dcl,lang,Deferred,Block,utils,types,EventedMixin,Referenced,registry,on,_Base){
// summary:
// The Call Block model.
// This block makes calls to another blocks in the same scope by action name
// module:
// xblox.model.code.CallMethod
return dcl([Block,EventedMixin.dcl,Referenced.dcl,_Base],{
declaredClass:"xblox.model.events.OnEvent",
//method: (String)
// block action name
name:'On Event',
event:'',
reference:'',
references:null,
sharable:true,
_didSubscribe:false,
filterPath:"item.name",
filterValue:"",
valuePath:"item.value",
_nativeEvents:[
"onclick",
"ondblclick",
"onmousedown",
"onmouseup",
"onmouseover",
"onmousemove",
"onmouseout",
"onkeypress",
"onkeydown",
"onkeyup",
"onfocus",
"onblur",
"onchange"
],
stop:function(){
this._destroy();
},
/***
* Returns the block run result
* @param scope
* @param settings
* @param run
* @param error
* @returns {Array}
*/
solve:function(scope,settings,isInterface,error) {
if(isInterface){
this._destroy();
}
settings = this._lastSettings = settings || this._lastSettings;
if(!this._didSubscribe){
this._registerEvent(this.event);
this.onSuccess(this, settings);
return false;
}
this.onSuccess(this, settings);
this._currentIndex=0;
this._return=[];
var ret=[], items = this[this._getContainer()];
if(items.length) {
//console.log('solve ',settings);
var res = this.runFrom(items,0,settings);
this.onSuccess(this, settings);
return res;
}else{
this.onSuccess(this, settings);
}
return ret;
},
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText:function(){
var result = this.getBlockIcon() + ' ' + this.name + ' :: ';
if(this.event){
result+= this.event;
}
return result;
},
// standard call from interface
canAdd:function(){
return [];
},
// standard call for editing
getFields:function(){
var fields = this.inherited(arguments) || this.getDefaultFields();
var thiz=this;
var _ref = this.deserialize(this.reference);
var isNative = utils.contains(this._nativeEvents,this.event)>-1;
var options = null;
if(!isNative){
options = this.scope.getEventsAsOptions(this.event);
}else{
options = [
{label:"onclick", value:"onclick"},
{label:"ondblclick",value:"ondblclick"},
{label:"onmousedown",value:"onmousedown"},
{label:"onmouseup",value:"onmouseup"},
{label:"onmouseover",value:"onmouseover"},
{label:"onmousemove",value:"onmousemove"},
{label:"onmouseout",value:"onmouseout"},
{label:"onkeypress",value:"onkeypress"},
{label:"onkeydown",value:"onkeydown"},
{label:"onkeyup", value:"onkeyup"},
{label:"onfocus", value:"onfocus"},
{label:"onblur", value:"onblur"},
{label:"onchange", value:"onchange"}
];
//select the event we are listening to
for (var i = 0; i < options.length; i++) {
var obj = options[i];
if(obj.value===this.event){
obj.selected=true;
break;
}
}
}
fields.push(utils.createCI('Event',types.ECIType.ENUMERATION,this.event,{
group:'General',
options:options,
dst:'event',
widget:{
search:true
}
}));
fields.push(utils.createCI('Filter Path',13,this.filterPath,{
group:'General',
dst:'filterPath'
}));
fields.push(utils.createCI('Filter Value',13,this.filterValue,{
group:'General',
dst:'filterValue'
}));
fields.push(utils.createCI('Value Path',13,this.valuePath,{
group:'General',
dst:'valuePath'
}));
fields.push(utils.createCI('Object/Widget',types.ECIType.WIDGET_REFERENCE,this.reference,{
group:'Widget',
dst:'reference',
value:this.reference
}));
return fields;
},
getBlockIcon:function(){
return '<span class="fa-bell"></span>';
},
onEvent:function(evt){
this._lastResult=evt;
/*
if(this.scope && evt.scope && evt.scope!==this.scope){
return;
}*/
if(this.filterPath && this.filterValue){
var value = this.getValue(evt,this.filterPath);
if(value && this.filterValue !==value){
return;
}
}
var eventValue = null;
if(this.valuePath){
if(!this._lastSettings){
this._lastSettings = {};
}
eventValue = this.getValue(evt,this.valuePath);
if(eventValue!==null){
!this._lastSettings.override && (this._lastSettings.override = {});
this._lastSettings.override.args = [eventValue];
}
}
//console.log('on event ',this._lastSettings);
this.solve(this.scope,this._lastSettings);
},
_subscribe:function(evt,handler,obj){
if(!evt){
return;
}
var isNative = utils.contains(this._nativeEvents,evt);
if(isNative==-1){
if(this.__events && this.__events[evt]) {
var _handles = this.__events[evt];
_.each(_handles, function (e) {
this.unsubscribe(e.type, e.handler);
e.remove();
}, this);
_.each(_handles, function (e) {
this.__events[evt].remove(e);
}, this);
}
this.subscribe(evt, this.onEvent);
}else{
if(obj) {
var _event = evt.replace('on', ''),
thiz = this;
var handle = on(obj, _event, function (e) {
thiz.onEvent(e)
});
console.log('wire native event : ' + _event);
this._events.push(handle);
}
}
},
_registerEvent:function(evt){
try {
if (!evt || !evt.length) {
return;
}
console.log('register event : ' + evt + ' for ' + this.reference);
var objects = this.resolveReference(this.deserialize(this.reference));
var thiz = this;
if (objects && objects.length) {
for (var i = 0; i < objects.length; i++) {
var obj = objects[i];
//try widget
if (obj && obj.id) {
var _widget = registry.byId(obj.id);
if (_widget && _widget.on) {
var _event = this.event.replace('on', '');
console.log('found widget : ' + obj.id + ' will register event ' + _event);
var _handle = _widget.on(_event, lang.hitch(this, function (e) {
console.log('event triggered : ' + thiz.event);
thiz.onEvent(e);
}));
this._events.push(_handle);
} else {
this._subscribe(evt, this.onEvent, obj);
}
} else {
this._subscribe(evt, this.onEvent, obj);
}
}
console.log('objects found : ', objects);
} else {
this._subscribe(evt, this.onEvent);
}
}catch(e){
logError(e,'registering event failed');
}
this._didSubscribe=evt;
},
onLoad:function(){
this._onLoaded=true;
if(this.event && this.event.length && this.enabled){
this._registerEvent(this.event);
}
},
updateEventSelector:function(objects,cis){
var options = [];
if(!objects || !objects.length){
options= this.scope.getEventsAsOptions(this.event);
}else{
options = [{label:"onclick", value:"onclick"},
{label:"ondblclick",value:"ondblclick"},
{label:"onmousedown",value:"onmousedown"},
{label:"onmouseup",value:"onmouseup"},
{label:"onmouseover",value:"onmouseover"},
{label:"onmousemove",value:"onmousemove"},
{label:"onmouseout",value:"onmouseout"},
{label:"onkeypress",value:"onkeypress"},
{label:"onkeydown",value:"onkeydown"},
{label:"onkeyup", value:"onkeyup"},
{label:"onfocus", value:"onfocus"},
{label:"onblur", value:"onblur"},
{label:"onchange", value:"onchange"}];
//select the event we are listening to
for (var i = 0; i < options.length; i++) {
var obj = options[i];
if(obj.value===this.event){
obj.selected=true;
break;
}
}
}
for (var i = 0; i < cis.length; i++) {
var ci = cis[i];
if(ci['widget'] && ci['widget'].title==='Event'){
//console.log('event!');
var widget = ci['_widget'];
widget.nativeWidget.set('options',options);
widget.nativeWidget.reset();
widget.nativeWidget.set('value',this.event);
this.publish(types.EVENTS.RESIZE,{});
break;
}
}
},
onReferenceChanged:function(newValue,cis){
this._destroy();//unregister previous event(s)
this.reference = newValue;
var objects = this.resolveReference(this.deserialize(newValue));
this.updateEventSelector(objects,cis);
this._registerEvent(this.event);
},
onChangeField:function(field,newValue,cis){
if(field=='event'){
this._destroy(); //unregister previous event
if(this._onLoaded){ // we've have been activated at load time, so re-register our event
this.event = newValue;
this._registerEvent(newValue);
}
}
if(field=='reference'){
this.onReferenceChanged(newValue,cis);
}
this.inherited(arguments);
},
activate:function(){
this._destroy();//you never know
this._registerEvent(this.event);
},
deactivate:function(){
this._destroy();
},
_destroy:function(){
if(!this._events){this._events=[];}
_.each(this._events, dojo.unsubscribe);
this.unsubscribe(this.event,this.onEvent);
this._lastResult=null;
this._didSubscribe = false;
}
});
});

View File

@ -0,0 +1,276 @@
define([
'dcl/dcl',
"dojo/_base/lang",
"dojo/_base/array",
"dojo/Deferred",
"xblox/model/Block",
'xide/utils',
'xide/types',
'xide/mixins/EventedMixin',
'xblox/model/Referenced',
'xblox/model/Contains',
'xblox/model/events/OnEvent',
'xide/registry',
'dojo/on'
], function(dcl,lang,array,Deferred,Block,utils,types,EventedMixin,Referenced,Contains,OnEvent,registry,on){
// summary:
// The Call Block model.
// This block makes calls to another blocks in the same scope by action name
// module:
// xblox.model.code.CallMethod
return dcl([Block,EventedMixin.dcl,Referenced.dcl,Contains],{
declaredClass:"xblox.model.events.OnKey",
//method: (String)
// block action name
name:'On Key',
event:'',
reference:'',
references:null,
description:'Triggers when a keyboard sequence ' + this.event +' has been entered',
listeners:null,
sharable:true,
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText:function(){
var result = this.getBlockIcon() + ' ' + this.name + ' :: ';
if(this.event){
result+= this.event;
}
return result;
},
// standard call from interface
canAdd:function(){
return [];
},
// standard call for editing
getFields:function(){
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(utils.createCI('Keyboard Sequence',types.ECIType.STRING,this.event,{
group:'General',
dst:'event',
value:this.event,
intermediateChanges:false
}));
fields.push(utils.createCI('Object/Widget',types.ECIType.WIDGET_REFERENCE,this.reference,{
group:'General',
dst:'reference',
value:this.reference
}));
return fields;
},
getBlockIcon:function(){
return '<span class="fa-keyboard-o"></span>';
},
/////////////////////////////////////////////////////////////////////////////////////
//
// Store
//
/////////////////////////////////////////////////////////////////////////////////////
onEvent:function(evt){
this._lastResult=evt;
this.solve(this.scope,this._lastRunSettings);
},
_addListerner:function(keys,handler,obj){
if(this.listeners==null){
this.listeners=[];
}
var my_defaults = {
is_unordered : true,
prevent_repeat : false,
prevent_default : false,
on_keyup:function(e){
console.log('up');
},
on_keydown:function(e){
console.log('down');
},
on_release:function(e){
console.log('release');
}
};
var listener =null;
listener = new window.keypress.Listener(obj, my_defaults);
listener.simple_combo(keys, function(e) {
if(handler){
handler(arguments);
}
});
this.listeners.push(listener);
},
_subscribe:function(keys,handler,obj){
if(!keys){
return;
}
if(obj && obj.domNode){
obj = obj.domNode;
}
this._addListerner(keys,handler,obj);
},
_registerEvent:function(evt){
if(!evt || !evt.length){
return;
}
var objects = this.resolveReference(this.deserialize(this.reference));
var thiz=this;
if (objects && objects.length) {
for (var i = 0; i < objects.length; i++) {
var obj = objects[i];
//try widget
if (obj && obj.id) {
var _widget = registry.byId(obj.id);
_widget=null;
if (_widget && _widget.on) {
var _event = this.event.replace('on','');
var _handle = _widget.on(_event,lang.hitch(this,function(e){
thiz.onEvent(e);
}));
this._events.push( _handle);
}else{
this._subscribe(evt, function(){thiz.onEvent(arguments)},obj);
}
}else{
this._subscribe(evt, function(){thiz.onEvent(arguments)},obj);
}
}
}else{
this._subscribe(evt, function(){thiz.onEvent(arguments)});
}
},
onLoad:function(){
this._onLoaded=true;
if(this.event && this.event.length && this.enabled){
this._registerEvent(this.event);
}
},
destroy:function(){
this.inherited(arguments);
},
updateEventSelector:function(objects,cis){
var options = [];
if(!objects || !objects.length){
options= this.scope.getEventsAsOptions(this.event);
}else{
options = [{label:"onclick", value:"onclick"},
{label:"ondblclick",value:"ondblclick"},
{label:"onmousedown",value:"onmousedown"},
{label:"onmouseup",value:"onmouseup"},
{label:"onmouseover",value:"onmouseover"},
{label:"onmousemove",value:"onmousemove"},
{label:"onmouseout",value:"onmouseout"},
{label:"onkeypress",value:"onkeypress"},
{label:"onkeydown",value:"onkeydown"},
{label:"onkeyup", value:"onkeyup"},
{label:"onfocus", value:"onfocus"},
{label:"onblur", value:"onblur"},
{label:"onchange", value:"onchange"}];
//select the event we are listening to
for (var i = 0; i < options.length; i++) {
var obj = options[i];
if(obj.value===this.event){
obj.selected=true;
break;
}
}
}
for (var i = 0; i < cis.length; i++) {
var ci = cis[i];
if(ci['widget'] && ci['widget'].title==='Event'){
//console.log('event!');
var widget = ci['_widget'];
widget.nativeWidget.set('options',options);
widget.nativeWidget.reset();
widget.nativeWidget.set('value',this.event);
this.publish(types.EVENTS.RESIZE,{});
break;
}
}
},
onReferenceChanged:function(newValue,cis){
this._destroy();//unregister previous event(s)
this.reference = newValue;
var objects = this.resolveReference(this.deserialize(newValue));
this._registerEvent(this.event);
},
onChangeField:function(field,newValue,cis){
if(field=='event'){
this._destroy(); //unregister previous event
if(this._onLoaded){ // we've have been activated at load time, so re-register our event
this.event = newValue;
this._registerEvent(newValue);
}
}
if(field=='reference'){
this.onReferenceChanged(newValue,cis);
}
this.inherited(arguments);
},
activate:function(){
this._destroy();//you never know
this._registerEvent(this.event);
},
deactivate:function(){
this._destroy();
},
_destroy:function(){
if(this.listeners){
for (var i = 0; i < this.listeners.length; i++) {
var obj = this.listeners[i];
obj.stop_listening();
var combos = obj.get_registered_combos();
if(combos){
obj.unregister_many(combos);
}
obj.reset();
console.log('did destroy listener');
}
}
this.listeners=[];
},
onFieldsRendered:function(block,cis){}
});
});

View File

@ -0,0 +1,219 @@
define([
'dcl/dcl',
'xide/utils',
'xide/types',
'dojo/Deferred',
"xblox/model/Block",
"xcf/model/Command"
], function(dcl,utils,types,Deferred,Block,Command){
// summary:
// The Call Block model.
// This block makes calls to another blocks in the same scope by action name
// module:
// xblox.model.functions.CallBlock
/**
* @augments module:xide/mixins/EventedMixin
* @extends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
return dcl(Command,{
declaredClass:"xblox.model.functions.CallBlock",
//command: (String)
// block action name
command:'Select command please',
icon:'',
args:null,
_timeout:100,
isCommand:true,
_commandHandles:null,
/**
* onCommandFinish will be excecuted which a driver did run a command
* @param msg {object}
* @param msg.id {string} the command job id
* @param msg.src {string} the source id, which is this block id
* @param msg.cmd {string} the command string being sent
*/
onCommandProgress:function(msg){
var scope = this.getScope();
var context = scope.getContext();//driver instance
var result = {};
var params = msg.params;
if(params && params.id){
this._emit('cmd:'+msg.cmd + '_' + params.id,{
msg:msg
});
msg.lastResponse && this.storeResult(msg.lastResponse);
this._emit('progress',{
msg:msg,
id:params.id
});
}
var command = this._lastCommand;
this._lastResult = null;
this._lastResult = msg ? msg.result : null;
var items = this.getItems(types.BLOCK_OUTLET.PROGRESS);
if(!this._lastSettings){
this._lastSettings = {}
}
this._lastSettings.override = {};
if(items.length) {
this.runFrom(items,0,this._lastSettings);
}
},
stop:function(){
this._lastCommand && this._lastCommand.stop();
},
pause:function(){
this._lastCommand && this._lastCommand.pause();
},
destroy:function(){
_.invoke(this._commandHandles,'remove');
delete this._commandHandles;
delete this._lastCommand;
},
/***
* Returns the block run result
* @param scope
*/
solve:function(scope,settings) {
if(!this._commandHandles){
this._commandHandles=[];
}else{
//_.invoke(this._commandHandles,'remove');
this._commandHandles = [];
}
var timeout = this._timeout || 50;
if(_.isString(timeout)){
timeout = parseInt(timeout);
}
var dfd = new Deferred();
var handles = this._commandHandles;
settings = settings || {}
setTimeout(function(){
if (this.command){
var _args = null;
if(this.args){
settings.override = settings.override || {};
var args = scope.expressionModel.replaceVariables(scope,this.args,false,false,null,null,{
begin:"%%",
end:"%%"
});
try {
_args = utils.fromJson(args);
}catch(e){
_args = args;
}
settings.override['args']= _.isArray(_args) ? _args : [args];
settings.override['mixin']=_args;
}
this._lastCommand = scope.resolveBlock(this.command);
if(this._lastCommand && this._lastCommand._on){
handles.push(this._lastCommand._on('paused',this.onCommandPaused,this));
handles.push(this._lastCommand._on('finished',this.onCommandFinish,this));
handles.push(this._lastCommand._on('stopped',this.onCommandStopped,this));
handles.push(this._lastCommand._on('error',this.onCommandError,this));
handles.push(this._lastCommand._on('progress',this.onCommandProgress,this));
}
var res = scope.solveBlock(this.command,settings);
if(res){
this.onSuccess(this,settings);
}else{
this.onFailed(this,settings);
}
dfd.resolve(res);
return res;
}
}.bind(this),timeout);
return dfd;
},
hasInlineEdits:true,
/**
*
* @param field
* @param pos
* @param type
* @param title
* @param mode: inline | popup
* @returns {string}
*/
makeEditable:function(field,pos,type,title,mode,options,value){
var optionsString = "";
return "<a " + optionsString + " tabIndex=\"-1\" pos='" + pos +"' display-mode='" + (mode||'popup') + "' display-type='" + (type || 'text') +"' data-prop='" + field + "' data-title='" + title + "' class='editable editable-click' href='#'>" + this[field] +"</a>";
},
getFieldOptions:function(field){
if(field ==="command"){
return this.scope.getCommandsAsOptions("text");
}
},
toText:function(){
var text = 'Unknown';
var block = this.scope.getBlock(this.command);
if(block){
text = block.name;
}
if(this.command.indexOf('://')!==-1) {
text = '<span class="text-info">' +this.scope.toFriendlyName(this,this.command) + '</span>';
}
var _out = this.getBlockIcon('D') + 'Call Command : ' + text;
return _out;
},
// standard call for editing
getFields:function(){
var fields = this.getDefaultFields();
var thiz=this;
var title = 'Command';
if(this.command.indexOf('://')){
title = this.scope.toFriendlyName(this,this.command);
}
fields.push(utils.createCI('value','xcf.widgets.CommandPicker',this.command,{
group:'General',
title:'Command',
dst:'command',
options:this.scope.getCommandsAsOptions(),
block:this,
pickerType:'command',
value:this.command
}));
fields.push(utils.createCI('arguments',27,this.args,{
group:'Arguments',
title:'Arguments',
dst:'args'
}));
fields.push(utils.createCI('timeout',13,this._timeout,{
group:'General',
title:'Delay',
dst:'_timeout'
}));
return fields;
}
});
});

View File

@ -0,0 +1,88 @@
/** @module xblox/model/functions/PauseBlock **/
define([
'dcl/dcl',
'xide/utils',
'xide/types',
'dojo/Deferred',
"xblox/model/Block"
], function(dcl,utils,types,Deferred,Block){
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
return dcl(Block,{
declaredClass:"xblox.model.functions.PauseBlock",
command:'Select command please',
icon:'',
args:null,
_timeout:100,
hasInlineEdits:true,
/***
* Returns the block run result
* @param scope
*/
solve:function(scope,settings) {
if (this.command){
var _args = null;
var block = scope.resolveBlock(this.command);
if(block && block.pause){
var res = block.pause();
this.onSuccess(this,settings);
}else{
this.onFailed(this,settings);
}
return res;
}
},
/**
*
* @param field
* @param pos
* @param type
* @param title
* @param mode: inline | popup
* @returns {string}
*/
makeEditable:function(field,pos,type,title,mode,options,value){
var optionsString = "";
return "<a " + optionsString + " tabIndex=\"-1\" pos='" + pos +"' display-mode='" + (mode||'popup') + "' display-type='" + (type || 'text') +"' data-prop='" + field + "' data-title='" + title + "' class='editable editable-click' href='#'>" + this[field] +"</a>";
},
getFieldOptions:function(field){
if(field ==="command"){
return this.scope.getCommandsAsOptions("text");
}
},
toText:function(){
var text = 'Unknown';
var block = this.scope.getBlock(this.command);
if(block){
text = block.name;
}
if(this.command.indexOf('://')!==-1) {
text = '<span class="text-info">' +this.scope.toFriendlyName(this,this.command) + '</span>';
}
var _out = this.getBlockIcon('D') + 'Pause Command : ' + text;
return _out;
},
getFields:function(){
var fields = this.inherited(arguments) || this.getDefaultFields();
var thiz=this;
var title = 'Command';
if(this.command.indexOf('://')){
title = this.scope.toFriendlyName(this,this.command);
}
fields.push(utils.createCI('value','xcf.widgets.CommandPicker',this.command,{
group:'General',
title:'Command',
dst:'command',
options:this.scope.getCommandsAsOptions(),
block:this,
pickerType:'command',
value:this.command
}));
return fields;
}
});
});

View File

@ -0,0 +1,117 @@
define([
'dcl/dcl',
'xide/utils',
'xide/types',
'dojo/Deferred',
"xblox/model/Block",
"xide/lodash"
], function(dcl,utils,types,Deferred,Block,_){
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
return dcl(Block,{
declaredClass:"xblox.model.functions.SetProperties",
command:'Select block',
icon:'',
args:null,
_timeout:100,
hasInlineEdits:false,
solve:function(scope,settings) {
var dfd = new Deferred();
if (this.command){
var block = scope.resolveBlock(this.command);
if(block && this.props){
for(var prop in this.props){
block.set(prop,this.props[prop]);
block[prop] = this.props[prop];
block.onChangeField && block.onChangeField(prop,this.props[prop]);
}
this.onSuccess(this,settings);
}else{
this.onFailed(this,settings);
}
dfd.resolve([]);
return dfd;
}
return dfd;
},
/**
*
* @param field
* @param pos
* @param type
* @param title
* @param mode: inline | popup
* @returns {string}
*/
makeEditable:function(field,pos,type,title,mode){
var optionsString = "";
return "<a " + optionsString + " tabIndex=\"-1\" pos='" + pos +"' display-mode='" + (mode||'popup') + "' display-type='" + (type || 'text') +"' data-prop='" + field + "' data-title='" + title + "' class='editable editable-click' href='#'>" + this[field] +"</a>";
},
getFieldOptions:function(field){
if(field ==="command"){
return this.scope.getCommandsAsOptions("text");
}
},
toText:function(){
var text = 'Unknown';
var block = this.scope.getBlock(this.command);
if(block){
text = block.name;
}
if(this.command.indexOf('://')!==-1) {
text = '<span class="text-info">' +this.scope.toFriendlyName(this,this.command) + '</span>';
}
return this.getBlockIcon('D') + 'Set Properties : ' + text;
},
serializeObject:function(field){
return field === 'props';
},
onChangeField:function(field){
if(field==='command'){
delete this.props;
this.props = {};
}
},
init:function(){
if(this.props && _.isString(this.props)){
this.props = utils.fromJson(this.props);
}
},
getFields:function(){
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(utils.createCI('value','xcf.widgets.CommandPicker',this.command,{
group:'General',
title:'Command',
dst:'command',
options:this.scope.getCommandsAsOptions(),
block:this,
pickerType:'command',
value:this.command
}));
var block = this.scope.resolveBlock(this.command);
if(block && block.getFields){
if(!this.props){
this.props = {};
}
var _fields = block.getFields();
var descr = _.find(_fields,{
dst:"description"
});
_fields.remove(descr);
_.each(_fields,function(_field){
_field.group = "Properties";
_field.value = utils.getAt(this.props,_field.dst,_field.value);
_field.dst = "props." + _field.dst;
},this);
fields = fields.concat(_fields);
}
return fields;
}
});
});

View File

@ -0,0 +1,76 @@
/** @module xblox/model/functions/StopBlock **/
define([
'dcl/dcl',
'xide/utils',
"xblox/model/Block"
], function(dcl,utils,Block){
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
return dcl(Block,{
declaredClass:"xblox.model.functions.StopBlock",
command:'Select command please',
icon:'',
args:null,
_timeout:100,
hasInlineEdits:true,
solve:function(scope,settings) {
if (this.command){
var block = scope.resolveBlock(this.command);
if(block && block.stop){
var res = block.stop();
this.onSuccess(this,settings);
}else{
this.onFailed(this,settings);
}
return res;
}
},
/**
*
* @param field
* @param pos
* @param type
* @param title
* @param mode: inline | popup
* @returns {string}
*/
makeEditable:function(field,pos,type,title,mode){
return "<a tabIndex=\"-1\" pos='" + pos +"' display-mode='" + (mode||'popup') + "' display-type='" + (type || 'text') +"' data-prop='" + field + "' data-title='" + title + "' class='editable editable-click' href='#'>" + this[field] +"</a>";
},
getFieldOptions:function(field){
if(field ==="command"){
return this.scope.getCommandsAsOptions("text");
}
},
toText:function(){
var text = 'Unknown';
var block = this.scope.getBlock(this.command);
if(block){
text = block.name;
}
if(this.command.indexOf('://')!==-1) {
text = '<span class="text-info">' +this.scope.toFriendlyName(this,this.command) + '</span>';
}
return this.getBlockIcon('D') + 'Stop Command : ' + text;
},
onChangeField:function(what,value){
},
getFields:function(){
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(utils.createCI('value','xcf.widgets.CommandPicker',this.command,{
group:'General',
title:'Command',
dst:'command',
options:this.scope.getCommandsAsOptions(),
block:this,
pickerType:'command',
value:this.command
}));
return fields;
}
});
});

View File

@ -0,0 +1,109 @@
define([
"dojo/_base/declare",
"xblox/model/Block",
'xide/utils',
'xide/types',
'xide/mixins/EventedMixin',
'xblox/model/Targeted'
], function(declare,Block,utils,types,EventedMixin,Targeted){
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
return declare("xblox.model.html.SetCSS",[Block,EventedMixin,Targeted],{
//method: (String)
// block name
name:'Set CSS',
file:'',
reference:'',
references:null,
description:'Sets HTML Node CSS',
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
solve:function(scope,settings) {
this.onSuccess(this,settings);
},
toText:function(){
var result = this.getBlockIcon() + ' ' + this.name + ' :: ';
if(this.event){
result+= this.event;
}
return result;
},
// standard call for editing
getFields:function(){
try {
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(utils.createCI('File', types.ECIType.FILE, this.file, {
group: 'General',
dst: 'file',
value: this.file,
intermediateChanges: false,
acceptFolders: false,
acceptFiles: true,
encodeFilePath: false,
buildFullPath: true,
filePickerOptions: {
dialogTitle: 'Select CSS File',
filePickerMixin: {
beanContextName: 'CSSFilePicker',
persistent: false,
globalPanelMixin: {
allowLayoutCookies: false
}
},
configMixin: {
beanContextName: 'CSSFilePicker',
LAYOUT_PRESET: types.LAYOUT_PRESET.SINGLE,
PANEL_OPTIONS:{
ALLOW_MAIN_MENU:false
}
},
defaultStoreOptions: {
"fields": 1663,
"includeList": "css",
"excludeList": "*"
},
startPath: this.file
}
}));
fields.push(utils.createCI('Target', types.ECIType.WIDGET_REFERENCE, this.reference, {
group: 'General',
dst: 'reference',
value: this.reference
}));
}catch(e){
}
return fields;
},
getBlockIcon:function(){
return '<span class="fa-paint-brush"></span>';
},
onReferenceChanged:function(newValue){
this._destroy();//unregister previous event(s)
this.reference = newValue;
},
onChangeField:function(field,newValue,cis){
if(field=='reference'){
this.onReferenceChanged(newValue,cis);
}
this.inherited(arguments);
},
activate:function(){
this._destroy();
},
deactivate:function(){
this._destroy();
},
_destroy:function(){
}
});
});

View File

@ -0,0 +1,258 @@
define([
"dcl/dcl",
"xblox/model/Block",
'xide/utils',
'xide/types',
'xide/mixins/EventedMixin',
'xblox/model/Referenced',
"dojo/dom-attr",
"dojo/dom-style",
"dojo/_base/Color",
"xide/registry"
], function (dcl, Block, utils, types, EventedMixin, Referenced, domAttr, domStyle, Color, registry) {
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
var Impl = {
declaredClass: "xblox.model.html.SetState",
name: 'Set State',
reference: '',
references: null,
description: 'Switches to a state',
value: '',
mode: 1,
sharable: false,
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
/**
* Run this block
* @param scope
* @param settings
*/
solve: function (scope, settings) {
var value = this.value;
settings = settings || {};
settings.flags = types.CIFLAG.DONT_PARSE;
var objects = this.resolveReference(this.deserialize(this.reference), settings);
if (this.override && this.override.variables) {
value = utils.replace(value, null, this.override.variables, {
begin: '{',
end: '}'
});
}
if (objects && objects.length) {
_.each(objects, function (object) {
var widget = object
var _widget = registry.byId(widget.id) || widget;
if (widget != _widget) {
}
if (_widget && _widget.setState) {
_widget.setState(value);
}
});
}
this.onSuccess(this, settings);
this.onDidRun(); //clear overrides
},
/**
* Get human readable string for the UI
* @returns {string}
*/
toText: function () {
var _ref = this.deserialize(this.reference);
var result = this.getBlockIcon() + ' ' + this.name + ' :: on ' + (_ref.reference || 'this' ) + ' to' || ' ' + ' to ';
if (this.value) {
result += ' ' + this.value;
}
return result;
},
/**
* Standard call when editing this block
* @returns {*}
*/
getFields: function () {
var fields = this.getDefaultFields(false);
var referenceArgs = {
group: 'General',
dst: 'reference',
value: this.reference
};
fields.push(utils.createCI('State', types.ECIType.STRING, this.value, {
group: 'General',
dst: 'value',
value: this.value,
intermediateChanges: false
}));
fields.push(utils.createCI('Target', types.ECIType.WIDGET_REFERENCE, this.reference, referenceArgs));
return fields;
},
getBlockIcon: function () {
return '<span class="fa-paint-brush"></span>';
},
getPropValue: function (stylesObject, prop) {
for (var _prop in stylesObject) {
if (_prop === prop) {
return stylesObject[_prop];
}
}
return null;
},
updateObject: function (obj, style, mode) {
if (!obj) {
return false;
}
mode = mode || 1;
if (obj.domNode != null) {
obj = obj.domNode;
}
var currentStyle = domAttr.get(obj, 'style');
if (currentStyle === ";") {
currentStyle = "";
}
if (currentStyle === "") {
if (obj['lastStyle'] != null) {
currentStyle = obj['lastStyle'];
} else {
currentStyle = style;
}
}
if (currentStyle === ";") {
currentStyle = style;
}
switch (mode) {
//set
case 1:
{
var currentStyleMap = this._toObject(currentStyle);
var props = style.split(';');
for (var i = 0; i < props.length; i++) {
var _style = props[i].split(':');
if (_style.length == 2) {
currentStyleMap[_style[0]] = _style[1];
}
}
var styles = [];
for (var p in currentStyleMap) {
styles.push(p + ':' + currentStyleMap[p]);
}
$(obj).attr('style', styles.join(';'));
break;
}
//add
case 2:
{
var _newStyle = currentStyle + ';' + style,
_newStyleT = _.uniq(_newStyle.split(';')).join(';');
domAttr.set(obj, 'style', _newStyleT);
break;
}
//remove
case 3:
{
domAttr.set(obj, 'style', utils.replaceAll(style, '', currentStyle));
break;
}
//increase
case 4:
//decrease
case 5:
{
var numbersOnlyRegExp = new RegExp(/(\D*)(-?)(\d+)(\D*)/);
/**
* compute current style values of the object
* @type {{}}
*/
var stylesRequested = this._toObject(style);
var stylesComputed = {};
var jInstance = $(obj);
///determine from node it self
if (stylesRequested) {
for (var prop in stylesRequested) {
stylesComputed[prop] = this._getStyle(prop, obj, jInstance);
}
}
var _newStyleObject = {};
/**
* compute the new style
* @type {number}
*/
for (var prop in stylesRequested) {
var _prop = '' + prop.trim();
var multiplicator = 1;
if (stylesComputed[_prop] != null) {
var _valueRequested = stylesRequested[prop];
var _valueComputed = stylesComputed[prop];
var _isHex = _valueRequested.indexOf('#') != -1;
var _isRGB = _valueRequested.indexOf('rgb') != -1;
var _isRGBA = _valueRequested.indexOf('rgba') != -1;
if (_isHex || _isRGB || _isRGBA) {
var dColorMultiplicator = dojo.colorFromString(_valueRequested);
var dColorNow = dojo.colorFromString(_valueRequested);
var dColorComputed = dojo.colorFromString(_valueComputed);
var dColorNew = new Color();
_.each(["r", "g", "b", "a"], function (x) {
dColorNew[x] = Math.min(dColorComputed[x] + dColorMultiplicator[x], x == "a" ? 1 : 255);
});
var _valueOut = '';
if (_isHex) {
_valueOut = dColorNew.toHex();
} else if (_isRGB) {
_valueOut = dColorNew.toCss(false);
} else if (_isRGBA) {
_valueOut = dColorNew.toCss(true);
}
_newStyleObject[prop] = _valueOut;
domStyle.set(obj, prop, _valueOut);
} else {
//extract actual number :
var numberOnly = numbersOnlyRegExp.exec(stylesComputed[_prop]);
if (numberOnly && numberOnly.length >= 3) {
var _int = parseInt(numberOnly[3]);
if (_int && _int > 0) {
multiplicator = _int;
}
}
}
}
}
//now get an object array of the styles we'd like to alter
var styles = this._toObject(currentStyle);
if (!styles) {
return false;
}
break;
}
}
},
activate: function () {
this._destroy(); //you never know
},
deactivate: function () {
this._destroy();
}
};
//package via declare
var _class = dcl([Block, EventedMixin.dcl, Referenced.dcl], Impl);
//static access to Impl.
_class.Impl = Impl;
return _class;
});

View File

@ -0,0 +1,489 @@
/** @module xblox/model/html/SetStyle **/
define([
"dcl/dcl",
"xblox/model/Block",
'xide/utils',
'xide/types',
'xide/mixins/EventedMixin',
'xblox/model/Referenced',
"dojo/dom-attr",
"dojo/dom-style",
"dojo/_base/Color",
"xide/registry"
// not loaded yet
], function (dcl, Block, utils, types, EventedMixin, Referenced, domAttr, domStyle, Color, registry) {
var debug = false;
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
/**
*
* @class module:xblox/model/html/SetStyle
* @extends module:xblox/model/Block
*/
var Impl = {
declaredClass: "xblox.model.html.SetStyle",
name: 'Set Style',
reference: '',
references: null,
description: 'Sets HTML Node Style Attribute',
value: '',
mode: 1,
sharable: true,
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
/**
*
* @param params (object in that format : reference(string) | mode (string))
*/
/**
* Run this block
* @param scope
* @param settings
*/
solve: function (scope, settings) {
debug && console.log('-set style solve');
var value = this.value;
settings = settings || {};
var override = settings.override || this.override || {};
if (override.variables) {
value = utils.replace(value, null, override.variables, {
begin: '{',
end: '}'
});
}
if (override.args && override.args[0] !== null) {
value = utils.replace(value, null, {value: override.args[0]}, {
begin: '{',
end: '}'
});
}
this.updateObjects(null, value, this.mode, settings);
this.onSuccess(this, settings);
this.onDidRun();
},
/**
* Get human readable string for the UI
* @returns {string}
*/
toText: function () {
var _ref = this.deserialize(this.reference);
var result = this.getBlockIcon() + ' ' + this.name + ' :: on ' + _ref.reference + ' to' || ' ' + ' to ';
if (this.value) {
result += ' ' + this.value;
}
return result;
},
/**
* Standard call when editing this block
* @returns {*}
*/
getFields: function () {
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(utils.createCI('Value', types.ECIType.DOM_PROPERTIES, this.value, {
group: 'General',
dst: 'value',
value: this.value,
intermediateChanges: false
}));
fields.push(utils.createCI('Mode', types.ECIType.ENUMERATION, this.mode, {
group: 'General',
options: [
utils.createOption('Set', 1),
utils.createOption('Add', 2),
utils.createOption('Remove', 3),
utils.createOption('Increase', 4),
utils.createOption('Decrease', 5)
],
dst: 'mode'
}));
var referenceArgs = {
group: 'General',
dst: 'reference',
value: this.reference
};
if (this.scope) {
if (this.scope.global) {
referenceArgs.window = this.scope.global;
referenceArgs.allowHTMLNodes = true;
referenceArgs.allowWidgets = false;
}
if (this.scope.document) {
referenceArgs.document = this.scope.document;
}
}
fields.push(utils.createCI('Target', types.ECIType.WIDGET_REFERENCE, this.reference, referenceArgs));
return fields;
},
getBlockIcon: function () {
return '<span class="fa-paint-brush"></span>';
},
/////////////////////////////////////////////////////////////////////////////////////
//
// Lifecycle
//
/////////////////////////////////////////////////////////////////////////////////////
updateEventSelector: function (objects, cis) {
var options = [];
if (!objects || !objects.length) {
options = this.scope.getEventsAsOptions(this.event);
} else {
options = [{label: "onclick", value: "onclick"},
{label: "ondblclick", value: "ondblclick"},
{label: "onmousedown", value: "onmousedown"},
{label: "onmouseup", value: "onmouseup"},
{label: "onmouseover", value: "onmouseover"},
{label: "onmousemove", value: "onmousemove"},
{label: "onmouseout", value: "onmouseout"},
{label: "onkeypress", value: "onkeypress"},
{label: "onkeydown", value: "onkeydown"},
{label: "onkeyup", value: "onkeyup"},
{label: "onfocus", value: "onfocus"},
{label: "onblur", value: "onblur"},
{label: "onchange", value: "onchange"}];
//select the event we are listening to
for (var i = 0; i < options.length; i++) {
var obj = options[i];
if (obj.value === this.event) {
obj.selected = true;
break;
}
}
}
for (var i = 0; i < cis.length; i++) {
var ci = cis[i];
if (ci['widget'] && ci['widget'].title === 'Event') {
var widget = ci['_widget'];
widget.nativeWidget.set('options', options);
widget.nativeWidget.reset();
widget.nativeWidget.set('value', this.event);
this.publish(types.EVENTS.RESIZE, {});
break;
}
}
},
onReferenceChanged: function (newValue, cis, settings) {
this.reference = newValue;
this.references = this.resolveReference(this.deserialize(newValue), settings);
this.updateObjects(this.references, this.value, null, settings);
},
getPropValue: function (stylesObject, prop) {
for (var _prop in stylesObject) {
if (_prop === prop) {
return stylesObject[_prop];
}
}
return null;
},
_getStyle: function (name, obj, jObj) {
switch (name) {
case "height": {
return jObj.outerHeight();
}
case "width": {
return jObj.outerWidth();
}
case "color": {
return jObj.css("color");
}
case "border-color": {
return jObj.css("border-color") || "rgba(0,0,0,0)";
}
}
return null;
},
updateObject: function (obj, style, mode, settings) {
if (!obj) {
return false;
}
mode = mode || 1;
var _obj = obj.id ? registry.byId(obj.id) : null;
if (_obj) {
obj = _obj;
}
if (obj.domNode != null) {
obj = obj.domNode;
}
var currentStyle = domAttr.get(obj, 'style');
if (currentStyle === ";") {
currentStyle = "";
}
if (currentStyle === "") {
if (obj['lastStyle'] != null) {
currentStyle = obj['lastStyle'];
} else {
currentStyle = style;
}
}
if (currentStyle === ";") {
currentStyle = style;
}
switch (mode) {
//set
case 1: {
var currentStyleMap = this._toObject(currentStyle);
var props = style.split(';');
var css = {};
for (var i = 0; i < props.length; i++) {
var _style = props[i].split(':');
if (_style.length == 2) {
currentStyleMap[_style[0]] = _style[1];
}
}
var styles = [];
for (var p in currentStyleMap) {
styles.push(p + ':' + currentStyleMap[p]);
}
$(obj).attr('style', styles.join(';'));
break;
}
//add
case 2: {
var _newStyle = currentStyle + ';' + style,
_newStyleT = _.uniq(_newStyle.split(';')).join(';');
domAttr.set(obj, 'style', _newStyleT);
break;
}
//remove
case 3: {
domAttr.set(obj, 'style', utils.replaceAll(style, '', currentStyle));
break;
}
//increase
case 4:
//decrease
case 5: {
var numbersOnlyRegExp = new RegExp(/(\D*)(-?)(\d+)(\D*)/);
/**
* compute current style values of the object
* @type {{}}
*/
var stylesRequested = this._toObject(style);
var stylesComputed = {};
var jInstance = $(obj);
///determine from node it self
if (stylesRequested) {
for (var prop in stylesRequested) {
var currentStyle = this._getStyle(prop, obj, jInstance);
stylesComputed[prop] = currentStyle;
}
}
var _newStyleObject = {};
/**
* compute the new style
* @type {number}
*/
for (var prop in stylesRequested) {
var _prop = '' + prop.trim();
var multiplicator = 1;
if (stylesComputed[_prop] != null) {
var _valueRequested = stylesRequested[prop];
var _valueComputed = stylesComputed[prop];
var _isHex = _valueRequested.indexOf('#') != -1;
var _isRGB = _valueRequested.indexOf('rgb') != -1;
var _isRGBA = _valueRequested.indexOf('rgba') != -1;
if (_isHex || _isRGB || _isRGBA) {
var dColorMultiplicator = dojo.colorFromString(_valueRequested);
var dColorNow = dojo.colorFromString(_valueRequested);
var dColorComputed = dojo.colorFromString(_valueComputed);
var dColorNew = new Color();
_.each(["r", "g", "b", "a"], function (x) {
dColorNew[x] = Math.min(dColorComputed[x] + dColorMultiplicator[x], x == "a" ? 1 : 255);
});
console.log('color computed ' + dColorComputed.toRgba() + ' color requested: ' + dColorNow.toRgba() + ' | multiplicator color = ' + dColorMultiplicator.toRgba() + ' is then = ' + dColorNew.toRgba());
var _valueOut = '';
if (_isHex) {
_valueOut = dColorNew.toHex();
} else if (_isRGB) {
_valueOut = dColorNew.toCss(false);
} else if (_isRGBA) {
_valueOut = dColorNew.toCss(true);
}
_newStyleObject[prop] = _valueOut;
domStyle.set(obj, prop, _valueOut);
} else {
//extract actual number :
var numberOnly = numbersOnlyRegExp.exec(stylesComputed[_prop]);
if (numberOnly && numberOnly.length >= 3) {
var _int = parseInt(numberOnly[3]);
if (_int && _int > 0) {
multiplicator = _int;
}
}
}
}
}
var delta = mode == 4 ? 1 : -1;
//now get an object array of the styles we'd like to alter
var styles = this._toObject(currentStyle);
var inStyles = this._toObject(style);
if (!styles) {
return false;
}
var _skipped = [];
for (var prop in styles) {
var _prop = '' + prop.trim();
}
var newStyleString = this._toStyleString(_newStyleObject);
break;
}
}
},
onDomStyleChanged: function (objects, newStyle, mode, settings) {
objects = objects || this.resolveReference(this.deserialize(this.reference), settings);
if (!objects) {
debug && console.warn('have no objects');
return;
}
debug && console.log('change dom style to ' + newStyle + ' on ' + objects.length + ' objects');
for (var i = 0; i < objects.length; i++) {
var obj = objects[i];
if (obj && obj.id && obj.id.indexOf('davinci') != -1) {
continue;
}
this.updateObject(obj, newStyle, mode, settings);
}
},
/**
*
* @param objects
* @param domStyleString
* @param mode
* @param settings
*/
updateObjects: function (objects, domStyleString, mode, settings) {
objects = objects || this.resolveReference(this.deserialize(this.reference), settings);
this.onDomStyleChanged(objects, domStyleString, mode, settings);
},
onChangeField: function (field, newValue, cis) {
this._destroy();
if (field == 'mode' && newValue !== this.mode) {
this.mode = newValue;
}
if (field == 'value' && newValue !== this.value) {
this.onDomStyleChanged(null, newValue, this.mode);
this.value = newValue;
}
if (field == 'reference') {
this.onReferenceChanged(newValue, cis);
}
this.inherited(arguments);
},
activate: function () {
this._destroy();//you never know
},
deactivate: function () {
this._destroy();
},
_destroy: function () {
},
/////////////////////////////////////////////////////////////////////////////////////
//
// Utils
//
/////////////////////////////////////////////////////////////////////////////////////
_changeValue: function (value, delta) {
if (!value) {
return "";
}
var split = value.split(" ");
var result = "";
for (var i = 0; i < split.length; i++) {
if (i > 0)
result += " ";
var bits = split[i].match(/([-\d\.]+)([a-zA-Z%]*)/);
if (!bits) {
result += split[i];
} else {
if (bits.length == 1) {
result += bits[0];
} else {
for (var z = 1; z < bits.length; z++) {
if (!isNaN(bits[z]) && bits[z] != "") {
result += parseFloat(bits[z]) + delta;
} else {
result += bits[z];
}
}
}
}
}
return result;
},
/**
* Convert Style String to an object array, eg: { color:value,.... }
* @param styleString
* @returns {{}}
* @private
*/
_toObject: function (styleString) {
if (!styleString) {
return {};
}
var _result = {};
var _values = styleString.split(';');
for (var i = 0; i < _values.length; i++) {
var obj = _values[i];
if (!obj || obj.length == 0 || !obj.split) {
continue;
}
var keyVal = obj.split(':');
if (!keyVal || !keyVal.length) {
continue;
}
var key = obj.substring(0, obj.indexOf(':'));
var value = obj.substring(obj.indexOf(':') + 1, obj.length);
_result[key] = value;
}
return _result;
},
_toStyleString: function (values) {
var _values = [];
for (var prop in values) {
_values.push(prop + ':' + values[prop]);
}
return _values.join(';') + ';';
}
};
//package via declare
var _class = dcl([Block, Referenced.dcl], Impl);
//static access to Impl.
_class.Impl = Impl;
return _class;
});

View File

@ -0,0 +1,183 @@
define([
'dcl/dcl',
"dojo/Deferred",
"xblox/model/Block",
'xide/utils',
'xide/types',
'xide/mixins/EventedMixin'
], function (dcl, Deferred, Block, utils, types, EventedMixin) {
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
return dcl([Block, EventedMixin.dcl], {
declaredClass: "xblox.model.logging.Log",
name: 'Log Message',
level: 'info',
message: 'return "Message: " + arguments[0];',
_type: 'XBlox',
host: 'this host',
sharable: true,
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText: function () {
var _cls = 'text-primary';
switch (this.level) {
case 'info': {
_cls = 'text-info';
break;
}
case 'warn': {
_cls = 'text-warning';
break;
}
case 'error': {
_cls = 'text-danger';
break;
}
}
var result = this.getBlockIcon() + " " + this.name + " : " + "<span class='" + _cls + " small'> " + ' :: ';
if (this.message) {
result += this.message;
}
return result + "</span>";
},
/***
* Returns the block run result
* @param expression
* @param scope
* @param settings
* @param run
* @param error
* @returns {string}
*/
_solveExpression: function (expression, scope, settings, run, error) {
var _script = '' + expression;
if (_script && _script.length) {
_script = utils.convertAllEscapes(_script, "none");
var _args = this.getArgs();
try {
var _parsed = scope.parseExpression(_script, null, null, null, null, this, _args);
if (run) {
run('Expression ' + _script + ' evaluates to ' + _parsed);
}
return _parsed;
} catch (e) {
if (error) {
error('invalid expression : \n' + _script + ': ' + e);
}
this.onFailed(this, settings);
return _script;
}
}
return _script;
},
/**
*
* @param scope
* @param settings
* @param run
* @param error
*/
solve: function (scope, settings, run, error) {
var dfd = new Deferred();
var device = scope.device;
var _message = this._solveExpression(this.message, scope, settings, run, error);
var message = {
message: _message,
level: this.level,
type: this._type,
details: this.getArgs(),
time: new Date().getTime(),
data: {
device: device ? device.info : null
},
write: true
};
this.onSuccess(this, settings);
dfd.resolve(_message);
try {
this.publish(types.EVENTS.ON_SERVER_LOG_MESSAGE, message);
} catch (e) {
this.onFailed(this, settings);
}
return dfd;
},
// standard call from interface
canAdd: function () {
return null;
},
// standard call for editing
getFields: function () {
var fields = this.inherited(arguments) || this.getDefaultFields();
var thiz = this;
var options = [
{
value: 'info',
label: 'Info'
},
{
value: 'warn',
label: 'Warning'
},
{
value: 'error',
label: 'Error'
},
{
value: 'debug',
label: 'Debug'
},
{
value: 'help',
label: 'Help'
},
{
value: 'verbose',
label: 'verbose'
},
{
value: 'silly',
label: 'Silly'
}
];
fields.push(utils.createCI('Level', 3, this.level, {
group: 'General',
options: options,
dst: 'level'
}));
fields.push(
utils.createCI('message', 25, this.message, {
group: 'General',
title: 'Message',
dst: 'message',
delegate: {
runExpression: function (val, run, error) {
thiz._solveExpression(val, thiz.scope, null, run, error);
}
}
}));
fields.push(
utils.createCI('message', 13, this._type, {
group: 'General',
title: 'Type',
dst: '_type'
}));
return fields;
},
getBlockIcon: function () {
return '<span class="fa-bug"></span>';
}
});
});

View File

@ -0,0 +1,38 @@
define([
'dcl/dcl',
'xblox/model/Block'
], function (dcl, Block) {
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
// summary:
// The Case Block model. Each case block contains a comparation and a commands block.
// If the comparation result is true, the block is executed
//
// This block should have an "SwitchBlock" parent
// module:
// xblox.model.logic.CaseBlock
return dcl(Block, {
declaredClass: "xblox.model.logic.BreakBlock",
name: 'Break',
icon: 'fa-stop',
hasInlineEdits: false,
canAdd: false,
toText: function () {
return '&nbsp;<span class="fa-stop text-warning"></span>&nbsp;&nbsp;<span>' + this.name + '</span>';
},
/***
* Solves the case block
* @param scope
* @param settings
*/
solve: function (scope, settings) {
this.onSuccess(this, settings);
}
});
});

View File

@ -0,0 +1,185 @@
define([
'dcl/dcl',
'xide/utils',
'xblox/model/Block',
'dojo/Deferred',
"xblox/model/logic/BreakBlock"
], function (dcl, utils, Block, Deferred, BreakBlock) {
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
// summary:
// The Case Block model. Each case block contains a comparation and a commands block.
// If the comparation result is true, the block is executed
//
// This block should have an "SwitchBlock" parent
// module:
// xblox.model.logic.CaseBlock
return dcl(Block, {
declaredClass: "xblox.model.logic.CaseBlock",
//comparator: xblox.model.Comparator
// Comparison to be applied -> compare <switch variable> width <expression>
comparator: null,
//expression: xblox.model.Expression
// expression to be compared
expression: null,
//items: Array (xblox.model.Block)
// block to be executed if the comparison result is true
items: null,
name: 'Case',
icon: '',
hasInlineEdits: true,
toText: function () {
var _comparator = '' + this.comparator;
if (_comparator == '==') {
//_comparator =''
}
return '<span style="text-indent: 1em;">&nbsp;&nbsp;&nbsp;' + this.getBlockIcon('I') + this.name + ' ' + this.makeEditable('comparator', 'right', 'text', 'Enter a comparison', 'inline') + (this.expression != null ? ' ' + this.makeEditable('expression', 'right', 'text', 'Enter a value to compare') : '') + '</span>';
},
canAdd: function () {
return [];
},
/**
*
* @param scope
* @param settings
* @param switchblock
* @returns {Array}
* @private
*/
_solve: function (scope, settings, switchblock) {
settings = settings || {
highlight: false
};
var ret = [];
for (var n = 0; n < this.items.length; n++) {
var block = this.items[n];
if (block.declaredClass.indexOf('BreakBlock') !== -1) {
switchblock.stop();
}
this.addToEnd(ret, block.solve(scope, settings));
}
return ret;
},
/***
* Solves the case block
* @param scope
* @param settings
* @param switchBlock => parent SwitchCommand block
*/
solve: function (scope, switchBlock, settings) {
try {
var _var = scope.getVariableById(switchBlock.variable);
if (!_var && settings.args && settings.args[0]) {
_var = {value: settings.args[0]};
}
// Get the variable to evaluate
var switchVarValue = '';
if (_var) {
switchVarValue = this._getArg(_var.value, true);
} else {
this.onFailed(this, settings);
// Comparation is false
return false;
}
//var compResult = scope.parseExpression("'" + switchVarValue+ "'" + this.comparator + this.expression);
var compResult = scope.parseExpression("" + switchVarValue + "" + this.comparator + this._getArg(this.expression, true));
if (compResult !== true) {
this.onFailed(this, settings);
// Comparation is false
return false;
} else {
this.onSuccess(this, settings);
// Comparation is true. Return block.solve();
this._solve(scope, settings, switchBlock);
return true;
}
} catch (e) {
logError(e);
}
},
/**
* Store function override
* @returns {Array}
*/
getChildren: function () {
return this.items;
},
// standard call for editing
getFields: function () {
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(utils.createCI('Expression', 13, this.expression, {
group: 'General',
title: 'Expression',
dst: 'expression'
}));
function makeOption(value, label) {
return {
label: label || value,
value: value
}
}
fields.push(utils.createCI('Comparator', 3, this.comparator, {
group: 'General',
title: 'Comparator',
dst: 'comparator',
widget: {
options: [
/*makeOption('==',"Equals"),
makeOption('<=',"Smaller or equal"),
makeOption('=>',"Greater or equal"),
makeOption('!=',"Not equal"),
makeOption('<',"Smaller than"),
makeOption('>',"Greater than")*/
makeOption('=='),
makeOption('<='),
makeOption('=>'),
makeOption('!='),
makeOption('<'),
makeOption('>')
],
editable: true
}
}));
return fields;
},
runAction: function (action) {
if (action.command === 'New/Break') {
var dfd = new Deferred();
var newBlock = this.add(BreakBlock, {
group: null
});
var defaultDfdArgs = {
select: [newBlock],
focus: true,
append: false
};
dfd.resolve(defaultDfdArgs);
newBlock.refresh();
return dfd;
}
},
getActions: function () {
return [this.createAction({
label: 'Break',
command: 'New/Break',
tab: 'Home',
icon: 'fa-stop',
group: 'File',
mixin: {
addPermission: true,
custom: true,
quick: false
}
})
]
}
});
});

View File

@ -0,0 +1,16 @@
define([
"dojo/_base/declare",
"../ModelBase"], function (declare, ModelBase) {
// summary:
// The comparator model. A comparator compares two values and returns a boolean, indicating if the comparison
// is true of false
// module:
// xblox.model.Comparator
return declare("xblox.model.Comparator", [ModelBase], {
//name: string
// Comparator public name/representation (=,>...)
name: ''
});
});

View File

@ -0,0 +1,32 @@
define([
'dcl/dcl',
'xblox/model/Block'
], function (dcl, Block) {
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
// summary:
// The Case Block model. Each case block contains a comparation and a commands block.
// If the comparation result is true, the block is executed
//
// This block should have an "SwitchBlock" parent
// module:
// xblox.model.logic.CaseBlock
return dcl(Block, {
declaredClass: "xblox.model.logic.DefaultBlock",
name: 'Default',
icon: '',
hasInlineEdits: false,
toText: function () {
return '&nbsp;<span class="fa-eject text-info"></span>&nbsp;&nbsp;<span>' + this.name + '</span>';
},
solve: function (scope, settings) {
this.onSuccess(this, settings);
return this._solve(scope, settings);
}
});
});

View File

@ -0,0 +1,62 @@
define([
"dcl/dcl",
"xblox/model/Block",
"xblox/model/Contains"
], function (dcl, Block, Contains) {
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
// summary:
// The ElseIf Block model. Each ElseIf block contains a condition and a consequent to be run if the condition
// is true
//
// This block should have an "IfBlock" parent
// module:
// xblox.model.logic.ElseIfBlock
return dcl([Block, Contains], {
declaredClass: "xblox.model.logic.ElseIfBlock",
// condition: (String) expression to be evaluated
condition: "",
// consequent: (Block) block to be run if the condition is true
consequent: null,
name: 'else if',
icon: '',
solve: function (scope, settings) {
if (this._checkCondition(scope)) {
return this._solve(scope, settings)
}
return false;
},
toText: function () {
return "<span class='text-primary'>" + this.getBlockIcon('E') + this.name + " </span>" + "<span class='text-warning small'>" + (this.condition || "") + "<span>";
},
// checks the ElseIf condition
_checkCondition: function (scope) {
if (this.condition !== null) {
return scope.parseExpression(this.condition);
}
return false;
},
getFields: function () {
var thiz = this;
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(
this.utils.createCI('condition', this.types.ECIType.EXPRESSION_EDITOR, this.condition, {
group: 'General',
title: 'Expression',
dst: 'condition',
delegate: {
runExpression: function (val, run, error) {
return thiz.scope.expressionModel.parse(thiz.scope, val, false, run, error);
}
}
})
);
return fields;
}
});
});

View File

@ -0,0 +1,306 @@
/** @module xblox/model/logic/IfBlock **/
define([
"dcl/dcl",
"xblox/model/Block",
"xblox/model/Statement",
"xblox/model/logic/ElseIfBlock",
"dojo/Deferred",
"xide/utils"
], function (dcl, Block, Statement, ElseIfBlock, Deferred, utils) {
/**
* Base block class.
*
* @class module:xblox/model/logic/IfBlock
* @augments module:xblox/model/ModelBase
* @extends module:xblox/model/Block
*/
return dcl(Block, {
declaredClass: "xblox.model.logic.IfBlock",
// condition: (String) expression to be evaluated
condition: 'Invalid Expression',
// consequent: (Block) block to be run if the condition is true
consequent: null,
// elseIfBlocks: (optional) Array[ElseIfBlock] -> blocks to be run if the condition is false. If any of these blocks condition is
// true, the elseIf/else sequence stops
elseIfBlocks: null,
// alternate: (optional) (Block) -> block to be run if the condition is false and none of the "elseIf" blocks is true
alternate: null,
// standard call from interface
canAdd: function () {
return [];
},
// autoCreateElse : does auto creates the else part
autoCreateElse: true,
// name : this name is displayed in the block row editor
name: 'if',
icon: '',
// add
//
// @param proto {mixed : Prototype|Object} : the new block's call prototype or simply a ready to use block
// @param ctrArgs {Array} : constructor arguments for the new block
// @param where {String} : consequent or alternate or elseif
// @returns {Block}
//
add: function (proto, ctrArgs, where) {
if (where == null) {
where = 'consequent';
}
return this._add(proto, ctrArgs, where, false);
},
// overrides default store integration
__addToStore: function (store) {
//add our self to the store
store.put(this);
},
/**
* Store function override
* @returns {boolean}
*/
mayHaveChildren: function () {
return (this.items !== null && this.items.length) ||
(this.elseIfBlocks !== null && this.elseIfBlocks.length) ||
(this.consequent != null && this.consequent.length) ||
(this.alternate != null && this.alternate.length);
},
/**
* Store function override
* @returns {Array}
*/
getChildren: function () {
var result = [];
if (this.consequent) {
result = result.concat(this.consequent);
}
if (this.elseIfBlocks) {
result = result.concat(this.elseIfBlocks);
}
if (this.alternate) {
result = result.concat(this.alternate);
}
return result;
},
/**
* Block row editor, returns the entire text for this block
* @returns {string}
*/
toText: function () {
return "<span class='text-primary'>" + this.getBlockIcon('E') + this.name + " </span>" + "<span class='text-warning small'>" + this.condition + "<span>";
},
_checkCondition: function (scope) {
return scope.parseExpression(this.condition, null, null);
},
solve: function (scope, settings) {
// 1. Check the condition
var solvedCondition = this._checkCondition(scope);
var elseIfBlocks = this.getElseIfBlocks();
var others = this.childrenByNotClass(ElseIfBlock);
var result = null;
others = others.filter(function (block) {
return !block.isInstanceOf(Statement);
});
// 2. TRUE? => run consequent
if (solvedCondition == true || solvedCondition > 0) {
this.onSuccess(this, settings);
if (others && others.length) {
for (var i = 0; i < others.length; i++) {
result = others[i].solve(scope, settings);
}
}
return result;
} else {
// 3. FALSE?
var anyElseIf = false;
this.onFailed(this, settings);
if (elseIfBlocks) {
// 4. ---- check all elseIf blocks. If any of the elseIf conditions is true, run the elseIf consequent and
// stop the process
for (var n = 0; ( n < elseIfBlocks.length ) && (!anyElseIf); n++) {
var _elseIfBlock = elseIfBlocks[n];
if (_elseIfBlock._checkCondition(scope)) {
_elseIfBlock.onSuccess(_elseIfBlock, settings);
anyElseIf = true;
return _elseIfBlock.solve(scope, settings);
} else {
_elseIfBlock.onFailed(_elseIfBlock, settings);
}
}
}
var alternate = this.childrenByClass(Statement);
// 5. ---- If none of the ElseIf blocks has been run, run the alternate
if (alternate.length > 0 && (!anyElseIf)) {
result = null;
for (var i = 0; i < alternate.length; i++) {
result = alternate[i].solve(scope, settings);
}
return result;
}
}
return [];
},
/**
* Default override empty. We have 3 arrays to clean : items, alternate and consequent
*/
empty: function () {
this._empty(this.alternate);
this._empty(this.consequent);
this._empty(this.elseIfBlocks);
},
/**
* Deletes us or children block in alternate or consequent
* @param what
*/
removeBlock: function (what) {
if (what) {
if (what && what.empty) {
what.empty();
}
delete what.items;
what.parent = null;
this.alternate.remove(what);
this.consequent.remove(what);
this.elseIfBlocks.remove(what);
}
},
// evaluate the if condition
_getContainer: function (item) {
if (this.consequent.indexOf(item) != -1) {
return 'consequent';
} else if (this.alternate.indexOf(item) != -1) {
return 'alternate';
} else if (this.elseIfBlocks.indexOf(item) != -1) {
return 'elseIfBlocks';
}
return '_';
},
/**
* Default override, prepare all variables
*/
init: function () {
this.alternate = this.alternate || [];
this.consequent = this.consequent || [];
this.elseIfBlocks = this.elseIfBlocks || [];
for (var i = 0; i < this.alternate.length; i++) {
this.alternate[i].parentId = this.id;
this.alternate[i].parent = this;
}
for (var i = 0; i < this.elseIfBlocks.length; i++) {
this.elseIfBlocks[i].parentId = this.id;
this.elseIfBlocks[i].parent = this;
}
for (var i = 0; i < this.consequent.length; i++) {
this.consequent[i].parentId = this.id;
this.consequent[i].parent = this;
}
//var store = this.scope.blockStore;
},
getFields: function () {
var thiz = this;
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(
this.utils.createCI('condition', this.types.ECIType.EXPRESSION_EDITOR, this.condition, {
group: 'General',
title: 'Expression',
dst: 'condition',
delegate: {
runExpression: function (val, run, error) {
return thiz.scope.expressionModel.parse(thiz.scope, val, false, run, error);
}
}
})
);
return fields;
},
postCreate: function () {
if (this._postCreated) {
return;
}
this._postCreated = true;
},
toCode: function (lang, params) {
},
getElseIfBlocks: function () {
return this.childrenByClass(ElseIfBlock);
},
runAction: function (action) {
var store = this.scope.blockStore;
var command = action.command;
if (command === 'New/Else' || command === 'New/Else If') {
var newBlockClass = command === 'New/Else If' ? ElseIfBlock : Statement;
var args = utils.mixin({
name: 'else',
items: [],
dstField: 'alternate',
parentId: this.id,
parent: this,
scope: this.scope,
canAdd: function () {
return [];
},
canEdit: function () {
return false;
}
}, newBlockClass == ElseIfBlock ? {name: 'else if', dstField: 'elseIfBlocks'} : {
name: 'else', dstField: 'alternate'
}
);
var newBlock = this.add(newBlockClass, args, newBlockClass == Statement ? 'alternate' : 'elseIfBlocks');
var defaultDfdArgs = {
select: [newBlock],
focus: true,
append: false,
expand: true,
delay: 10
};
var dfd = new Deferred();
store._emit('added', {
target: newBlock
});
dfd.resolve(defaultDfdArgs);
newBlock.refresh();
return dfd;
}
},
getActions: function () {
var result = [];
if (this.alternate.length == 0) {
result.push(this.createAction({
label: 'Else',
command: 'New/Else',
icon: this.getBlockIcon('I'),
tab: 'Home',
group: 'File',
mixin: {
addPermission: true,
custom: true
}
}));
}
result.push(this.createAction({
label: 'Else If',
command: 'New/Else If',
icon: this.getBlockIcon('I'),
tab: 'Home',
group: 'File',
mixin: {
addPermission: true,
custom: true
}
}));
return result;
}
});
});

View File

@ -0,0 +1,152 @@
/** @module xblox/model/logic/SwitchBlock **/
define([
'dcl/dcl',
"xblox/model/Block",
"xblox/model/logic/CaseBlock",
"xblox/model/logic/DefaultBlock",
"dojo/Deferred",
"xide/lodash"
], function (dcl, Block, CaseBlock, DefaultBlock, Deferred, _) {
/**
*
* @class module:xblox/model/logic/SwitchBlock
* @extends module:xblox/model/Block
*/
return dcl(Block, {
declaredClass: "xblox.model.logic.SwitchBlock",
items: null,
name: 'Switch',
icon: null,
toText: function () {
return this.getBlockIcon('H') + this.name + ' ';
},
/**
*
* @param what {module:xblox/model/Block}
* @returns {*}
*/
canAdd: function (what) {
if(what && what.isInstanceOf){
return what.isInstanceOf(CaseBlock) || what.isInstanceOf(DefaultBlock);
}
return [];
},
getFields: function () {
return this.getDefaultFields(false, false);
},
/***
* Solve the switchblock
* @param scope
* @param settings
* @returns {string} execution result
*/
solve: function (scope, settings) {
this._stopped = false;
var anyCase = false; // check if any case is reached
var ret = [];
this.onSuccess(this, settings);
// iterate all case blocks
for (var n = 0; n < this.items.length; n++) {
var block = this.items[n];
if (block.declaredClass === 'xblox.model.logic.CaseBlock'/* instanceof CaseBlock*/) {
var caseret;
// solve each case block. If the comparison result is false, the block returns "false"
caseret = block.solve(scope, this, settings);
if (caseret != false) {
// If the case block return is not false, don't run "else" block
anyCase = true;
this.addToEnd(ret, caseret);
break;
}
}
if (this._stopped) {
break;
}
}
// iterate all "else" blocks if none of the cases occurs
if (!anyCase) {
for (var n = 0; n < this.items.length; n++) {
var block = this.items[n];
if (!(block.declaredClass == 'xblox.model.logic.CaseBlock')) {
this.addToEnd(ret, block.solve(scope, settings));
}
}
}
return ret;
},
/**
* Store function override
* @returns {Array}
*/
getChildren: function () {
return this.items;
},
stop: function () {
this._stopped = true;
},
runAction: function (action) {
var command = action.command;
if (command === 'New/Case' || action.command === 'New/Default') {
var store = this.scope.blockStore;
var dfd = new Deferred();
var newBlock = null;
switch (command) {
case 'New/Case': {
newBlock = this.add(CaseBlock, {
comparator: "==",
expression: "on",
group: null
});
break;
}
case 'New/Default': {
newBlock = this.add(DefaultBlock, {
group: null
});
break;
}
}
dfd.resolve({
select: [newBlock],
focus: true,
append: false
});
newBlock.refresh();
store._emit('added', {
target: newBlock
});
}
},
getActions: function (permissions, owner) {
var result = [this.createAction({
label: 'New Case',
command: 'New/Case',
icon: this.getBlockIcon('I'),
tab: 'Home',
group: 'File',
mixin: {
addPermission: true,
custom: true,
quick: false
}
})];
if (!_.find(this.items, {declaredClass: 'xblox.model.logic.DefaultBlock'})) {
result.push(this.createAction({
label: 'Default',
command: 'New/Default',
icon: 'fa-eject',
tab: 'Home',
group: 'File',
mixin: {
addPermission: true,
custom: true,
quick: false
}
}));
}
return result;
}
});
});

View File

@ -0,0 +1,329 @@
define([
"dcl/dcl",
"xblox/model/Block",
"xblox/model/variables/Variable",
'xblox/model/Contains',
"dojo/promise/all",
"dojo/Deferred"
], function (dcl, Block, Variable, Contains, all, Deferred) {
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
// summary:
// The for block model. It repeats a block a number of times, while the condition is true.
//
// module:
// xblox.model.loops.ForBlock
return dcl([Block, Contains], {
declaredClass: "xblox.model.loops.ForBlock",
// initial: xcf.model.Expression
// the initial value
initial: null,
// final: xcf.model.Expression
// the final value to be compared with the counter. Once the final value equals to the counter, the loop stops
"final": null,
//comparator: xblox.model.Comparator
// Comparison to be applied -> compare <counter variable> width <final>
comparator: null,
// modifier: xcf.model.Expression
// expression to be applied to the counter on every step. Expression: "<counter><modifier>"
modifier: null,
//items: Array (xblox.model.Block)
// block to be executed while the condition compare <counter variable> width <final> is false
items: null,
//counterName: String
// the counter variable name
counterName: null,
// (private) counter: xblox.model.Variable
// counter to be compared and updated on every step
_counter: null,
name: 'For',
sharable: true,
icon: '',
ignoreErrors: false,
deferred: true,
_forState: false,
_currentForIndex: 0,
runFrom: function (_blocks, index, settings) {
var thiz = this,
blocks = _blocks || this.items,
allDfds = [];
var onFinishBlock = function (block, results) {
block._lastResult = block._lastResult || results;
thiz._currentIndex++;
thiz.runFrom(blocks, thiz._currentIndex, settings);
};
var wireBlock = function (block) {
block._deferredObject.then(function (results) {
onFinishBlock(block, results);
});
};
if (blocks.length) {
for (var n = index; n < blocks.length; n++) {
var block = blocks[n];
if (block.enabled === false) {
continue;
}
if (block.deferred === true) {
block._deferredObject = new Deferred();
this._currentIndex = n;
wireBlock(block);
allDfds.push(block.solve(this.scope, settings));
break;
} else {
allDfds.push(block.solve(this.scope, settings));
}
}
} else {
this.onSuccess(this, settings);
}
return allDfds;
},
runFromDirect: function (_blocks, index, settings) {
var thiz = this,
blocks = _blocks || this.items,
allDfds = [];
var onFinishBlock = function (block, results) {
block._lastResult = block._lastResult || results;
thiz._currentIndex++;
thiz.runFrom(blocks, thiz._currentIndex, settings);
};
var wireBlock = function (block) {
block._deferredObject.then(function (results) {
onFinishBlock(block, results);
});
};
if (blocks.length) {
for (var n = index; n < blocks.length; n++) {
var block = blocks[n];
if (block.enabled === false) {
continue;
}
if (block.deferred === true) {
block._deferredObject = new Deferred();
this._currentIndex = n;
wireBlock(block);
allDfds.push(block.solve(this.scope, settings));
break;
} else {
allDfds.push(block.solve(this.scope, settings));
}
}
} else {
this.onSuccess(this, settings);
}
return allDfds;
},
solveSubs: function (dfd, result, items, settings) {
var thiz = this;
settings.override = settings.override || {};
settings.override['args'] = [this._currentForIndex];
//more blocks?
if (items.length) {
var subDfds = thiz.runFrom(items, 0, settings);
all(subDfds).then(function (what) {
}, function (err) {
thiz.onDidRunItem(dfd, err, settings);
});
return subDfds;
} else {
thiz.onDidRunItem(dfd, result, settings);
}
},
solveSubsDirect: function (dfd, result, items, settings) {
var thiz = this;
settings.override = settings.override || {};
settings.override['args'] = [this._currentForIndex];
//more blocks?
if (items.length) {
return thiz.runFromDirect(items, 0, settings);
}
},
_solve: function (scope, settings) {
var dfd = new Deferred(),
self = this;
var result = this.solveSubs(dfd, null, this.items, settings);
if (result) {
all(result).then(function (res) {
var falsy = res.indexOf(false);
if (self.ignoreErrors !== true && falsy !== -1) {
dfd.resolve(false);
} else {
dfd.resolve(true);
}
});
} else {
dfd.resolve(true);
}
return dfd;
},
step: function (scope, settings) {
var state = this._checkCondition(scope, settings);
var dfd = new Deferred();
if (state) {
//run blocks
var subs = this._solve(scope, settings);
subs.then(function (result) {
if (result == true) {
dfd.resolve(true);
} else {
dfd.resolve(false);
}
});
}
return dfd;
},
loop: function (scope, settings) {
var stepResult = this.step(scope, settings),
self = this;
stepResult.then(function (proceed) {
self._updateCounter(scope);
self._currentForIndex = self._counter.value;
if (proceed == true) {
self.loop(scope, settings);
} else {
self.onFailed(self, settings);
}
});
},
_solveDirect: function (scope, settings) {
return this.solveSubsDirect(null, null, this.items, settings);
},
stepDirect: function (scope, settings) {
return this._solveDirect(scope, settings);
},
loopDirect: function (scope, settings) {
this.stepDirect(scope, settings)
for (var index = parseInt(this.initial, 10); index < parseInt(this['final'], 10); index++) {
this.stepDirect(scope, settings);
}
},
// solves the for block (runs the loop)
solve: function (scope, settings) {
// 1. Create and initialize counter variable
this._counter = new Variable({
title: this.counterName,
value: this.initial,
scope: scope,
register: false
});
//prepare
this._forState = true;
this._currentForIndex = this.initial;
this.deferred ? this.loop(scope, settings) : this.loopDirect(scope, settings);
return [];
},
// checks the loop condition
_checkCondition: function (scope, settings) {
var expression = '' + this._counter.value + this.comparator + this['final'];
var result = scope.parseExpression(expression);
if (result != false) {
this.onSuccess(this, settings);
}
this._forState = result;
return result;
},
// updates the counter
_updateCounter: function (scope) {
var value = this._counter.value;
var expression = '' + value + this.modifier;
value = scope.parseExpression(expression);
// Detect infinite loops
if (value == this._counter.value) {
return false;
} else {
this._counter.value = value;
return true;
}
},
/**
* Store function override
* @returns {boolean}
*/
mayHaveChildren: function () {
return this.items != null && this.items.length > 0;
},
/**
* Store function override
* @returns {Array}
*/
getChildren: function () {
var result = [];
if (this.items) {
result = result.concat(this.items);
}
return result;
},
/**
* should return a number of valid classes
* @returns {Array}
*/
canAdd: function () {
return [];
},
/**
* UI, Block row editor, returns the entire text for this block
* @returns {string}
*/
toText: function () {
return this.getBlockIcon('F') + this.name + ' ' + this.initial + ' ' + this.comparator + ' ' + this['final'] + ' with ' + this.modifier;
},
/**
* UI
* @returns {*[]}
*/
getFields: function () {
var fields = this.inherited(arguments) || this.getDefaultFields();
fields = fields.concat([
this.utils.createCI('initial', 13, this.initial, {
group: 'General',
title: 'Initial',
dst: 'initial'
}),
this.utils.createCI('Final', 13, this['final'], {
group: 'General',
title: 'Final',
dst: 'final'
}),
this.utils.createCI('comparator', 13, this.comparator, {
group: 'General',
title: 'Comparision',
dst: 'comparator'
}),
this.utils.createCI('modifier', 13, this.modifier, {
group: 'General',
title: 'Modifier',
dst: 'modifier'
}),
this.utils.createCI('Abort on Error', 0, this.ignoreErrors, {
group: 'General',
title: 'Ignore Errors',
dst: 'ignoreErrors'
}),
this.utils.createCI('Deferred', 0, this.deferred, {
group: 'General',
title: 'Use Deferred',
dst: 'deferred'
})
]);
return fields;
}
});
});

View File

@ -0,0 +1,189 @@
define([
"dcl/dcl",
"xblox/model/Block",
"xblox/model/variables/Variable"
], function(dcl,Block,Variable){
// summary:
// The while block model. It repeats a block a number of times, while the condition is true.
//
// module:
// xblox.model.loops.WhileBlock
return dcl(Block,{
declaredClass:"xblox.model.loops.WhileBlock",
// condition: (String) expression to be evaluated every step
condition: null,
/**
* Blocks to be executed while the condition is true
* @type {xblox.model.Block[]}
* @inheritDoc
*/
items: null,
loopLimit: 1500,
name:'While',
wait:0,
_currentIndex:0,
sharable:true,
icon:"",
_timer:null,
// standard call from interface
canAdd:function(){
return [];
},
_solve:function(scope,settings){
var ret=[];
for(var n = 0; n < this.items.length ; n++)
{
this.items[n].solve(scope,settings);
}
return ret;
},
doStep:function(settings){
if(this._currentIndex < this.loopLimit){
var ret = [];
var _cond = this._checkCondition(this.scope);
if(_cond) {
this.onSuccess(this,settings);
this.addToEnd( ret , this._solve(this.scope,settings));
this._currentIndex++;
}else{
if(this._timer){
clearInterval(this._timer);
}
this.onFailed(this,settings);
}
}else{
console.error('--while block : reached loop limit');
this.reset();
}
},
reset:function(){
if(this._timer){
clearTimeout(this._timer);
this._timer = null;
}
this._currentIndex=0;
},
// solves the while block (runs the loop)
solve:function(scope,settings) {
//console.log('solve while ');
this.loopLimit = 1500;
settings = settings || { };
var iterations = 0;
var ret = [],
thiz = this;
var delay = this._getArg(this.wait);
this.reset();
// has delay
if(delay>0){
this._timer = setInterval(function(){
thiz.doStep(settings);
},delay);
return [];
}
// Evaluate condition
while ((this._checkCondition(scope)) && (iterations < this.loopLimit)) {
this._solve(scope,settings);
iterations++;
}
//cleanup
this.reset();
return ret;
},
/**
* Block row editor, returns the entire text for this block
* @returns {string}
*/
toText:function(){
return this.getBlockIcon('G') + this.name + ' ' + this.condition;
},
// checks the loop condition
_checkCondition:function(scope) {
return scope.parseExpression(this.condition);
},
/**
* Store function override
* @param parent
* @returns {boolean}
*/
mayHaveChildren:function(parent){
return this.items!=null && this.items.length>0;
},
/**
* Store function override
* @param parent
* @returns {Array}
*/
getChildren:function(parent){
var result=[];
if(this.items){
result=result.concat(this.items);
}
return result;
},
getFields:function(){
var thiz=this;
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(
this.utils.createCI('condition',25,this.condition,{
group:'General',
title:'Expression',
dst:'condition',
delegate:{
runExpression:function(val,run,error){
return thiz.scope.expressionModel.parse(thiz.scope,val,false,run,error);
}
}
})
);
fields.push(this.utils.createCI('wait',13,this.wait,{
group:'General',
title:'Wait',
dst:'wait'
}));
return fields;
}
});
});

View File

@ -0,0 +1,239 @@
/** @module xblox/model/mqtt/Publish **/
define([
'dcl/dcl',
'xdojo/has',
'xide/utils',
'xide/types',
'xcf/model/Command'
//'xdojo/has!host-node?dojo/node!tracer',
//'xdojo/has!host-node?nxapp/utils/_console'
], function (dcl, has, utils, types, Command, tracer, _console) {
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
var isServer = has('host-node');
var console = typeof window !== 'undefined' ? window.console : global.console;
if (isServer && tracer && console && console.error) {
console = _console;
}
// summary:
// The Call Block model.
// This block makes calls to another blocks in the same scope by action name
// module:
// xblox.model.code.CallMethod
/**
* Base block class.
*
* @class module:xblox/model/mqtt/Publish
* @extends module:xblox/model/Block
*/
return dcl(Command, {
declaredClass: "xblox.model.mqtt.Publish",
//method: (String)
// block action name
name: 'Publish',
//method: (String)
// block action name
topic: '',
args: '',
deferred: false,
sharable: true,
context: null,
icon: 'fa-send',
isCommand: true,
qos: 0,
retain: false,
/**
* @type {string|null}
*/
path: null,
flags: 0,
onData: function (message) {
if (message && message.topic && message.topic == this.topic) {
var thiz = this,
ctx = this.getContext(),
items = this[this._getContainer()];
var settings = this._lastSettings;
var ret = [];
if (items.length > 0) {
var value = message;
this.path = 'value';
if (this.path && _.isObject(message)) {
value = utils.getAt(message, this.path, message);
}
for (var n = 0; n < this.items.length; n++) {
var block = this.items[n];
if (block.enabled) {
block.override = {
args: [value]
};
ret.push(block.solve(this.scope, settings));
}
}
}
this.onSuccess(this, this._lastSettings);
return ret;
}
},
observed: [
'topic'
],
getContext: function () {
return this.context || (this.scope.getContext ? this.scope.getContext() : this);
},
/**
*
* @param scope
* @param settings
* @param isInterface
* @returns {boolean}
*/
solve: function (scope, settings, isInterface) {
this._currentIndex = 0;
this._return = [];
settings = this._lastSettings = settings || this._lastSettings;
var instance = this.getInstance();
if (isInterface === true && this._loop) {
this.reset();
}
var args = this.args;
var inArgs = this.getArgs(settings);
if (inArgs[0]) {
args = inArgs[0];
}
if (instance) {
var flags = settings.flags || this.flags;
var parse = !(flags & types.CIFLAG.DONT_PARSE);
var isExpression = (flags & types.CIFLAG.EXPRESSION);
var value = utils.getJson(args, true, false);
if (value === null || value === 0 || value === true || value === false || !_.isObject(value)) {
value = {
payload: this.args
};
}
var topic = scope.expressionModel.replaceVariables(scope, this.topic, false, false);
if (value.payload && _.isString(value.payload) && parse) {
var override = settings.override || this.override || {};
var _overrides = (override && override.variables) ? override.variables : null;
if (parse !== false) {
value.payload = utils.convertAllEscapes(value.payload, "none");
}
if (isExpression && parse !== false) {
value.payload = scope.parseExpression(value.payload, null, _overrides, null, null, null, override.args);
}
}
instance.callMethod('publishTopic', utils.mixin({
topic: topic,
qos: this.qos,
retain: this.retain
}, value), this.id, this.id);
}
settings = settings || {};
this.onDidRun();
this.onSuccess(this, settings);
return true;
},
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText: function (icon) {
var out = '<span style="">' + this.getBlockIcon() + ' ' + this.makeEditable('name', 'top', 'text', 'Enter a name', 'inline') + ' :: ' + '</span>';
if (this.topic) {
out += this.makeEditable('topic', 'bottom', 'text', 'Enter a topic', 'inline');
this.startup && (out += this.getIcon('fa-bell inline-icon text-warning', 'text-align:right;float:right;', ''));
this.interval > 0 && (out += this.getIcon('fa-clock-o inline-icon text-warning', 'text-align:right;float:right', ''));
}
return out;
},
// standard call from interface
canAdd: function () {
return [];
},
// standard call for editing
getFields: function () {
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(utils.createCI('qos', types.ECIType.ENUMERATION, this.qos, {
group: 'General',
title: 'QOS',
dst: 'qos',
widget: {
options: [
{
label: "0 - at most once",
value: 0
},
{
label: "1 - at least once",
value: 1
},
{
label: "2 - exactly once",
value: 2
}
]
}
})
);
fields.push(utils.createCI('arguments', 27, this.args, {
group: 'Arguments',
title: 'Arguments',
dst: 'args'
}));
fields.push(this.utils.createCI('flags', 5, this.flags, {
group: 'Arguments',
title: 'Flags',
dst: 'flags',
data: [
{
value: 0x000001000,
label: 'Dont parse',
title: "Do not parse the string and use it as is"
},
{
value: 0x00000800,//2048
label: 'Expression',
title: 'Parse it as Javascript'
}
],
widget: {
hex: true
}
}));
fields.push(utils.createCI('topic', types.ECIType.STRING, this.topic, {
group: 'General',
title: 'Topic',
dst: 'topic',
select: true
}));
fields.push(utils.createCI('retain', types.ECIType.BOOL, this.retain, {
group: 'General',
title: 'Retain',
dst: 'retain'
}));
fields.remove(_.find(fields, {
name: "send"
}));
fields.remove(_.find(fields, {
name: "waitForResponse"
}));
return fields;
}
});
});

View File

@ -0,0 +1,183 @@
/** @module xblox/model/mqtt/Subscribe **/
define([
'dcl/dcl',
'xdojo/has',
"xblox/model/Block",
'xide/utils',
'xblox/model/Contains',
'xide/types'
//'dojo/has!host-node?dojo/node!tracer',
//'dojo/has!host-node?nxapp/utils/_console'
], function(dcl,has,Block,utils,Contains,types,tracer,_console){
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
var isServer = has('host-node');
var console = typeof window !== 'undefined' ? window.console : global.console;
if(isServer && tracer && console && console.error){
console = _console;
}
// summary:
// The Call Block model.
// This block makes calls to another blocks in the same scope by action name
// module:
// xblox.model.code.CallMethod
/**
* Base block class.
*
* @class module:xblox/model/mqtt/Subscribe
* @extends module:xblox/model/Block
*/
return dcl([Block,Contains],{
declaredClass:"xblox.model.mqtt.Subscribe",
//method: (String)
// block action name
name:'Subscribe',
//method: (String)
// block action name
topic:'Topic',
args:'',
deferred:false,
sharable:true,
context:null,
icon:'fa-bell',
/**
* @type {string|null}
*/
path:'',
qos:0,
stop:function(){
var instance = this.getInstance();
if(instance){
instance.callMethod('unSubscribeTopic',utils.mixin({
topic:this.topic
},utils.getJson(this.args || {})),this.id,this.id);
}
},
onData:function(message){
if(message && message.topic && message.topic==this.topic){
delete message['src'];
delete message['id'];
var items = this[this._getContainer()];
var settings = this._lastSettings;
var ret=[];
if(items.length>0){
var value = message;
var path = this.path && this.path.length ? this.path : (message.payload!==null) ? 'payload' : null;
if(path && _.isObject(message)){
value = utils.getAt(message,path,message);
}
for(var n = 0; n < this.items.length ; n++)
{
var block = this.items[n];
if(block.enabled) {
block.override ={
args: [value]
};
ret.push(block.solve(this.scope,settings));
}
}
}
this.onSuccess(this, this._lastSettings);
return ret;
}
},
observed:[
'topic'
],
getContext:function(){
return this.context || (this.scope.getContext ? this.scope.getContext() : this);
},
solve:function(scope,settings){
this._currentIndex = 0;
this._return=[];
this._lastSettings = settings;
var instance = this.getInstance();
if(instance){
instance.callMethod('subscribeTopic',utils.mixin({
topic:this.topic,
qos:this.qos
},utils.getJson(this.args ||"{}")),this.id,this.id);
}
settings = settings || {};
this.onRunThis(settings);
},
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText:function(){
var result = '<span style="">' +
this.getBlockIcon() + '' + this.makeEditable('topic','bottom','text','Enter a topic','inline') + ' :: '+'</span>';
if(this.topic){
result+= this.topic.substr(0,30);
}
return result;
},
// standard call from interface
canAdd:function(){
return [];
},
// standard call for editing
getFields:function(){
var fields = this.inherited(arguments) || this.getDefaultFields();
fields.push(
utils.createCI('name',13,this.name,{
group:'General',
title:'Name',
dst:'name'
})
);
fields.push(utils.createCI('arguments',27,this.args,{
group:'Arguments',
title:'Arguments',
dst:'args'
}));
fields.push(utils.createCI('topic',types.ECIType.STRING,this.topic,{
group:'General',
title:'Topic',
dst:'topic',
select:true
}));
fields.push(utils.createCI('name',types.ECIType.ENUMERATION,this.qos,{
group:'General',
title:'QOS',
dst:'qos',
widget: {
options: [
{
label:"0 - at most once",
value:0
},
{
label:"1 - at least once",
value:1
},
{
label:"2 - exactly once",
value:2
}
]
}
})
);
fields.push(utils.createCI('path',types.ECIType.STRING,this.path,{
group:'General',
title:'Value Path',
dst:'path'
}));
return fields;
}
});
});

View File

@ -0,0 +1,165 @@
define([
'dcl/dcl',
"dojo/Deferred",
"xblox/model/Block",
'xide/utils',
'xblox/model/Contains',
'xide/types'
], function(dcl,Deferred,Block,utils,Contains,types){
/**
* @augments module:xide/mixins/EventedMixin
* @lends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
* @extends module:xblox/model/ModelBase
*/
// summary:
// The Call Block model.
// This block makes calls to another blocks in the same scope by action name
// module:
// xblox.model.code.CallMethod
return dcl([Block,Contains],{
declaredClass:"xblox.model.code.SSH",
//method: (String)
// block action name
name:'Run Script',
//method: (String)
// block action name
method:'',
args:'',
deferred:false,
sharable:true,
context:null,
icon:'fa-code',
description:"Runs a piece of code on a SSH connection",
observed:[
'method'
],
getContext:function(){
return this.context || (this.scope.getContext ? this.scope.getContext() : this);
},
/***
* Returns the block run result
* @param scope
* @param settings
* @param run
* @param error
* @returns {Array}
*/
/**
*
* @param scope
* @param settings
* @param run
* @param error
*/
solve:function(scope,settings,run,error){
this._currentIndex = 0;
this._return=[];
var _script = '' + this._get('method');
var thiz=this,
ctx = this.getContext(),
items = this[this._getContainer()],
dfd = new Deferred(),
isDfd = thiz.deferred;
this.onRunThis(settings);
var _function = new Function("{" + _script + "}");
var _args = thiz.getArgs(settings) || [];
try {
if(isDfd){
ctx.resolve=function(result){
if(thiz._deferredObject) {
thiz._deferredObject.resolve();
}
thiz.onDidRunThis(dfd,result,items,settings);
}
}
var _parsed = _function.apply(ctx, _args || {});
thiz._lastResult = _parsed;
if (run) {
run('Expression ' + _script + ' evaluates to ' + _parsed);
}
if(!isDfd) {
thiz.onDidRunThis(dfd,_parsed,items,settings);
}
if (_parsed !== 'false' && _parsed !== false) {
thiz.onSuccess(thiz, settings);
} else {
thiz.onFailed(thiz, settings);
}
} catch (e) {
thiz.onDidRunItemError(dfd,e || {},settings);
thiz.onFailed(thiz,settings);
if (error) {
error('invalid expression : \n' + _script + ': ' + e);
}
}
return dfd;
},
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText:function(){
var result = this.getBlockIcon() + ' ' + this.name + ' :: ';
if(this.method){
result+= this.method.substr(0,50);
}
return result;
},
// standard call from interface
canAdd:function(){
return [];
},
// standard call for editing
getFields:function(){
var fields = this.inherited(arguments) || this.getDefaultFields();
var thiz=this;
fields.push(
utils.createCI('name',13,this.name,{
group:'General',
title:'Name',
dst:'name'
})
);
fields.push(
utils.createCI('deferred',0,this.deferred,{
group:'General',
title:'Deferred',
dst:'deferred'
})
);
fields.push(utils.createCI('arguments',27,this.args,{
group:'Arguments',
title:'Arguments',
dst:'args'
}));
fields.push(
utils.createCI('value',types.ECIType.EXPRESSION_EDITOR,this.method,{
group:'Script',
title:'Script',
dst:'method',
select:true,
widget:{
allowACECache:true,
showBrowser:false,
showSaveButton:true,
editorOptions:{
showGutter:true,
autoFocus:false
},
item:this
},
delegate:{
runExpression:function(val,run,error){
thiz.method=val;
thiz.solve(thiz.scope,null,run,error);
}
}
}));
return fields;
}
});
});

View File

@ -0,0 +1,345 @@
define([
"dcl/dcl",
"xblox/model/server/ServerBlock",
'xide/utils'
], function (dcl, ServerBlock, utils) {
/**
* Runs a JSON-RPC-2.0 method on the server. This assumes that this block's scope has
* a 'service object'
*/
return dcl(ServerBlock, {
declaredClass:"xblox.model.server.RunServerMethod",
description: 'Runs a JSON-RPC-2.0 method on the server',
/**
* The name of the block, used in the UI
* @member {string}
*/
name: 'Run Server Method',
/**
* The full string of the service class method, ie: MyPHPServerClass::method
* @member {string}
*/
method: 'XShell::run',
/**
* Arguments for the server call
* @member {string}
*/
args: '',
/**
* Override in super class, this block runs async by default
* @member {boolean}
*/
deferred: true,
/**
* The default for the server RPC class
* @member {string}
*/
defaultServiceClass: 'XShell',
/**
* The default for the server RPC class method
* @member {string}
*/
defaultServiceMethod: 'run',
sharable:true,
/**
* Callback when user edited the 'method' field. This will pre-populate the arguments field when empty
* with the known SMD parameters : if possible.
* @param newMethod
* @param cis
*/
onMethodChanged: function (newMethod, cis) {
this.method = newMethod;
//we auto populate the arguments field
if (!utils.isValidString(this.args)) {
var newServerParams = this.getServerParams();
if (newServerParams) {
this._updateArgs(newServerParams, cis);
}
}
},
_getArgs: function () {
/*
var test = [
{
"name": "shellType",
"default": "sh", "optional": false, "value": "notset"
},
{
"name": "cmd",
"optional": false,
"value": "[CurrentDirectory]"
},
{
"name": "cwd",
"default": null,
"optional": true,
"value": "[CurrentDirectory]"
}
];*/
var _args = utils.getJson(this.args || '[]');
var result = [];
if (_args) {
var isSMD = false;
//now check this is still in 'SMD' format
if (_args && _args[0] && _args[0]['optional'] != null) {
isSMD = true;
}
//if SMD true, evaluate the value field
if (isSMD) {
for (var i = 0; i < _args.length; i++) {
var _arg = _args[i];
var _variableValue = _arg.value;
var isBase64 = _arg.name.indexOf('Base64') != -1;
if(isBase64){
_variableValue = this.getService().base64_encode(_variableValue);
}
if (_arg.value !== 'notset') {
if (_arg.value.indexOf('[') != -1 && _arg.value.indexOf(']') != -1) {
_variableValue = this.scope.expressionModel.replaceVariables(this.scope, _arg.value, false, false);
if (_arg.name.indexOf('Base64') != -1) {
_variableValue = this.getService().base64_encode(_variableValue);
}
result.push(_variableValue);
} else {
result.push(_variableValue || _arg['default']);
}
} else {
result.push(_arg['default'] || _variableValue);
}
}
} else {
}
} else {
return [this.args];
}
return result;
},
/**
* Update this.args (string) with a SMD parameter set
* @param params
* @param cis
* @private
*/
_updateArgs: function (params, cis) {
var argumentWidget = this.utils.getCIWidgetByName(cis, 'args');
if (argumentWidget) {
var _string = JSON.stringify(params);
argumentWidget.editBox.set('value', _string);
this.args = _string;
}
},
/**
* Find SMD for the current method
* @returns {*}
*/
getServerParams: function () {
var service = this.getService();
var params = service.getParameterMap(this.getServiceClass(), this.getServiceMethod());
if (params) {
for (var i = 0; i < params.length; i++) {
var param = params[i];
param.value = 'notset';
}
}
return params;
},
onReloaded: function (evt) {
console.log('sdfsd');
this._solve()
},
_solve:function(scope,settings,run,error){
console.log('solve223');
},
/***
* Returns the block run result
* @param scope
* @param settings
* @param run
* @param error
* @returns {Array}
*/
solve: function (scope, settings, run, error) {
this._return = [];
this._lastResult=null;
var thiz = this;
var ret = [];
//this._solve();
//console.dir(this.scope);
if(!this.isInValidState()){
this.onFailed(this, settings);
return ret;
}
var _args = this._getArgs();//returns SMD ready array of values
var _cbSuccess = function (response) {
thiz._lastResult = thiz.utils.getJson(response) || [response];
thiz.resolved(thiz._lastResult);
thiz.onSuccess(thiz, settings);
};
var _cbError = function (response) {
//console.error(' server method ' + thiz.method + ' with params ' + JSON.stringify(_args) + 'failed ' + response);
thiz._lastResult = thiz.utils.getJson(response) || [response];
thiz.resolved(thiz._lastResult);
thiz.onFailed(thiz, settings);
};
this.onRun(this, settings);
var service = this.getService();
var serviceObject = this.scope.serviceObject;
//service.callMethodEx(this.getServiceClass(), this.getServiceMethod(), _args, _cbSuccess,_cbError);
console.error('run deferred');
var dfd = serviceObject.runDeferred(this.getServiceClass(),this.getServiceMethod(),_args);
if(dfd){
dfd.then(function(data){
console.error('returned ',data);
});
}
return dfd;
},
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText: function () {
var result = this.getBlockIcon() + ' ' + this.name + ' :: ';
if (this.method) {
result += this.method.substr(0, 50);
}
return result;
},
// standard call from interface
canAdd: function () {
return [];
},
// standard call for editing
getFields: function () {
var fields = this.inherited(arguments) || this.getDefaultFields();
var thiz = this;
fields.push(utils.createCI('value', 25, this.method, {
group: 'General',
title: 'Method',
dst: 'method',
description: 'This should be in the format : MyServerClass::myMethod',
delegate: {
runExpression: function (val, run, error) {
var old = thiz.method;
thiz.method = val;
var _res = thiz.solve(thiz.scope, null, run, error);
}
}
}));
fields = fields.concat(this.getServerDefaultFields());
return fields;
},
getBlockIcon: function () {
return '<span class="fa-plug"></span>';
},
/////////////////////////////////////////////////////////////////////////////////////
//
// Store
//
/////////////////////////////////////////////////////////////////////////////////////
/**
* Store function override
* @param parent
* @returns {boolean}
*/
mayHaveChildren: function (parent) {
return this.items != null && this.items.length > 0;
},
/**
* Store function override
* @param parent
* @returns {Array}
*/
getChildren: function (parent) {
return this.items;
},
onChangeField: function (field, newValue, cis) {
if (field === 'method') {
this.onMethodChanged(newValue, cis);
}
},
/**
* Check our scope has a service object
* @returns {boolean}
*/
isInValidState: function () {
return this.getService() != null;
},
getService: function () {
var service = this.scope.getService();
if(!service){
console.error('have no service object');
}
return service;
},
getServiceClass: function () {
return this.method.split('::')[0] || this.defaultServiceClass;
},
getServiceMethod: function () {
return this.method.split('::')[1] || this.defaultServiceMethod;
},
hasMethod: function (method) {
return this.isInValidState() &&
this.getService()[this.getServiceClass()] != null &&
this.getService()[this.getServiceClass()][this.getServiceMethod()] != null
},
hasServerClass: function (_class) {
return this.isInValidState() &&
this.getService()[this.getServiceClass()] != null;
},
getServerFunction: function () {
if (this.isInValidState() && this.getServiceClass() && this.getServiceMethod()) {
return this.getService()[this.getServiceClass()][this.getServiceMethod()];
}
return null;
}
});
});

View File

@ -0,0 +1,190 @@
define([
'dcl/dcl',
"xblox/model/Block",
"xide/utils"
], function (dcl,Block,utils) {
/**
* Runs a JSON-RPC-2.0 method on the server. This assumes that this block's scope has
* a 'service object'
*/
return dcl(Block, {
declaredClass:"xblox.model.server.ServerBlock",
/**
* The name of the block, used in the UI
* @member {string}
*/
name: 'Run Server Block',
/**
* The full string of the service class method, ie: MyPHPServerClass::method
* @member {string}
*/
method: 'XShell::run',
/**
* Arguments for the server call
* @member {string}
*/
args: '',
/**
* Override in super class, this block runs async by default
* @member {boolean}
*/
deferred: true,
/**
* The default for the server RPC class
* @member {string}
*/
defaultServiceClass: 'XShell',
/**
* The default for the server RPC class method
* @member {string}
*/
defaultServiceMethod: 'run',
/**
* Debugging
* @member {function}
*/
sharable:true,
onReloaded: function () {
},
/***
* Returns the block run result
* @param scope
* @param settings
* @param run
* @param error
* @returns {Array}
*/
solve: function (scope, settings, run, error) {
this._currentIndex = 0;
this._return = [];
var _script = '' + this.method;
var thiz = this;
if (_script && _script.length) {
var _function = new Function("{" + _script + "}");
var _args = this.getArgs();
try {
var _parsed = _function.apply(this, _args || {});
this._lastResult = _parsed;
if (run) {
run('Expression ' + _script + ' evaluates to ' + _parsed);
}
if (_parsed !== 'false' && _parsed !== false) {
this.onSuccess(this, settings);
} else {
this.onFailed(this, settings);
return [];
}
} catch (e) {
if (error) {
error('invalid expression : \n' + _script + ': ' + e);
}
this.onFailed(this, settings);
return [];
}
} else {
console.error('have no script');
}
var ret = [], items = this[this._getContainer()];
if (items.length) {
this.runFrom(items, 0, settings);
} else {
this.onSuccess(this, settings);
}
return ret;
},
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText: function () {
var result = this.getBlockIcon() + ' ' + this.name + ' :: ';
if (this.method) {
result += this.method.substr(0, 50);
}
return result;
},
// standard call from interface
canAdd: function () {
return [];
},
getServerDefaultFields:function(target){
var fields = target || [];
fields.push(utils.createCI('args', 27, this.args, {
group: 'General',
title: 'Arguments',
dst: 'args'
}));
return fields;
},
getBlockIcon: function () {
return '<span class="fa-plug"></span>';
},
/////////////////////////////////////////////////////////////////////////////////////
//
// Store
//
/////////////////////////////////////////////////////////////////////////////////////
/**
* Store function override
* @param parent
* @returns {boolean}
*/
mayHaveChildren: function (parent) {
return this.items != null && this.items.length > 0;
},
/**
* Store function override
* @param parent
* @returns {Array}
*/
getChildren: function (parent) {
return this.items;
},
/**
* Check our scope has a service object
* @returns {boolean}
*/
isInValidState: function () {
return this.getService() != null;
},
getService: function () {
return this.scope.getService();
},
getServiceClass: function () {
return this.method.split('::')[0] || this.defaultServiceClass;
},
getServiceMethod: function () {
return this.method.split('::')[1] || this.defaultServiceMethod;
},
hasMethod: function (method) {
return this.isInValidState() &&
this.getService()[this.getServiceClass()] != null &&
this.getService()[this.getServiceClass()][this.getServiceMethod()] != null
},
hasServerClass: function (_class) {
return this.isInValidState() &&
this.getService()[this.getServiceClass()] != null;
},
getServerFunction: function () {
if (this.isInValidState() && this.getServiceClass() && this.getServiceMethod()) {
return this.getService()[this.getServiceClass()][this.getServiceMethod()];
}
return null;
}
});
});

View File

@ -0,0 +1,301 @@
define([
"dcl/dcl",
"xblox/model/server/ServerBlock",
'xide/utils',
'xide/types',
'xcf/model/Command'
], function (dcl, ServerBlock, utils, types, Command) {
/**
* Runs a JSON-RPC-2.0 method on the server. This assumes that this block's scope has
* a 'service object'
*/
return dcl([Command, ServerBlock], {
declaredClass: "xblox.model.server.Shell",
description: 'Runs a JSON-RPC-2.0 method on the server',
/**
* The name of the block, used in the UI
* @member {string}
*/
name: 'Run Shell',
/**
* The full string of the service class method, ie: MyPHPServerClass::method
* @member {string}
*/
method: '',
/**
* Arguments for the server call
* @member {string}
*/
args: '',
/**
* Override in super class, this block runs async by default
* @member {boolean}
*/
deferred: true,
/**
* The default for the server RPC class
* @member {string}
*/
defaultServiceClass: 'XShell',
/**
* The default for the server RPC class method
* @member {string}
*/
defaultServiceMethod: 'run',
sharable: true,
/**
* Callback when user edited the 'method' field. This will pre-populate the arguments field when empty
* with the known SMD parameters : if possible.
* @param newMethod
* @param cis
*/
onMethodChanged: function (newMethod, cis) {
this.method = newMethod;
//we auto populate the arguments field
if (!utils.isValidString(this.args)) {
var newServerParams = this.getServerParams();
if (newServerParams) {
this._updateArgs(newServerParams, cis);
}
}
},
_getArgs: function () {
/*
var test = [
{
"name": "shellType",
"default": "sh", "optional": false, "value": "notset"
},
{
"name": "cmd",
"optional": false,
"value": "[CurrentDirectory]"
},
{
"name": "cwd",
"default": null,
"optional": true,
"value": "[CurrentDirectory]"
}
];*/
var _args = utils.getJson(this.args || '[]');
var result = [];
if (_args) {
var isSMD = false;
//now check this is still in 'SMD' format
if (_args && _args[0] && _args[0]['optional'] != null) {
isSMD = true;
}
//if SMD true, evaluate the value field
if (isSMD) {
for (var i = 0; i < _args.length; i++) {
var _arg = _args[i];
var _variableValue = _arg.value;
var isBase64 = _arg.name.indexOf('Base64') != -1;
if (isBase64) {
_variableValue = this.getService().base64_encode(_variableValue);
}
if (_arg.value !== 'notset') {
if (_arg.value.indexOf('[') != -1 && _arg.value.indexOf(']') != -1) {
_variableValue = this.scope.expressionModel.replaceVariables(this.scope, _arg.value, false, false);
if (_arg.name.indexOf('Base64') != -1) {
_variableValue = this.getService().base64_encode(_variableValue);
}
result.push(_variableValue);
} else {
result.push(_variableValue || _arg['default']);
}
} else {
result.push(_arg['default'] || _variableValue);
}
}
} else {
}
} else {
return [this.args];
}
return result;
},
/**
* Update this.args (string) with a SMD parameter set
* @param params
* @param cis
* @private
*/
_updateArgs: function (params, cis) {
var argumentWidget = this.utils.getCIWidgetByName(cis, 'args');
if (argumentWidget) {
var _string = JSON.stringify(params);
argumentWidget.editBox.set('value', _string);
this.args = _string;
}
},
/**
* Find SMD for the current method
* @returns {*}
*/
getServerParams: function () {
var service = this.getService();
var params = service.getParameterMap(this.getServiceClass(), this.getServiceMethod());
if (params) {
for (var i = 0; i < params.length; i++) {
var param = params[i];
param.value = 'notset';
}
}
return params;
},
/***
* Returns the block run result
* @param scope
* @param settings
* @param run
* @param error
* @returns {Array}
*/
solve: function (scope, settings, isInterface, send, run, error) {
this._return = [];
this._lastResult = null;
var thiz = this;
var ret = [];
settings = this._lastSettings = settings || this._lastSettings;
var instance = this.getInstance();
var code = scope.expressionModel.replaceVariables(scope, this.method, false, false);
if (instance) {
instance.runShell(code, utils.mixin({}, {}), this.id, this.id, this);
}else{
scope.ctx.getDeviceManager().sendManagerCommand(types.SOCKET_SERVER_COMMANDS.RUN_SHELL, {
cmd: code,
args: [],
options: utils.mixin({}, {
params: {
id: this.id,
src: this.id
}
})
});
}
return;
},
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText: function () {
var result = this.getBlockIcon() + ' ' + this.name + ' :: ';
if (this.method) {
result += this.method.substr(0, 50);
}
return result;
},
// standard call from interface
canAdd: function () {
return [];
},
// standard call for editing
getFields: function () {
var fields = this.inherited(arguments) || this.getDefaultFields();
var thiz = this;
fields.push(utils.createCI('value', 25, this.method, {
group: 'General',
title: 'Cmd',
dst: 'method',
delegate: {
runExpression: function (val, run, error) {
var old = thiz.method;
thiz.method = val;
var _res = thiz.solve(thiz.scope, null, run, error);
}
}
}));
return fields;
},
getBlockIcon: function () {
return '<span class="fa-plug"></span>';
},
/////////////////////////////////////////////////////////////////////////////////////
//
// Store
//
/////////////////////////////////////////////////////////////////////////////////////
/**
* Store function override
* @param parent
* @returns {boolean}
*/
mayHaveChildren: function (parent) {
return this.items != null && this.items.length > 0;
},
/**
* Store function override
* @param parent
* @returns {Array}
*/
getChildren: function (parent) {
return this.items;
},
onChangeField: function (field, newValue, cis) {
if (field === 'method') {
this.onMethodChanged(newValue, cis);
}
},
/**
* Check our scope has a service object
* @returns {boolean}
*/
isInValidState: function () {
return this.getService() != null;
},
getService: function () {
var service = this.scope.getService();
if (!service) {
console.error('have no service object');
}
return service;
},
getServiceClass: function () {
return this.method.split('::')[0] || this.defaultServiceClass;
},
getServiceMethod: function () {
return this.method.split('::')[1] || this.defaultServiceMethod;
},
hasMethod: function (method) {
return this.isInValidState() &&
this.getService()[this.getServiceClass()] != null &&
this.getService()[this.getServiceClass()][this.getServiceMethod()] != null
},
hasServerClass: function (_class) {
return this.isInValidState() &&
this.getService()[this.getServiceClass()] != null;
},
getServerFunction: function () {
if (this.isInValidState() && this.getServiceClass() && this.getServiceMethod()) {
return this.getService()[this.getServiceClass()][this.getServiceMethod()];
}
return null;
}
});
});

View File

@ -0,0 +1,15 @@
[
{
"_containsChildrenIds": [],
"group": "conditionalProcess",
"id": "02f10325-cc03-6fd5-c1ce-c47f2d1a0f5a",
"method": "test",
"declaredClass": "xblox.model.code.CallMethod",
"name": "Call Method",
"args": "",
"serializeMe": true,
"enabled": true,
"renderBlockIcon": true,
"order": 0
}
]

View File

@ -0,0 +1,213 @@
define([
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/Deferred",
"xblox/model/Block",
'xide/utils'
], function (declare, lang, Deferred, Block, utils) {
/**
* poor man clone
* @param point
* @returns {*[]}
*/
function clone(point) { //TODO: use gl-vec2 for this
return [point[0], point[1]]
}
/**
* help struct
* @param x
* @param y
* @returns {*[]}
*/
function vec2(x, y) {
return [x, y]
}
/**
* the bezier func
* @param opt
* @example
var bezier = require('adaptive-bezier-curve')
var start = [20, 20],
c1 = [100, 159],
c2 = [50, 200],
end = [200, 20],
scale = 2
var points = bezier(start, c1, c2, end, scale)
//returns a list of 2d points: [ [x,y], [x,y], [x,y] ... ]
* @returns {Function}
*/
function createQuadraticBuilder(opt) {
opt = opt || {}
var RECURSION_LIMIT = typeof opt.recursion === 'number' ? opt.recursion : 8;
var FLT_EPSILON = typeof opt.epsilon === 'number' ? opt.epsilon : 1.19209290e-7;
var PATH_DISTANCE_EPSILON = typeof opt.pathEpsilon === 'number' ? opt.pathEpsilon : 1.0;
var curve_angle_tolerance_epsilon = typeof opt.angleEpsilon === 'number' ? opt.angleEpsilon : 0.01;
var m_angle_tolerance = opt.angleTolerance || 0;
return function quadraticCurve(start, c1, end, scale, points) {
if (!points)
points = []
scale = typeof scale === 'number' ? scale : 1.0
var distanceTolerance = PATH_DISTANCE_EPSILON / scale;
distanceTolerance *= distanceTolerance;
begin(start, c1, end, points, distanceTolerance);
return points
};
////// Based on:
////// https://github.com/pelson/antigrain/blob/master/agg-2.4/src/agg_curves.cpp
function begin(start, c1, end, points, distanceTolerance) {
points.push(clone(start));
var x1 = start[0],
y1 = start[1],
x2 = c1[0],
y2 = c1[1],
x3 = end[0],
y3 = end[1];
recursive(x1, y1, x2, y2, x3, y3, points, distanceTolerance, 0);
points.push(clone(end))
}
function recursive(x1, y1, x2, y2, x3, y3, points, distanceTolerance, level) {
if (level > RECURSION_LIMIT)
return;
var pi = Math.PI;
// Calculate all the mid-points of the line segments
//----------------------
var x12 = (x1 + x2) / 2;
var y12 = (y1 + y2) / 2;
var x23 = (x2 + x3) / 2;
var y23 = (y2 + y3) / 2;
var x123 = (x12 + x23) / 2;
var y123 = (y12 + y23) / 2;
var dx = x3 - x1;
var dy = y3 - y1;
var d = Math.abs(((x2 - x3) * dy - (y2 - y3) * dx));
if (d > FLT_EPSILON) {
// Regular care
//-----------------
if (d * d <= distanceTolerance * (dx * dx + dy * dy)) {
// If the curvature doesn't exceed the distance_tolerance value
// we tend to finish subdivisions.
//----------------------
if (m_angle_tolerance < curve_angle_tolerance_epsilon) {
points.push(vec2(x123, y123));
return
}
// Angle & Cusp Condition
//----------------------
var da = Math.abs(Math.atan2(y3 - y2, x3 - x2) - Math.atan2(y2 - y1, x2 - x1))
if (da >= pi) da = 2 * pi - da
if (da < m_angle_tolerance) {
// Finally we can stop the recursion
//----------------------
points.push(vec2(x123, y123));
return
}
}
}
else {
// Collinear case
//-----------------
dx = x123 - (x1 + x3) / 2;
dy = y123 - (y1 + y3) / 2;
if (dx * dx + dy * dy <= distanceTolerance) {
points.push(vec2(x123, y123));
return
}
}
// Continue subdivision
//----------------------
recursive(x1, y1, x12, y12, x123, y123, points, distanceTolerance, level + 1);
recursive(x123, y123, x23, y23, x3, y3, points, distanceTolerance, level + 1);
}
}
/**
* Block to interpolate any value given a curve and a boundary.
*
* This is will be THE performance test for xblox it self!
*
* Prove that this block can be fast enough to interpolate any value,
* fast enough for being used in animations. This block is meant
* to be used with SetStyle on certain properties like position/color
*
* Block signature
//frames
InParameter("Number of steps", INT, "100");
//curve, static for now
InParameter("Progression Curve", 2DCURVE );
//window
InParameter("Min", FLOAT, "0.0");
InParameter("Max", FLOAT, "1.0");
//outs
OutParameter("Value", FLOAT, "0.0" );
OutParameter("Delta", FLOAT, "0.0" );
* @link http://www.jasondavies.com/animated-bezier/
* @link https://github.com/mattdesl/adaptive-bezier-curve
**/
var bezInterpolate = declare("xblox.model.transform.BezierInterpolate", [Block], {
name: 'Bezier-Interpolate',
method: '',
args: '',
deferred: false,
sharable: true,
context: null,
description: "Transform a float value into another float value according to a 2d bezier curve shape, and given boundaries.",
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
canAdd: function () {
return [];
},
// standard call for editing
getFields: function () {
var fields = this.inherited(arguments) || this.getDefaultFields();
var thiz = this;
fields.push(this.utils.createCI('value', 13, this.args, {
group: 'General',
title: 'Value',
dst: 'args'
}));
return fields;
},
getBlockIcon: function () {
return '<span class="fa-code"></span>';
}
});
bezInterpolate.createQuadraticBuilder = createQuadraticBuilder();//statics
return bezInterpolate;
});

View File

@ -0,0 +1,60 @@
define([
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/Deferred",
"xblox/model/Block",
'xide/utils'
], function(declare,lang,Deferred,Block,utils){
return declare("xblox.model.transorm.Bezier",[Block],{
name:'Run Script',
method:'',
args:'',
deferred:false,
sharable:true,
context:null,
description:"Transform a float value into another float value according to a 2d bezier curve shape, and given boundaries. ",
/////////////////////////////////////////////////////////////////////////////////////
//
// UI
//
/////////////////////////////////////////////////////////////////////////////////////
toText:function(){
var result = this.getBlockIcon() + ' ' + this.name + ' :: ';
if(this.method){
result+= this.method.substr(0,50);
}
return result;
},
// standard call from interface
canAdd:function(){
return [];
},
// standard call for editing
getFields:function(){
var fields = this.inherited(arguments) || this.getDefaultFields();
var thiz=this;
fields.push(
this.utils.createCI('value',25,this.method,{
group:'General',
title:'Script',
dst:'method',
delegate:{
runExpression:function(val,run,error){
var old = thiz.method;
thiz.method=val;
var _res = thiz.solve(thiz.scope,null,run,error);
}
}
}));
fields.push(this.utils.createCI('value',27,this.args,{
group:'General',
title:'Arguments',
dst:'args'
}));
return fields;
},
getBlockIcon:function(){
return '<span class="fa-code"></span>';
}
});
});

View File

@ -0,0 +1,127 @@
/** @module xblox/model/variables/Variable */
define([
'dcl/dcl',
'xide/types',
"xblox/model/Block"
], function(dcl,types,Block){
/**
* The command model. A 'command' consists out of a few parameters and a series of
* expressions. Those expressions need to be evaluated before send them to the device
*
* @class module:xblox.model.variables.Variable
* @augments module:xide/mixins/EventedMixin
* @extends module:xblox/model/Block_UI
* @extends module:xblox/model/Block
*/
return dcl(Block,{
declaredClass:"xblox.model.variables.Variable",
//name: String
// the variable's name, it should be unique within a scope
name:null,
//value: Current variable value
value:null,
register:true,
readOnly:false,
initial:null,
isVariable:true,
flags: 0x000001000,
getValue:function(){
return this.value;
},
canDisable:function(){
return false;
},
canMove:function(){
return false;
},
getIconClass:function(){
return 'el-icon-quotes-alt';
},
getBlockIcon:function(){
return '<span class="'+this.icon+'"></span> ';
},
toText:function(){
return "<span class='text-primary'>" + this.getBlockIcon() + this.makeEditable('name','right','text','Enter a unique name','inline') +"</span>";
},
solve:function(){
var _result = this.scope.parseExpression(this.getValue(),true);
//console.log('resolved variable ' + this.title + ' to ' + _result);
return [];
},
getFields:function(){
var fields = this.getDefaultFields();
var thiz=this,
defaultArgs = {
allowACECache:true,
showBrowser:false,
showSaveButton:true,
editorOptions:{
showGutter:false,
autoFocus:false,
hasConsole:false
},
aceOptions:{
hasEmmet:false,
hasLinking:false,
hasMultiDocs:false
},
item:this
};
fields.push(this.utils.createCI('title',types.ECIType.STRING,this.name,{
group:'General',
title:'Name',
dst:'name'
}));
fields.push(this.utils.createCI('value',types.ECIType.EXPRESSION,this.value,{
group:'General',
title:'Value',
dst:'value',
delegate:{
runExpression:function(val,run,error){
return thiz.scope.expressionModel.parse(thiz.scope,val,false,run,error);
}
}
}));
//this.types.ECIType.EXPRESSION_EDITOR
/*
fields.push(this.utils.createCI('initial',this.types.ECIType.EXPRESSION,this.initial,{
group:'General',
title:'Initial',
dst:'initial',
widget:defaultArgs,
delegate:{
runExpression:function(val,run,error){
if(thiz.group=='processVariables'){
var _val = thiz.scope.getVariable("value");
var extra = "";
if(_val) {
_val = _val.value;
if(!thiz.isNumber(_val)){
_val = ''+_val;
_val = "'" + _val + "'";
}
extra = "var value = " + _val +";\n";
}
}
return thiz.scope.expressionModel.parse(thiz.scope,extra + val,false,run,error);
}
}
}));
*/
return fields;
}
});
});

View File

@ -0,0 +1,260 @@
/** @module xblox/model/variables/VariableAssignmentBlock **/
define([
'dcl/dcl',
"xblox/model/Block",
"xide/utils",
"xide/types",
'dstore/legacy/DstoreAdapter',
"xide/factory",
'xdojo/has'
], function(dcl,Block,utils,types,DstoreAdapter,factory,has){
var isServer = has('host-node');
var BLOCK_INSERT_ROOT_COMMAND = 'Step/Insert';
/**
*
* @class module:xblox/model/variables/VariableAssignmentBlock
* @extends xblox/model/Block
*/
var Module = dcl(Block,{
declaredClass: "xblox.model.variables.VariableAssignmentBlock",
//variable: (String)
// variable title
variable:null,
//value: (String)
// Expression to be asigned
value:null,
name:'Set Variable',
icon:'',
hasInlineEdits:true,
flags:0x00000004,
toText:function(){
var _variable = this.scope.getVariableById(this.variable);
var _text = _variable ? _variable.name : '';
if(this.variable && this.variable.indexOf('://')!==-1) {
_text = '<span class="text-info">' +this.scope.toFriendlyName(this, this.variable)+'</span>';
}
return this.getBlockIcon('C') + this.name + ' ' + _text + "<span class='text-muted small'> to <kbd class='text-warning'>" + this.makeEditable("value",'bottom','text','Enter the string to send','inline') + "</kbd></span>";
},
_getPreviousResult: function () {
var parent = null;
var prev = this.getPreviousBlock();
if(!prev || !prev._lastResult || !prev.enabled){
parent = this.getParent();
}else{
parent = prev;
}
if (parent && parent._lastResult != null) {
if (this.isArray(parent._lastResult)) {
return parent._lastResult;
} else {
return parent._lastResult;
}
}
return null;
},
/***
* Makes the assignation
* @param scope
*/
solve:function(scope,settings) {
var value = this.value;
var changed = false;
if(!value){
var _value = this.getArgs();
if(_value.length>0){
value = _value[0];
}
}
if (this.variable && value!==null){
this.onRun(this,settings);
//var _variable = scope.getVariable(this.variable).value = scope.parseExpression(this.value);
var _variable = this.variable.indexOf('://')!==-1 ? this.scope.resolveBlock(this.variable) : scope.getVariableById(this.variable);
//console.log('assign variable',settings);
var _value = this._getArg(value);
var _args = this.getArgs(settings) || [];
//console.log('run with args ' , _args);
if(!_variable){
//console.error(' no such variable : ' + this.variable);
return [];
}
var _parsed = null;
if(this.isScript(_value)){
var override = this.override || {};
_parsed = scope.parseExpression(value,null,null,null,null,null,_args || override.args);
//_parsed = scope.parseExpression(_value);
_parsed = this.replaceAll("'",'',_parsed);
//_variable.value = scope.parseExpression(_value);
//_variable.value = this.replaceAll("'",'',_variable.value);
if(_variable.value!==_parsed){
changed = true;
}
}else{
if(_args && _args.length==1 && value==null){
_value = _args[0];
}
if(_variable.value!==_value){
changed = true;
}
_variable.value = _value;
_parsed = _value;
}
_variable.set('value',_parsed);
var publish = false;
var context = this.getContext();
if(context) {
var device = context.device;
if(device && device.info && isServer && device.info.serverSide) {
if (this.flags & types.VARIABLE_FLAGS.PUBLISH_IF_SERVER) {
publish = true;
}else{
publish=false;
}
}
}
if(this.flags & types.VARIABLE_FLAGS.PUBLISH && changed){
publish = true;
}
changed && factory.publish(types.EVENTS.ON_DRIVER_VARIABLE_CHANGED,{
item:_variable,
scope:this.scope,
save:false,
block:this,
name:_variable.name,
value:_value,
publish:publish,
result:_parsed
});
this.onSuccess(this,settings);
return [];
}
},
canAdd:function(){
return null;
},
getFields:function(){
var fields = this.inherited(arguments) || this.getDefaultFields(false);
var thiz=this;
/*
fields.push(this.utils.createCI('Variable',3,this.variable,{
group:'General',
dst:'variable',
widget:{
store:new DstoreAdapter(this.scope.blockStore),
query:{
group:'basicVariables'
}
}
}));
*/
fields.push(this.utils.createCI('value',29,this.value,{
group:'General',
title:'Value',
dst:'value',
widget:{
allowACECache:true,
showBrowser:false,
showSaveButton:true,
editorOptions:{
showGutter:false,
autoSelect: false,
autoFocus: false,
hasConsole:false,
hasItemActions:function(){
return false
}
},
item:this
},
delegate:{
runExpression:function(val,run,error){
return thiz.scope.expressionModel.parse(thiz.scope,val,false,run,error);
}
}
}));
fields.push(utils.createCI('value','xcf.widgets.CommandPicker',this.variable,{
group:'Variable',
title:'Variable',
dst:'variable',
//options:this.scope.getVariablesAsOptions(),
block:this,
pickerType:'variable',
value:this.variable,
widget:{
store:this.scope.blockStore,
labelField:'name',
valueField:'id',
value:this.variable,
query:[
{
group:'basicVariables'
},
{
group:'processVariables'
}
]
}
}));
fields.push(this.utils.createCI('flags',5,this.flags,{
group:'Variable',
title:'Flags',
dst:'flags',
data:[
{
value: 0x00000002,
label: 'Publish to network',
title:"Publish to network in order to make a network variable"
},
{
value: 0x00000004,//2048
label: 'Publish if server',
title: 'Publish only on network if this is running server side'
}
],
widget:{
hex:true
}
}));
return fields;
}
});
return Module;
});

View File

@ -0,0 +1,56 @@
/** @module xblox/model/variables/VariableSwitch **/
define([
'dcl/dcl',
"xblox/model/logic/SwitchBlock",
'xide/types',
"xblox/model/logic/CaseBlock",
"xblox/model/logic/DefaultBlock",
"dojo/Deferred"
], function(dcl,SwitchBlock,types,CaseBlock,DefaultBlock,Deferred){
/**
*
* The switch command model. These kind of commands takes a existing variable and applies some comparison.
* Depending on the comparison results, the code into each case block is executed or not.
* @class module:xblox/model/variables/VariableSwitch
* @extends module:xblox/model/Block
*/
return dcl(SwitchBlock,{
declaredClass:"xblox.model.variables.VariableSwitch",
name:'Switch on Variable',
icon:'',
variable:"PowerState",
toText:function(){
var _variable = this.scope.getVariableById(this.variable);
var _text = _variable ? _variable.name : '';
return this.getBlockIcon('H') + this.name + ' ' + _text;
},
// standard call for editing
getFields:function(){
//options:this.scope.getVariablesAsOptions(),
var fields = this.getDefaultFields(false,false);
fields = fields.concat([
this.utils.createCI('Variable',3,this.variable,
{
group:'General',
widget:{
store:this.scope.blockStore,
labelField:'name',
valueField:'id',
query:[
{
group:'basicVariables'
},
{
group:'processVariables'
}
]
},
dst:'variable'
}
)
]);
return fields;
}
});
});

View File

@ -0,0 +1,29 @@
{
"name": "xblox",
"version": "1.0.0-dev",
"dependencies": {
},
"devDependencies": {
"intern": "2.2",
"intern-geezer": "latest",
"grunt": "~0.4.5",
"grunt-contrib-jshint": "~0.6.3",
"grunt-contrib-clean": "~0.6.0",
"grunt-contrib-less": "~0.8.3",
"grunt-contrib-uglify": "~0.2.2",
"jsdoc-amddcl": "git://github.com/mc007/jsdoc-amddcl#master"
},
"licenses": [
{
"type": "BSD",
"url": "https://github.com/mc007/xgrid/blob/master/LICENSE"
}
],
"bugs": "https://github.com/mc007/xgrid/issues",
"dojoBuild": "layer.profile.js",
"private": true,
"directories": {
"doc": "./doc",
"lib": "."
}
}

View File

@ -0,0 +1,168 @@
{
"class":"cmx.types.Resources",
"includes":[
],
"items":[
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/lib/external/moment-with-langs.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/xfileTheme/claro/document.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/css/elusive-icons/elusive-webfont.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/css/elusive-icons/elusive-webfont-ie7.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/xfileTheme/claro/claro.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/css/Widgets.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/css/xasCommons.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/css/xasdijitOverride.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/css/xfile/main.css",
"enabled":true,
"preventCache":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/xfile/ext/cm/lib/codemirror.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/css/xbox/xboxAdmin.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/lib/dijit/icons/editorIcons.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/lib/dgrid/css/dgrid.css",
"enabled":false
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/lib/dgrid/css/columnset.css",
"enabled":false
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/lib/cbtree/icons/networkIcons.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/lib/dojox/layout/resources/ToggleSplitter.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/xfile/ext/cm/lib/codemirror.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/xfile/ext/cm/addon/mode/loadmode.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/xfile/ext/cm/addon/dialog/dialog.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/xfile/ext/cm/addon/dialog/dialog.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/xfile/ext/cm/addon/search/searchcursor.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/xfile/ext/cm/addon/search/search.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/lib/external/ckeditorfull/ckeditor.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"HTML",
"url":"%XASWEB%/lib/xide/templates/body.html",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-SCRIPT-TAG",
"url":"%XASWEB%/lib/xide/Header.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"http://code.jquery.com/ui/1.10.3/themes/%JQUERY_THEME%/jquery-ui.css",
"enabled":false
}
]
}

View File

@ -0,0 +1,47 @@
{
"class":"cmx.types.Resources",
"distDir":"%XASWEB%/%XLIB%/xbox",
"includes":[
{
"class":"cmx.types.ResourceInclude",
"path":"%XLIB%/resourceConfigs/commons-release.json"
}
],
"items":[
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/xbox/xbox/resources/app.css"
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/%XLIB%/dojox/widget/Wizard/Wizard.css"
},
{
"class":"cmx.types.Resource",
"type":"CSS",
"url":"%XASWEB%/xfile/ext/cm/addon/dialog/dialog.css",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/xfile/ext/cm/addon/dialog/dialog.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/xfile/ext/cm/addon/search/searchcursor.js",
"enabled":true
},
{
"class":"cmx.types.Resource",
"type":"JS-HEADER-INCLUDE",
"url":"%XASWEB%/xfile/ext/cm/addon/search/search.js",
"enabled":true
}
]
}

View File

@ -0,0 +1,31 @@
/**
* This file is used to reconfigure parts of the loader at runtime for this application. Weve put this extra
* configuration in a separate file, instead of adding it directly to index.html, because it contains options that
* can be shared if the application is run on both the client and the server.
*
* If you arent planning on running your app on both the client and the server, you could easily move this
* configuration into index.html (as a dojoConfig object) if it makes your life easier.
*/
require({
// The base path for all packages and modules. If you don't provide this, baseUrl defaults to the directory
// that contains dojo.js. Since all packages are in the root, we just leave it blank. (If you change this, you
// will also need to update app.profile.js).
/*baseUrl: '../',*/
// A list of packages to register. Strictly speaking, you do not need to register any packages,
// but you can't require "app" and get app/main.js if you do not register the "app" package (the loader will look
// for a module at <baseUrl>/app.js instead). Unregistered packages also cannot use the packageMap feature, which
// might be important to you if you need to relocate dependencies. TL;DR, register all your packages all the time:
// it will make your life easier.
packages: [
// If you are registering a package that has an identical name and location, you can just pass a string
// instead, and it will configure it using that string for both the "name" and "location" properties. Handy!
'xblox'
],
// This is a hack. In order to allow app/main and app/run to be built together into a single file, a cache key needs
// to exist here in order to force the loader to actually process the other modules in the file. Without this hack,
// the loader will think that code for app/main has not been loaded yet and will try to fetch it again, resulting in
// a needless extra HTTP request.
cache: {}
// Require 'app'. This loads the main application file, app/main.js.
}, ['xblox']);

View File

@ -0,0 +1,20 @@
define([ 'require' ], function (require) {
return {
// Expose this module as an AMD plugin which will wait until the
// link element has loaded the stylesheet.
// (This uses least-common-denominator logic from xstyle/core/load-css.)
load: function (id, parentRequire, loaded) {
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = require.toUrl('../../css/dgrid.css');
document.getElementsByTagName('head')[0].appendChild(link);
var interval = setInterval(function () {
if (link.style) {
clearInterval(interval);
loaded();
}
}, 15);
}
};
});

View File

@ -0,0 +1,3 @@
define([
'intern/node_modules/dojo/has!host-browser?./core/createDestroy'
], function () {});

View File

@ -0,0 +1,48 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/query',
'dgrid/OnDemandList',
'dgrid/test/data/createSyncStore',
'dgrid/test/data/genericData'
], function (test, assert, query, OnDemandList, createSyncStore, genericData) {
test.suite('OnDemandList with zero rowHeight', function () {
var list;
var store = createSyncStore({ data: genericData });
test.beforeEach(function () {
list = new OnDemandList({
collection: store,
renderRow: function () {
return document.createElement('div');
}
});
document.body.appendChild(list.domNode);
list.startup();
});
test.afterEach(function () {
list.destroy();
});
test.test('_processScroll should bail out if rowHeight is 0', function () {
// Bailing out with rowHeight === 0 is important because otherwise
// _processScroll has the potential to loop infinitely.
// _processScroll will call _trackError if it doesn't bail out and
// thinks it should render more items, so replace it to fail the test
list._trackError = function () {
throw new assert.AssertionError({
message: '_processScroll with 0 rowHeight should not result in any query'
});
};
list._processScroll();
});
test.test('refresh with zero rowHeight should only render minRowsPerPage rows', function () {
// This tests GitHub issue #965.
assert.strictEqual(query('.dgrid-row', list.contentNode).length, list.minRowsPerPage);
});
});
});

View File

@ -0,0 +1,302 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/_base/lang',
'dojo/_base/declare',
'dojo/aspect',
'dojo/Deferred',
'dgrid/OnDemandList',
// column.set can't be tested independently from a Grid,
// so we are testing through OnDemandGrid for now.
'dgrid/OnDemandGrid',
'dgrid/ColumnSet',
'dgrid/test/data/createSyncStore',
'dgrid/test/data/genericData',
'dojo/domReady!'
], function (test, assert, lang, declare, aspect, Deferred,
OnDemandList, OnDemandGrid, ColumnSet, createSyncStore, genericData) {
// Helper method used to set column set() methods for various grid compositions
function testSetMethod(grid, dfd) {
var store = createSyncStore({ data: genericData });
grid.set('collection', store);
document.body.appendChild(grid.domNode);
grid.startup();
var changes = [
{
objectId: 0,
field: 'col1',
newValue: 'sleepy',
expectedSavedValue: 'SLEEPY'
},
{
objectId: 1,
field: 'col3',
newValue: 'dopey',
expectedSavedValue: 'DOPEY'
},
{
objectId: 2,
field: 'col4',
newValue: 'rutherford',
expectedSavedValue: 'RUTHERFORD'
}
],
len = changes.length,
i,
change;
for (i = 0; i < len; i++) {
change = changes[i];
grid.updateDirty(change.objectId, change.field, change.newValue);
}
grid.save().then(
dfd.callback(function () {
for (var i = 0, change; i < changes.length; i++) {
change = changes[i];
assert.strictEqual(store.getSync(change.objectId)[change.field],
change.expectedSavedValue);
}
}),
lang.hitch(dfd, 'reject')
);
return dfd;
}
// the set() method to use for column.set() tests
function sampleSetMethod(item) {
return item[this.field].toUpperCase();
}
test.suite('_StoreMixin', function () {
var grid; // Reused for each test and common afterEach logic
test.afterEach(function () {
grid.destroy();
});
test.suite('_StoreMixin#_setCollection', function () {
var store;
test.beforeEach(function () {
store = createSyncStore({ data: genericData });
grid = new OnDemandList({
collection: store
});
document.body.appendChild(grid.domNode);
grid.startup();
});
test.test('null', function () {
assert.isDefined(grid._renderedCollection,
'grid._renderedCollection should be defined');
assert.notStrictEqual(grid.contentNode.children.length, 0,
'grid.contentNode should contain children when refreshing with a store');
grid.set('collection', null);
assert.isNull(grid._renderedCollection,
'grid._renderedCollection should be null after setting collection to null');
assert.strictEqual(grid.contentNode.children.length, 1,
'grid.contentNode should contain one child when refreshing with a null collection');
assert.strictEqual(grid.contentNode.children[0], grid.noDataNode,
'grid.contentNode should contain the noDataNode');
grid.set('collection', store);
assert.isNotNull(grid._renderedCollection,
'grid._renderedCollection should not be null after setting collection to store again');
assert.notStrictEqual(grid.contentNode.children.length, 0,
'grid.contentNode should contain children when refreshing with a store');
});
test.test('dirty data preservation/cleanup', function () {
grid.updateDirty(0, 'col1', 'modified');
assert.isDefined(grid.dirty[0], 'Dirty hash should contain entry for item 0 after updateDirty');
grid.set('sort', 'col3');
assert.isDefined(grid.dirty[0], 'Dirty hash should still contain entry for item 0 after sort');
grid.set('collection', store.filter({ col2: false }));
assert.isDefined(grid.dirty[0], 'Dirty hash should still contain entry for item 0 after filter');
grid.set('collection', createSyncStore({ data: genericData }));
assert.isUndefined(grid.dirty[0],
'Dirty hash should be cleared after setting collection based on different store');
});
});
test.test('_StoreMixin#_onNotification', function () {
var store = createSyncStore({ data: genericData }),
notificationCount = 0,
lastNotificationEvent = null;
grid = new OnDemandList({
collection: store,
_onNotification: function (rows, event) {
notificationCount++;
lastNotificationEvent = event;
}
});
document.body.appendChild(grid.domNode);
grid.startup();
var item = store.getSync(1);
store.removeSync(item.id);
assert.equal(notificationCount, 1);
assert.isNotNull(lastNotificationEvent);
assert.equal(lastNotificationEvent.type, 'delete');
assert.equal(lastNotificationEvent.id, item.id);
lastNotificationEvent = null;
store.addSync(item);
assert.equal(notificationCount, 2);
assert.isNotNull(lastNotificationEvent);
assert.equal(lastNotificationEvent.type, 'add');
assert.equal(lastNotificationEvent.target, item);
item.col1 = 'changed';
lastNotificationEvent = null;
store.putSync(item);
assert.equal(notificationCount, 3);
assert.isNotNull(lastNotificationEvent);
assert.equal(lastNotificationEvent.type, 'update');
assert.equal(lastNotificationEvent.target, item);
});
test.suite('_StoreMixin#_trackError', function () {
var emittedErrorCount,
lastEmittedError,
expectedValue;
function expectedSuccess(actualValue) {
assert.strictEqual(actualValue, expectedValue,
'Resolved promise should yield expected value');
assert.strictEqual(emittedErrorCount, 0,
'dgrid-error event should not have fired');
}
function expectedError(error) {
assert.strictEqual(error.message, expectedValue,
'An error with the expected message should be thrown');
assert.strictEqual(emittedErrorCount, 1,
'A dgrid-error event should have fired');
assert.strictEqual(lastEmittedError, error,
'The error should be accessible from the dgrid-error event');
}
function unexpectedSuccess() {
throw new Error('Unexpected resolution');
}
test.beforeEach(function () {
grid = new OnDemandList();
grid.on('dgrid-error', function (event) {
emittedErrorCount++;
lastEmittedError = event.error;
});
emittedErrorCount = 0;
lastEmittedError = null;
});
test.test('_StoreMixin#_trackError - sync value', function () {
expectedValue = 'expected';
return grid._trackError(function () {
return expectedValue;
}).then(expectedSuccess);
});
test.test('_StoreMixin#_trackError - async value', function () {
expectedValue = 'expected-async';
return grid._trackError(function () {
var dfd = new Deferred();
setTimeout(function () {
dfd.resolve(expectedValue);
}, 100);
return dfd.promise;
}).then(expectedSuccess);
});
test.test('_StoreMixin#_trackError - sync error', function () {
expectedValue = 'expected-error';
return grid._trackError(function () {
throw new Error(expectedValue);
}).then(unexpectedSuccess, expectedError);
});
test.test('_StoreMixin#_trackError - async error', function () {
// async error
expectedValue = 'expected-async-error';
return grid._trackError(function () {
var dfd = new Deferred();
setTimeout(function () {
dfd.reject(new Error(expectedValue));
}, 100);
return dfd.promise;
}).then(unexpectedSuccess, expectedError);
});
});
test.suite('_StoreMixin#save / column.set tests', function () {
test.test('column.set in subRows', function () {
grid = new OnDemandGrid({
subRows: [
[
{ label: 'Column 1', field: 'col1', set: sampleSetMethod },
{ label: 'Column 2', field: 'col2', sortable: false },
{ label: 'Column 1', field: 'col1', rowSpan: 2 },
{ label: 'Column 4', field: 'col4', set: sampleSetMethod }
],
[
{ label: 'Column 3', field: 'col3', colSpan: 2, set: sampleSetMethod },
{ label: 'Column 5', field: 'col5' }
]
]
});
testSetMethod(grid, this.async());
});
test.test('column.set in columnSets', function () {
grid = new (declare([OnDemandGrid, ColumnSet]))({
columnSets: [
[
[
{ label: 'Column 1', field: 'col1', set: sampleSetMethod },
{ label: 'Column 2', field: 'col2', sortable: false }
],
[
{label: 'Column 3', field: 'col3', colSpan: 2, set: sampleSetMethod }
]
],
[
[
{ label: 'Column 1', field: 'col1', rowSpan: 2 },
{ label: 'Column 4', field: 'col4', set: sampleSetMethod }
],
[
{ label: 'Column 5', field: 'col5' }
]
]
]
});
testSetMethod(grid, this.async());
});
});
test.suite('Effect of set-before-startup on refresh calls', function(){
test.test('set(\'collection\') before startup should not cause superfluous refresh', function () {
var numCalls = 0;
grid = new OnDemandList();
aspect.before(grid, 'refresh', function () {
numCalls++;
});
grid.set('collection', createSyncStore(genericData));
document.body.appendChild(grid.domNode);
grid.startup();
assert.strictEqual(numCalls, 1, 'refresh should only have been called once');
});
});
});
});

View File

@ -0,0 +1,335 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/_base/declare',
'dojo/dom-style',
'dojo/dom-construct',
'dojo/query',
'dgrid/Grid',
'dgrid/ColumnSet',
'dgrid/extensions/ColumnHider',
'dgrid/extensions/ColumnResizer',
'dgrid/util/misc'
], function (test, assert, declare, domStyle, domConstruct, query,
Grid, ColumnSet, ColumnHider, ColumnResizer, miscUtil) {
test.suite('addCssRule', function () {
var testDiv;
var grid;
// Setup / teardown
test.before(function () {
testDiv = domConstruct.create('div', null, document.body);
});
test.after(function () {
domConstruct.destroy(testDiv);
});
test.afterEach(function () {
if (grid) {
grid.destroy();
}
});
// Tests
test.test('addCssRule + remove', function () {
testDiv.className = 'foo';
// cache original style and update .foo
var origStyle = domStyle.getComputedStyle(testDiv).fontSize,
rule = miscUtil.addCssRule('.foo', 'font-size: 8px;');
// check updated font size was applied
assert.strictEqual('8px', domStyle.getComputedStyle(testDiv).fontSize,
'Node with matching class has expected font-size');
// remove the rule & make sure the computed style is good again
rule.remove();
assert.strictEqual(origStyle, domStyle.getComputedStyle(testDiv).fontSize,
'Node with matching class no longer has font-size from removed rule');
testDiv.className = '';
});
test.test('addCssRule get/set/remove', function () {
testDiv.className = 'bar';
// cache original style and update .foo
var origStyle = domStyle.getComputedStyle(testDiv).fontSize,
rule = miscUtil.addCssRule('.bar', 'font-size: 8px;');
// check updated font size was applied
assert.strictEqual('8px', rule.get('fontSize'),
'rule.get(\'fontSize\') reports expected value');
// update the font size
rule.set('fontSize', '9px');
// verify that the size updated
assert.strictEqual('9px', domStyle.getComputedStyle(testDiv).fontSize,
'Node with matching class has expected font-size after set');
// make sure rule.get works the same
assert.strictEqual('9px', rule.get('fontSize'),
'rule.get(\'fontSize\') reports expected value after set');
// remove the rule & make sure it updates
rule.remove();
assert.strictEqual(origStyle, domStyle.getComputedStyle(testDiv).fontSize,
'Node with matching class no longer has font-size from removed rule');
testDiv.className = '';
});
test.test('add/remove multiple rules in mixed order', function () {
var origStyle = domStyle.getComputedStyle(testDiv).fontSize,
rules = [],
expected = { // hash containing test classes / values
foo: '7px',
bar: '8px',
baz: '9px'
},
cls;
function check() {
// Test that all expected styles hold
var cls;
for (cls in expected) {
testDiv.className = cls;
assert.strictEqual(expected[cls], domStyle.getComputedStyle(testDiv).fontSize,
'Node with class ' + cls + ' has expected font-size');
}
testDiv.className = '';
}
// Create rules and maintain references to returned objects
for (cls in expected) {
rules.push(miscUtil.addCssRule('.' + cls, 'font-size: ' + expected[cls] + ';'));
}
// Do initial check, then remove rules one by one, out of order,
// updating the hash and checking each time along the way
check();
rules[2].remove();
expected.baz = origStyle;
check();
rules[0].remove();
expected.foo = origStyle;
check();
rules[1].remove();
expected.bar = origStyle;
check();
});
test.test('addCssRule via dgrid APIs', function () {
var values = ['7px', '8px'],
origValues;
function createGrid(cleanAddedRules) {
grid = new Grid({
id: 'my.grid', // test escaping of CSS identifiers from methods
columns: {
name: 'Name',
value: 'Value',
comment: 'Comment' // unstyled buffer
},
cleanAddedRules: cleanAddedRules
});
document.body.appendChild(grid.domNode);
grid.startup();
}
function addRules() {
var rules = [];
rules.push(grid.addCssRule('.field-value', 'font-size: ' + values[0] + ';'));
rules.push(grid.styleColumn('name', 'font-size: ' + values[1] + ';'));
return rules;
}
function getStyles() {
return [
domStyle.getComputedStyle(query('.field-value', grid.domNode)[0]).fontSize,
domStyle.getComputedStyle(query('.dgrid-column-name', grid.domNode)[0]).fontSize
];
}
function checkRules(expected) {
var actual = getStyles(grid);
assert.strictEqual(expected[0], actual[0],
'Style modified via addCssRule has expected value');
assert.strictEqual(expected[1], actual[1],
'Style modified via styleColumn has expected value');
}
// Create grid for the first time
createGrid(true);
// Collect original style values for later cleanup check
origValues = getStyles();
// Add rules and make sure they applied as expected
addRules();
checkRules(values);
// Destroy the grid, which should remove the style rules
grid.destroy();
// Create grid for the second time, with cleanAddedRules: false
createGrid(false);
// Before adding styles, make sure the ones from last time were removed
checkRules(origValues);
// Add rules and check again;
// store the rules for tearDown since they won't be cleaned up
this.rules = addRules();
checkRules(values);
// Destroy the grid, which should *not* remove the style rules
grid.destroy();
// Create grid for the third time
createGrid(true);
// Check that styles from last time still exist
checkRules(values);
// Destroy the grid (which won't remove the styles from last time,
// since no handles were added to this exact instance)
grid.destroy();
// Clean up rule litter from cleanAddedRules: false test
var i;
for (i in this.rules) {
this.rules[i].remove();
}
});
test.test('Grid columns as array (numeric IDs)', function () {
grid = new Grid({
columns: [
{ field: 'name' },
{ field: 'value' }
]
});
document.body.appendChild(grid.domNode);
grid.startup();
assert.doesNotThrow(function () {
grid.styleColumn(0, 'font-size: 8px;');
}, null, 'styleColumn with numeric column ID should not throw error');
assert.strictEqual('8px', domStyle.getComputedStyle(query('.dgrid-cell')[0]).fontSize,
'Column cell should have expected style');
});
});
test.suite('CSS escaping of column IDs via dgrid APIs', function () {
var grid;
test.suite('Grid columns and columnsets', function () {
function makeTest(func, id) {
return function () {
assert.doesNotThrow(function () {
grid[func](id, 'font-size: 8px;');
}, null, func + ' should escape special characters and not throw error');
assert.strictEqual('8px', domStyle.getComputedStyle(query('.dgrid-cell')[0]).fontSize,
'Column cell should have expected style');
};
}
test.beforeEach(function () {
grid = new (declare([Grid, ColumnSet]))({
id: 'i:d',
columnSets: [[[
{ field: 'foo', id: 'col:umn' }
]]]
});
document.body.appendChild(grid.domNode);
grid.startup();
});
test.afterEach(function () {
grid.destroy();
});
test.test('styleColumn', makeTest('styleColumn', 'col:umn'));
// Currently ColumnSet IDs can't be customized so that isn't really an issue,
// but this still tests escaping the grid ID
test.test('styleColumnSet', makeTest('styleColumnSet', '0'));
});
test.suite('ColumnHider', function () {
var ColumnHiderGrid = declare([Grid, ColumnHider]);
test.afterEach(function () {
grid.destroy();
});
test.test('Hiding column after construction', function () {
assert.doesNotThrow(function () {
grid = new ColumnHiderGrid({
id: 'i:d',
columns: [
{ field: 'foo', id: 'col:umn' }
]
});
}, null, 'ColumnHider should not throw error during construction');
document.body.appendChild(grid.domNode);
grid.startup();
assert.doesNotThrow(function () {
grid._hideColumn('col:umn');
}, null, '_hideColumn should escape special characters and not throw error');
assert.strictEqual(query('.dgrid-cell')[0].offsetHeight, 0,
'Column should be hidden');
});
test.test('Hiding column during construction', function () {
assert.doesNotThrow(function () {
grid = new ColumnHiderGrid({
id: 'i:d',
columns: [
{ field: 'foo', id: 'col:umn', hidden: true }
]
});
}, null, 'ColumnHider should not throw error during construction');
document.body.appendChild(grid.domNode);
grid.startup();
assert.strictEqual(query('.dgrid-cell')[0].offsetHeight, 0,
'Column should be hidden');
});
});
test.suite('ColumnResizer', function () {
var ColumnResizerGrid = declare([Grid, ColumnResizer]);
test.afterEach(function () {
grid.destroy();
});
test.test('Resizing column after construction', function () {
assert.doesNotThrow(function () {
grid = new ColumnResizerGrid({
id: 'i:d',
columns: [
{ field: 'foo', id: 'col:umn' },
{ field: 'bar' }
]
});
}, null, 'ColumnResizer should not throw error during construction');
document.body.appendChild(grid.domNode);
grid.startup();
assert.doesNotThrow(function () {
grid.resizeColumnWidth('col:umn', 100);
}, null, 'resizeColumnWidth should escape special characters and not throw error');
assert.strictEqual(query('.dgrid-cell')[0].offsetWidth, 100,
'Column should be the expected width');
});
test.test('Resizing column during construction', function () {
assert.doesNotThrow(function () {
grid = new ColumnResizerGrid({
id: 'i:d',
columns: [
{ field: 'foo', id: 'col:umn', width: 100 },
{ field: 'bar' }
]
});
}, null, 'ColumnResizer should not throw error during construction');
document.body.appendChild(grid.domNode);
grid.startup();
assert.strictEqual(query('.dgrid-cell')[0].offsetWidth, 100,
'Column should be the expected width');
});
});
});
});

View File

@ -0,0 +1,102 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/_base/declare',
'dojo/query',
'dgrid/Grid',
'dgrid/ColumnSet',
'dgrid/test/data/orderedData'
], function (test, assert, declare, query, Grid, ColumnSet, orderedData) {
var grid;
function runClassNameTests() {
var domNode = grid.domNode,
node;
assert.strictEqual(query('.dgrid-cell.field-order', domNode).length, 10,
'Each row (including header) should contain a cell with the field-order class');
assert.strictEqual(query('.dgrid-cell.field-name', domNode).length, 10,
'Each row (including header) should contain a cell with the field-name class');
assert.strictEqual(query('.dgrid-cell.field-description', domNode).length, 10,
'Each row (including header) should contain a cell with the field-description class');
assert.strictEqual(query('.dgrid-cell.field-name.name-column.main-column', domNode).length, 10,
'Each row\'s (including header\'s) field-name cell should have the name-column and main-column classes');
assert.strictEqual(query('.dgrid-cell.field-description.desc-row', domNode).length, 9,
'Each body row\'s description cell should also have the desc-row class');
node = query('.dgrid-header .dgrid-cell.field-description', domNode)[0];
assert.strictEqual(node.className.indexOf('undefined'), -1,
'Header row\'s description cell should NOT contain \'undefined\' due to className returning \'\'');
assert.isTrue(query('.dgrid-content .dgrid-cell.field-description', domNode).every(function (cell) {
return (/desc-\w+ desc-row/).test(cell.className);
}),
'Each body row\'s description cell has two desc-* classes (one being desc-row)');
}
test.suite('columns', function () {
test.afterEach(function () {
grid.destroy();
});
test.test('className property', function () {
grid = new Grid({
columns: {
order: 'Order',
name: {
label: 'Name',
className: 'name-column main-column'
},
description: {
label: 'Description',
className: function (object) {
return object ?
'desc-' + object.name.replace(/ /g, '') + ' desc-row' :
'';
}
}
}
});
document.body.appendChild(grid.domNode);
grid.startup();
grid.renderArray(orderedData.items);
runClassNameTests();
});
});
test.suite('columnSets', function () {
test.afterEach(function () {
grid.destroy();
});
test.test('className property', function () {
grid = new (declare([Grid, ColumnSet]))({
columnSets: [
[[
{ field: 'order', label: 'Order' },
{
field: 'name',
label: 'Name',
className: 'name-column main-column'
}
]], [[
{
field: 'description',
label: 'Description',
className: function (object) {
return object ?
'desc-' + object.name.replace(/ /g, '') + ' desc-row' :
'';
}
}
]]
]
});
document.body.appendChild(grid.domNode);
grid.startup();
grid.renderArray(orderedData.items);
runClassNameTests();
});
});
});

View File

@ -0,0 +1,26 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/_base/declare',
'dojo/on',
'dgrid/List',
'dgrid/OnDemandList',
'dstore/Memory'
], function (test, assert, declare, on, List, OnDemandList, Memory) {
test.suite('createDestroy', function () {
test.test('no params list', function () {
/*
assert.strictEqual(list.contentNode.children.length, 3,
'List\'s contentNode has expected number of children after renderArray');
list.destroy();
assert.notStrictEqual(document.body, list.parentNode,
'List is removed from body after destroy');
*/
});
});
});

View File

@ -0,0 +1,95 @@
define([
'intern!tdd',
'intern/chai!assert',
'dgrid/List',
'dgrid/Grid',
'dgrid/GridFromHtml',
'dojo/_base/array',
'dojo/parser',
'dojo/dom-class',
'dojo/dom-construct',
'dojo/text!../resources/setClass.html'
], function (test, assert, List, Grid, GridFromHtml, arrayUtil, parser, domClass, domConstruct, gridTemplate) {
test.suite('setClass', function () {
// Tests
test.test('Lists + initially-defined classes', function () {
function renderRow(item) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(item.name));
return div;
}
// Build three lists
var listC = window.listC = new List({
'class': 'c',
renderRow: renderRow
}),
listCN = window.listCN = new List({
className: 'cn',
renderRow: renderRow
}),
listDOM = window.listDOM = new List({
renderRow: renderRow
}, domConstruct.create('div', {'class': 'dom'}));
// Check the classes on each List.domNode
assert.ok(domClass.contains(listC.domNode, 'c'));
assert.ok(domClass.contains(listCN.domNode, 'cn'));
assert.ok(domClass.contains(listDOM.domNode, 'dom'));
// Destroy the lists after performing the tests
listC.destroy();
listCN.destroy();
listDOM.destroy();
});
test.test('Grids + initially-defined classes', function () {
// Build three grids
var columns = {
order: 'step', // give column a custom name
name: {},
description: { label: 'what to do', sortable: false }
},
gridC = window.gridC = new Grid({
'class': 'c',
columns: columns
}),
gridCN = window.gridCN = new Grid({
'class': 'cn',
columns: columns
}),
gridDOM = window.gridDOM = new Grid({
columns: columns
}, domConstruct.create('div', { 'class': 'dom' }));
// Check the classes on each List.domNode
assert.ok(domClass.contains(gridC.domNode, 'c'));
assert.ok(domClass.contains(gridCN.domNode, 'cn'));
assert.ok(domClass.contains(gridDOM.domNode, 'dom'));
// Destroy the grids after performing the tests
gridC.destroy();
gridCN.destroy();
gridDOM.destroy();
});
test.test('Declarative Grid + initially-defined class', function () {
/* global gridDecl */
// Create markup for a grid to be declaratively parsed
var node = domConstruct.create('div', {
innerHTML: gridTemplate
});
// Expose GridFromHtml via a global namespace for parser to use
window.dgrid = { GridFromHtml: GridFromHtml };
parser.parse(node);
// Make sure the expected class exists on the parsed instance
assert.ok(domClass.contains(gridDecl.domNode, 'dom'));
// Destroy the grid after performing the test
gridDecl.destroy();
});
});
});

View File

@ -0,0 +1,178 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/_base/declare',
'dojo/aspect',
'dojo/on',
'dgrid/Grid',
'dgrid/OnDemandGrid',
'dgrid/extensions/Pagination',
'dgrid/test/data/errorStores',
'dgrid/test/data/createSyncStore',
'dgrid/test/data/createAsyncStore',
'dgrid/test/data/genericData',
'dgrid/test/data/testPerformanceStore',
'../addCss!'
], function (test, assert, declare, aspect, on, Grid, OnDemandGrid, Pagination,
errorStores, createSyncStore, createAsyncStore, genericData, testPerformanceStore) {
var PaginationGrid = declare([Grid, Pagination]);
var grid;
var handles = [];
// Common reusable function for tests
function storeTest(CustomGrid, store, expectSuccess, dfd) {
var expectedEvent = expectSuccess ? 'dgrid-refresh-complete' : 'dgrid-error',
unexpectedEvent = !expectSuccess ? 'dgrid-refresh-complete' : 'dgrid-error';
grid = new CustomGrid({
collection: store
});
// Hook up event handler before calling startup, to be able to
// test both synchronous and asynchronous stores
handles.push(on.once(grid, expectedEvent, function () {
// After receiving the expected event, perform a refresh,
// to also test resolution/rejection of the promise it returns.
grid.refresh().then(function () {
dfd[expectSuccess ? 'resolve' : 'reject']();
}, function () {
dfd[!expectSuccess ? 'resolve' : 'reject']();
});
}));
// Also hook up the opposite event handler, to signal failure
handles.push(on.once(grid, unexpectedEvent, function () {
dfd.reject(new Error('Expected ' + expectedEvent + ' to fire, but ' +
unexpectedEvent + ' fired instead.'));
}));
document.body.appendChild(grid.domNode);
grid.startup();
return dfd;
}
function createReleaseRangeGrid(CustomGrid) {
grid = new CustomGrid({
collection: testPerformanceStore,
columns: {
id: 'ID'
},
sort: 'id'
});
document.body.appendChild(grid.domNode);
grid.startup();
}
function testReleaseRange(visibleId) {
var numInserts = 0;
handles.push(aspect.after(grid, 'insertRow', function () {
numInserts++;
}, true));
testPerformanceStore.putSync(testPerformanceStore.getSync(0));
assert.strictEqual(numInserts, 0,
'Item from unrendered range should not be added to grid when updated');
testPerformanceStore.putSync(testPerformanceStore.getSync(visibleId || 19999));
assert.strictEqual(numInserts, 1,
'Item from rendered range should be re-added to grid when updated');
}
test.suite('stores', function () {
// Setup / teardown
test.afterEach(function () {
for (var i = handles.length; i--;) {
handles[i].remove();
}
handles = [];
grid.destroy();
});
var store = createSyncStore({ data: genericData }),
asyncStore = createAsyncStore({ data: genericData });
test.test('OnDemandGrid + sync store', function () {
storeTest(OnDemandGrid, store, true, this.async());
});
test.test('OnDemandGrid + async store', function () {
storeTest(OnDemandGrid, asyncStore, true, this.async());
});
test.test('OnDemandGrid + async store w/ error', function () {
storeTest(OnDemandGrid, errorStores.asyncFetch, false, this.async());
});
test.test('OnDemandGrid + async store w/ total error', function () {
storeTest(OnDemandGrid, errorStores.asyncFetchTotal, false, this.async());
});
test.test('OnDemandGrid observes/releases ranges appropriately', function () {
var dfd = this.async();
createReleaseRangeGrid(OnDemandGrid);
// Since _processScroll gets called on a debounce, need to wait for it
handles.push(aspect.after(grid, '_processScroll', dfd.callback(function () {
testReleaseRange();
}), true));
grid.scrollTo({ y: grid.bodyNode.scrollHeight });
return dfd;
});
test.test('PaginationGrid + sync store', function () {
storeTest(PaginationGrid, store, true, this.async());
});
test.test('PaginationGrid + async store', function () {
storeTest(PaginationGrid, asyncStore, true, this.async());
});
test.test('PaginationGrid + async store w/ error', function () {
storeTest(PaginationGrid, errorStores.asyncFetch, false, this.async());
});
test.test('Pagination observes/releases ranges appropriately', function () {
createReleaseRangeGrid(PaginationGrid);
grid.gotoPage(2);
testReleaseRange(10);
});
});
test.suite('Async empty stores', function () {
test.afterEach(function () {
grid.destroy();
});
var emptyStore = createAsyncStore({ data: [] });
function createTest(Grid) {
return function () {
var dfd = this.async(1000);
grid = new Grid({
columns: {
id: 'ID'
},
collection: emptyStore
});
document.body.appendChild(grid.domNode);
grid.startup();
grid.on('dgrid-error', function () {
dfd.reject('dgrid-error should not be emitted on consecutive synchronous refresh');
});
grid.refresh().then(function () {
dfd.resolve();
});
};
}
test.test('OnDemandGrid consecutive refresh with async empty store (#1065)',
createTest(OnDemandGrid));
test.test('Pagination consecutive refresh with async empty store',
createTest(PaginationGrid));
});
});

View File

@ -0,0 +1,388 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/_base/declare',
'dojo/aspect',
'dojo/dom-class',
'dojo/query',
'../../data/createSyncStore',
'dgrid/OnDemandList'
], function (test, assert, declare, aspect, domClass, query, createSyncStore, OnDemandList) {
var widget,
storeCounter = 0;
function destroyWidget() {
if (widget) {
widget.destroy();
widget = null;
}
}
function indexToId(index) {
return (index + 1) * 10;
}
function createItem(index) {
var id = indexToId(index);
return { id: id, value: 'Value ' + id + ' / Store ' + storeCounter };
}
function createData(numStoreItems) {
var data = [];
for (var i = 0; i < numStoreItems; i++) {
data.push(createItem(i));
}
return data;
}
function createStore(numStoreItems) {
storeCounter++;
return createSyncStore({
data: createData(numStoreItems)
});
}
function renderRow(object) {
var div = document.createElement('div');
div.appendChild(document.createTextNode(object.value));
return div;
}
function createList(numStoreItems, itemsPerQuery, overlap, shouldTrackCollection) {
widget = new OnDemandList({
collection: createStore(numStoreItems),
minRowsPerPage: itemsPerQuery,
maxRowsPerPage: itemsPerQuery,
queryRowsOverlap: overlap,
renderRow: renderRow,
shouldTrackCollection: shouldTrackCollection !== false,
sort: 'id'
});
document.body.appendChild(widget.domNode);
widget.startup();
}
function itemTest(itemAction, index, numToModify, backwards) {
// Creates a single test case for performing an action on numToModify rows/items.
var description = itemAction.actionName + ' ' + numToModify + ' item' + (numToModify > 1 ? 's' : '') +
' starting at index ' + index + ', in ' + (backwards ? 'decreasing' : 'increasing') + ' order';
numToModify = numToModify || 1;
test.test(description, function () {
var i,
cnt,
step = function () {
cnt++;
backwards ? i-- : i++;
},
tmp,
expectedValues = [],
msgPrefix;
function testRow(element, i) {
var expectedValue = expectedValues[i];
if (expectedValue == null || expectedValue.deleted) {
assert.isTrue(element == null, msgPrefix + 'row at index ' + i + ' should not be found');
}
else {
expectedValue = expectedValue.value;
assert.isTrue(element != null,
msgPrefix + 'row at index ' + i + ' with an expected value of "' +
expectedValue + '" is missing');
assert.strictEqual(expectedValue, element.innerHTML,
msgPrefix + element.innerHTML + ' should be ' + expectedValue);
}
}
// Perform the actions and update the array of expected values.
expectedValues = createData(widget.collection.data.length);
for (i = index, cnt = 0; cnt < numToModify; step()) {
itemAction(indexToId(i), expectedValues);
}
// Use the dgrid widget API to test if the action was performed properly.
msgPrefix = 'dgrid API: ';
tmp = [];
for (i = 0; i < expectedValues.length; i++) {
var expectedValue = expectedValues[i],
expectedId = expectedValue.id;
testRow(widget.row(expectedId).element, i);
if (!expectedValue.deleted) {
tmp.push(expectedValue);
}
}
expectedValues = tmp;
// Query the DOM to verify the structure matches the expected results.
msgPrefix = 'DOM query: ';
query('.dgrid-row', widget.domNode).forEach(testRow);
});
}
function itemTestSuite(widgetClassName, storeSize, itemsPerQuery, overlap, config) {
// Create a test suite that performs one action type (itemAction) on 1 to config.itemsModifiedMax with
// a given amount of overlap.
var index, numToModify;
test.suite(widgetClassName + ' with ' + overlap + ' overlap', function () {
test.beforeEach(function () {
createList(storeSize, itemsPerQuery, overlap);
});
test.afterEach(destroyWidget);
// Modify items counting up.
for (numToModify = 1; numToModify <= config.itemsModifiedMax; numToModify++) {
for (index = 0; index <= (storeSize - numToModify); index++) {
itemTest(config.itemAction, index, numToModify);
}
}
// Modify items counting down. Starting at a count of 2 because
// single item modification were tested above.
for (numToModify = 2; numToModify <= config.itemsModifiedMax; numToModify++) {
for (index = numToModify - 1; index < storeSize; index++) {
itemTest(config.itemAction, index, numToModify, true);
}
}
});
}
function itemActionTestSuite(description, itemAction, config) {
// Creates multiple item test suites for a given action (itemAction):
// - a list that executes a single query
// - lists with overlap from 0 to config.itemOverlapMax
// Note: for debugging, comment out the contents of destroyWidget so the dgrid widgets are not destroyed.
// Each widget uses a different store id and those ids are used in the row contents allowing you to
// easily match up an error message like
// "Error: dgrid API: row at index 2 with an expected value of "Value 30 / Store 10 / Changed!" is missing"
// with the correct widget on the page.
config.itemAction = itemAction;
test.suite(description, function () {
// Test widgets with only one query: total item count equals item count per query.
itemTestSuite('OnDemandList one query', config.itemsPerQuery, config.itemsPerQuery, 0, config);
// Test widgets that make multiple query requests: twice as many items as items per query so multiple
// queries will create multiple observers.
var storeSize = config.itemsPerQuery * 2;
// Test with OnDemandList with varying overlap values
for (var overlap = 0; overlap <= config.itemOverlapMax; overlap++) {
itemTestSuite('OnDemandList multiple queries', storeSize, config.itemsPerQuery, overlap, config);
}
});
}
function itemAddEmptyStoreTest(itemsToAddCount, itemsPerQuery, overlap) {
var i;
function rowHasClass(rowNode, cssClass) {
assert.isTrue(domClass.contains(rowNode, cssClass), rowNode.outerHTML + ' should have ' + cssClass);
}
test.test('Add ' + itemsToAddCount + ' items with ' + overlap + ' overlap', function () {
createList(0, itemsPerQuery, overlap);
var store = widget.collection;
for (i = 0; i < itemsToAddCount; i++) {
store.put(createItem(i));
}
var rows = query('.dgrid-content > div', widget.domNode);
rowHasClass(rows[0], 'dgrid-preload');
for (i = 1; i <= itemsToAddCount; i++) {
rowHasClass(rows[i], (i % 2) ? 'dgrid-row-even' : 'dgrid-row-odd');
}
rowHasClass(rows[i], 'dgrid-preload');
for (i = 0; i < itemsToAddCount; i++) {
store.put(createItem(i));
}
rows = query('.dgrid-content > div', widget.domNode);
rowHasClass(rows[0], 'dgrid-preload');
for (i = 1; i <= itemsToAddCount; i++) {
rowHasClass(rows[i], (i % 2) ? 'dgrid-row-even' : 'dgrid-row-odd');
}
rowHasClass(rows[i], 'dgrid-preload');
});
}
function itemAddEmptyStoreTestSuite(config) {
test.suite('Add items to empty store', function () {
test.afterEach(destroyWidget);
itemAddEmptyStoreTest(1, config.itemsPerQuery, 0);
// Test with OnDemandList with varying overlap values
for (var overlap = 0; overlap <= config.itemOverlapMax; overlap++) {
itemAddEmptyStoreTest(config.itemsPerQuery + overlap + 1, config.itemsPerQuery, overlap);
}
});
}
test.suite('Trackable lists', function () {
// Creates test suites that execute the following actions on OnDemandLists with varying amount of
// overlap and modifying varying number of items:
// - modify existing items
// - remove existing items
// - add new items before existing items
// - add new items after existing items
function findIndex(id, objs) {
for (var i = 0; i < objs.length; i++) {
var obj = objs[i];
if (obj && obj.id === id) {
return i;
}
}
return -1;
}
var modifyAction = function (id, expectedValues) {
var index = findIndex(id, expectedValues);
var value = expectedValues[index].value + ' / Changed!';
var dataObj = {id: id, value: value};
widget.collection.put(dataObj);
expectedValues[index] = dataObj;
};
modifyAction.actionName = 'Modify';
var removeAction = function (id, expectedValues) {
widget.collection.remove(id);
var index = findIndex(id, expectedValues);
expectedValues[index].deleted = true;
};
removeAction.actionName = 'Remove';
var addBeforeAction = function (id, expectedValues) {
var index = findIndex(id, expectedValues);
var obj = { id: id - 5, value: expectedValues[index].value + ' / Added before!' };
widget.collection.add(obj);
expectedValues.splice(index, 0, obj);
};
addBeforeAction.actionName = 'Add before';
var addAfterAction = function (id, expectedValues) {
var index = findIndex(id, expectedValues);
var obj = {id: id + 5, value: expectedValues[index].value + ' / Added after!'};
widget.collection.add(obj);
expectedValues.splice(index + 1, 0, obj);
};
addAfterAction.actionName = 'Add after';
// Run a test case with each action (modify, remove, add before, add after) and vary the amount of
// queryRowsOverlap and vary the number of items modified during each test case. A configuration
// object controls the amount of variation. The properties are:
// itemsPerQuery - The OnDemandList is configured to request this number of items per query.
// This property also determines the size of the store. Test cases run with a store size
// equal to this number and test cases run with a store size twice this number.
// itemOverlapMax - Each test case is executed with a queryRowsOverap value of 0 up to this number.
// itemsModifiedMax - Each test is executed where the number of items modified, deleted or added is
// 1 up to this number; all of the test cases are run where 1 item is modified and then again
// with 2 items being modified and so on.
var config = {
itemsPerQuery: 3,
itemOverlapMax: 2,
itemsModifiedMax: 2
};
itemActionTestSuite('Modify store items', modifyAction, config);
itemActionTestSuite('Remove store items', removeAction, config);
itemActionTestSuite('Insert store items before', addBeforeAction, config);
itemActionTestSuite('Insert store items after', addAfterAction, config);
itemAddEmptyStoreTestSuite(config);
});
test.suite('Multiple updates to trackable list', function () {
test.afterEach(destroyWidget);
test.test('Multiple puts', function () {
var i;
var data = [];
for (i = 0; i < 10; i++) {
data.push({ id: i, value: 'Value ' + i, enabled: true });
}
var store = createSyncStore({ data: data });
widget = new OnDemandList({
collection: store.filter({ enabled: true }),
renderRow: renderRow
});
document.body.appendChild(widget.domNode);
widget.startup();
// Test putting several items with a filter applied;
// the put objects should all be seen as removed
for (i = 0; i < 5; i++){
var item = data[i];
item.enabled = false;
store.put(item);
}
assert.strictEqual(widget._rows.length, 5,
'5 items should remain in the _rows array (5 should be removed)');
});
});
test.suite('shouldTrackCollection = false + store modifications', function () {
var numItems = 3;
var store;
var handles = [];
test.before(function () {
createList(numItems, 25, 0, false);
});
test.beforeEach(function () {
store = createStore(numItems);
widget.set('collection', store);
});
test.afterEach(function () {
for (var i = handles.length; i--;) {
handles[i].remove();
}
handles = [];
});
test.after(destroyWidget);
function countRows() {
var count = query('.dgrid-row', widget.contentNode).length;
return count;
}
test.test('shouldTrackCollection = false + add', function () {
var numRows = countRows();
store.addSync(createItem(3));
assert.strictEqual(countRows(), numRows);
});
test.test('shouldTrackCollection = false + put', function () {
var calls = 0;
handles.push(aspect.before(widget, 'removeRow', function () {
calls++;
}));
handles.push(aspect.before(widget, 'insertRow', function () {
calls++;
}));
for (var i = 0; i < numItems; i++) {
store.putSync(store.getSync(indexToId(i)));
}
assert.strictEqual(calls, 0, 'insertRow and removeRow should never be called');
});
test.test('shouldTrackCollection = false + remove', function () {
var numRows = countRows();
for (var i = 0; i < numItems; i++) {
store.removeSync(indexToId(i));
}
assert.strictEqual(countRows(), numRows);
});
});
});

View File

@ -0,0 +1,39 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/_base/declare',
'dgrid/Grid',
'dgrid/extensions/ColumnHider'
], function (test, assert, declare, Grid, ColumnHider) {
var ColumnHiderGrid = declare([ Grid, ColumnHider ]);
var grid;
test.suite('ColumnHider', function () {
test.beforeEach(function () {
grid = new ColumnHiderGrid({
columns: {
col1: 'Column 1',
col2: {
label: 'Column 2',
unhidable: true
}
}
});
document.body.appendChild(grid.domNode);
grid.startup();
grid.renderArray([]);
});
test.afterEach(function () {
grid.destroy();
});
test.suite('#toggleColumnHiddenState', function () {
test.test('unhidable column', function () {
assert.doesNotThrow(function () {
grid.toggleColumnHiddenState('col2');
});
});
});
});
});

View File

@ -0,0 +1,713 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/_base/declare',
'dgrid/Grid',
'dgrid/extensions/CompoundColumns'
], function (test, assert, declare, Grid, CompoundColumns) {
var CompoundColumnGrid = declare([Grid, CompoundColumns]),
data = [],
grid;
// Generate data to be used for all tests
for (var itemIndex = 0; itemIndex < 12; itemIndex++) {
var item = { id: itemIndex };
for (var propIndex = 0; propIndex < 10; propIndex++) {
item['data' + propIndex] = 'Value ' + itemIndex + ':' + propIndex;
}
data.push(item);
}
function createGrid(columns, hideHeader) {
grid = new CompoundColumnGrid({
columns: columns,
showHeader: !hideHeader
});
document.body.appendChild(grid.domNode);
grid.startup();
grid.renderArray(data);
}
test.suite('CompoundColumns', function () {
test.suite('cell method', function () {
test.afterEach(function () {
grid.destroy();
});
test.test('simple grid', function () {
createGrid({
data0: 'Data 0',
data1: 'Data 1',
data2: 'Data 2',
data3: 'Data 3',
data4: 'Data 4'
}, true);
assert.strictEqual(grid.cell(0, 0).element.innerHTML, 'Value 0:0');
assert.strictEqual(grid.cell(0, 4).element.innerHTML, 'Value 0:4');
assert.strictEqual(grid.cell(11, 0).element.innerHTML, 'Value 11:0');
assert.strictEqual(grid.cell(11, 4).element.innerHTML, 'Value 11:4');
assert.isUndefined(grid.cell(0, 5).element);
assert.isUndefined(grid.cell(12, 0).element);
});
test.test('simple grid with column ids', function () {
createGrid({
data0: { label: 'Data 0', id: 'myData0' },
data1: { label: 'Data 1', id: 'myData1' },
data2: { label: 'Data 2', id: 'myData2' },
data3: { label: 'Data 3', id: 'myData3' },
data4: { label: 'Data 4', id: 'myData4' }
}, true);
assert.strictEqual(grid.cell(0, 'myData0').element.innerHTML, 'Value 0:0');
assert.strictEqual(grid.cell(0, 'myData4').element.innerHTML, 'Value 0:4');
assert.strictEqual(grid.cell(11, 'myData0').element.innerHTML, 'Value 11:0');
assert.strictEqual(grid.cell(11, 'myData4').element.innerHTML, 'Value 11:4');
assert.isUndefined(grid.cell(0, 'myData5').element);
assert.isUndefined(grid.cell(12, 'myData0').element);
assert.isUndefined(grid.cell(0, 0).element);
});
test.test('grid with children', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
children: [
{ field: 'data1', label: 'Data 1' },
{ field: 'data2', label: 'Data 2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
children: [
{ field: 'data4', label: 'Data 4' },
{ field: 'data5', label: 'Data 5' }
]
}
]);
assert.strictEqual(grid.cell(0, 0).element.innerHTML, 'Value 0:0');
assert.strictEqual(grid.cell(0, 1).element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 2).element.innerHTML, 'Value 0:2');
assert.strictEqual(grid.cell(0, 5).element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(11, 0).element.innerHTML, 'Value 11:0');
assert.strictEqual(grid.cell(11, 1).element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 2).element.innerHTML, 'Value 11:2');
assert.strictEqual(grid.cell(11, 5).element.innerHTML, 'Value 11:5');
assert.isUndefined(grid.cell(0, 6).element);
assert.isUndefined(grid.cell(12, 0).element);
});
test.test('grid with children and children ids', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{ field: 'data5', label: 'Data 5', id: 'myData5' }
]
}
]);
assert.strictEqual(grid.cell(0, 0).element.innerHTML, 'Value 0:0');
assert.strictEqual(grid.cell(0, 3).element.innerHTML, 'Value 0:3');
assert.strictEqual(grid.cell(0, 'myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'myData2').element.innerHTML, 'Value 0:2');
assert.strictEqual(grid.cell(0, 'myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(11, 0).element.innerHTML, 'Value 11:0');
assert.strictEqual(grid.cell(11, 3).element.innerHTML, 'Value 11:3');
assert.strictEqual(grid.cell(11, 'myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'myData2').element.innerHTML, 'Value 11:2');
assert.strictEqual(grid.cell(11, 'myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(0, '0-1-myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, '0-1-myData2').element.innerHTML, 'Value 0:2');
assert.strictEqual(grid.cell(0, '0-4-myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(11, '0-1-myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, '0-1-myData2').element.innerHTML, 'Value 11:2');
assert.strictEqual(grid.cell(11, '0-4-myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(0, '1-myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, '1-myData2').element.innerHTML, 'Value 0:2');
assert.strictEqual(grid.cell(0, '4-myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(11, '1-myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, '1-myData2').element.innerHTML, 'Value 11:2');
assert.strictEqual(grid.cell(11, '4-myData5').element.innerHTML, 'Value 11:5');
});
test.test('grid with children and ids', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
id: 'compound1',
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
id: 'compound2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{ field: 'data5', label: 'Data 5', id: 'myData5' }
]
}
]);
assert.strictEqual(grid.cell(0, 0).element.innerHTML, 'Value 0:0');
assert.strictEqual(grid.cell(0, 3).element.innerHTML, 'Value 0:3');
assert.strictEqual(grid.cell(0, 'myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'myData2').element.innerHTML, 'Value 0:2');
assert.strictEqual(grid.cell(0, 'myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(11, 0).element.innerHTML, 'Value 11:0');
assert.strictEqual(grid.cell(11, 3).element.innerHTML, 'Value 11:3');
assert.strictEqual(grid.cell(11, 'myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'myData2').element.innerHTML, 'Value 11:2');
assert.strictEqual(grid.cell(11, 'myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(0, 'compound1-myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'compound1-myData2').element.innerHTML, 'Value 0:2');
assert.strictEqual(grid.cell(0, 'compound2-myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(11, 'compound1-myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'compound1-myData2').element.innerHTML, 'Value 11:2');
assert.strictEqual(grid.cell(11, 'compound2-myData5').element.innerHTML, 'Value 11:5');
});
test.test('grid with nested children and ids', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
id: 'compound1',
children: [
{
label: 'Nested Compound 1',
id: 'nested1',
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data9', label: 'Data 9', id: 'myData9' }
]
},
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
id: 'compound2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{
label: 'Nested Compound 2',
id: 'nested2',
children: [
{ field: 'data5', label: 'Data 5', id: 'myData5' },
{ field: 'data8', label: 'Data 8', id: 'myData8' }
]
}
]
}
]);
assert.strictEqual(grid.cell(0, 'myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'myData2').element.innerHTML, 'Value 0:2');
assert.strictEqual(grid.cell(0, 'myData9').element.innerHTML, 'Value 0:9');
assert.strictEqual(grid.cell(0, 'myData4').element.innerHTML, 'Value 0:4');
assert.strictEqual(grid.cell(0, 'myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(0, 'myData8').element.innerHTML, 'Value 0:8');
assert.strictEqual(grid.cell(11, 'myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'myData2').element.innerHTML, 'Value 11:2');
assert.strictEqual(grid.cell(11, 'myData9').element.innerHTML, 'Value 11:9');
assert.strictEqual(grid.cell(11, 'myData4').element.innerHTML, 'Value 11:4');
assert.strictEqual(grid.cell(11, 'myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(11, 'myData8').element.innerHTML, 'Value 11:8');
assert.strictEqual(grid.cell(0, 'nested1-myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'nested1-myData9').element.innerHTML, 'Value 0:9');
assert.strictEqual(grid.cell(0, 'nested2-myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(0, 'nested2-myData8').element.innerHTML, 'Value 0:8');
assert.strictEqual(grid.cell(11, 'nested1-myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'nested1-myData9').element.innerHTML, 'Value 11:9');
assert.strictEqual(grid.cell(11, 'nested2-myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(11, 'nested2-myData8').element.innerHTML, 'Value 11:8');
assert.strictEqual(grid.cell(0, 'compound1-nested1-myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'compound1-nested1-myData9').element.innerHTML, 'Value 0:9');
assert.strictEqual(grid.cell(0, 'compound2-nested2-myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(0, 'compound2-nested2-myData8').element.innerHTML, 'Value 0:8');
assert.strictEqual(grid.cell(11, 'compound1-nested1-myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'compound1-nested1-myData9').element.innerHTML, 'Value 11:9');
assert.strictEqual(grid.cell(11, 'compound2-nested2-myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(11, 'compound2-nested2-myData8').element.innerHTML, 'Value 11:8');
});
test.test('grid with nested children and ids hiding all headers', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
id: 'compound1',
children: [
{
label: 'Nested Compound 1',
id: 'nested1',
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data9', label: 'Data 9', id: 'myData9' }
]
},
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
id: 'compound2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{
label: 'Nested Compound 2',
id: 'nested2',
children: [
{ field: 'data5', label: 'Data 5', id: 'myData5' },
{ field: 'data8', label: 'Data 8', id: 'myData8' }
]
}
]
}
], true);
assert.strictEqual(grid.cell(0, 'myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'myData2').element.innerHTML, 'Value 0:2');
assert.strictEqual(grid.cell(0, 'myData9').element.innerHTML, 'Value 0:9');
assert.strictEqual(grid.cell(0, 'myData4').element.innerHTML, 'Value 0:4');
assert.strictEqual(grid.cell(0, 'myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(0, 'myData8').element.innerHTML, 'Value 0:8');
assert.strictEqual(grid.cell(11, 'myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'myData2').element.innerHTML, 'Value 11:2');
assert.strictEqual(grid.cell(11, 'myData9').element.innerHTML, 'Value 11:9');
assert.strictEqual(grid.cell(11, 'myData4').element.innerHTML, 'Value 11:4');
assert.strictEqual(grid.cell(11, 'myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(11, 'myData8').element.innerHTML, 'Value 11:8');
assert.strictEqual(grid.cell(0, 'nested1-myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'nested1-myData9').element.innerHTML, 'Value 0:9');
assert.strictEqual(grid.cell(0, 'nested2-myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(0, 'nested2-myData8').element.innerHTML, 'Value 0:8');
assert.strictEqual(grid.cell(11, 'nested1-myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'nested1-myData9').element.innerHTML, 'Value 11:9');
assert.strictEqual(grid.cell(11, 'nested2-myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(11, 'nested2-myData8').element.innerHTML, 'Value 11:8');
assert.strictEqual(grid.cell(0, 'compound1-nested1-myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'compound1-nested1-myData9').element.innerHTML, 'Value 0:9');
assert.strictEqual(grid.cell(0, 'compound2-nested2-myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(0, 'compound2-nested2-myData8').element.innerHTML, 'Value 0:8');
assert.strictEqual(grid.cell(11, 'compound1-nested1-myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'compound1-nested1-myData9').element.innerHTML, 'Value 11:9');
assert.strictEqual(grid.cell(11, 'compound2-nested2-myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(11, 'compound2-nested2-myData8').element.innerHTML, 'Value 11:8');
});
test.test('grid with nested children and ids hiding child headers', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
id: 'compound1',
children: [
{
label: 'Nested Compound 1',
id: 'nested1',
showChildHeaders: false,
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data9', label: 'Data 9', id: 'myData9' }
]
},
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
id: 'compound2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{
label: 'Nested Compound 2',
id: 'nested2',
showChildHeaders: false,
children: [
{ field: 'data5', label: 'Data 5', id: 'myData5' },
{ field: 'data8', label: 'Data 8', id: 'myData8' }
]
}
]
}
]);
assert.strictEqual(grid.cell(0, 'myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'myData2').element.innerHTML, 'Value 0:2');
assert.strictEqual(grid.cell(0, 'myData9').element.innerHTML, 'Value 0:9');
assert.strictEqual(grid.cell(0, 'myData4').element.innerHTML, 'Value 0:4');
assert.strictEqual(grid.cell(0, 'myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(0, 'myData8').element.innerHTML, 'Value 0:8');
assert.strictEqual(grid.cell(11, 'myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'myData2').element.innerHTML, 'Value 11:2');
assert.strictEqual(grid.cell(11, 'myData9').element.innerHTML, 'Value 11:9');
assert.strictEqual(grid.cell(11, 'myData4').element.innerHTML, 'Value 11:4');
assert.strictEqual(grid.cell(11, 'myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(11, 'myData8').element.innerHTML, 'Value 11:8');
assert.strictEqual(grid.cell(0, 'nested1-myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'nested1-myData9').element.innerHTML, 'Value 0:9');
assert.strictEqual(grid.cell(0, 'nested2-myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(0, 'nested2-myData8').element.innerHTML, 'Value 0:8');
assert.strictEqual(grid.cell(11, 'nested1-myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'nested1-myData9').element.innerHTML, 'Value 11:9');
assert.strictEqual(grid.cell(11, 'nested2-myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(11, 'nested2-myData8').element.innerHTML, 'Value 11:8');
assert.strictEqual(grid.cell(0, 'compound1-nested1-myData1').element.innerHTML, 'Value 0:1');
assert.strictEqual(grid.cell(0, 'compound1-nested1-myData9').element.innerHTML, 'Value 0:9');
assert.strictEqual(grid.cell(0, 'compound2-nested2-myData5').element.innerHTML, 'Value 0:5');
assert.strictEqual(grid.cell(0, 'compound2-nested2-myData8').element.innerHTML, 'Value 0:8');
assert.strictEqual(grid.cell(11, 'compound1-nested1-myData1').element.innerHTML, 'Value 11:1');
assert.strictEqual(grid.cell(11, 'compound1-nested1-myData9').element.innerHTML, 'Value 11:9');
assert.strictEqual(grid.cell(11, 'compound2-nested2-myData5').element.innerHTML, 'Value 11:5');
assert.strictEqual(grid.cell(11, 'compound2-nested2-myData8').element.innerHTML, 'Value 11:8');
});
});
test.suite('column method', function () {
test.afterEach(function () {
grid.destroy();
});
test.test('simple grid', function () {
createGrid({
data0: 'Data 0',
data1: 'Data 1',
data2: 'Data 2',
data3: 'Data 3',
data4: 'Data 4'
}, true);
assert.strictEqual(grid.column(0).label, 'Data 0');
assert.strictEqual(grid.column(4).label, 'Data 4');
assert.isUndefined(grid.column(5));
});
test.test('simple grid with column ids', function () {
createGrid({
data0: { label: 'Data 0', id: 'myData0' },
data1: { label: 'Data 1', id: 'myData1' },
data2: { label: 'Data 2', id: 'myData2' },
data3: { label: 'Data 3', id: 'myData3' },
data4: { label: 'Data 4', id: 'myData4' }
}, true);
assert.strictEqual(grid.column('myData0').label, 'Data 0');
assert.strictEqual(grid.column('myData4').label, 'Data 4');
assert.isUndefined(grid.column('myData5'));
assert.isUndefined(grid.column(0));
});
test.test('grid with children', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
children: [
{ field: 'data1', label: 'Data 1' },
{ field: 'data2', label: 'Data 2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
children: [
{ field: 'data4', label: 'Data 4' },
{ field: 'data5', label: 'Data 5' }
]
}
]);
assert.strictEqual(grid.column(0).label, 'Data 0');
assert.strictEqual(grid.column(1).label, 'Data 1');
assert.strictEqual(grid.column(2).label, 'Data 2');
assert.strictEqual(grid.column(5).label, 'Data 5');
assert.isUndefined(grid.column(6));
});
test.test('grid with children and children ids', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{ field: 'data5', label: 'Data 5', id: 'myData5' }
]
}
]);
assert.strictEqual(grid.column(0).label, 'Data 0');
assert.strictEqual(grid.column(3).label, 'Data 3');
assert.strictEqual(grid.column('myData1').label, 'Data 1');
assert.strictEqual(grid.column('myData2').label, 'Data 2');
assert.strictEqual(grid.column('myData5').label, 'Data 5');
assert.strictEqual(grid.column('0-1-myData1').label, 'Data 1');
assert.strictEqual(grid.column('0-1-myData2').label, 'Data 2');
assert.strictEqual(grid.column('0-4-myData5').label, 'Data 5');
assert.strictEqual(grid.column('1-myData1').label, 'Data 1');
assert.strictEqual(grid.column('1-myData2').label, 'Data 2');
assert.strictEqual(grid.column('4-myData5').label, 'Data 5');
});
test.test('grid with children and ids', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
id: 'compound1',
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
id: 'compound2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{ field: 'data5', label: 'Data 5', id: 'myData5' }
]
}
]);
assert.strictEqual(grid.column(0).label, 'Data 0');
assert.strictEqual(grid.column(3).label, 'Data 3');
assert.strictEqual(grid.column('myData1').label, 'Data 1');
assert.strictEqual(grid.column('myData2').label, 'Data 2');
assert.strictEqual(grid.column('myData5').label, 'Data 5');
assert.strictEqual(grid.column('compound1-myData1').label, 'Data 1');
assert.strictEqual(grid.column('compound1-myData2').label, 'Data 2');
assert.strictEqual(grid.column('compound2-myData5').label, 'Data 5');
});
test.test('grid with nested children and ids', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
id: 'compound1',
children: [
{
label: 'Nested Compound 1',
id: 'nested1',
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data9', label: 'Data 9', id: 'myData9' }
]
},
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
id: 'compound2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{
label: 'Nested Compound 2',
id: 'nested2',
children: [
{ field: 'data5', label: 'Data 5', id: 'myData5' },
{ field: 'data8', label: 'Data 8', id: 'myData8' }
]
}
]
}
]);
assert.strictEqual(grid.column('myData1').label, 'Data 1');
assert.strictEqual(grid.column('myData2').label, 'Data 2');
assert.strictEqual(grid.column('myData9').label, 'Data 9');
assert.strictEqual(grid.column('myData4').label, 'Data 4');
assert.strictEqual(grid.column('myData5').label, 'Data 5');
assert.strictEqual(grid.column('myData8').label, 'Data 8');
assert.strictEqual(grid.column('nested1-myData1').label, 'Data 1');
assert.strictEqual(grid.column('nested1-myData9').label, 'Data 9');
assert.strictEqual(grid.column('nested2-myData5').label, 'Data 5');
assert.strictEqual(grid.column('nested2-myData8').label, 'Data 8');
assert.strictEqual(grid.column('compound1-nested1-myData1').label, 'Data 1');
assert.strictEqual(grid.column('compound1-nested1-myData9').label, 'Data 9');
assert.strictEqual(grid.column('compound2-nested2-myData5').label, 'Data 5');
assert.strictEqual(grid.column('compound2-nested2-myData8').label, 'Data 8');
});
test.test('grid with nested children and ids hiding all headers', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
id: 'compound1',
children: [
{
label: 'Nested Compound 1',
id: 'nested1',
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data9', label: 'Data 9', id: 'myData9' }
]
},
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
id: 'compound2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{
label: 'Nested Compound 2',
id: 'nested2',
children: [
{ field: 'data5', label: 'Data 5', id: 'myData5' },
{ field: 'data8', label: 'Data 8', id: 'myData8' }
]
}
]
}
], true);
assert.strictEqual(grid.column('myData1').label, 'Data 1');
assert.strictEqual(grid.column('myData2').label, 'Data 2');
assert.strictEqual(grid.column('myData9').label, 'Data 9');
assert.strictEqual(grid.column('myData4').label, 'Data 4');
assert.strictEqual(grid.column('myData5').label, 'Data 5');
assert.strictEqual(grid.column('myData8').label, 'Data 8');
assert.strictEqual(grid.column('nested1-myData1').label, 'Data 1');
assert.strictEqual(grid.column('nested1-myData9').label, 'Data 9');
assert.strictEqual(grid.column('nested2-myData5').label, 'Data 5');
assert.strictEqual(grid.column('nested2-myData8').label, 'Data 8');
assert.strictEqual(grid.column('compound1-nested1-myData1').label, 'Data 1');
assert.strictEqual(grid.column('compound1-nested1-myData9').label, 'Data 9');
assert.strictEqual(grid.column('compound2-nested2-myData5').label, 'Data 5');
assert.strictEqual(grid.column('compound2-nested2-myData8').label, 'Data 8');
});
test.test('grid with nested children and ids hiding child headers', function () {
createGrid([
{ field: 'data0', label: 'Data 0' },
{
label: 'Compound 1',
id: 'compound1',
children: [
{
label: 'Nested Compound 1',
id: 'nested1',
showChildHeaders: false,
children: [
{ field: 'data1', label: 'Data 1', id: 'myData1' },
{ field: 'data9', label: 'Data 9', id: 'myData9' }
]
},
{ field: 'data2', label: 'Data 2', id: 'myData2' }
]
},
{ field: 'data3', label: 'Data 3' },
{
label: 'Compound 2',
id: 'compound2',
children: [
{ field: 'data4', label: 'Data 4', id: 'myData4' },
{
label: 'Nested Compound 2',
id: 'nested2',
showChildHeaders: false,
children: [
{ field: 'data5', label: 'Data 5', id: 'myData5' },
{ field: 'data8', label: 'Data 8', id: 'myData8' }
]
}
]
}
]);
assert.strictEqual(grid.column('myData1').label, 'Data 1');
assert.strictEqual(grid.column('myData2').label, 'Data 2');
assert.strictEqual(grid.column('myData9').label, 'Data 9');
assert.strictEqual(grid.column('myData4').label, 'Data 4');
assert.strictEqual(grid.column('myData5').label, 'Data 5');
assert.strictEqual(grid.column('myData8').label, 'Data 8');
assert.strictEqual(grid.column('nested1-myData1').label, 'Data 1');
assert.strictEqual(grid.column('nested1-myData9').label, 'Data 9');
assert.strictEqual(grid.column('nested2-myData5').label, 'Data 5');
assert.strictEqual(grid.column('nested2-myData8').label, 'Data 8');
assert.strictEqual(grid.column('compound1-nested1-myData1').label, 'Data 1');
assert.strictEqual(grid.column('compound1-nested1-myData9').label, 'Data 9');
assert.strictEqual(grid.column('compound2-nested2-myData5').label, 'Data 5');
assert.strictEqual(grid.column('compound2-nested2-myData8').label, 'Data 8');
});
});
test.suite('sort method', function () {
test.afterEach(function () {
grid.destroy();
});
test.test('sort grid programmatically by field present in header', function () {
createGrid({
data0: 'Data 0',
data1: 'Data 1',
data2: 'Data 2',
data3: 'Data 3',
data4: 'Data 4'
});
assert.doesNotThrow(function () {
grid.set('sort', 'data0');
}, null, 'Sorting grid programmatically should not throw error');
});
});
});
});

View File

@ -0,0 +1,243 @@
define([
'intern!tdd',
'intern/chai!assert',
'dojo/_base/declare',
'dojo/on',
'dojo/query',
'dojo/string',
'dgrid/Grid',
'dgrid/extensions/Pagination',
'dgrid/test/data/createSyncStore',
'dgrid/test/data/genericData',
'dojo/domReady!'
], function (test, assert, declare, on, query, string, Grid, Pagination, createSyncStore, genericData) {
var grid,
PaginationGrid = declare([Grid, Pagination]);
function getColumns() {
return {
id: 'id',
col1: 'Column 1',
col2: 'Column 2',
col5: 'Column 5'
};
}
function createTestStore() {
return createSyncStore({ data: genericData });
}
test.suite('Pagination', function () {
test.beforeEach(function () {
grid = new PaginationGrid({
collection: createTestStore(),
columns: getColumns()
});
document.body.appendChild(grid.domNode);
grid.startup();
});
test.afterEach(function () {
grid.destroy();
});
test.test('Pagination info updates on page switch', function () {
// switch pages and ensure that the status message and links are
// updated
var disabledLinks = query('span.dgrid-page-disabled', grid.paginationLinksNode),
expectedText = string.substitute(grid.i18nPagination.status,
{ start: 1, end: 10, total: 100 });
function testAssertions(expectedPage) {
assert.strictEqual(grid.paginationStatusNode.innerHTML, expectedText,
'should find expected status message; received \'' + status + '\'');
assert.strictEqual(disabledLinks.length, 1,
'should find expected number of disabled page links: found ' +
disabledLinks.length);
assert.strictEqual(string.trim(disabledLinks[0].innerHTML), expectedPage,
'link for active page (' + expectedPage + ') should be disabled');
for (var i = 0; i < disabledLinks.length; i++) {
assert.equal(disabledLinks[i].tabIndex, -1, 'disabled link should have -1 tabIndex');
}
}
testAssertions('1');
grid.gotoPage(2);
disabledLinks = query('span.dgrid-page-disabled', grid.paginationLinksNode);
expectedText = string.substitute(grid.i18nPagination.status, {start: 11, end: 20, total: 100});
testAssertions('2');
});
test.test('Pagination info updates when an item is added/removed', function () {
function testAssertions(expectedTotal, expectedLastPage) {
assert.strictEqual(grid.paginationStatusNode.innerHTML,
string.substitute(grid.i18nPagination.status, {
start: 1,
end: 10,
total: expectedTotal
}),
'total displayed in status area should be ' + expectedTotal
);
assert.strictEqual(grid.paginationLinksNode.lastChild.innerHTML, '' + expectedLastPage,
'last page number displayed should be ' + expectedLastPage);
}
testAssertions(100, 10);
grid.collection.addSync({ id: 100 });
testAssertions(101, 11);
grid.collection.removeSync(100);
testAssertions(100, 10);
});
});
test.suite('Pagination size selector initialization tests', function () {
// Each test in this suite is responsible for instantiating the grid
test.afterEach(function () {
grid.destroy();
});
test.test('pageSizeOptions + unique rowsPerPage during creation', function () {
grid = new PaginationGrid({
collection: createTestStore(),
columns: getColumns(),
// Purposely set pageSizeOptions out of order to test setter
pageSizeOptions: [25, 15, 5],
// Purposely set rowsPerPage to a value not in pageSizeOptions
rowsPerPage: 10
});
document.body.appendChild(grid.domNode);
grid.startup();
assert.strictEqual(grid.paginationSizeSelect.tagName, 'SELECT',
'paginationSizeSelect should reference a SELECT element');
assert.lengthOf(grid.pageSizeOptions, 4,
'pageSizeOptions should have additional item for unique rowsPerPage');
assert.strictEqual(grid.pageSizeOptions[0], 5,
'pageSizeOptions should be sorted ascending');
// Now empty pageSizeOptions and confirm that the select was removed
grid.set('pageSizeOptions', null);
assert.isNull(grid.paginationSizeSelect,
'paginationSizeSelect reference should be cleared after setting empty pageSizeOptions');
assert.strictEqual(query('select', grid.footerNode).length, 0,
'paginationSizeSelect node should have been destroyed');
});
test.test('pageSizeOptions added after creation', function () {
grid = new PaginationGrid({
collection: createTestStore(),
columns: getColumns(),
rowsPerPage: 10
});
document.body.appendChild(grid.domNode);
grid.startup();
assert.isUndefined(grid.paginationSizeSelect,
'paginationSizeSelect should not exist yet (no options)');
// Now add pageSizeOptions (purposely out of order) and confirm it
// is properly added
grid.set('pageSizeOptions', [25, 15, 5]);
assert.strictEqual(grid.paginationSizeSelect.tagName, 'SELECT',
'paginationSizeSelect should reference a SELECT element');
assert.lengthOf(grid.pageSizeOptions, 4,
'pageSizeOptions should have additional item for unique rowsPerPage');
assert.strictEqual(grid.pageSizeOptions[0], 5,
'pageSizeOptions should be sorted ascending');
});
});
test.suite('Pagination size selector', function () {
test.before(function () {
grid = new PaginationGrid({
collection: createTestStore(),
columns: getColumns(),
pageSizeOptions: [5, 10, 15]
});
document.body.appendChild(grid.domNode);
grid.startup();
});
test.after(function () {
grid.destroy();
});
function verifyOptions(options, expectedCount) {
// verify that the values of the given set of options are in increasing order
// optionally verify that there are the expected number of options
var opt,
lastVal = options[0].value;
for (var i = 1; i < options.length; i++) {
opt = options[i];
assert.isTrue(+lastVal < +opt.value, 'values should be in order');
lastVal = opt.value;
}
if (expectedCount !== undefined) {
assert.lengthOf(options, expectedCount,
'selector should have expected number of options');
}
}
function rowsPerPageTest(rowsPerPage) {
// update the grid's rowsPerPage and ensure that the selector value
// is correct afterwards
var select = grid.paginationSizeSelect;
grid.set('rowsPerPage', rowsPerPage);
assert.strictEqual(select.value, '' + rowsPerPage,
'size select should have expected value ' + select.value);
verifyOptions(select.options);
}
function getNonSelectedValue(options) {
// return a value that isn't selected, assuming there are at least
// two options and unique option values
if (options[0].selected) {
return options[1].value;
} else {
return options[0].value;
}
}
test.test('setting rowsPerPage to a low value properly updates select', function () {
rowsPerPageTest(2);
});
test.test('setting rowsPerPage properly updates select', function () {
rowsPerPageTest(7);
});
test.test('setting rowsPerPage to a high value properly updates select', function () {
rowsPerPageTest(20);
});
test.test('setting rowsPerPage to an existing value doesn\'t add a value', function () {
var selector = grid.paginationSizeSelect,
initialCount = selector.options.length,
value = getNonSelectedValue(selector.options);
grid.set('rowsPerPage', +value);
assert.strictEqual(selector.value, value,
'size select should have expected value ' + selector.value);
verifyOptions(selector.options, initialCount);
});
test.test('selecting a value from the selector doesn\'t change the selector options', function () {
var selector = grid.paginationSizeSelect,
initialCount = selector.options.length,
targetValue = getNonSelectedValue(selector.options);
// TODO: this would do better as a functional test;
// as-is, we need to emit a change event manually
selector.value = targetValue;
on.emit(selector, 'change', {});
assert.strictEqual(selector.value, targetValue,
'size select should have expected value ' + selector.value);
assert.strictEqual(+targetValue, grid.rowsPerPage,
'rowsPerPage should have numeric value equivalent to the selected value');
verifyOptions(selector.options, initialCount);
});
});
});

Some files were not shown because too many files have changed in this diff Show More