From 444f78ceb707dc6aabce11b28b509e1720fdb104 Mon Sep 17 00:00:00 2001 From: Babayaga Date: Tue, 10 Feb 2026 00:02:02 +0100 Subject: [PATCH] xblox - port 1/3 --- .../ref-control-freak/xblox-ts/.gitignore | 37 + .../xblox/ref-control-freak/xblox-ts/LICENSE | 29 + .../ref-control-freak/xblox-ts/README.md | 2 + .../ref-control-freak/xblox-ts/package.json | 44 + .../ref-control-freak/xblox-ts/src/enums.ts | 291 +++ .../ref-control-freak/xblox-ts/tsconfig.json | 35 + .../xblox/ref-control-freak/xblox/.gitmodules | 0 .../ref-control-freak/xblox/BlockActions.js | 1707 ++++++++++++++ .../xblox/ref-control-freak/xblox/CSSState.js | 140 ++ .../ref-control-freak/xblox/Gruntfile.js | 189 ++ .../xblox/ref-control-freak/xblox/Readme.md | 0 .../ref-control-freak/xblox/RunScript.js | 564 +++++ .../ref-control-freak/xblox/StyleState.js | 83 + .../xblox/ref-control-freak/xblox/_State.js | 182 ++ .../xblox/ref-control-freak/xblox/_Stated.js | 78 + .../xblox/bak/BlocksFileEditor.js | 759 +++++++ .../xblox/bak/BlocksFileEditor.js2 | 861 +++++++ .../ref-control-freak/xblox/component.js | 68 + .../ref-control-freak/xblox/data/Store.js | 72 + .../ref-control-freak/xblox/docs/Command.md | 76 + .../xblox/docs/Events/OnEvent.md | 106 + .../xblox/docs/File/ReadJSON.md | 63 + .../ref-control-freak/xblox/docs/_index.md | 5 + .../xblox/ref-control-freak/xblox/embedded.js | 74 + .../ref-control-freak/xblox/embedded_ui.js | 6 + .../ref-control-freak/xblox/factory/Blocks.js | 542 +++++ .../xblox/generator/Javascript.js | 88 + .../xblox/generator/_Base.js | 5 + .../ref-control-freak/xblox/layer.profile.js | 163 ++ .../xblox/ref-control-freak/xblox/main.js | 5 + .../xblox/ref-control-freak/xblox/mainr.js | 30 + .../xblox/manager/BlockManager.js | 330 +++ .../xblox/manager/BlockManagerUI.js | 14 + .../ref-control-freak/xblox/min/component.js | 70 + .../ref-control-freak/xblox/min/xblox.js | 0 .../ref-control-freak/xblox/model/Block.js | 1036 +++++++++ .../xblox/model/BlockModel.js | 33 + .../ref-control-freak/xblox/model/Block_UI.js | 424 ++++ .../ref-control-freak/xblox/model/Contains.js | 107 + .../xblox/model/Expression.js | 251 +++ .../xblox/model/File/ReadJSON.js | 177 ++ .../xblox/model/ModelBase.js | 168 ++ .../xblox/model/Reference.js | 11 + .../xblox/model/Referenced.js | 35 + .../xblox/model/Scope.bak.js | 1332 +++++++++++ .../ref-control-freak/xblox/model/Scope.js | 1646 ++++++++++++++ .../xblox/model/Statement.js | 28 + .../ref-control-freak/xblox/model/Targeted.js | 12 + .../xblox/model/code/CallMethod.js | 94 + .../xblox/model/code/RunBlock.js | 151 ++ .../xblox/model/code/RunScript.html | 10 + .../xblox/model/code/RunScript.js | 300 +++ .../xblox/model/events/OnEvent.js | 393 ++++ .../xblox/model/events/OnKey.js | 276 +++ .../xblox/model/functions/CallBlock.js | 219 ++ .../xblox/model/functions/PauseBlock.js | 88 + .../xblox/model/functions/SetProperties.js | 117 + .../xblox/model/functions/StopBlock.js | 76 + .../xblox/model/html/SetCSS.js | 109 + .../xblox/model/html/SetState.js | 258 +++ .../xblox/model/html/SetStyle.js | 489 ++++ .../xblox/model/logging/Log.js | 183 ++ .../xblox/model/logic/BreakBlock.js | 38 + .../xblox/model/logic/CaseBlock.js | 185 ++ .../xblox/model/logic/Comparator.js | 16 + .../xblox/model/logic/DefaultBlock.js | 32 + .../xblox/model/logic/ElseIfBlock.js | 62 + .../xblox/model/logic/IfBlock.js | 306 +++ .../xblox/model/logic/SwitchBlock.js | 152 ++ .../xblox/model/loops/ForBlock.js | 329 +++ .../xblox/model/loops/WhileBlock.js | 189 ++ .../xblox/model/mqtt/Publish.js | 239 ++ .../xblox/model/mqtt/Subscribe.js | 183 ++ .../xblox/model/network/SSH.js | 165 ++ .../xblox/model/server/RunServerMethod.js | 345 +++ .../xblox/model/server/ServerBlock.js | 190 ++ .../xblox/model/server/Shell.js | 301 +++ .../ref-control-freak/xblox/model/simple.json | 15 + .../model/transform/BezierInterpolate.js | 213 ++ .../xblox/model/transform/BezierTransform.js | 60 + .../xblox/model/variables/Variable.js | 127 ++ .../variables/VariableAssignmentBlock.js | 260 +++ .../xblox/model/variables/VariableSwitch.js | 56 + .../ref-control-freak/xblox/package.json | 29 + .../xblox/resources-debug.json | 168 ++ .../xblox/resources-release.json | 47 + packages/xblox/ref-control-freak/xblox/run.js | 31 + .../xblox/test/intern/addCss.js | 20 + .../xblox/test/intern/all.js | 3 + .../xblox/test/intern/core/OnDemandList.js | 48 + .../xblox/test/intern/core/_StoreMixin.js | 302 +++ .../xblox/test/intern/core/addCssRule.js | 335 +++ .../xblox/test/intern/core/columns.js | 102 + .../xblox/test/intern/core/createDestroy.js | 26 + .../xblox/test/intern/core/setClass.js | 95 + .../xblox/test/intern/core/stores.js | 178 ++ .../xblox/test/intern/core/trackable.js | 388 ++++ .../test/intern/extensions/ColumnHider.js | 39 + .../test/intern/extensions/CompoundColumns.js | 713 ++++++ .../test/intern/extensions/Pagination.js | 243 ++ .../xblox/test/intern/functional.js | 3 + .../intern/functional/Editor-OnDemand.html | 89 + .../xblox/test/intern/functional/Editor.html | 72 + .../xblox/test/intern/functional/Editor.js | 350 +++ .../test/intern/functional/Keyboard.html | 81 + .../xblox/test/intern/functional/Keyboard.js | 34 + .../test/intern/functional/KeyboardTab.html | 71 + .../test/intern/functional/KeyboardTab.js | 98 + .../test/intern/functional/Selector.html | 110 + .../xblox/test/intern/functional/Selector.js | 196 ++ .../xblox/test/intern/functional/Tree.html | 72 + .../xblox/test/intern/functional/Tree.js | 72 + .../xblox/test/intern/functional/util.js | 87 + .../xblox/test/intern/intern.js | 69 + .../xblox/test/intern/intern.local.js | 16 + .../xblox/test/intern/mixins/ColumnSet.js | 172 ++ .../xblox/test/intern/mixins/Editor-radio.js | 157 ++ .../xblox/test/intern/mixins/Editor.js | 465 ++++ .../xblox/test/intern/mixins/Keyboard.js | 506 +++++ .../xblox/test/intern/mixins/Selection.js | 556 +++++ .../xblox/test/intern/mixins/Selector.js | 96 + .../intern/mixins/Tree-additional-filter.js | 147 ++ .../test/intern/mixins/Tree-expand-promise.js | 467 ++++ .../xblox/test/intern/mixins/Tree.js | 467 ++++ .../xblox/test/intern/resources/setClass.html | 8 + .../xblox/test/intern/runTests.html | 11 + .../xblox/tests/TestBlock.js | 59 + .../xblox/tests/TestBlockGrid.js | 578 +++++ .../xblox/tests/TestBlockGridClass.js | 257 +++ .../tests/TestBlockGridClassLineNumber.js | 256 +++ .../xblox/tests/TestBlocksFileEditor.js | 959 ++++++++ .../xblox/tests/TestClass.js | 1337 +++++++++++ .../xblox/tests/TestClass2.js | 374 +++ .../xblox/tests/TestCopyPaste.js | 1294 +++++++++++ .../xblox/tests/TestDNDClass.js | 716 ++++++ .../xblox/tests/TestDNDPaletteClass.js | 1039 +++++++++ .../xblox/tests/TestNewPallete.js | 437 ++++ .../xblox/tests/TestReload.js | 103 + .../xblox/tests/TestUtils.js | 1077 +++++++++ .../ref-control-freak/xblox/tests/bak/Test.js | 1656 ++++++++++++++ .../xblox/tests/bak/TestClass.js | 1938 ++++++++++++++++ .../xblox/tests/bak/TestEditor.js | 1777 +++++++++++++++ .../xblox/tests/bak/TestGrid.js | 2007 +++++++++++++++++ .../ref-control-freak/xblox/types/Types.js | 231 ++ .../xblox/utils/TestUtils.js | 1083 +++++++++ .../xblox/views/BlockDesignEditor.js | 185 ++ .../xblox/views/BlockEditDialog.js | 75 + .../xblox/views/BlockEditView.js | 13 + .../xblox/views/BlockGrid.js | 934 ++++++++ .../xblox/views/BlockGridPalette.js | 631 ++++++ .../xblox/views/BlocksFileEditor.js | 762 +++++++ .../xblox/views/DeleteDialog.js | 70 + .../ref-control-freak/xblox/views/DnD.js | 325 +++ .../ref-control-freak/xblox/views/Grid.js | 98 + .../xblox/views/ThumbRenderer.js | 139 ++ .../xblox/widgets/BlockEditor.js | 66 + .../xblox/ref-control-freak/xblox/xblox.js | 0 157 files changed, 45919 insertions(+) create mode 100644 packages/xblox/ref-control-freak/xblox-ts/.gitignore create mode 100644 packages/xblox/ref-control-freak/xblox-ts/LICENSE create mode 100644 packages/xblox/ref-control-freak/xblox-ts/README.md create mode 100644 packages/xblox/ref-control-freak/xblox-ts/package.json create mode 100644 packages/xblox/ref-control-freak/xblox-ts/src/enums.ts create mode 100644 packages/xblox/ref-control-freak/xblox-ts/tsconfig.json create mode 100644 packages/xblox/ref-control-freak/xblox/.gitmodules create mode 100644 packages/xblox/ref-control-freak/xblox/BlockActions.js create mode 100644 packages/xblox/ref-control-freak/xblox/CSSState.js create mode 100644 packages/xblox/ref-control-freak/xblox/Gruntfile.js create mode 100644 packages/xblox/ref-control-freak/xblox/Readme.md create mode 100644 packages/xblox/ref-control-freak/xblox/RunScript.js create mode 100644 packages/xblox/ref-control-freak/xblox/StyleState.js create mode 100644 packages/xblox/ref-control-freak/xblox/_State.js create mode 100644 packages/xblox/ref-control-freak/xblox/_Stated.js create mode 100644 packages/xblox/ref-control-freak/xblox/bak/BlocksFileEditor.js create mode 100644 packages/xblox/ref-control-freak/xblox/bak/BlocksFileEditor.js2 create mode 100644 packages/xblox/ref-control-freak/xblox/component.js create mode 100644 packages/xblox/ref-control-freak/xblox/data/Store.js create mode 100644 packages/xblox/ref-control-freak/xblox/docs/Command.md create mode 100644 packages/xblox/ref-control-freak/xblox/docs/Events/OnEvent.md create mode 100644 packages/xblox/ref-control-freak/xblox/docs/File/ReadJSON.md create mode 100644 packages/xblox/ref-control-freak/xblox/docs/_index.md create mode 100644 packages/xblox/ref-control-freak/xblox/embedded.js create mode 100644 packages/xblox/ref-control-freak/xblox/embedded_ui.js create mode 100644 packages/xblox/ref-control-freak/xblox/factory/Blocks.js create mode 100644 packages/xblox/ref-control-freak/xblox/generator/Javascript.js create mode 100644 packages/xblox/ref-control-freak/xblox/generator/_Base.js create mode 100644 packages/xblox/ref-control-freak/xblox/layer.profile.js create mode 100644 packages/xblox/ref-control-freak/xblox/main.js create mode 100644 packages/xblox/ref-control-freak/xblox/mainr.js create mode 100644 packages/xblox/ref-control-freak/xblox/manager/BlockManager.js create mode 100644 packages/xblox/ref-control-freak/xblox/manager/BlockManagerUI.js create mode 100644 packages/xblox/ref-control-freak/xblox/min/component.js create mode 100644 packages/xblox/ref-control-freak/xblox/min/xblox.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Block.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/BlockModel.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Block_UI.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Contains.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Expression.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/File/ReadJSON.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/ModelBase.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Reference.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Referenced.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Scope.bak.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Scope.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Statement.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/Targeted.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/code/CallMethod.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/code/RunBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/code/RunScript.html create mode 100644 packages/xblox/ref-control-freak/xblox/model/code/RunScript.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/events/OnEvent.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/events/OnKey.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/functions/CallBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/functions/PauseBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/functions/SetProperties.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/functions/StopBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/html/SetCSS.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/html/SetState.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/html/SetStyle.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/logging/Log.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/logic/BreakBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/logic/CaseBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/logic/Comparator.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/logic/DefaultBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/logic/ElseIfBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/logic/IfBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/logic/SwitchBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/loops/ForBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/loops/WhileBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/mqtt/Publish.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/mqtt/Subscribe.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/network/SSH.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/server/RunServerMethod.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/server/ServerBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/server/Shell.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/simple.json create mode 100644 packages/xblox/ref-control-freak/xblox/model/transform/BezierInterpolate.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/transform/BezierTransform.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/variables/Variable.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/variables/VariableAssignmentBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/model/variables/VariableSwitch.js create mode 100644 packages/xblox/ref-control-freak/xblox/package.json create mode 100644 packages/xblox/ref-control-freak/xblox/resources-debug.json create mode 100644 packages/xblox/ref-control-freak/xblox/resources-release.json create mode 100644 packages/xblox/ref-control-freak/xblox/run.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/addCss.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/all.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/core/OnDemandList.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/core/_StoreMixin.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/core/addCssRule.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/core/columns.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/core/createDestroy.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/core/setClass.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/core/stores.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/core/trackable.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/extensions/ColumnHider.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/extensions/CompoundColumns.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/extensions/Pagination.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor-OnDemand.html create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor.html create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/Keyboard.html create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/Keyboard.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/KeyboardTab.html create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/KeyboardTab.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/Selector.html create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/Selector.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/Tree.html create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/Tree.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/functional/util.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/intern.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/intern.local.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/mixins/ColumnSet.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/mixins/Editor-radio.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/mixins/Editor.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/mixins/Keyboard.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/mixins/Selection.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/mixins/Selector.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree-additional-filter.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree-expand-promise.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree.js create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/resources/setClass.html create mode 100644 packages/xblox/ref-control-freak/xblox/test/intern/runTests.html create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestBlock.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestBlockGrid.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestBlockGridClass.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestBlockGridClassLineNumber.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestBlocksFileEditor.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestClass.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestClass2.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestCopyPaste.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestDNDClass.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestDNDPaletteClass.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestNewPallete.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestReload.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/TestUtils.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/bak/Test.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/bak/TestClass.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/bak/TestEditor.js create mode 100644 packages/xblox/ref-control-freak/xblox/tests/bak/TestGrid.js create mode 100644 packages/xblox/ref-control-freak/xblox/types/Types.js create mode 100644 packages/xblox/ref-control-freak/xblox/utils/TestUtils.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/BlockDesignEditor.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/BlockEditDialog.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/BlockEditView.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/BlockGrid.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/BlockGridPalette.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/BlocksFileEditor.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/DeleteDialog.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/DnD.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/Grid.js create mode 100644 packages/xblox/ref-control-freak/xblox/views/ThumbRenderer.js create mode 100644 packages/xblox/ref-control-freak/xblox/widgets/BlockEditor.js create mode 100644 packages/xblox/ref-control-freak/xblox/xblox.js diff --git a/packages/xblox/ref-control-freak/xblox-ts/.gitignore b/packages/xblox/ref-control-freak/xblox-ts/.gitignore new file mode 100644 index 00000000..5148e527 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox-ts/.gitignore @@ -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 diff --git a/packages/xblox/ref-control-freak/xblox-ts/LICENSE b/packages/xblox/ref-control-freak/xblox-ts/LICENSE new file mode 100644 index 00000000..09d493bf --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox-ts/LICENSE @@ -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. diff --git a/packages/xblox/ref-control-freak/xblox-ts/README.md b/packages/xblox/ref-control-freak/xblox-ts/README.md new file mode 100644 index 00000000..58d60b2f --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox-ts/README.md @@ -0,0 +1,2 @@ +# xblox +xblox-core diff --git a/packages/xblox/ref-control-freak/xblox-ts/package.json b/packages/xblox/ref-control-freak/xblox-ts/package.json new file mode 100644 index 00000000..b14cb8c1 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox-ts/package.json @@ -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" +} diff --git a/packages/xblox/ref-control-freak/xblox-ts/src/enums.ts b/packages/xblox/ref-control-freak/xblox-ts/src/enums.ts new file mode 100644 index 00000000..6534486d --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox-ts/src/enums.ts @@ -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 = 'onRunBlock', + ON_RUN_BLOCK_FAILED = 'onRunBlockFailed', + ON_RUN_BLOCK_SUCCESS = 'onRunBlockSuccess', + ON_BLOCK_SELECTED = 'onItemSelected', + ON_BLOCK_UNSELECTED = 'onBlockUnSelected', + ON_BLOCK_EXPRESSION_FAILED = 'onExpressionFailed', + ON_BUILD_BLOCK_INFO_LIST = 'onBuildBlockInfoList', + ON_BUILD_BLOCK_INFO_LIST_END = 'onBuildBlockInfoListEnd', + ON_BLOCK_PROPERTY_CHANGED = 'onBlockPropertyChanged', + ON_SCOPE_CREATED = 'onScopeCreated', + ON_VARIABLE_CHANGED = 'onVariableChanged', + ON_CREATE_VARIABLE_CI = 'onCreateVariableCI' +} + + +export enum Type { + AssignmentExpression = 'AssignmentExpression', + ArrayExpression = 'ArrayExpression', + BlockStatement = 'BlockStatement', + BinaryExpression = 'BinaryExpression', + BreakStatement = 'BreakStatement', + CallExpression = 'CallExpression', + CatchClause = 'CatchClause', + ConditionalExpression = 'ConditionalExpression', + ContinueStatement = 'ContinueStatement', + DoWhileStatement = 'DoWhileStatement', + DebuggerStatement = 'DebuggerStatement', + EmptyStatement = 'EmptyStatement', + ExpressionStatement = 'ExpressionStatement', + ForStatement = 'ForStatement', + ForInStatement = 'ForInStatement', + FunctionDeclaration = 'FunctionDeclaration', + FunctionExpression = 'FunctionExpression', + Identifier = 'Identifier', + IfStatement = 'IfStatement', + Literal = 'Literal', + LabeledStatement = 'LabeledStatement', + LogicalExpression = 'LogicalExpression', + MemberExpression = 'MemberExpression', + NewExpression = 'NewExpression', + ObjectExpression = 'ObjectExpression', + Program = 'Program', + Property = 'Property', + ReturnStatement = 'ReturnStatement', + SequenceExpression = 'SequenceExpression', + SwitchStatement = 'SwitchStatement', + SwitchCase = 'SwitchCase', + ThisExpression = 'ThisExpression', + ThrowStatement = 'ThrowStatement', + TryStatement = 'TryStatement', + UnaryExpression = 'UnaryExpression', + UpdateExpression = 'UpdateExpression', + VariableDeclaration = 'VariableDeclaration', + VariableDeclarator = 'VariableDeclarator', + WhileStatement = 'WhileStatement', + WithStatement = 'WithStatement' +}; \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox-ts/tsconfig.json b/packages/xblox/ref-control-freak/xblox-ts/tsconfig.json new file mode 100644 index 00000000..c3f6c477 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox-ts/tsconfig.json @@ -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" + ] +} \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/.gitmodules b/packages/xblox/ref-control-freak/xblox/.gitmodules new file mode 100644 index 00000000..e69de29b diff --git a/packages/xblox/ref-control-freak/xblox/BlockActions.js b/packages/xblox/ref-control-freak/xblox/BlockActions.js new file mode 100644 index 00000000..3ae45419 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/BlockActions.js @@ -0,0 +1,1707 @@ +/** @module xblox/BlockActions **/ +define([ + 'xdojo/declare', + 'xdojo/has', + 'xide/utils', + 'xide/types', + 'xaction/DefaultActions', + 'xide/factory', + 'dojo/promise/all', + "dojo/Deferred", + 'xide/views/History', + 'xfile/views/FilePreview', + 'xblox/views/BlockEditView', + 'xide/views/_CIPanelDialog', + 'xide/mixins/Electron', + 'xblox/model/variables/VariableAssignmentBlock', + 'xcf/model/Variable', + 'xblox/views/DeleteDialog', + 'xide/widgets/ArgumentsWidget' + +], function (declare, has, utils, types, DefaultActions, factory, all, Deferred, History, FilePreview, BlockEditView, + _CIPanelDialog, Electron, VariableAssignmentBlock, Variable, DeleteDialog) { + + var _debugHightlighting = false; + var _debugRun = false; + var BLOCK_INSERT_ROOT_COMMAND = 'Step/Insert'; + var GLOBAL_CLIPBOARD = {}; + /** + * Action implementation for block grids. + * @class module:xblox/BlockActions + * @extends module:xblox/views/BlockGrid + * @augments module:xide/mixins/EventedMixin + */ + var Module = declare('xblox.BlockActions', [Electron], { + clipboardCopy: function () { + this.currentCutSelection = null; + this.currentCopySelection = this.getSelection(); + GLOBAL_CLIPBOARD = null; + GLOBAL_CLIPBOARD = this.currentCopySelection; + var clipboard = this.getClipboard(); + if (clipboard) { + var blocks = this.blockScope.blocksToJson(this.currentCopySelection); + var blocksStr = JSON.stringify(blocks, null, 2); + clipboard.writeText(blocksStr); + } + }, + download: function () { + var blocks = this.blockScope.blocksToJson(); + var blocksStr = JSON.stringify(blocks, null, 2); + utils.download("blocks.json", blocksStr); + }, + save: function () { + var driver = this.userData.driver, + ctx = this.ctx, + fileManager = ctx.getFileManager(), + scope = this.blockScope, + path = driver.path.replace('.meta.json', '.xblox'), + mount = driver.scope; + + if (scope) { + var _onSaved = function () {}; + fileManager.setContent(mount, path, scope.toString(), _onSaved); + } + }, + /***********************************************************************/ + /* + * Shared Property View + */ + propertyStruct: { + currentCIView: null, + targetTop: null, + _lastItem: null + }, + getPropertyStruct: function () { + return this.propertyStruct; + }, + setPropertyStruct: function (struct) { + this.propertyStruct = struct; + }, + /** + * + * @param source + * @param target + * @param before + * @param grid + * @param targetState + * @param insert + * @returns {*} + */ + onDrop: function (source, target, before, grid, targetState, insert) { + var ctrArgs = source.ctrArgs || {}; + var proto = source.proto; + var newBlock = null; + var isNewItem = false; + + //prepare args + if (source.ctrArgs) { //comes from factory + ctrArgs.scope = ctrArgs.scope || target.scope; + ctrArgs.group = ctrArgs.group || target.group; + ctrArgs.parentId = ctrArgs.parentId || target.id; + isNewItem = true; + } + + if (isNewItem) { + //new item at root level + if (!target.parentId && !insert) { + ctrArgs.parentId = null; + factory.createBlock(proto, ctrArgs); //root block + } else if (insert && target.canAdd && target.canAdd() != null) { //new item at item level + target.add(proto, ctrArgs, null); + } + } else { + //real item move, before or after + if (targetState === 'Moved') { + if (source.scope && target.scope && source.scope == target.scope) { + source.group = null; + target.scope.moveTo(source, target, before, insert); + return source; + } + } + } + }, + _paste: function (items, owner, cut) { + if (!items) { + return; + } + var target = this.getSelection()[0], + _flatten, + source, + thiz = this, + scope = thiz.blockScope; + + if (!owner) { + return; //@TODO : support + } + + //special case when paste on nothing + if (!target) { + //save parentIds + for (var i = 0; i < items.length; i++) { + var obj = items[i]; + if (obj.parentId) { + obj._parentOri = '' + obj.parentId; + obj.parentId = null; + } + } + //flatten them + _flatten = scope.flatten(items); + var _flat2 = _.uniq(_flatten, false, function (item) { + return item.id; + }); + //clone them + var _clones = scope.cloneBlocks2(_flat2, this.newRootItemGroup); + + scope.flatten(_clones); + var firstItem = null; + for (var i = 0; i < _clones.length; i++) { + var clone = _clones[i]; + if (!firstItem) { + firstItem = clone; + } + } + //restore parentIds + for (var i = 0; i < items.length; i++) { + var obj = items[i]; + if (obj._parentOri) { //restore + obj.parentId = obj._parentOri; + delete obj['_parentOri']; + } + } + return _clones; + } + + var grid = owner; + + var srcScope = items[0].scope; + var dstScope = target.scope; + if (srcScope != dstScope) { + return; + } + var insert = target.canAdd() || false; + var parent = srcScope.getBlockById(target.parentId); + if (!parent) { + parent = target; + } + var targetState = 'Moved'; + var before = false; + _flatten = scope.flatten(items); + _flatten = _.uniq(_flatten, false, function (item) { + return item.id; + }); + + items = srcScope.cloneBlocks2(_flatten); + var newItems = []; + for (var i = 0; i < items.length; i++) { + source = items[i]; + //Case source=target + if (source.parentId) { + newItems.push(source); + } else { + source.parentId = parent.id; + parent.add(source); + var nItem = this.onDrop(source, target, before, grid, targetState, insert); + newItems.push(nItem); + } + } + return newItems; + }, + paste: function (items, owner, cut) { + if (!items) { + return; + } + var target = this.getSelection()[0], + _flatten, + source, + thiz = this, + scope = thiz.blockScope; + + if (!owner) { + return; //@TODO : support + } + + //special case when paste on nothing + if (!target) { + //save parentIds + for (var i = 0; i < items.length; i++) { + var obj = items[i]; + if (obj.parentId) { + obj._parentOri = '' + obj.parentId; + obj.parentId = null; + } + } + //flatten them + _flatten = scope.flatten(items); + + var _flat2 = _.uniq(_flatten, false, function (item) { + return item.id; + }); + //clone them + var _clones = scope.cloneBlocks2(_flat2, this.newRootItemGroup); + scope.flatten(_clones); + var firstItem = null; + for (var i = 0; i < _clones.length; i++) { + var clone = _clones[i]; + if (!firstItem) { + firstItem = clone; + } + } + //restore parentIds + for (var i = 0; i < items.length; i++) { + var obj = items[i]; + if (obj._parentOri) { //restore + obj.parentId = obj._parentOri; + delete obj['_parentOri']; + } + } + return _clones; + } + + var grid = owner; + var srcScope = items[0].scope; + var dstScope = target.scope; + if (srcScope != dstScope) { + return; + } + var insert = target.canAdd() || false; + var parent = srcScope.getBlockById(target.parentId); + if (!parent) { + parent = target; + } + var targetState = 'Moved'; + var before = false; + + _flatten = scope.flatten(items); + + items = srcScope.cloneBlocks2(_flatten); + var newItems = []; + var parentTop = target.getTopRoot(); + + for (var i = 0; i < items.length; i++) { + source = items[i]; + var sourceParent = source.getTopRoot(); + //Case source=target + if (source.parentId) { + + if (!parentTop) { + var _parent = dstScope.getBlockById(source.parentId); + //the parent is part of the copy + if (_parent && _.find(items, { + id: _parent.id + })) { + + } else { + source.parentId = null; + } + } + } + if (source.parentId && parentTop) { + var sourceTop = source.getTopRoot(); + //same roots + if (sourceTop.id && parentTop.id && (sourceTop.id !== target.id)) { + source.parentId = null; + } + } + + if (source.parentId) { + newItems.push(source); + } else { + source.parentId = parent.id; + parent.add(source); + var nItem = this.onDrop(source, target, before, grid, targetState, insert); + newItems.push(nItem); + } + } + return newItems; + }, + /** + * + * @param items + * @param owner + * @param cut + * @returns {*} + */ + defaultActionResult: function (items) { + var dfd = new Deferred(); + var defaultSelectArgs = { + focus: true, + append: false, + delay: 0, + select: items, + expand: true + }; + dfd.resolve(defaultSelectArgs); + return dfd; + }, + setParentBlock: function (block) { + block = _.isString(block) ? this.collection.getSync(block) : block; + this.set('collection', this.collection.filter({ + parentId: block.id + })); + + var history = this.getHistory(); + history.push(block.id); + }, + refreshCurrent: function () { + var history = this.getHistory(); + var now = history.getNow(); + if (now) { + this.setParentBlock(now); + } + }, + /** + * Action runner + * @param action {module xaction/ActionModel| String } + * @returns {boolean} + */ + runAction: function (action) { + action = this.getAction(action); + if (!action) { + return null; + } + var renderer = this.getSelectedRenderer(); + var _rendererResult = renderer && renderer.runAction ? renderer.runAction.apply(this, [action]) : null; + if (_rendererResult) { + return _rendererResult; + } + try { + + if (action.command.indexOf(BLOCK_INSERT_ROOT_COMMAND) !== -1) { + return this.addItem(action.item, action); + } + + var selection = this.getSelection(), + item = selection[0], + thiz = this, + ACTION = types.ACTION; + + if (action.command == ACTION.DOWNLOAD) { + return this.download(); + } + + function addItem(_class, group, parent) { + var dfd = new Deferred(); + var cmd = null; + if (_class == Variable) { + cmd = factory.createBlock(_class, { + name: "No Title", + scope: thiz.blockScope, + group: group + }); + + } else { + cmd = thiz.addItem({ + proto: _class, + ctrArgs: { + scope: thiz.blockScope, + group: group + } + }); + } + + var defaultDfdArgs = { + select: [cmd], + focus: true, + append: false + }; + + var ref = thiz.refresh(); + ref && ref.then && ref.then(function () { + dfd.resolve(defaultDfdArgs); + }); + + if (thiz.grids) { + _.each(thiz.grids, function (grid) { + if (grid && grid.refreshRoot) { + grid.refreshRoot(); + grid.refresh(); + grid.refreshCurrent(); + } + }); + } else { + if (thiz.refreshRoot) { + thiz.refreshRoot(); + thiz.refresh(); + thiz.refreshCurrent(); + } + } + return dfd; + } + + switch (action.command) { + + case 'File/Delete': + { + return this.deleteBlock(selection); + } + case 'File/Reload': + { + return this.refresh(); + } + case 'Step/Run': + { + return this.execute(selection); + } + case 'Step/Stop': + { + return this.stop(selection); + } + case 'Step/Pause': + { + return this.pause(selection); + } + case 'Step/Back': + { + return this.goBack(); + } + case 'Step/Move Left': + { + return this.reParentBlock(-1); + } + case 'Step/Move Right': + { + return this.reParentBlock(1); + } + case 'Step/Move Up': + { + this.move(-1); + return this._itemChanged('moved', item); + } + case 'Step/Move Down': + { + this.move(1); + return this._itemChanged('moved', item); + } + case 'Step/Edit': + { + return this.editBlock(); + } + case 'Step/Open': + { + return this.openBlock(); + } + case 'New/Variable': + { + return addItem(Variable, 'Variables'); + } + case 'File/Select/None': + { + return this.deselectAll(); + } + case 'Step/Enable': + { + _.each(selection, function (item) { + item && (item.enabled = !item.enabled); + item.enabled ? item.activate() : item.deactivate(); + item.set('enabled', item.enabled); + }); + this.refreshRoot(); + this.refreshCurrent(); + return this.defaultActionResult(selection); + } + case 'View/Show/Properties': + { + var dfd = new Deferred(); + var right = this.getRightPanel(); + + function collapse() { + + var splitter = right.getSplitter(); + if (splitter) { + if (splitter.isCollapsed()) { + splitter.expand(); + return true; + } else { + right.collapse(); + return false; + } + } + } + + var wasClosed = collapse(); + if (wasClosed) { + this.showProperties(item); + } + var defaultSelectArgs = { + focus: true, + append: false, + delay: 500, + select: selection + }; + dfd.resolve(defaultSelectArgs); + this.onAfterAction('View/Show/Properties'); + return dfd; + } + case 'Step/Help': + { + return this.showHelp(item); + } + + } + + } catch (e) { + logError(e); + } + return this.inherited(arguments); + }, + /** + * Step/Move Left & Step/Move Right action + * @param dir + * @returns {boolean} + */ + 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; + }, + /** + * Step/Move Down & Step/Move Up action + * @param dir + */ + move: function (dir) { + var items = this.getSelection(); + if (!items || !items.length /*|| !item.parentId*/ ) { + console.log('cant move, no selection or parentId', items); + return; + } + var thiz = this; + _.each(items, function (item) { + item.move(dir); + }.bind(this)); + + thiz.refresh(); + this.select(items, null, true, { + focus: true, + delay: 10 + }).then(function () { + thiz.refreshActions(); + }); + }, + /** + * Clipboard/Paste action + */ + clipboardPaste: function () { + var selection = this.currentCopySelection || this.currentCutSelection || GLOBAL_CLIPBOARD; + var clipboard = this.getClipboard(); + + if (clipboard && (!selection || !selection[0])) { + var eClipboard = clipboard.readText(); + if (eClipboard) { + try { + var blocks = JSON.parse(eClipboard); + if (blocks && blocks.length) { + var tmpScope = this.blockScope.owner.getScope(utils.createUUID(), null, false); + var newBlocks = tmpScope.blocksFromJson(blocks); //add it to our scope + if (newBlocks && newBlocks.length) { + selection = newBlocks; + } + } + + } catch (e) { + logError(e, 'clipboard extraction from electron failed'); + } + } + } + if (!selection || !selection[0]) { + return; + } + + var newItems = this.paste(selection, this, this.currentCutSelection != null); + var dfd = new Deferred(); + if (newItems) { + this.refreshRoot(); + this.refreshCurrent(); + var defaultSelectArgs = { + focus: true, + append: false, + delay: 2, + select: newItems, + expand: true + }; + dfd.resolve(defaultSelectArgs); + } + return dfd; + }, + /** + * File/Delete Action + * @param items + */ + deleteBlock: function (items) { + var self = this; + var dfd = new Deferred(); + + var item = items[0]; + var parent = item.getParent(); + var _prev = item.next(null, -1) || item.next(null, 1) || parent; + + function main() { + function _delete(item) { + if (!item) { + return false; + } + + //try individual item remove function + if (item.remove) { + item.remove(); + } + + if (item.destroy) { + item.destroy(); + } + + //this should be redundant as item.remove should do the same too + var store = this.getStore(item); + if (store) { + var _itm = store.getSync(item[store.idProperty]); + store.removeSync(item[store.idProperty]); + } + + item._destroyed = true; + item.scope._emit(types.EVENTS.ON_ITEM_REMOVED, { + item: item + }); + + return true; + } + _.each(items, _delete, self); + this.refreshRoot(); + this.refreshCurrent(); + } + var title = 'Delete ' + items.length + ' ' + 'items ?'; + var dlg = new DeleteDialog({ + ctx: self.ctx, + notificationMessage: null, + title: title, + type: types.DIALOG_TYPE.DANGER, + onCancel: function () { + dfd.resolve({ + select: items, + focus: true, + append: false + }); + }, + onOk: function () { + main.bind(self)(); + dfd.resolve({ + focus: true, + append: false, + select: _prev + }); + } + }); + dlg.show(); + return dfd; + }, + /** + * File/Edit action + * @param item + * @param changedCB + * @param select + */ + editBlock2: function (item, changedCB, select) { + if (!item) { + return; + } + if (!item.canEdit()) { + return; + } + var title = 'Edit ', + thiz = this; + + title += item.name; + var cis = item.getFields(); + var cisView = null; + var self = this; + + function ok(changedCIS) { + _.each(changedCIS, function (evt) { + thiz.onCIChanged && thiz.onCIChanged(evt.ci, item, evt.oldValue, evt.newValue, evt.ci.dst, cis, evt.props); + }); + self.showProperties(item, true); + } + + var panel = new _CIPanelDialog({ + title: title, + containerClass: 'CIDialog', + onOk: function (changedCIS) { + ok(changedCIS); + this.headDfd.resolve(changedCIS); + }, + onCancel: function (changedCIS) { + this.headDfd.resolve(changedCIS); + }, + cis: cis, + CIViewClass: BlockEditView, + CIViewOptions: { + delegate: this, + resizeToParent: true, + ciSort: true, + options: { + groupOrder: { + 'General': 1, + 'Send': 2, + 'Advanced': 4, + 'Description': 5 + } + }, + cis: cis + } + }); + var dfd = panel.show(); + return dfd; + }, + _getText: function (url) { + if (has('debug')) { + url += '?time=' + new Date().getTime(); + } + try { + var result; + dojo.xhrGet({ + url: url, + sync: true, + handleAs: 'text', + cache: false, + load: function (text) { + result = text; + } + }); + return (result || 'No help avaiable'); + } catch (e) { + return false; + } + }, + _helpCache: {}, + getHelp: function (item) { + + var mid = utils.replaceAll('.', '/', item.declaredClass); + //special treat for xcf + mid = mid.replace('xcf', 'xblox'); + var rootNS = 'xblox/model/'; + mid = 'xblox/docs/' + mid.replace(rootNS, ''); + + var cached = this._helpCache[mid]; + if (cached === false) { + return false; + } else if (_.isString(cached)) { + return cached; + } + + var helpText = this._getText(require.toUrl(mid) + '.md'); + if (!helpText) { + return false; + } + //https://github.com/showdownjs/showdown + if (typeof showdown !== 'undefined') { + var converter = new showdown.Converter(); + helpText = converter.makeHtml(helpText); + console.log('load from ' + require.toUrl(mid) + '.md'); + } else { + console.warn('have no `showdown`, proceed without'); + } + this._helpCache[mid] = helpText; + return helpText; + }, + showHelp: function (item, parent) { + var row = this.row(item), + el = row.element, + self = this; + + function render(where, item) { + var help = self.getHelp(item); + if (help) { + where.html(help); + } + where.addClass('xbloxHelp'); + } + + if (this._preview) { + this._preview.open(); + render(this._preview.preview, item); + return; + } + var _prev = new FilePreview({ + node: $(el), + parent: $(el), + ctx: self.ctx, + container: parent ? parent.containerNode : null, + title: 'Help' + }); + parent ? _prev.buildRenderingEmbedded() : _prev.buildRendering(); + _prev.init(); + _prev.exec(); + this._preview = _prev; + this._preview.handler = this; + _prev.onOpened = function () { + render(_prev.preview, item); + }; + this.add(_prev); + self._on('selectionChanged', function (e) { + if (self._preview.getstate() == 0) { + return; + } + var _item = self.getSelectedItem(); + if (_item) { + _prev.item = _item; + render(self._preview.preview, _item); + } + }); + }, + getHistory: function () { + if (!this._history) { + this._history = new History(); + } + + return this._history; + }, + goBack: function () { + var store = this.collection, + history = this.getHistory(), + now = null, + prev = history.getNow(), + self = this, + head = new Deferred(), + select = true; + history.pop(); + now = history.getNow(); + + function resolve(item) { + head.resolve({ + select: select !== false ? (item ? item : self.getRows()[0]) : null, + focus: true, + append: false, + delay: 250 + }); + } + if (now && store.getSync(now)) { + this.set('collection', this.collection.filter({ + parentId: now + })); + resolve(); + } else { + store.reset(); + this.refreshRoot(); + resolve(store.getSync(prev)); + } + return head; + + }, + openBlock: function (item, changedCB, select) { + var selection = this.getSelection(); + item = selection[0] || item; + if (!item) { + return; + } + var head = new Deferred(); + var self = this; + var isBack = false; + this.setParentBlock(item); + select = true; + var rows = self.getRows()[0]; + if (!rows) { + setTimeout(function () { + self.contentNode.focus(); + }, 10); + } + head.resolve({ + select: select !== false ? (isBack ? item : rows) : null, + focus: true, + append: false, + delay: 1 + }); + + return head; + }, + editBlock: function (item, changedCB, select) { + var selection = this.getSelection(); + item = selection[0] || item; + if (!item) { + return; + } + var head = new Deferred(); + var children = item.getChildren(), + self = this; + + var dlgDfd = this.editBlock2(item, null); + if (!dlgDfd) { + head.resolve(); + return; + } + dlgDfd.then(function () { + head.resolve({ + select: [item], + focus: true, + append: false, + delay: 1 + }); + }); + + return head; + }, + execute: function (_blocks) { + var thiz = this, + dfds = [], //all Deferreds of selected blocks to run + handles = [], + EVENTS = types.EVENTS, + blocks = _.isArray(_blocks) ? _blocks : [_blocks]; + + + function run(block) { + + var _runHandle = block._on(EVENTS.ON_RUN_BLOCK, function (evt) { + _debugHightlighting && console.error('active'); + thiz.mark(thiz.toNode(evt), 'activeBlock', block); + }), + + //event handle "Success" + _successHandle = block._on(EVENTS.ON_RUN_BLOCK_SUCCESS, function (evt) { + _debugHightlighting && console.log('marke success', evt); + thiz.mark(thiz.toNode(evt), 'successBlock', block); + + }), + + //event handle "Error" + _errHandle = block._on(EVENTS.ON_RUN_BLOCK_FAILED, function (evt) { + _debugHightlighting && console.error('failed', evt); + thiz.mark(thiz.toNode(evt), 'failedBlock', block); + }); + + _debugRun && console.error('run block '); + + + if (!block || !block.scope) { + console.error('have no scope'); + return; + } + + try { + var blockDfd = block.scope.solveBlock(block, { + highlight: true, + force: true, + listener: thiz + }); + + dfds.push(blockDfd); + + } catch (e) { + logError(e, 'excecuting block - ' + block.name + ' failed! : '); + } + handles = handles.concat([_runHandle, _errHandle, _successHandle]); + return true; + } + + function _patch(block) { + + block.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) { + console.log('----def block finish'); + onFinishBlock(block, results); + }); + }; + + if (blocks.length) { + + for (var n = index; n < blocks.length; n++) { + var block = blocks[n]; + console.log('run child \n' + block.method); + + _patch(block); + + if (block.deferred === true) { + block._deferredObject = new Deferred(); + this._currentIndex = n; + wireBlock(block); + //this.addToEnd(this._return, block.solve(this.scope, settings)); + var blockDfd = block.solve(this.scope, settings); + allDfds.push(blockDfd); + break; + } else { + //this.addToEnd(this._return, block.solve(this.scope, settings)); + + var blockDfd = block.solve(this.scope, settings); + allDfds.push(blockDfd); + } + + } + + } else { + this.onSuccess(this, settings); + } + + return allDfds; + }; + + block.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()], + + //outer,head dfd + dfd = new Deferred(), + listener = settings.listener, + isDfd = thiz.deferred; + + //moved to Contains#onRunThis + if (listener) { + listener._emit(types.EVENTS.ON_RUN_BLOCK, thiz); + } + + //function when a block did run successfully, + // moved to Contains#onDidRunItem + function _finish(dfd, result, event) { + + if (listener) { + listener._emit(event || types.EVENTS.ON_RUN_BLOCK_SUCCESS, thiz); + } + dfd.resolve(result); + + + } + + //function when a block did run successfully + function _error(result) { + dfd.reject(result); + if (listener) { + listener._emit(types.EVENTS.ON_RUN_BLOCK_FAILED, thiz); + } + } + + + //moved to Contains#onDidRunThis + function _headDone(result) { + + + //more blocks? + if (items.length) { + var subDfds = thiz.runFrom(items, 0, settings); + + all(subDfds).then(function (what) { + console.log('all solved!', what); + _finish(dfd, result); + }, function (err) { + console.error('error in chain', err); + _finish(dfd, err); + }); + + } else { + _finish(dfd, result); + } + } + + + if (_script && _script.length) { + + var runScript = function () { + + var _function = new Function("{" + _script + "}"); + var _args = thiz.getArgs() || []; + try { + + if (isDfd) { + ctx.resolve = function (result) { + if (thiz._deferredObject) { + thiz._deferredObject.resolve(); + } + _headDone(result); + }; + } + 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); + } + } catch (e) { + e = e || {}; + _error(e); + if (error) { + error('invalid expression : \n' + _script + ': ' + e); + } + } + }; + + 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 { + runScript(); + } + } else { + console.error('have no script'); + } + return dfd; + }; + } + + _.each(blocks, run); + all(dfds).then(function () { + _debugRun && console.log('did run all selected blocks!', thiz); + // _.invoke(handles, 'remove'); + }); + }, + stop: function (_blocks) { + var blocks = _.isArray(_blocks) ? _blocks : [_blocks]; + + function stop(block) { + if (!block || !block.scope) { + console.error('have no scope'); + return; + } + try { + block.stop(false); + } catch (e) { + logError(e, 'stop block - ' + block.name + ' failed! : '); + } + return true; + } + + _.each(blocks, stop); + }, + pause: function (_blocks) { + var blocks = _.isArray(_blocks) ? _blocks : [_blocks]; + + function stop(block) { + if (!block || !block.scope) { + console.error('have no scope'); + return; + } + try { + block.pause(true); + } catch (e) { + logError(e, 'pause block - ' + block.name + ' failed! : '); + } + return true; + } + + _.each(blocks, stop); + }, + /** + * + * @param item + * @returns {Array} + */ + getAddActions: function (item) { + var thiz = this; + var items = this.inherited(arguments) || []; + item = item || {}; + items = factory.getAllBlocks(this.blockScope, this, item, this.blockGroup, false); + //tell everyone + thiz.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items, + view: this, + owner: this.owner, + item: item, + group: this.blockGroup, + scope: this.blockScope + }); + + thiz._emit(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items, + view: this, + owner: this.owner + }); + return items; + }, + getBlockActions: function (permissions) { + var result = this.inherited(arguments) || [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + thiz = this, + actionStore = thiz.getActionStore(); + + var defaultMixin = { + addPermission: true + }; + + result.push(thiz.createAction({ + label: 'Save', + command: 'File/Save', + icon: 'fa-save', + tab: 'Home', + group: 'File', + keycombo: ['ctrl s'], + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(thiz.createAction({ + label: 'Save As', + command: 'File/Save As', + icon: 'fa-save', + tab: 'Home', + group: 'File', + mixin: defaultMixin + })); + + result.push(this.createAction({ + label: 'Open', + command: 'Step/Open', + icon: 'fa-folder-open', + keycombo: ['ctrl enter'], + tab: 'Home', + group: 'Navigation', + shouldDisable: isItem, + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(thiz.createAction({ + label: 'Go up', + command: 'Step/Back', + icon: ACTION_ICON.GO_UP, + tab: 'Home', + group: 'Navigation', + keycombo: ['backspace'], + mixin: { + quick: true + } + })); + + function _selection() { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return null; + } + var item = selection[0]; + if (!item) { + return null; + } + return selection; + } + + function canParent() { + var selection = thiz.getSelection(); + if (!selection) { + return true; + } + var item = selection[0]; + if (!item) { + return true; + } + if (this.command === 'Step/Move Left') { + return !item.getParent(); + } else { + return item.getParent(); + } + } + + function isItem() { + var selection = _selection(); + if (!selection) { + return true; + } + return false; + } + + function canMove() { + var selection = _selection(); + if (!selection) { + return true; + } + var item = selection[0]; + if (this.command === 'Step/Move Up') { + return !item.canMove(null, -1); + } else { + return !item.canMove(null, 1); + } + } + + result.push(thiz.createAction({ + label: 'Run', + command: 'Step/Run', + icon: 'text-success fa-play', + tab: 'Home', + group: 'Step', + keycombo: ['r'], + shouldDisable: isItem, + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(thiz.createAction({ + label: 'Stop', + command: 'Step/Stop', + icon: 'text-warning fa-stop', + tab: 'Home', + group: 'Step', + keycombo: ['s'], + shouldDisable: isItem, + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(thiz.createAction({ + label: 'Pause', + command: 'Step/Pause', + icon: 'text-info fa-pause', + tab: 'Home', + group: 'Step', + keycombo: ['p'], + shouldDisable: isItem, + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + ////////////////////////////////////////////////////////////////// + // + // Step - Move + // + + result.push(thiz.createAction({ + label: 'MoveUp', + command: 'Step/Move Up', + icon: 'text-info fa-arrow-up', + tab: 'Home', + group: 'Move', + keycombo: ['alt up'], + shouldDisable: canMove, + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(thiz.createAction({ + label: 'MoveDown', + command: 'Step/Move Down', + icon: 'text-info fa-arrow-down', + tab: 'Home', + group: 'Move', + keycombo: ['alt down'], + shouldDisable: canMove, + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(thiz.createAction({ + label: 'MoveLeft', + command: 'Step/Move Left', + icon: 'text-info fa-arrow-left', + tab: 'Home', + group: 'Move', + keycombo: ['alt left'], + shouldDisable: canMove, + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(thiz.createAction({ + label: 'MoveRight', + command: 'Step/Move Right', + icon: 'text-info fa-arrow-right', + tab: 'Home', + group: 'Move', + keycombo: ['alt right'], + shouldDisable: canParent, + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(thiz.createAction({ + label: 'Help', + command: 'Step/Help', + icon: 'fa-question', + tab: 'Home', + group: 'Step', + keycombo: ['h'], + shouldDisable: isItem, + mixin: defaultMixin + })); + + + result.push(thiz.createAction({ + label: 'On', + command: 'Step/Enable', + icon: 'fa-toggle-off', + tab: 'Home', + group: 'Step', + keycombo: ['alt o'], + shouldDisable: isItem, + mixin: utils.mixin({ + actionType: 'multiToggle', + quick: true + }, defaultMixin), + onCreate: function (action) { + //action.set('value',true); + action.value = true; + action.setVisibility(types.ACTION_VISIBILITY.RIBBON, { + group: 'Common' + }); + }, + onChange: function (property, value) { + var item = thiz.getSelectedItem(); + if (item) { + item.set('enabled', value); + } + this.value = value; + thiz.refreshActions(); + thiz.refresh(true); + thiz.select(item); + } + })); + + result.push(this.createAction({ + label: 'Variable', + command: 'New/Variable', + icon: 'fa-code', + tab: 'Home', + group: 'Insert', + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(this.createAction({ + label: 'Edit', + command: 'Step/Edit', + icon: ACTION_ICON.EDIT, + keycombo: ['f4', 'enter', 'dblclick'], + tab: 'Home', + group: 'Step', + shouldDisable: isItem, + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + result.push(thiz.createAction({ + label: 'Properties', + command: 'View/Show/Properties', + icon: 'fa-gears', + tab: 'Home', + group: 'Show', + keycombo: ['alt p'], + shouldDisable: isItem, + mixin: utils.mixin({ + actionType: 'multiToggle', + value: true + }, defaultMixin), + onCreate: function (action) { + var right = thiz.getRightPanel(); + right.getSplitter(); + //action.set('value',true); + action.value = true; + }, + onChange: function (property, value) { + var right = thiz.getRightPanel(); + + function collapse() { + var splitter = right.getSplitter(); + if (splitter) { + if (splitter.isCollapsed()) { + splitter.expand(); + return true; + } else { + right.collapse(); + return false; + } + } + } + + var wasClosed = collapse(); + var result = true; + if (wasClosed) { + thiz.showProperties(null); + } + thiz.onAfterAction('View/Show/Properties'); + return result; + } + })); + + + var newBlockActions = this.getAddActions(); + var levelName = ''; + + var rootAction = BLOCK_INSERT_ROOT_COMMAND; + result.push(thiz.createAction({ + label: 'Add Block', + command: '' + rootAction, + icon: 'fa-plus', + tab: 'Home', + group: 'Insert', + mixin: utils.mixin({ + quick: true + }, defaultMixin) + })); + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + result.push(thiz.createAction({ + label: levelName, + command: path, + icon: item.iconClass, + tab: 'Home', + group: 'Insert', + mixin: utils.mixin({ + item: item, + quick: true + }, defaultMixin) + })); + if (isContainer) { + addItems(path, item.items); + } + } + } + + addItems(rootAction, newBlockActions); + //////////////////////////////////////////////////////// + // + // Variables + // + var setVariableRoot = rootAction + '/Set Variable'; + + function _getSetVariableActions(permissions, owner) { + var variables = thiz.blockScope.getVariables(); + var result = []; + for (var i = 0; i < variables.length; i++) { + var variable = variables[i]; + result.push(thiz.createAction({ + label: variable.name, + command: '' + setVariableRoot + '/' + variable.name, + icon: 'fa-paper-plane', + tab: 'Home', + group: 'Step', + mixin: utils.mixin({ + item: variable, + proto: VariableAssignmentBlock, + custom: true, + ctrArgs: { + variable: variable.id, + variableId: variable.id, + scope: thiz.blockScope, + value: '0' + } + }, defaultMixin) + })); + } + return result; + } + + result.push(thiz.createAction({ + label: 'Set Variable', + command: '' + setVariableRoot, + icon: 'fa-paper-plane', + tab: 'Home', + group: 'Step', + mixin: utils.mixin({ + global: true, + quick: true, + getActions: _getSetVariableActions + }, defaultMixin) + })); + result.push(thiz.createAction({ + label: 'None', + command: '' + setVariableRoot + '/None', + icon: 'fa-paper-plane', + tab: 'Home', + group: 'Step', + mixin: utils.mixin({ + proto: VariableAssignmentBlock, + item: VariableAssignmentBlock, + quick: true, + ctrArgs: { + scope: this.blockScope, + value: '0' + } + }, defaultMixin) + })); + + this._emit('onAddActions', { + actions: result, + permissions: permissions, + store: actionStore + }); + return result; + } + + }); + Module.BLOCK_INSERT_ROOT_COMMAND = BLOCK_INSERT_ROOT_COMMAND; + return Module; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/CSSState.js b/packages/xblox/ref-control-freak/xblox/CSSState.js new file mode 100644 index 00000000..c1d0b238 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/CSSState.js @@ -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]); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/Gruntfile.js b/packages/xblox/ref-control-freak/xblox/Gruntfile.js new file mode 100644 index 00000000..a5a3e983 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/Gruntfile.js @@ -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' ]); +}; \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/Readme.md b/packages/xblox/ref-control-freak/xblox/Readme.md new file mode 100644 index 00000000..e69de29b diff --git a/packages/xblox/ref-control-freak/xblox/RunScript.js b/packages/xblox/ref-control-freak/xblox/RunScript.js new file mode 100644 index 00000000..e181fcd3 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/RunScript.js @@ -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]); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/StyleState.js b/packages/xblox/ref-control-freak/xblox/StyleState.js new file mode 100644 index 00000000..f676dcde --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/StyleState.js @@ -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]); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/_State.js b/packages/xblox/ref-control-freak/xblox/_State.js new file mode 100644 index 00000000..dade44e8 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/_State.js @@ -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; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/_Stated.js b/packages/xblox/ref-control-freak/xblox/_Stated.js new file mode 100644 index 00000000..a755d7bb --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/_Stated.js @@ -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 || []; + } + }) +}); diff --git a/packages/xblox/ref-control-freak/xblox/bak/BlocksFileEditor.js b/packages/xblox/ref-control-freak/xblox/bak/BlocksFileEditor.js new file mode 100644 index 00000000..40a32002 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/bak/BlocksFileEditor.js @@ -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="" +label + ""; + var pre = ""; + if(intend>0){ + for (var i = 0; i < intend; i++) { + pre+=" "; + pre+=" "; + pre+=" "; + } + } + 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="" +label + ""; + var pre = ""; + + if(intend>0){ + for (var i = 0; i < intend; i++) { + pre+=" "; + pre+=" "; + pre+=" "; + } + } + 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: '
Select the event for the new group:
', + 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()); + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/bak/BlocksFileEditor.js2 b/packages/xblox/ref-control-freak/xblox/bak/BlocksFileEditor.js2 new file mode 100644 index 00000000..86e48c24 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/bak/BlocksFileEditor.js2 @@ -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="" +label + ""; + var pre = ""; + if(intend>0){ + for (var i = 0; i < intend; i++) { + pre+=" "; + pre+=" "; + pre+=" "; + } + } + 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 ? "" +label + "" : label; + var pre = ""; + if(intend>0){ + for (var i = 0; i < intend; i++) { + pre+=" "; + pre+=" "; + pre+=" "; + } + } + 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,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                        ', + 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; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/component.js b/packages/xblox/ref-control-freak/xblox/component.js new file mode 100644 index 00000000..eace97e4 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/component.js @@ -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; + } + }); +}); + diff --git a/packages/xblox/ref-control-freak/xblox/data/Store.js b/packages/xblox/ref-control-freak/xblox/data/Store.js new file mode 100644 index 00000000..155a5f0e --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/data/Store.js @@ -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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/docs/Command.md b/packages/xblox/ref-control-freak/xblox/docs/Command.md new file mode 100644 index 00000000..e6a25b3b --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/docs/Command.md @@ -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. + +
+ + +**Send on Startup** + +If true, this block will be executed when the device is connected. + +Field Name: "startup" + +Type: Boolean + +
+ +**Interval** + +If greater than 0, the block will be looped by that interval in ms. + +Field Name: "interval" + +Type: Boolean + +
+ + +**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 + +
+ +**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 + + +
+ diff --git a/packages/xblox/ref-control-freak/xblox/docs/Events/OnEvent.md b/packages/xblox/ref-control-freak/xblox/docs/Events/OnEvent.md new file mode 100644 index 00000000..69dc215f --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/docs/Events/OnEvent.md @@ -0,0 +1,106 @@ +## Description + +This block enables to subscribe to system events. + + +## Parameters + +**Enabled** + +Field Name : "enabled" + +Type : Boolean + +
+ +**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 + +
+ +**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" + +
+ +**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]; + +
+ +### Tips: + +Attach a Run Script block with this code: + +``` +console.log(arguments); +``` + +to see the event payload in the Dev-Tools console + diff --git a/packages/xblox/ref-control-freak/xblox/docs/File/ReadJSON.md b/packages/xblox/ref-control-freak/xblox/docs/File/ReadJSON.md new file mode 100644 index 00000000..25ff2cf9 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/docs/File/ReadJSON.md @@ -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. + +
+ + +**Path** + +Field Name : "path" + +Type : String + +Description : An absolute path. This path must exist on the device server. + + +
+ + +**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. + + +
+ + +**Tips**: + +In child blocks you can retrieve the data by using "return arguments[0]". + diff --git a/packages/xblox/ref-control-freak/xblox/docs/_index.md b/packages/xblox/ref-control-freak/xblox/docs/_index.md new file mode 100644 index 00000000..85002d03 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/docs/_index.md @@ -0,0 +1,5 @@ +### XBlocks Reference + +### Driver related + +[Command](Blocks/Command) diff --git a/packages/xblox/ref-control-freak/xblox/embedded.js b/packages/xblox/ref-control-freak/xblox/embedded.js new file mode 100644 index 00000000..85599f55 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/embedded.js @@ -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); + }; + } +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/embedded_ui.js b/packages/xblox/ref-control-freak/xblox/embedded_ui.js new file mode 100644 index 00000000..4bd650c3 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/embedded_ui.js @@ -0,0 +1,6 @@ +define([ + 'xblox/embedded', + 'xblox/factory/Blocks', + 'xide/widgets/ScriptWidget' +], function () { +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/factory/Blocks.js b/packages/xblox/ref-control-freak/xblox/factory/Blocks.js new file mode 100644 index 00000000..08e9fe58 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/factory/Blocks.js @@ -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; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/generator/Javascript.js b/packages/xblox/ref-control-freak/xblox/generator/Javascript.js new file mode 100644 index 00000000..71101125 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/generator/Javascript.js @@ -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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/generator/_Base.js b/packages/xblox/ref-control-freak/xblox/generator/_Base.js new file mode 100644 index 00000000..b88aceb2 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/generator/_Base.js @@ -0,0 +1,5 @@ +define([ + 'dojo/_base/declare' +],function(declare){ + return declare("xblox.generator._Base", null,{}); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/layer.profile.js b/packages/xblox/ref-control-freak/xblox/layer.profile.js new file mode 100644 index 00000000..28aa0200 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/layer.profile.js @@ -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', + + // We’re 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, you’ll 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), we’re 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 don’t 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 don’t 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 don’t 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 don’t need that either + 'dojo-publish-privates':0, + // We’re 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 aren’t 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 + }; + } + } +}; diff --git a/packages/xblox/ref-control-freak/xblox/main.js b/packages/xblox/ref-control-freak/xblox/main.js new file mode 100644 index 00000000..c4df4cd4 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/main.js @@ -0,0 +1,5 @@ +define([ + "dojo/_base/kernel" +], function(){ + return dojo.xblox; +}); diff --git a/packages/xblox/ref-control-freak/xblox/mainr.js b/packages/xblox/ref-control-freak/xblox/mainr.js new file mode 100644 index 00000000..1fc8dce8 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/mainr.js @@ -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(){}); diff --git a/packages/xblox/ref-control-freak/xblox/manager/BlockManager.js b/packages/xblox/ref-control-freak/xblox/manager/BlockManager.js new file mode 100644 index 00000000..c9335272 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/manager/BlockManager.js @@ -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(); + } + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/manager/BlockManagerUI.js b/packages/xblox/ref-control-freak/xblox/manager/BlockManagerUI.js new file mode 100644 index 00000000..310b3832 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/manager/BlockManagerUI.js @@ -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 () {} + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/min/component.js b/packages/xblox/ref-control-freak/xblox/min/component.js new file mode 100644 index 00000000..6d419e61 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/min/component.js @@ -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; + } + }); +}); + diff --git a/packages/xblox/ref-control-freak/xblox/min/xblox.js b/packages/xblox/ref-control-freak/xblox/min/xblox.js new file mode 100644 index 00000000..e69de29b diff --git a/packages/xblox/ref-control-freak/xblox/model/Block.js b/packages/xblox/ref-control-freak/xblox/model/Block.js new file mode 100644 index 00000000..cf77aa94 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Block.js @@ -0,0 +1,1036 @@ +/** @module xblox/model/Block **/ +define([ + 'dcl/dcl', + "dojo/Deferred", + "dojo/_base/lang", + "dojo/has", + "xblox/model/ModelBase", + "xide/factory", + "xide/utils", + "xide/types", + "xide/utils/ObjectUtils", + "xide/lodash", + "xdojo/has!xblox-ui?xblox/model/Block_UI" +], function (dcl, Deferred, lang, has, ModelBase, factory, utils, types, ObjectUtils, _, Block_UI) { + + var bases = [ModelBase]; + + function index(what, items) { + for (var j = 0; j < items.length; j++) { + if (what.id === items[j].id) { + return j; + } + } + } + + function compare(left, right) { + return index(left) - index(right); + } + + if (!has('host-browser')) { + bases.push(dcl(null, { + getStatusIcon: function () {}, + getStatusClass: function () {}, + setStatusClass: function () {}, + onActivity: function () {}, + onRun: function () {}, + onFailed: function () {}, + onSuccess: function () {}, + getIconClass: function () {} + })); + } + if (Block_UI) { + bases.push(Block_UI); + } + + /*** + * First things first, extend the core types for block flags: + */ + utils.mixin(types, { + /** + * 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 + */ + 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 + */ + 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 + } + + }); + /** + * Base block class. + * + * @class module:xblox/model/Block + * @extends module:xblox/model/ModelBase + * @lends module:xblox/model/Block_UI + */ + var Block = dcl(bases, { + declaredClass: "xblox.model.Block", + scopeId: null, + isCommand: false, + outlet: 0, + _destroyed: false, + /** + * Switch to include the block for execution. + * @todo, move to block flags + * @type {boolean} + * @default true + */ + enabled: true, + /** + * Switch to include the block for serialization. + * @todo, move to block flags + * @type {boolean} + * @default true + */ + serializeMe: true, + /** + * Name is used for the interface, mostly as prefix within + * this.toText() which includes also the 'icon' (as icon font). + * This should be unique and expressive. + * + * This field can be changed by the user. Examples + * 'Run Script' will result in result 'Run Script + this.script' + * + * @todo: move that in user space, combine that with a template system, so any block ui parts gets off from here! + * @type {string|null} + * @default null + * @required false + */ + name: null, + /** + * @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 + */ + shareTitle: '', + /** + * 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 {boolean} + * @default 'No Description' + * @required true + */ + sharable: false, + /** + * Container holding a 'child' blocks. Subclassing block might hold that somewhere else. + * @type {Block[]} + * @default null + * @required false + */ + items: null, + /** + * Parent up-link + * @type {string|Block} + * @default null + * @required false + */ + parent: null, + /** + * @var temporary variable to hold remainin blocks to run + */ + _return: null, + /** + * @var temporary variable to store the last result + */ + _lastResult: null, + _deferredObject: null, + _currentIndex: 0, + _lastRunSettings: null, + _onLoaded: false, + beanType: 'BLOCK', + override: {}, + /** + * ignore these due to serialization + */ + ignoreSerialize: [ + '_didSubscribe', + '_currentIndex', + '_deferredObject', + '_destroyed', + '_return', + 'parent', + '__started', + 'ignoreSerialize', + '_lastRunSettings', + '_onLoaded', + 'beanType', + 'sharable', + 'override', + 'virtual', + '_scenario', + '_didRegisterSubscribers', + 'additionalProperties', + 'renderBlockIcon', + 'serializeMe', + '_statusIcon', + '_statusClass', + 'hasInlineEdits', + '_loop', + 'help', + 'owner', + '_lastCommand', + 'allowActionOverride', + 'canDelete', + 'isCommand', + 'lastCommand', + 'autoCreateElse', + '_postCreated', + '_counter' + ], + _parseString: function (string, settings, block, flags) { + try { + settings = settings || this._lastSettings || {}; + flags = flags || settings.flags || types.CIFLAG.EXPRESSION; + var scope = this.scope; + var value = string; + var parse = !(flags & types.CIFLAG.DONT_PARSE); + var isExpression = (flags & types.CIFLAG.EXPRESSION); + if (flags & types.CIFLAG.TO_HEX) { + value = utils.to_hex(value); + } + if (parse !== false) { + value = utils.convertAllEscapes(value, "none"); + } + var override = settings.override || this.override || {}; + var _overrides = (override && override.variables) ? override.variables : null; + var res = ""; + if (isExpression && parse !== false) { + res = scope.parseExpression(value, null, _overrides, null, null, null, override.args, flags); + } else { + res = '' + value; + } + } catch (e) { + console.error(e); + } + return res; + }, + postCreate: function () {}, + /** + * + * @param clz + * @returns {Array} + */ + childrenByClass: function (clz) { + var all = this.getChildren(); + var out = []; + for (var i = 0; i < all.length; i++) { + var obj = all[i]; + if (obj.isInstanceOf(clz)) { + out.push(obj); + } + } + return out; + }, + /** + * + * @param clz + * @returns {Array} + */ + childrenByNotClass: function (clz) { + var all = this.getChildren(); + var out = []; + for (var i = 0; i < all.length; i++) { + var obj = all[i]; + if (!obj.isInstanceOf(clz)) { + out.push(obj); + } + } + return out; + }, + /** + * + * @returns {null} + */ + getInstance: function () { + var instance = this.scope.instance; + if (instance) { + return instance; + } + return null; + }, + pause: function () {}, + mergeNewModule: function (source) { + for (var i in source) { + var o = source[i]; + if (o && _.isFunction(o)) { + this[i] = o; //swap + } + } + }, + __onReloaded: function (newModule) { + this.mergeNewModule(newModule.prototype); + var _class = this.declaredClass; + var _module = lang.getObject(utils.replaceAll('/', '.', _class)) || lang.getObject(_class); + if (_module) { + if (_module.prototype && _module.prototype.solve) { + this.mergeNewModule(_module.prototype); + } + } + }, + reparent: function () { + var item = this; + if (!item) { + return false; + } + var parent = item.getParent(); + if (parent) {} else { + var _next = item.next(null, 1) || item.next(null, -1); + if (_next) { + item.group = null; + _next._add(item); + } + } + }, + unparent: function (blockgroup, move) { + var item = this; + if (!item) { + return false; + } + var parent = item.getParent(); + if (parent && parent.removeBlock) { + parent.removeBlock(item, false); + } + + item.group = blockgroup; + item.parentId = null; + item.parent = null; + if (move !== false) { + item._place(null, -1, null); + item._place(null, -1, null); + } + }, + move: function (dir) { + var 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 { + item._place(null, dir); + return true; + } + }, + _place: function (ref, direction, items) { + var store = this._store, + dst = this; + ref = ref || dst.next(null, direction); + if (!ref) { + console.error('have no next', this); + return; + } + ref = _.isString(ref) ? store.getSync(ref) : ref; + dst = _.isString(dst) ? store.getSync(dst) : dst; + items = items || store.storage.fullData; + direction = direction == -1 ? 0 : 1; + items.remove(dst); + if (direction == -1) { + direction = 0; + } + items.splice(Math.max(index(ref, items) + direction, 0), 0, dst); + store._reindex(); + }, + index: function () { + var item = this, + parent = item.getParent(), + items = null, + group = item.group, + store = this._store; + + if (parent) { + items = parent[parent._getContainer(item)] || []; + items = items.filter(function (item) { + return item.group === group; + }); + if (!items || items.length < 2 || !this.containsItem(items, item)) { + return false; + } + return this.indexOf(items, item); + } else { + items = store.storage.fullData; + items = items.filter(function (item) { + return item.group === group; + }); + return this.indexOf(items, item); + } + }, + numberOfParents: function () { + var result = 0; + var parent = this.getParent(); + if (parent) { + result++; + result += parent.numberOfParents(); + } + return result; + }, + getTopRoot: function () { + var last = this.getParent(); + if (last) { + var next = last.getParent(); + if (next) { + last = next; + } + } + return last; + }, + next: function (items, dir) { + items = items || this._store.storage.fullData; + + function _next(item, items, dir, step, _dstIndex) { + var start = item.indexOf(items, item); + var upperItem = items[start + (dir * step)]; + if (upperItem) { + if (!upperItem.parentId && upperItem.group && upperItem.group === item.group) { + _dstIndex = start + (dir * step); + return upperItem; + } else { + step++; + return _next(item, items, dir, step, _dstIndex); + } + } + return null; + } + return _next(this, items, dir, 1, 0); + }, + /** + * + * @param createRoot + * @returns {module:xblox/model/Block|null} + */ + getParent: function (createRoot) { + if (this.parentId) { + return this.scope.getBlockById(this.parentId); + } + }, + getScope: function () { + var scope = this.scope; + if (this.scopeId && this.scopeId.length > 0) { + var owner = scope.owner; + if (owner && owner.hasScope) { + if (owner.hasScope(this.scopeId)) { + scope = owner.getScope(this.scopeId); + } else { + console.error('have scope id but cant resolve it', this); + } + } + } + return scope; + }, + /** + * + * @returns {null} + */ + canAdd: function () { + return null; + }, + getTarget: function () { + var _res = this._targetReference; + if (_res) { + return _res; + } + var _parent = this.getParent(); + if (_parent && _parent.getTarget) { + _res = _parent.getTarget(); + } + return _res; + }, + // adds array2 at the end of array1 => useful for returned "solve" commands + addToEnd: function (array1, array2) { + if (array2 && array1.length != null && array2.length != null) { + array1.push.apply(array1, array2); + } + return array1; + }, + /** + * + * @param what + * @param del delete block + */ + removeBlock: function (what, del) { + if (what) { + if (del !== false && what.empty) { + what.empty(); + } + if (del !== false) { + delete what.items; + } + what.parent = null; + what.parentId = null; + if (this.items) { + this.items.remove(what); + } + } + }, + ///////////////////////////////////////////////////////////////////////////////////// + // + // Accessors + // + ///////////////////////////////////////////////////////////////////////////////////// + _getContainer: function (item) { + return 'items'; + }, + ///////////////////////////////////////////////////////////////////////////////////// + // + // Utils + // + ///////////////////////////////////////////////////////////////////////////////////// + empty: function (what) { + try { + this._empty(what) + } catch (e) { + + debugger; + } + }, + /* + * Empty : removes all child blocks, recursively + * @param proto : prototype|instance + * @param ctrArgs + * @returns {*} + */ + _empty: function (what) { + var data = what || this.items; + if (data) { + for (var i = 0; i < data.length; i++) { + var subBlock = data[i]; + + if (subBlock && subBlock.empty) { + subBlock.empty(); + } + if (subBlock && this.scope && this.scope.blockStore) { + this.scope.blockStore.remove(subBlock.id); + } + } + } + }, + /** + * This was needed. FF bug. + * @param data + * @param obj + * @returns {boolean} + */ + containsItem: function (data, obj) { + var i = data.length; + while (i--) { + if (data[i].id === obj.id) { + return true; + } + } + return false; + }, + /** + * This was needed. FF bug + * @param data + * @param obj + * @returns {*} + */ + indexOf: function (data, obj) { + var i = data.length; + while (i--) { + if (data[i].id === obj.id) { + return i; + } + } + return -1; + }, + _getBlock: function (dir) { + var item = this; + if (!item || !item.parentId) { + return false; + } + //get parent + var parent = this.scope.getBlockById(item.parentId); + if (!parent) { + return null; + } + var items = parent[parent._getContainer(item)]; + if (!items || items.length < 2 || !this.containsItem(items, item)) { + return null; + } + var cIndex = this.indexOf(items, item); + if (cIndex + (dir) < 0) { + return false; + } + var upperItem = items[cIndex + (dir)]; + if (upperItem) { + return upperItem; + } + return null; + }, + getPreviousBlock: function () { + return this._getBlock(-1); + }, + getNextBlock: function () { + return this._getBlock(1); + }, + _getPreviousResult: function () { + var parent = this.getPreviousBlock() || this.getParent(); + if (parent && parent._lastResult != null) { + if (this.isArray(parent._lastResult)) { + return parent._lastResult; + } else { + return [parent._lastResult]; + } + } + return null; + }, + 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) { + var _newParent = parent.getParent(); + if (_newParent) { + parent = _newParent; + } + } + + if (parent && parent._lastResult != null) { + if (this.isArray(parent._lastResult)) { + return parent._lastResult; + } else { + return parent._lastResult; + } + } + return null; + }, + _getArg: function (val, escape) { + var _float = parseFloat(val); + if (!isNaN(_float)) { + return _float; + } else { + if (val === 'true' || val === 'false') { + return val === 'true'; + } else if (val && escape && _.isString(val)) { + return '\'' + val + '\''; + } + return val; + } + }, + /** + * + * @returns {Array} + */ + getArgs: function (settings) { + var result = []; + settings = settings || {}; + var _inArgs = settings.args || this._get('args'); + if (settings.override && settings.override.args) { + _inArgs = settings.override.args; + } + if (_inArgs) { //direct json + result = utils.getJson(_inArgs, null, false); + } + //try comma separated list + if (result && result.length == 0 && _inArgs && _inArgs.length && _.isString(_inArgs)) { + + if (_inArgs.indexOf(',') !== -1) { + var splitted = _inArgs.split(','); + for (var i = 0; i < splitted.length; i++) { + //try auto convert to number + var _float = parseFloat(splitted[i]); + if (!isNaN(_float)) { + result.push(_float); + } else { + if (splitted[i] === 'true' || splitted[i] === 'false') { + result.push(utils.toBoolean(splitted[i])); + } else { + result.push(splitted[i]); //whatever + } + } + } + return result; + } else { + result = [this._getArg(_inArgs)]; //single argument + } + } + + !_.isArray(result) && (result = []); + //add previous result + var previousResult = this.getPreviousResult(); + if (previousResult != null) { + if (_.isArray(previousResult) && previousResult.length == 1) { + result.push(previousResult[0]); + } else { + result.push(previousResult); + } + } + + return result || [_inArgs]; + }, + /* + * Remove : as expected, removes a block + * @param proto : prototype|instance + * @param ctrArgs + * @returns {*} + */ + remove: function (what) { + this._destroyed = true; + if (this.parentId != null && this.parent == null) { + this.parent = this.scope.getBlockById(this.parentId); + } + if (this.parent && this.parent.removeBlock) { + this.parent.removeBlock(this); + return; + } + what = what || this; + if (what) { + if (what.empty) { + what.empty(); + } + delete what.items; + what.parent = null; + if (this.items) { + this.items.remove(what); + } + } + + }, + prepareArgs: function (ctorArgs) { + if (!ctorArgs) { + ctorArgs = {}; + } + //prepare items + if (!ctorArgs['id']) { + ctorArgs['id'] = this.createUUID(); + } + if (!ctorArgs['items']) { + ctorArgs['items'] = []; + } + }, + /** + * Private add-block function + * @param proto + * @param ctrArgs + * @param where + * @param publish + * @returns {*} + * @private + */ + _add: function (proto, ctrArgs, where, publish) { + var block = null; + try { + //create or set + if (ctrArgs) { + //use case : normal object construction + this.prepareArgs(ctrArgs); + block = factory.createBlock(proto, ctrArgs, null, publish); + } else { + //use case : object has been created so we only do the leg work + if (ctrArgs == null) { + block = proto; + } + //@TODO : allow use case to use ctrArgs as mixin for overriding + } + /////////////////////// + // post work + + //inherit scope + block.scope = this.scope; + //add to scope + if (this.scope) { + block = this.scope.registerBlock(block, publish); + } + if (has('debug')) { + if (block.id === this.id) { + console.error('adding new block to our self'); + debugger; + } + } + block.parent = this; + block.parentId = this.id; + block.scope = this.scope; + + var container = where || this._getContainer(); + if (container) { + if (!this[container]) { + this[container] = []; + } + var index = this.indexOf(this[container], block); + if (index !== -1) { + console.error(' have already ' + block.id + ' in ' + container); + } else { + if (this.id == block.id) { + console.error('tried to add our self to ' + container); + return; + } + this[container].push(block); + } + } + block.group = null; + return block; + } catch (e) { + logError(e, '_add'); + } + return null; + + }, + getStore: function () { + return this.getScope().getStore(); + }, + /** + * Public add block function + * @param proto {} + * @param ctrArgs + * @param where + * @returns {*} + */ + add: function (proto, ctrArgs, where) { + var block = this._add(proto, ctrArgs, where); + return block.getStore().getSync(block.id); + }, + ///////////////////////////////////////////////////////////////////////////////////// + // + // Run + // + ///////////////////////////////////////////////////////////////////////////////////// + getContext: function () { + if (this.scope.instance && this.scope.instance) { + return this.scope.instance; + } + return null; + }, + resolved: function () { + if (this._deferredObject) { + this._deferredObject.resolve(); + delete this._deferredObject; + } + }, + /*** + * Solves all the commands into items[] + * + * @param manager => BlockManager + * @return list of commands to send + */ + _solve: function (scope, settings) { + settings = settings || { + highlight: false + }; + var ret = []; + for (var n = 0; n < this.items.length; n++) { + var block = this.items[n]; + this.addToEnd(ret, block.solve(scope, settings)); + } + return ret; + }, + /*** + * Solves all the commands into items[] + * + * @param manager => BlockManager + * @return list of commands to send + */ + solve: function (scope, settings) { + settings = settings || { + highlight: false + }; + var ret = []; + for (var n = 0; n < this.items.length; n++) { + var block = this.items[n]; + this.addToEnd(ret, block.solve(scope, settings)); + } + return ret; + }, + /*** + * Solves all the commands into items[] + * + * @param manager => BlockManager + * @return list of commands to send + */ + solveMany: function (scope, settings) { + 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; + }, + runFrom: function (blocks, index, settings) { + var thiz = this; + blocks = blocks || this.items; + if (!this._return) { + this._return = []; + } + 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.deferred === true) { + block._deferredObject = new Deferred(); + this._currentIndex = n; + wireBlock(block); + this.addToEnd(this._return, block.solve(this.scope, settings)); + break; + } else { + this.addToEnd(this._return, block.solve(this.scope, settings)); + } + } + } else { + this.onSuccess(this, settings); + } + return this._return; + }, + serializeField: function (name) { + return this.ignoreSerialize.indexOf(name) == -1; //is not in our array + }, + onLoad: function () {}, + activate: function () {}, + deactivate: function () {}, + _get: function (what) { + if (this.override) { + return (what in this.override ? this.override[what] : this[what]); + } + }, + onDidRun: function () { + if (this.override) { + this.override.args && delete this.override.args; + delete this.override; + } + }, + destroy: function () { + this.stop(true); + this.reset(); + this._destroyed = true; + delete this.virtual; + }, + reset: function () { + this._lastSettings = {}; + clearTimeout(this._loop); + this._loop = null; + delete this.override; + this.override = null; + delete this._lastResult; + this.override = {}; + }, + stop: function () { + this.reset(); + this.getItems && _.invoke(this.getItems(), 'stop'); + } + + }); + Block.FLAGS = types.BLOCK_FLAGS; + Block.EMITS = types.BLOCK_CALLBACKMASK; + //that's really weird: using dynamic base classes nor Block.extend doesnt work. + //however, move dojo complete out of blox + if (!Block.prototype.onSuccess) { + Block.prototype.onSuccess = function () {}; + Block.prototype.onRun = function () {}; + Block.prototype.onFailed = function () {}; + } + dcl.chainAfter(Block, 'stop'); + dcl.chainAfter(Block, 'destroy'); + dcl.chainAfter(Block, 'onDidRun'); + return Block; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/BlockModel.js b/packages/xblox/ref-control-freak/xblox/model/BlockModel.js new file mode 100644 index 00000000..46742c7b --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/BlockModel.js @@ -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 ''; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/Block_UI.js b/packages/xblox/ref-control-freak/xblox/model/Block_UI.js new file mode 100644 index 00000000..c01dd4f5 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Block_UI.js @@ -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 ' ' + (after || ""); + }, + /** + * + * @param field + * @param pos + * @param type + * @param title + * @param mode: inline | popup + * @returns {string} + */ + makeEditable2:function(field,pos,type,title,mode){ + return "" + this[field] +""; + }, + makeEditable:function(field,pos,type,title,mode){ + var editableClass = this.canEdit() ? 'editable' : 'disabled'; + return "" + this[field] +""; + }, + /** + * 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 ? '' + symbol + '' : ''; + }, + /** + * 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); + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/Contains.js b/packages/xblox/ref-control-freak/xblox/model/Contains.js new file mode 100644 index 00000000..4ba91990 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Contains.js @@ -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) { + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/Expression.js b/packages/xblox/ref-control-freak/xblox/model/Expression.js new file mode 100644 index 00000000..4dc6c001 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Expression.js @@ -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')); + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/File/ReadJSON.js b/packages/xblox/ref-control-freak/xblox/model/File/ReadJSON.js new file mode 100644 index 00000000..061dd31e --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/File/ReadJSON.js @@ -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 = '' + this.getBlockIcon() + ' ' + this.name + ' :: ' + ''; + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/ModelBase.js b/packages/xblox/ref-control-freak/xblox/model/ModelBase.js new file mode 100644 index 00000000..296a6792 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/ModelBase.js @@ -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; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/Reference.js b/packages/xblox/ref-control-freak/xblox/model/Reference.js new file mode 100644 index 00000000..3abc8809 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Reference.js @@ -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 + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/Referenced.js b/packages/xblox/ref-control-freak/xblox/model/Referenced.js new file mode 100644 index 00000000..d0d2c8d4 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Referenced.js @@ -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; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/Scope.bak.js b/packages/xblox/ref-control-freak/xblox/model/Scope.bak.js new file mode 100644 index 00000000..368d7221 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Scope.bak.js @@ -0,0 +1,1332 @@ +/** @module xblox/model/Scope **/ +define([ + "dojo/_base/declare", + "dojo/_base/lang", + "./ModelBase", + "./Expression", + "./variables/Variable", + "xide/factory", + "xide/utils", + "xide/types", + "dojo/store/Memory", + "dojo/store/Observable", + "xide/mixins/ReloadMixin"], function(declare,lang,ModelBase,Expression,Variable,factory,utils,types,Memory,Observable,ReloadMixin){ + + /** + * The scope acts as a real scope as usual. All registered variables and blocks are excecuted in this scope only. + * @class module:xblox/model/Scope + */ + return declare('xblox.model.Scope',[ModelBase,ReloadMixin],{ + + context:null, + + + ///////////////////////////////////////////////////////// + // + // Service uplink related + // + ///////////////////////////////////////////////////////// + + /** @member {Object} */ + serviceObject:null, + getService:function(){ + return this.serviceObject; + }, + ///////////////////////////////////////////////////////// + // + // Store related + // + ///////////////////////////////////////////////////////// + blockStore:null, + /** + * + */ + variableStore:null, + clearCache:function(){ + this.expressionModel.expressionCache={}; + this.expressionModel.variableFuncCache={}; + }, + /** + * @returns {dojo/store/Memory} + */ + getVariableStore:function(){ + return this.variableStore; + }, + getBlockStore:function(){ + return this.blockStore; + }, + getVariables:function(query){ + //no store, + if(!this.variableStore){ + return []; + } + query = query||{id:/\S+/};//all variables + return this.variableStore.query(query); + }, + loopBlock:function(block){ + + if(block && block.auto>0){ + var thiz=this; + setTimeout(function(){ + block.solve(thiz); + thiz.loopBlock(block); + },block.auto); + } + + }, + getEventsAsOptions:function(selected){ + + var result = []; + for(var e in types.EVENTS){ + var label = types.EVENTS[e]; + + var item = { + label:label, + value:types.EVENTS[e] + }; + + /*if(selected===types.EVENTS[e]){ + item.selected=true; + }*/ + + result.push(item); + } + + + result = result.concat([{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 < result.length; i++) { + var obj = result[i]; + if(obj.value===selected){ + obj.selected=true; + break; + } + } + + + return result; + + }, + /** + * + * @returns {{}} + */ + getVariablesAsObject:function() { + + var variables = this.getVariables(); + var result = {}; + for(var i=0; i xblox.model.Variable + */ + registerVariable:function(variable) { + this.variables[variable.title] = variable; + if(this.variableStore){ + this.variableStore.putSync(variable); + } + }, + /*** + * Returns a variable from the scope + * + * @param title => variable title + * @return variable + */ + getVariable:function(title) { + //return this.variables[title]; + + var _var = this.variableStore.getSync(title); + if(_var){ + return _var; + } + var _var = this.variableStore.query({title:title}); + if(_var){ + return _var[0]; + } + console.error('couldnt find variable with name ' + title); + return null; + }, + /*** + * Returns a variable from the scope + * + * @param title => variable title + * @return variable + */ + getVariableById:function(id) { + + var parts = id.split('/'); + var scope = this; + if(parts.length==2){ + + var owner = scope.owner; + if(owner && owner.hasScope){ + if(owner.hasScope(parts[0])){ + scope = owner.getScope(parts[0]); + }else{ + console.error('have scope id but cant resolve it',this); + } + } + + id = parts[1]; + } + + var _var = scope.variableStore.getSync(id); + if(_var){ + return _var; + } + /* + var _var = this.variableStore.query({title:title}); + if(_var){ + return _var[0]; + } + console.error('couldnt find variable with name ' + title); + */ + return null; + }, + /*** + * Register a block into the scope + * + * The block name is unique within the scope + * + * @param block => xblox.model.Block + */ + registerBlock:function(block) { + + if (block.id) { + if(!this.blocks[block.id]) { + this.blocks[block.id] = block; + }else{ + console.error('block already in map '+block.id,block); + } + } + if(this.blockStore){ + var added = this.blockStore.getSync(block.id); + if(added){ + console.error('block already in store! '+block.id,block); + return; + }else{ + + } + //custom add block to store function + if(block.addToStore){ + block.addToStore(this.blockStore); + }else{ + this.blockStore.putSync(block); + } + } + }, + /*** + * Return all blocks + * + * @param block => Array(xblox.model.Block) + */ + allBlocks:function(block) { + + return this.getBlocks(); + + var result = []; + + for(var b in this.blocks){ + if(this.blocks[b].id!=null){ + result.push(this.blocks[b]); + } + } + return result; + }, + /** + * Returns whether there is any block belongs to a given group + * @param group {String} + * @returns {boolean} + */ + hasGroup:function(group){ + + var all = this.allGroups(); + for (var i = 0; i < all.length; i++) { + var obj = all[i]; + if (obj === group) { + return true; + } + } + return false; + }, + /** + * Return all block groups + */ + allGroups:function(){ + + var result = []; + var all = this.allBlocks(); + + var _has = function(what){ + for (var i = 0; i < result.length; i++) { + if(result[i]===what){ + return true; + } + } + return false; + }; + + + for (var i = 0; i < all.length; i++) { + var obj = all[i]; + + if(obj.parentId){ + continue; + } + + if(obj.group){ + if(!_has(obj.group)){ + result.push(obj.group); + } + }else{ + if(!_has('No Group')){ + result.push('No Group'); + } + } + } + + return result; + }, + /** + * Serializes all variables + * @returns {Array} + */ + variablesToJson:function(){ + var result = []; + var data = this.variableStore ? this.getVariables() : this.variables; + for(var e in data){ + var variable = data[e]; + if(variable.serializeMe===false){ + continue; + } + if(variable.keys==null){ + continue; + } + var varOut={ + }; + for(var prop in variable){ + + //copy all serializables over + if( + this.isString(variable[prop])|| + this.isNumber(variable[prop])|| + this.isBoolean(variable[prop]) + ) + { + varOut[prop]=variable[prop]; + } + } + + result.push(varOut); + } + //return JSON.stringify(result); + //console.log('saving all variables ' + JSON.stringify(result),result); + return result; + }, + isScript:function(val){ + return this.isString(val) && ( + val.indexOf('return')!=-1|| + val.indexOf(';')!=-1|| + val.indexOf('[')!=-1|| + val.indexOf('{')!=-1|| + val.indexOf('}')!=-1 + ); + }, + /* + parseVariable:function(_var){ + + var value = ''+ _var.value; + try{ + //put other variables on the stack; + var _otherVariables = this.variablesToJavascript(_var,false); + if(_otherVariables){ + value = _otherVariables + value; + } + var _parsed = (new Function("{" + value+ "}")).call(this.expressionModel.context||{}); + //wasnt a script + if(_parsed==='undefined' || typeof _parsed ==='undefined'){ + //console.error(' parsed variable to undefined : ' + _var.title + ' with value : ' + value); + value = '' + _var.value; + }else{ + value = ''+_parsed; + value = "'" + value + "'"; + } + }catch(e){ + console.error('parse variable failed : ' + ) + } + return value; + },*/ + /** + * Serializes all variables + * @returns {Array} + */ + variablesToJavascriptEx:function(skipVariable,expression){ + + var result=[]; + var data = this.variableStore ? this.getVariables() : this.variables; + for(var i = 0 ; i < data.length ; i++){ + var _var = data[i]; + if(_var == skipVariable){ + continue; + } + var _varVal = ''+_var.value; + + //optimization + if(skipVariable && skipVariable.value && skipVariable.value.indexOf(_var.title)==-1){ + continue; + } + if(expression && expression.indexOf(_var.title)==-1){ + continue; + } + + if(_varVal.length==0){ + continue; + } + if(!this.isScript(_varVal) && _varVal.indexOf("'")==-1){ + _varVal = "'" + _varVal + "'"; + } + else if(this.isScript(_varVal)){ + _varVal = this.expressionModel.parseVariable(this,_var); + } + if(_varVal==="''"){ + _varVal="'0'"; + } + + //result+="var " + _var.title + " = " + _varVal + ";"; + //result+="\n"; + result.push(_varVal); + } + + return result; + }, + variablesToJavascript:function(skipVariable,expression){ + + var result=''; + var data = this.variableStore ? this.getVariables() : this.variables; + for(var i = 0 ; i < data.length ; i++){ + var _var = data[i]; + if(_var == skipVariable){ + continue; + } + var _varVal = ''+_var.value; + + //optimization + if(skipVariable && skipVariable.value && skipVariable.value.indexOf(_var.title)==-1){ + continue; + } + if(expression && expression.indexOf(_var.title)==-1){ + continue; + } + + if(_varVal.length==0){ + continue; + } + if(!this.isScript(_varVal) && _varVal.indexOf("'")==-1){ + _varVal = "'" + _varVal + "'"; + } + else if(this.isScript(_varVal)){ + //_varVal = "''"; + _varVal = this.expressionModel.parseVariable(this,_var); + } + + if(_varVal==="''"){ + _varVal="'0'"; + } + + result+="var " + _var.title + " = " + _varVal + ";"; + result+="\n"; + } + + return result; + }, + /** + * Convert from JSON data. Creates all Variables in this scope + * @param data + * @returns {Array} + */ + variablesFromJson:function(data){ + var result = []; + for(var i = 0; i < data.length ; i++){ + var variable = data[i]; + variable['scope'] = this; + if(!variable.declaredClass){ + console.log(' variable has no class '); + continue; + } + var _class = utils.replaceAll('.','/',variable.declaredClass); + var variableClassProto = require(_class); + if(!variableClassProto){ + console.log('couldnt resolve ' + _class); + continue; + } + result.push(new variableClassProto(variable));//looks like a leak but the instance is tracked and destroyed in this scope + } + return result; + }, + regenerateIDs:function(blocks){ + + var thiz=this; + var updateChildren=function(block){ + var newId = utils.createUUID(); + var children = thiz.getBlocks({ + parentId:block.id + }); + if(children && children.length>0){ + for(var i = 0 ; i < children.length ; i ++) { + var child = children[i]; + child.parentId=newId; + updateChildren(child); + } + } + block.id=newId; + }; + + for(var i = 0 ; i < blocks.length ; i ++){ + var block=blocks[i]; + updateChildren(block); + } + }, + /** + * Clone blocks + * @param blocks + */ + cloneBlocks2:function(blocks,forceGroup){ + + var blocksJSON = this.blocksToJson(blocks); + var tmpScope = this.owner.getScope(utils.createUUID(),null,false); + var newBlocks = tmpScope.blocksFromJson(blocksJSON,false); + newBlocks = tmpScope.allBlocks(); + + tmpScope.regenerateIDs(newBlocks); + blocksJSON = tmpScope.blocksToJson(newBlocks); + + if(forceGroup) { + for (var i = 0; i < blocksJSON.length; i++) { + var block = blocksJSON[i]; + if(block.parentId==null) {//groups are only needed for top level blocks + block.group = forceGroup; + } + } + } + newBlocks = this.blocksFromJson(blocksJSON);//add it us + return newBlocks; + + }, + /** + * Clone blocks + * @param blocks + */ + cloneBlocks:function(blocks){ + + var blocksJSON = this.blocksToJson(blocks); + var tmpScope = this.owner.getScope(utils.createUUID(),null,false); + var newBlocks = tmpScope.blocksFromJson(blocksJSON,false); + newBlocks = tmpScope.allBlocks(); + + for(var i = 0 ; i < newBlocks.length ; i ++){ + var block=newBlocks[i]; + block.id = utils.createUUID(); + block.parentId=null; + } + + blocksJSON = this.blocksToJson(newBlocks); + this.blocksFromJson(newBlocks);//add it us + return newBlocks; + + }, + blockToJson:function(block){ + + + var blockOut={ + + // this property is used to recreate the child blocks in the JSON -> blocks process + _containsChildrenIds: [] + }; + + for(var prop in block){ + + if (prop == 'ctrArgs') { + continue; + } + + if( typeof block[prop] !=='function' && !block.serializeField(prop)){ + continue; + } + + //copy all strings over + if( this.isString(block[prop])|| + this.isNumber(block[prop])|| + this.isBoolean(block[prop])) + { + blockOut[prop]=block[prop]; + } + + + //flatten children to ids. Skip "parent" field + + if (prop != 'parent') { + + if ( this.isBlock(block[prop]) ) + { + // if the field is a single block container, store the child block's id + blockOut[prop] = block[prop].id; + + // register this field name as children ID container + blockOut._containsChildrenIds.push(prop); + + } else if ( this.areBlocks(block[prop])) + { + // if the field is a multiple blocks container, store all the children blocks' id + blockOut[prop] = []; + + for(var i = 0; i < block[prop].length ; i++){ + blockOut[prop].push(block[prop][i].id); + } + + // register this field name as children IDs container + blockOut._containsChildrenIds.push(prop); + } + } + + } + + return blockOut; + }, + /** + * Serializes all blocks to JSON data. + * It needs a custom conversation because we're having cyclic + * object dependencies. + * @returns {Array} + */ + blocksToJson:function(data){ + try{ + var result = []; + data = (data && data.length) ? data : (this.blockStore ? this.getBlocks() : this.blocks); + + for(var b in data){ + var block = data[b]; + if(block.keys==null){ + continue; + } + if(block.serializeMe===false){ + continue; + } + var blockOut={ + + // this property is used to recreate the child blocks in the JSON -> blocks process + _containsChildrenIds: [] + }; + + for(var prop in block){ + + if (prop == 'ctrArgs') { + continue; + } + + if( typeof block[prop] !=='function' && !block.serializeField(prop)){ + continue; + } + + //copy all strings over + if( this.isString(block[prop])|| + this.isNumber(block[prop])|| + this.isBoolean(block[prop])) + { + blockOut[prop]=block[prop]; + } + + + //flatten children to ids. Skip "parent" field + + if (prop != 'parent') { + if ( this.isBlock(block[prop]) ) + { + // if the field is a single block container, store the child block's id + blockOut[prop] = block[prop].id; + + // register this field name as children ID container + blockOut._containsChildrenIds.push(prop); + + } else if ( this.areBlocks(block[prop])) + { + // if the field is a multiple blocks container, store all the children blocks' id + blockOut[prop] = []; + + for(var i = 0; i < block[prop].length ; i++){ + blockOut[prop].push(block[prop][i].id); + } + + // register this field name as children IDs container + blockOut._containsChildrenIds.push(prop); + } + } + + } + + result.push(blockOut); + } + }catch(e){ + console.error('from json failed : ' +e); + } + //return JSON.stringify(result); + // console.log(JSON.stringify(result)); + //console.dir(result); + return result; + }, + _createBlockStore:function(){ + + /* + debugger; + var blockData={ + identifier: "id", + label: "title", + items:[] + }; + + var blockStore = new StoreAdapter(Observable(new Memory({ + data: blockData, + getChildren: function(parent, options){ + + if(parent.getChildren){ + return parent.getChildren(parent); + } + + // Support persisting the original query via options.originalQuery + // so that child levels will filter the same way as the root level + var op = lang.mixin({}, options && options.originalQuery || null, { parentId: parent.id }); + var res = this.query(op, options); + + + return res; + }, + mayHaveChildren: function(parent){ + if(parent.mayHaveChildren){ + return parent.mayHaveChildren(parent); + } + return parent.items!=null && parent.items.length>0; + }, + query: function (query, options){ + query = query || {}; + options = options || {}; + + if (!query.parentId && !options.deep) { + // Default to a single-level query for root items (no parent) + query.parentId = undefined; + } + return this.queryEngine(query, options)(this.data); + } + + }))); + + return blockStore; + */ + }, + + blockFromJson:function(block){ + + + block['scope'] = this; + if(block._containsChildrenIds==null){ + block._containsChildrenIds=[]; + } + + // Store all children references into "children" + var children = []; + for(var cf = 0 ; cf < block._containsChildrenIds.length ; cf ++) + { + var propName = block._containsChildrenIds[cf]; + children[propName] = block[propName]; + block[propName] = null; + } + delete block._containsChildrenIds; + + // Create the block + if(!block.declaredClass){ + console.log(' not a class '); + return null; + } + var blockClassProto=null; + var _class=null; + try{ + _class = utils.replaceAll('.','/',block.declaredClass); + blockClassProto = require(_class); + }catch(e){ + console.error('couldnt resolve class '+_class); + + } + if(!blockClassProto){ + console.log('couldn`t resolve ' + _class); + return null; + } + + var blockOut = null; + try{ + blockOut = factory.createBlock(blockClassProto,block); + }catch(e){ + console.error('error in block creation ' , e); + return null; + } + + // assign the children references into block._children + blockOut._children=children; + + return blockOut; + }, + /** + * Convert from JSON data. Creates all blocks in this scope + * @param data + * @returns {Array} + */ + blocksFromJson:function(data,check) { + + var resultSelected = []; + for(var i = 0; i < data.length ; i++){ + var block = data[i]; + block['scope'] = this; + if(block._containsChildrenIds==null){ + block._containsChildrenIds=[]; + } + + // Store all children references into "children" + var children = []; + for(var cf = 0 ; cf < block._containsChildrenIds.length ; cf ++) + { + var propName = block._containsChildrenIds[cf]; + children[propName] = block[propName]; + block[propName] = null; + } + delete block._containsChildrenIds; + + // Create the block + if(!block.declaredClass){ + console.log(' not a class '); + continue; + } + var blockClassProto=null; + var _class=null; + try{ + _class = utils.replaceAll('.','/',block.declaredClass); + blockClassProto = require(_class); + }catch(e){ + console.error('couldnt resolve class '+_class); + + } + if(!blockClassProto){ + console.log('couldnt resolve ' + _class); + continue; + } + + var blockOut = null; + try{ + blockOut = factory.createBlock(blockClassProto,block); + }catch(e){ + console.error('error in block creation ' , e); + continue; + } + + // assign the children references into block._children + blockOut._children=children; + resultSelected.push(blockOut); + + } + + //2nd pass, update child blocks + var allBlocks = this.allBlocks(); + for(var i = 0; i < allBlocks.length ; i++){ + + var block = allBlocks[i]; + + if(block._children) { + // get all the block container fields + for (var propName in block._children) + { + if (typeof block._children[propName] == "string") + { + // single block + var child = this.getBlockById( block._children[propName] ); + if (!child) { + console.log(' couldnt resolve child: ' + block._children[propName],block); + continue; + } + block[propName] = child; + child.parent=block; + } + else if (typeof block._children[propName] == "object") + { + // multiple blocks + block[propName] = []; + for(var j = 0; j < block._children[propName].length ; j++){ + var child = this.getBlockById(block._children[propName][j]); + if (!child) { + console.log(' couldnt resolve child: ' + block._children[propName][j]); + continue; + } + block[propName].push(child); + } + + } + } + delete block._children; + } + + if(check!==false && block.parentId!=null){ + var parent = this.getBlockById(block.parentId); + if(parent==null){ + console.error('have orphan block!',block); + block.remove(); + } + } + } + var result = this.allBlocks(); + //console.log('after json deserialize ' , result); + return resultSelected; + }, + /*** + * Returns a block from the scope + * + * @param name => block name + * @return block + */ + getBlockByName:function(name) { + for(var b in this.blocks){ + if(this.blocks[b].name===name){ + return this.blocks[b]; + } + } + }, + /*** + * Returns a block from the scope + * + * @param name => block name + * @return block + */ + getBlockById:function(id) { + return this.blocks[id]; + }, + /** + * Returns an array of blocks + * @param blocks + */ + flatten:function(blocks){ + var result = []; + + for(var b in blocks){ + + var block = blocks[b]; + + if(block.keys==null){ + continue; + } + result.push(block); + + for(var prop in block){ + + if (prop == 'ctrArgs') { + continue; + } + + //flatten children to ids. Skip "parent" field + if (prop != 'parent') { + if ( this.isBlock(block[prop]) ) + { + // if the field is a single block container, store the child block's id + result.push(block[prop]); + + } else if ( this.areBlocks(block[prop])) + { + for(var i = 0; i < block[prop].length ; i++){ + result.push(block[prop][i]); + } + } + } + } + } + return result; + }, + /*** + * Runs the block + * + * @param mixed + * @returns result + */ + solveBlock:function(mixed,settings,force) { + + + settings = settings || { + highlight:false + }; + + + var block = null; + if(this.isString(mixed)){ + block = this.getBlockByName(mixed); + }else if(this.isObject(mixed)){ + block = mixed; + } + var result = null; + if(block){ + if(settings.force !==true && block.enabled==false){ + console.error('block is not enabled'); + return null; + } + if(settings.force===true){ + settings.force=false; + } + result = block.solve(this,settings); + }else{ + console.error('solving block failed, have no block! ' , mixed); + } + return result; + }, + /*** + * Solves all the commands into [items] + * + * @param manager => BlockManager + * @return list of commands to send + */ + solve:function(scope,settings) { + var ret=''; + + for(var n = 0; n < this.items.length ; n++) + { + ret += this.items[n].solve(scope,settings); + } + + return ret; + }, + /*** + * Parses an expression + * + * @param expression + * @returns {String} parsed expression + */ + parseExpression:function(expression,addVariables,variableOverrides) { + return this.expressionModel.parse(this,expression,addVariables,null,null,null,variableOverrides); + }, + isString: function (a) { + return typeof a == "string" + }, + isNumber: function (a) { + return typeof a == "number" + }, + isBoolean: function (a) { + return typeof a == "boolean" + }, + isObject:function(a){ + return typeof a === 'object'; + }, + isBlock:function (a) { + var ret = false; + + if ( ( typeof a == "object" ) && ( a!=null ) && (a.length == undefined) ) + { + if ( a.serializeMe ) + { + ret = true; + } + } + return ret; + }, + areBlocks:function(a) { + var ret = false; + + if ( ( typeof a == "object" ) && ( a!=null ) && (a.length > 0) ) + { + if ( this.isBlock( a[0] )) { + ret = true; + } + } + return ret; + }, + /** + * + * @private + */ + _onVariableChanged:function(evt){ + if(evt.item && this.expressionModel.variableFuncCache[evt.item.title]){ + delete this.expressionModel.variableFuncCache[evt.item.title]; + } + }, + init:function(){ + this.subscribe(types.EVENTS.ON_DRIVER_VARIABLE_CHANGED,this._onVariableChanged); + }, + /** + * + */ + _destroy:function(){ + + var allblocks = this.allBlocks(); + for (var i = 0; i < allblocks.length; i++) { + var obj = allblocks[i]; + + if(obj._emit) { + obj._emit(types.EVENTS.ON_ITEM_REMOVED, { + item: obj + }); + } + + if(obj._destroy){ + obj._destroy(); + } + if(obj.destroy){ + obj.destroy(); + } + } + + }, + + /** + * + * @param source + * @param target + * @param before + * @param add + * @returns {boolean} + */ + moveTo:function(source,target,before,add){ + + + /** + * treat first the special case of adding an item + */ + if(add){ + + //remove it from the source parent and re-parent the source + if(target.canAdd && target.canAdd()){ + + var sourceParent = this.getBlockById(source.parentId); + if(sourceParent){ + sourceParent.removeBlock(source,false); + } + target.add(source,null,null); + return; + }else{ + console.error('cant reparent'); + return false; + } + } + + + //for root level move + if(!target.parentId && add==false){ + + console.error('root level move'); + + //if source is part of something, we remove it + var sourceParent = this.getBlockById(source.parentId); + if(sourceParent && sourceParent.removeBlock){ + sourceParent.removeBlock(source,false); + source.parentId=null; + source.group=target.group; + } + + var itemsToBeMoved=[]; + var groupItems = this.getBlocks({ + group:target.group + }); + + var rootLevelIndex=[]; + var store = this.getBlockStore(); + + var sourceIndex = store.index[source.id]; + var targetIndex = store.index[target.id]; + for(var i = 0; i= targetIndex : itemIndex <= targetIndex; + if(add){ + itemsToBeMoved.push(groupItems[i]); + rootLevelIndex.push(store.index[groupItems[i].id]); + } + } + } + + //remove them the store + for(var j = 0; j cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before ==true ? -1 : 1))); + for(var i = 0 ; i < distance -1; i++){ + parent.move(source,direction); + } + return true; + + // we move within the different parents + }else if( source.parentId && target.parentId && add==false && source.parentId !== target.parentId){ console.log('same parent!'); + + console.error('we move within the different parents'); + //collect data + + var sourceParent = this.getBlockById(source.parentId); + if(!sourceParent){ + console.error(' couldnt find source parent '); + return false; + } + + var targetParent = this.getBlockById(target.parentId); + if(!targetParent){ + console.error(' couldnt find target parent '); + return false; + } + + + //remove it from the source parent and re-parent the source + if(sourceParent && sourceParent.removeBlock && targetParent.canAdd && targetParent.canAdd()){ + sourceParent.removeBlock(source,false); + targetParent.add(source,null,null); + }else{ + console.error('cant reparent'); + return false; + } + + //now proceed as in the case above : same parents + var items = targetParent[targetParent._getContainer(source)]; + if(items==null){ + console.error('weird : target parent has no item container'); + } + var cIndexSource = targetParent.indexOf(items,source); + var cIndexTarget = targetParent.indexOf(items,target); + if(!cIndexSource || !cIndexTarget){ + console.error(' weird : invalid drop processing state, have no valid item indicies'); + return; + } + var direction = cIndexSource > cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before ==true ? -1 : 1))); + for(var i = 0 ; i < distance -1; i++){ + targetParent.move(source,direction); + } + return true; + } + + return false; + } + + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/Scope.js b/packages/xblox/ref-control-freak/xblox/model/Scope.js new file mode 100644 index 00000000..9750e16b --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Scope.js @@ -0,0 +1,1646 @@ +/** @module xblox/model/Scope **/ +define([ + 'dcl/dcl', + './ModelBase', + './Expression', + 'xide/factory', + 'xide/utils', + 'xide/types', + 'xide/mixins/EventedMixin', + 'dojo/_base/lang', + 'dojo/has', + 'xide/encoding/MD5', + 'xcf/model/Variable', + 'xdojo/has!host-node?nxapp/utils/_console' +], function (dcl, ModelBase, Expression, factory, utils, types, EventedMixin, lang, has, MD5, Variable, _console) { + var console = typeof window !== 'undefined' ? window.console : typeof global !== 'undefined' ? global.console : _console + if (_console) { + console = _console + } + /* + var console = typeof window !== 'undefined' ? window.console : console; + if(tracer && tracer.error && console && console.error){ + console = _console; + } + */ + + function mergeNewModule(block, source) { + for (var i in source) { + var o = source[i] + if (o && _.isFunction(o) /* && lang.isFunction(target[i]) */ ) { + block[i] = o // swap + } + } + } + + var debug = false + var isIDE = has('xcf-ui') + /** + * The scope acts as a real scope as usual. All registered variables and blocks are excecuted in this scope only. + * @class module:xblox/model/Scope + */ + var Module = dcl([ModelBase, EventedMixin.dcl], { + declaredClass: 'xblox.model.Scope', + variableStore: null, + serviceObject: null, + context: null, + blockStore: null, + /** + * @type {module:xblox/model/Expression} + */ + expressionModel: null, + start: function () { + if (this.__didStartBlocks === true) { + console.error('already started blocks') + return + } + this.__didStartBlocks = true + var responseVariable = this.getVariable('value') + if (!responseVariable) { + responseVariable = new Variable({ + id: utils.createUUID(), + name: 'value', + value: '', + scope: this, + type: 13, + group: 'processVariables', + gui: false, + cmd: false + }) + + this.blockStore.putSync(responseVariable) + } + var autoBlocks = [] + var initBlocks = this.getBlocks({ + group: types.COMMAND_TYPES.INIT_COMMAND + }) + + var self = this; + try { + _.each(initBlocks, function (block) { + if (block.enabled !== false && block.__started !== true) { + block.solve(self); + block.__started = true + } + }, this); + } catch (e) { + console.error('starting init blocks failed', e) + logError(e, this) + } + autoBlocks = autoBlocks.concat(this.getBlocks({ + group: types.COMMAND_TYPES.BASIC_COMMAND + })) + + // console.error('auto blocks : '+autoBlocks.length + ' ' + this.id); + for (var i = 0; i < autoBlocks.length; i++) { + var block = autoBlocks[i] + if (block.enabled && block.start && block.startup && block.__started !== true) { + block.start() + block.__started = true + } + } + }, + /** + * + * @returns {module:xblox/model/Expression} + */ + getExpressionModel: function () { + if (!this.expressionModel) { + this.expressionModel = new Expression() + } + return this.expressionModel + }, + /** + * + * @param block + * @param url + * @returns {*} + */ + toFriendlyName: function (block, url) { + if (!url || !block) { + return null + } + var blockScope = this, + ctx = this.ctx, + driver = this.driver, + deviceManager = ctx.getDeviceManager(), + driverManager = ctx.getDriverManager() + + if (url.indexOf('://') == -1) { + var _block = blockScope.getBlockById(url) + if (_block) { + return _block.name + } + return url + } + var parts = utils.parse_url(url) // strip scheme + + parts = utils.urlArgs(parts.host) // go on with query string + var _device = deviceManager.getItemById(parts.device.value) + if (_device) { + var info = deviceManager.toDeviceControlInfo(_device) + driver = driverManager.getDriverById(info.driverId) + var driverInstance = _device.driverInstance + if (driverInstance || driver) { + blockScope = driver.blockScope ? driver.blockScope : driverInstance ? driverInstance.blockScope : blockScope + if(!blockScope){ + console.error('failed to generate url '+url, block); + return 'failed'; + } + block = blockScope.getStore().getSync(parts.block.value) + if (block) { + return info.title + '/' + block.name + } else if (driverInstance && driverInstance.blockScope) { + block = driverInstance.blockScope.getBlock(parts.block.value) + if (block) { + return info.title + '/' + block.name + } + } + } + } + return url + }, + getContext: function () { + return this.instance + }, + toString: function () { + var all = { + blocks: null, + variables: null + } + var blocks = this.blocksToJson() + try { + utils.fromJson(JSON.stringify(blocks)) + } catch (e) { + debug && console.error('scope::toString : invalid data in scope') + return + } + all.blocks = blocks + return JSON.stringify(all, null, 2) + }, + /** + * @param data + * @param errorCB {function} + */ + initWithData: function (data, errorCB) { + data && this.blocksFromJson(data, null, errorCB) + this.clearCache() + }, + // /////////////////////////////////////////////////////// + // + // Service uplink related + // + // /////////////////////////////////////////////////////// + /** @member {Object} */ + getService: function () { + return this.serviceObject + }, + // /////////////////////////////////////////////////////// + // + // Store related + // + // /////////////////////////////////////////////////////// + getStore: function () { + return this.blockStore + }, + reset: function () { + this.getExpressionModel().reset() + }, + /** + * + */ + empty: function () { + this.clearCache() + var store = this.blockStore + var allBlocks = this.getBlocks() + store.silent(true) + _.each(allBlocks, function (block) { + if (block) { + store.removeSync(block.id) + } else { + debug && console.error('have no block') + } + }) + store.setData([]) + store.silent(false) + }, + fromScope: function (source) { + var store = this.blockStore + store.silent(true) + this.empty() + var _t = source.blocksToJson() + this.blocksFromJson(_t) + store.silent(false) + }, + /** + * + */ + clearCache: function () { + this.getExpressionModel().reset() + }, + /** + * @returns {dojo/store/Memory} + */ + getVariableStore: function () { + return this.blockStore + }, + getBlockStore: function () { + return this.blockStore + }, + getVariables: function (query) { + if (!this.blockStore) { + return [] + } + var all = this.blockStore.data + var out = [] + if (query && query.group === 'processVariables') { + for (var i = 0; i < all.length; i++) { + if (all[i].group === 'processVariables') { + out.push(all[i]) + } + } + return out + } + // query = query || {id:/\S+/};//all variables + if (!query) { + for (var i = 0; i < all.length; i++) { + var block = all[i], + cls = block.declaredClass + if (cls == 'xblox.model.variables.Variable' || cls == 'xcf.model.Variable') { + out.push(block) + } + } + return out + } + return this.blockStore.query(query) + }, + loopBlock: function (block, settings) { + if (block._destroyed == true) { + console.error('block destroyed') + } + var interval = block.getInterval ? block.getInterval() : 0 + if (block && interval > 0 && block.enabled && block._destroyed !== true) { + var thiz = this + if (block._loop) { + clearInterval(block._loop) + } + block._loop = setInterval(function () { + if (!block.enabled || block._destroyed) { + clearInterval(block._loop) + block._loop = null + return + } + block.solve(thiz, settings || block._lastSettings) + }, interval) + } + }, + getEventsAsOptions: function (selected) { + var result = [] + for (var e in types.EVENTS) { + var label = types.EVENTS[e] + + var item = { + label: label, + value: types.EVENTS[e] + } + result.push(item) + } + result = result.concat([{ + 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 < result.length; i++) { + var obj = result[i] + if (obj.value === selected) { + obj.selected = true + break; + } + } + return result + }, + /** + * + * @returns {{}} + */ + getVariablesAsObject: function () { + var variables = this.getVariables() + var result = {} + for (var i = 0; i < variables.length; i++) { + result[variables[i].title] = variables[i].value + } + return result + }, + getVariablesAsOptions: function () { + var variables = this.getVariables() + var result = [] + if (variables) { + for (var i = 0; i < variables.length; i++) { + result.push({ + label: variables[i].label, + value: variables[i].variable + }) + } + } + return result + }, + getCommandsAsOptions: function (labelField) { + var items = this.getBlocks({ + declaredClass: 'xcf.model.Command' + }) + var result = [] + if (items) { + for (var i = 0; i < items.length; i++) { + var item = {} + item[labelField || 'label'] = items[i].name + item['value'] = items[i].name + result.push(item) + } + } + return result + }, + _cached: null, + getBlocks: function (query, allowCache) { + if (!isIDE && allowCache !== false) { + if (!this._cached) { + this._cached = {} + } + if (query) { + var hash = MD5(JSON.stringify(query), 1) + var cached = this._cached[hash] + if (cached) { + return cached + } + } + } + // no store, + if (!this.blockStore) { + return [] + } + query = query || { + id: /\S+/ + } // all blocks + var result = _.isEmpty(query) ? this.blockStore.data : this.blockStore.query(query, null, true); + if (!isIDE && allowCache !== false) { + var hash = MD5(JSON.stringify(query), 1) + this._cached[hash] = result + } + return result + }, + /*** + * Register a variable into the scope + * + * The variable title is unique within the scope + * + * @param variable => xblox.model.Variable + */ + registerVariable: function (variable) { + this.variables[variable.title] = variable + if (this.blockStore) { + this.blockStore.putSync(variable) + } + }, + /*** + * Returns a variable from the scope + * + * @param title => variable title + * @return variable + */ + getVariable: function (title) { + var _variables = this.getVariables() + for (var i = 0; i < _variables.length; i++) { + var obj = _variables[i] + if (obj.name === title) { + return obj + } + } + return null + }, + /*** + * Returns a variable from the scope + * + * @param title => variable title + * @return variable + */ + getVariableById: function (id) { + if (!id) { + return null + } + var parts = id.split('/') + var scope = this + if (parts.length == 2) { + var owner = scope.owner + if (owner && owner.hasScope) { + if (owner.hasScope(parts[0])) { + scope = owner.getScope(parts[0]) + } else { + console.error('have scope id but cant resolve it', this) + } + } + id = parts[1] + } + var _var = scope.blockStore.getSync(id) + if (_var) { + return _var + } + return null + }, + /*** + * Register a block into the scope + * + * The block name is unique within the scope + * + * @param block => xblox.model.Block + */ + registerBlock: function (block, publish) { + var store = this.blockStore + if (store) { + var added = store.getSync(block.id) + if (added) { + debug && console.warn('block already in store! ' + block.id, block) + return added + } + var result = null + // custom add block to store function + if (block.addToStore) { + result = block.addToStore(store) + } else { + result = store.putSync(block, publish) + } + return result + } + }, + /*** + * Return all blocks + * + * @returns {xblox.model.Block[]} + */ + allBlocks: function (query, allowCache) { + return this.getBlocks({}, allowCache) + }, + /** + * Returns whether there is any block belongs to a given group + * @param group {String} + * @returns {boolean} + */ + hasGroup: function (group) { + var all = this.allGroups({}, false) + for (var i = 0; i < all.length; i++) { + var obj = all[i] + if (obj === group) { + return true + } + } + return false + }, + /** + * Return all block groups + * @returns {String[]} + */ + allGroups: function () { + var result = [] + var all = this.allBlocks({}, false) + var _has = function (what) { + for (var i = 0; i < result.length; i++) { + if (result[i] === what) { + return true + } + } + return false + } + for (var i = 0; i < all.length; i++) { + var obj = all[i] + if (obj.parentId) { + continue; + } + if (obj.group) { + if (!_has(obj.group)) { + result.push(obj.group) + } + } else { + if (!_has('No Group')) { + result.push('No Group') + } + } + } + return result + }, + /** + * Serializes all variables + * @returns {Array} + */ + variablesToJson: function () { + var result = [] + var data = this.variableStore ? this.getVariables() : this.variables + for (var e in data) { + var variable = data[e] + if (variable.serializeMe === false) { + continue; + } + if (variable.keys == null) { + continue; + } + var varOut = {} + for (var prop in variable) { + // copy all serializables over + if ( + this.isString(variable[prop]) || + this.isNumber(variable[prop]) || + this.isBoolean(variable[prop]) + ) { + varOut[prop] = variable[prop] + } + } + + result.push(varOut) + } + return result + }, + isScript: function (val) { + return this.isString(val) && ( + val.indexOf('return') != -1 || + val.indexOf(';') != -1 || + val.indexOf('[') != -1 || + val.indexOf('{') != -1 || + val.indexOf('}') != -1 + ) + }, + /** + * Serializes all variables + * @returns {Array} + */ + variablesToJavascriptEx: function (skipVariable, expression) { + var result = [] + var data = this.variableStore ? this.getVariables() : this.variables + for (var i = 0; i < data.length; i++) { + var _var = data[i] + if (_var == skipVariable) { + continue; + } + var _varVal = '' + _var.value + + // optimization + if (skipVariable && skipVariable.value && skipVariable.value.indexOf(_var.title) == -1) { + continue; + } + if (expression && expression.indexOf(_var.title) == -1) { + continue; + } + + if (_varVal.length == 0) { + continue; + } + if (!this.isScript(_varVal) && _varVal.indexOf("'") == -1) { + _varVal = "'" + _varVal + "'" + } else if (this.isScript(_varVal)) { + _varVal = this.expressionModel.parseVariable(this, _var) + } + if (_varVal === "''") { + _varVal = "'0'" + } + result.push(_varVal) + } + return result + }, + variablesToJavascript: function (skipVariable, expression) { + var result = '' + var data = this.variableStore ? this.getVariables() : this.variables || [] + for (var i = 0; i < data.length; i++) { + var _var = data[i] + if (_var == skipVariable) { + continue; + } + var _varVal = '' + _var.value + + // optimization + if (skipVariable && skipVariable.value && skipVariable.value.indexOf(_var.title) == -1) { + continue; + } + if (expression && expression.indexOf(_var.title) == -1) { + continue; + } + + if (_varVal.length == 0) { + continue; + } + if (!this.isScript(_varVal) && _varVal.indexOf("'") == -1) { + _varVal = "'" + _varVal + "'" + } else if (this.isScript(_varVal)) { + // _varVal = "''"; + _varVal = this.expressionModel.parseVariable(this, _var) + } + + if (_varVal === "''") { + _varVal = "'0'" + } + result += 'var ' + _var.title + ' = ' + _varVal + ';' + result += '\n' + } + + return result + }, + /** + * Convert from JSON data. Creates all Variables in this scope + * @param data + * @returns {Array} + */ + variablesFromJson: function (data) { + var result = [] + for (var i = 0; i < data.length; i++) { + var variable = data[i] + variable['scope'] = this + if (!variable.declaredClass) { + console.log(' variable has no class ') + continue; + } + var _class = utils.replaceAll('.', '/', variable.declaredClass) + var variableClassProto = require(_class) + if (!variableClassProto) { + continue; + } + result.push(new variableClassProto(variable)) // looks like a leak but the instance is tracked and destroyed in this scope + } + return result + }, + regenerateIDs: function (blocks) { + var thiz = this + var updateChildren = function (block) { + var newId = utils.createUUID() + var children = thiz.getBlocks({ + parentId: block.id + }) + if (children && children.length > 0) { + for (var i = 0; i < children.length; i++) { + var child = children[i] + child.parentId = newId + updateChildren(child) + } + } + block.id = newId + } + for (var i = 0; i < blocks.length; i++) { + var block = blocks[i] + updateChildren(block) + } + }, + /** + * Clone blocks + * @param blocks + * @returns {module:xblox/model/Block[]} + */ + cloneBlocks2: function (blocks, forceGroup) { + var blocksJSON = this.blocksToJson(blocks); + var tmpScope = this.owner.getScope(utils.createUUID(), null, false); + var newBlocks = tmpScope.blocksFromJson(blocksJSON, false); + var store = this.blockStore; + newBlocks = tmpScope.allBlocks(); + tmpScope.regenerateIDs(newBlocks); + blocksJSON = tmpScope.blocksToJson(newBlocks); + if (forceGroup) { + for (var i = 0; i < blocksJSON.length; i++) { + var block = blocksJSON[i]; + if (block.parentId == null) { // groups are only needed for top level blocks + block.group = forceGroup; + } + } + } + var result = []; + newBlocks = this.blocksFromJson(blocksJSON); // add it to our scope + _.each(newBlocks, function (block) { + result.push(store.getSync(block.id)); + }) + return result + }, + /** + * Clone blocks + * @param blocks + */ + cloneBlocks: function (blocks) { + var blocksJSON = this.blocksToJson(blocks) + var tmpScope = this.owner.getScope(utils.createUUID(), null, false) + var newBlocks = tmpScope.blocksFromJson(blocksJSON, false) + newBlocks = tmpScope.allBlocks() + for (var i = 0; i < newBlocks.length; i++) { + var block = newBlocks[i] + block.id = utils.createUUID() + block.parentId = null + } + + this.blocksToJson(newBlocks) + this.blocksFromJson(newBlocks) // add it us + return newBlocks + }, + /** + * + * @param block + * @returns {Object} + */ + blockToJson: function (block) { + var blockOut = { + // this property is used to recreate the child blocks in the JSON -> blocks process + _containsChildrenIds: [] + } + for (var prop in block) { + if (prop == 'ctrArgs') { + continue; + } + + if (typeof block[prop] !== 'function' && !block.serializeField(prop)) { + continue; + } + + // copy all strings over + if (this.isString(block[prop]) || + this.isNumber(block[prop]) || + this.isBoolean(block[prop])) { + blockOut[prop] = block[prop] + } + // flatten children to ids. Skip "parent" field + if (prop != 'parent') { + if (this.isBlock(block[prop])) { + // if the field is a single block container, store the child block's id + blockOut[prop] = block[prop].id + + // register this field name as children ID container + blockOut._containsChildrenIds.push(prop) + } else if (this.areBlocks(block[prop])) { + // if the field is a multiple blocks container, store all the children blocks' id + blockOut[prop] = [] + + for (var i = 0; i < block[prop].length; i++) { + blockOut[prop].push(block[prop][i].id) + } + + // register this field name as children IDs container + blockOut._containsChildrenIds.push(prop) + } + } + } + + return blockOut + }, + /** + * Serializes all blocks to JSON data. + * It needs a custom conversation because we're having cyclic + * object dependencies. + * @returns {Array} + */ + blocksToJson: function (data) { + try { + var result = [] + data = (data && data.length) ? data : (this.blockStore ? this.blockStore.data : this.blocks) + for (var b in data) { + var block = data[b] + if (block.keys == null) { + continue; + } + if (block.serializeMe === false) { + continue; + } + var blockOut = { + // this property is used to recreate the child blocks in the JSON -> blocks process + _containsChildrenIds: [] + } + + for (var prop in block) { + if (prop == 'ctrArgs') { + continue; + } + + if (typeof block[prop] !== 'function' && !block.serializeField(prop)) { + continue; + } + + // copy all strings over + if (this.isString(block[prop]) || + this.isNumber(block[prop]) || + this.isBoolean(block[prop])) { + blockOut[prop] = block[prop] + } + + if (_.isObject(block[prop]) && block.serializeObject) { + if (block.serializeObject(prop) === true) { + blockOut[prop] = JSON.stringify(block[prop], null, 2) + } + } + + // flatten children to ids. Skip "parent" field + + if (prop != 'parent') { + if (this.isBlock(block[prop])) { + // if the field is a single block container, store the child block's id + blockOut[prop] = block[prop].id + + // register this field name as children ID container + blockOut._containsChildrenIds.push(prop) + } else if (this.areBlocks(block[prop])) { + // if the field is a multiple blocks container, store all the children blocks' id + blockOut[prop] = [] + + for (var i = 0; i < block[prop].length; i++) { + blockOut[prop].push(block[prop][i].id) + } + + // register this field name as children IDs container + blockOut._containsChildrenIds.push(prop) + } + } + } + result.push(blockOut) + } + } catch (e) { + console.error('from json failed : ' + e) + } + return result + }, + _createBlockStore: function () {}, + blockFromJson: function (block) { + block['scope'] = this + if (block._containsChildrenIds == null) { + block._containsChildrenIds = [] + } + + // Store all children references into "children" + var children = {} + for (var cf = 0; cf < block._containsChildrenIds.length; cf++) { + var propName = block._containsChildrenIds[cf] + children[propName] = block[propName] + block[propName] = null + } + delete block._containsChildrenIds + + // Create the block + if (!block.declaredClass) { + console.log(' not a class ') + return null + } + var blockClassProto = null + var _class = null + try { + _class = utils.replaceAll('.', '/', block.declaredClass) + blockClassProto = require(_class) + } catch (e) { + try { + _class = utils.replaceAll('/', '.', block.declaredClass) + blockClassProto = require(_class) + } catch (e) { + debug && console.error('couldnt resolve class ' + _class) + } + debug && console.error('couldnt resolve class ' + _class) + } + if (!blockClassProto) { + blockClassProto = dcl.getObject(block.declaredClass) + } + if (!blockClassProto) { + debug && console.log('couldn`t resolve ' + _class) + return null + } + + var blockOut = null + try { + blockOut = factory.createBlock(blockClassProto, block) + } catch (e) { + debug && console.error('error in block creation ', e) + logError(e) + return null + } + + // assign the children references into block._children + blockOut._children = children + + return blockOut + }, + /** + * Convert from JSON data. Creates all blocks in this scope + * @param data + * @returns {Array} + */ + blocksFromJson: function (data, check, errorCB) { + // console.log('blocksFromJson !'); + var resultSelected = [] + var childMap = {} + for (var i = 0; i < data.length; i++) { + var block = data[i] + block['scope'] = this + + if (block._containsChildrenIds == null) { + block._containsChildrenIds = [] + } + + // Store all children references into "children" + var children = {} + for (var cf = 0; cf < block._containsChildrenIds.length; cf++) { + var propName = block._containsChildrenIds[cf] + children[propName] = block[propName] + block[propName] = null + } + delete block._containsChildrenIds + + // Create the block + if (!block.declaredClass) { + console.log(' not a class ') + continue; + } + var blockClassProto = null + var _class = null + try { + _class = utils.replaceAll('.', '/', block.declaredClass) + blockClassProto = require(_class) + } catch (e) { + console.error('couldnt resolve class ' + _class) + } + if (!blockClassProto) { + blockClassProto = dcl.getObject(block.declaredClass) + } + if (!blockClassProto) { + console.log('couldnt resolve ' + _class) + continue; + } + + var blockOut = null + try { + blockOut = factory.createBlock(blockClassProto, block, null, false) + } catch (e) { + console.error('error in block creation ', e + ' ' + block.declaredClass) + logError(e) + continue; + } + + // assign the children references into block._children + blockOut._children = children + childMap[blockOut.id] = children + resultSelected.push(blockOut) + } + + // 2nd pass, update child blocks + var allBlocks = this.allBlocks(null, false) + for (var i = 0; i < allBlocks.length; i++) { + var block = allBlocks[i] + block._children = childMap[block.id] + if (block._children) { + // get all the block container fields + for (var propName in block._children) { + if (typeof block._children[propName] == 'string') { + // single block + var child = this.getBlockById(block._children[propName]) + if (!child) { + this.blockStore.removeSync(block._children[propName]) + if (errorCB) { + errorCB(' couldnt resolve child: ' + block._children[propName] + '@' + block.name + ':' + block.declaredClass) + } + console.log(' couldnt resolve child: ' + block._children[propName] + '@' + block.name + ':' + block.declaredClass) + continue; + } + block[propName] = child + child.parent = block + if (child.postCreate) { + child.postCreate() + } + } else if (typeof block._children[propName] == 'object') { + // multiple blocks + block[propName] = [] + for (var j = 0; j < block._children[propName].length; j++) { + var child = this.getBlockById(block._children[propName][j]) + if (!child) { + if (errorCB) { + errorCB(' couldnt resolve child: ' + block._children[propName] + '@ ' + block.name + ' : ' + block.declaredClass) + } + console.log(' couldnt resolve child: ' + block._children[propName][j] + '@' + block.name + ':' + block.declaredClass) + continue; + } + block[propName].push(child) + var _parent = this.getBlockById(child.parentId) + if (_parent) { + child.parent = _parent + } else { + if(child._failed){ + return; + } + child._failed = true; + if (errorCB) { + + child.group = 'basic'; + child.parentId = null; + child.parent = null; + child.name = 'Failed - ' + child.name; + errorCB('child has no parent : child.id = ' + child.id + ''); + } else { + console.error('child has no parent '); + } + } + } + } + } + delete block._children + } + + if (check !== false && block.parentId != null) { + var parent = this.getBlockById(block.parentId) + if (parent == null) { + debug && console.error('have orphan block!', block) + block.parentId = null + } + } + block.postCreate() + } + var result = this.allBlocks() + return resultSelected + }, + /** + * + * @param url {String} + * @returns {module:xblox/model/Block[]} + */ + resolveDevice: function (url) { + var blockScope = this, + ctx = this.ctx, + driver = this.driver, + device = this.device, + deviceManager = ctx.getDeviceManager(), + driverManager = ctx.getDriverManager() + + if (url.indexOf('://') == -1) { + var _block = this.getBlockById(url) + if (_block) { + return _block + } + return url + } + var parts = utils.parse_url(url) // strip scheme + + parts = utils.urlArgs(parts.host) // go on with query string + var _device = deviceManager.getItemById(parts.device.value) + // support device by name + if (!_device) { + var _instance = deviceManager.getInstanceByName(parts.device.value) + if (_instance) { + _device = _instance.device + } + } + return device || _device; + }, + /** + * + * @param url {String} + * @returns {module:xblox/model/Block[]} + */ + resolveBlock: function (url) { + var blockScope = this, + ctx = this.ctx, + driver = this.driver, + device = this.device, + deviceManager = ctx.getDeviceManager(), + driverManager = ctx.getDriverManager() + + if (url.indexOf('://') == -1) { + var _block = this.getBlockById(url) + if (_block) { + return _block + } + return url + } + var parts = utils.parse_url(url) // strip scheme + + parts = utils.urlArgs(parts.host) // go on with query string + var _device = deviceManager.getItemById(parts.device.value) + // support device by name + if (!_device) { + var _instance = deviceManager.getInstanceByName(parts.device.value) + if (_instance) { + _device = _instance.device + } + } + if (_device) { + var info = deviceManager.toDeviceControlInfo(_device); + if (!info) { + console.warn('cant get device info for ' + _device.title, device); + return; + } + + driver = driverManager.getDriverById(info.driverId) + var driverInstance = _device.driverInstance + if (driverInstance || driver) { + blockScope = driverInstance ? driverInstance.blockScope : driver.blockScope + var block = blockScope ? blockScope.getStore().getSync(parts.block.value) : null + if (block) { + return block + } + } + } + }, + getBlock: function (id) { + return this.getBlockById(id) + }, + /*** + * Returns a block from the scope + * @param name {String} + * @return block {module:xblox/model/Block[]} + */ + getBlockByName: function (name) { + if (name.indexOf('://') !== -1) { + var block = this.resolveBlock(name) + if (block) { + return block + } + } + var allBlocks = this.getBlocks() + for (var i = 0; i < allBlocks.length; i++) { + var block = allBlocks[i] + if (block.name === name) { + return block + } + } + var blocks = this.blockStore.query({ + name: name + }) + return blocks && blocks.length > 0 ? blocks[0] : null + }, + /*** + * Returns a block from the scope + * + * @param name => block name + * @return block + */ + getBlockById: function (id) { + return this.blockStore.getSync(id); + /* || this.variableStore.getSync(id) */ + }, + /** + * Returns an array of blocks + * @param blocks {module:xblox/model/Block[] + * @returns {module:xblox/model/Block[]} + */ + _flatten: function (blocks) { + var result = [] + for (var b in blocks) { + var block = blocks[b] + if (block.keys == null) { + continue; + } + result.push(block) + for (var prop in block) { + if (prop == 'ctrArgs') { + continue; + } + // flatten children to ids. Skip "parent" field + if (prop !== 'parent') { + if (this.isBlock(block[prop])) { + // if the field is a single block container, store the child block's id + result.push(block[prop]) + } else if (this.areBlocks(block[prop])) { + for (var i = 0; i < block[prop].length; i++) { + result.push(block[prop][i]) + } + } + } + } + } + return result + }, + /** + * + * @param blocks {module:xblox/model/Block[]} + * @returns {module:xblox/model/Block[]} + */ + flatten: function (blocks) { + var result = [] + for (var b in blocks) { + var block = blocks[b] + + if (block.keys == null) { + continue; + } + var found = _.find(result, { + id: block.id + }) + + if (found) { + // console.error('already in array : ' +found.name); + } else { + result.push(block) + } + + for (var prop in block) { + if (prop == 'ctrArgs') { + continue; + } + // flatten children to ids. Skip "parent" field + if (prop !== 'parent') { + var value = block[prop] + if (this.isBlock(value)) { + // if the field is a single block container, store the child block's id + found = _.find(result, { + id: value.id + }) + if (found) { + + } else { + result.push(value) + } + } else if (this.areBlocks(value)) { + for (var i = 0; i < value.length; i++) { + var sBlock = value[i] + found = _.find(result, { + id: sBlock.id + }) + if (found) {} else { + result.push(sBlock) + } + result = result.concat(this.flatten([sBlock])) + } + } + } + } + } + result = _.uniq(result, false, function (item) { + return item.id + }) + return result + }, + _getSolve: function (block) { + return block.prototype ? block.prototype.solve : block.__proto__.solve + }, + solveBlock: function (mixed, settings, force, isInterface) { + settings = settings || { + highlight: false + } + var block = null + if (this.isString(mixed)) { + block = this.getBlockByName(mixed) + if (!block) { + block = this.getBlockById(mixed) + } + } else if (this.isObject(mixed)) { + block = mixed + } + var result = null + if (block) { + if (settings.force !== true && block.enabled == false) { + return null + } + if (settings.force === true) { + settings.force = false + } + var _class = block.declaredClass + var _module = lang.getObject(utils.replaceAll('/', '.', _class)) || lang.getObject(_class) + if (_module) { + if (_module.prototype && _module.prototype.solve) { + result = _module.prototype.solve.apply(block, [this, settings]) + } + } else { + result = block.solve(block.getScope(), settings, force, isInterface) + delete block.override + block.override = {} + } + } else { + debug && console.error('solving block failed, have no block! ', mixed) + } + return result + }, + /*** + * Solves all the commands into [items] + * + * @param manager => BlockManager + * @return list of commands to send + */ + solve: function (scope, settings) { + var ret = '' + for (var n = 0; n < this.items.length; n++) { + ret += this.items[n].solve(scope, settings) + } + return ret + }, + /*** + * Parses an expression + * + * @param expression + * @returns {String} parsed expression + */ + /** + * + * @param expression + * @param addVariables + * @param variableOverrides + * @param runCallback + * @param errorCallback + * @param context + * @param args + * @returns {*} + */ + parseExpression: function (expression, addVariables, variableOverrides, runCallback, errorCallback, context, args, flags) { + return this.getExpressionModel().parse(this, expression, addVariables, runCallback, errorCallback, context, variableOverrides, args, flags) + }, + isString: function (a) { + return typeof a == 'string' + }, + isNumber: function (a) { + return typeof a == 'number' + }, + isBoolean: function (a) { + return typeof a == 'boolean' + }, + isObject: function (a) { + return typeof a === 'object' + }, + isBlock: function (a) { + var ret = false + + if ((typeof a == 'object') && (a != null) && (a.length == undefined)) { + if (a.serializeMe) { + ret = true + } + } + return ret + }, + areBlocks: function (a) { + var ret = false + + if ((typeof a == 'object') && (a != null) && (a.length > 0)) { + if (this.isBlock(a[0])) { + ret = true + } + } + return ret + }, + /** + * + * @private + */ + _onVariableChanged: function (evt) { + if (evt.item && this.getExpressionModel().variableFuncCache[evt.item.title]) { + delete this.expressionModel.variableFuncCache[evt.item.title] + } + }, + + init: function () { + this.getExpressionModel() // create + this.subscribe(types.EVENTS.ON_DRIVER_VARIABLE_CHANGED, this._onVariableChanged) + var thiz = this + + this.subscribe(types.EVENTS.ON_MODULE_RELOADED, function (evt) { + var mid = evt.module, + newModule = evt.newModule, + blocks = thiz.getBlocks(), + instances = blocks.filter(function (block) { + if (block.declaredClass == mid || block.declaredClass == utils.replaceAll('/', '.', mid)) { + return block + } + return null + }) + + instances && _.each(instances, function (block) { + mergeNewModule(block, newModule.prototype) + }) + }) + }, + /** + * + */ + _destroy: function () { + var allblocks = this.allBlocks() + for (var i = 0; i < allblocks.length; i++) { + var obj = allblocks[i] + if (!obj) { + continue; + } + try { + if (obj && obj.stop) { + obj.stop(true) + } + + if (obj && obj.reset) { + obj.reset() + } + if (obj && obj._destroy) { + obj._destroy() + } + if (obj && obj.destroy) { + obj.destroy() + } + + if (obj._emit) { + obj._emit(types.EVENTS.ON_ITEM_REMOVED, { + item: obj + }) + } + } catch (e) { + debug && console.error('Scope::_destroy: error destroying block ' + e.message, obj ? (obj.id + ' ' + obj.name) : 'empty') + debug && console.trace() + } + } + }, + destroy: function () { + this._destroy() + this.reset() + this._destroyed = true + delete this.expressionModel + }, + /** + * + * @param source + * @param target + * @param before + * @param add + * @returns {boolean} + */ + moveTo: function (source, target, before, add) { + console.log('move to : ', arguments); + /** + * treat first the special cases of adding an item + */ + if (add) { + // remove it from the source parent and re-parent the source + if (target.canAdd && target.canAdd()) { + var sourceParent = this.getBlockById(source.parentId) + if (sourceParent) { + sourceParent.removeBlock(source, false) + } + return target.add(source, null, null); + } else { + console.error('cant reparent') + return false + } + } + + // for root level move + if (!target.parentId && add === false) { + // if source is part of something, we remove it + var sourceParent = this.getBlockById(source.parentId); + if (sourceParent && sourceParent.removeBlock) { + sourceParent.removeBlock(source, false); + source.parentId = null; + source.group = target.group + } + + var itemsToBeMoved = []; + var groupItems = this.getBlocks({ + group: target.group + }); + + var rootLevelIndex = []; + var store = this.getBlockStore(); + + var sourceIndex = store.storage.index[source.id]; + var targetIndex = store.storage.index[target.id]; + for (var i = 0; i < groupItems.length; i++) { + var item = groupItems[i]; + // keep all root-level items + + if (groupItems[i].parentId == null && // must be root + groupItems[i] != source // cant be source + ) { + var itemIndex = store.storage.index[item.id]; + var add = before ? itemIndex >= targetIndex : itemIndex <= targetIndex; + if (add) { + itemsToBeMoved.push(groupItems[i]); + rootLevelIndex.push(store.storage.index[groupItems[i].id]) + } + } + } + + // remove them the store + for (var j = 0; j < itemsToBeMoved.length; j++) { + store.remove(itemsToBeMoved[j].id) + } + + // remove source + this.getBlockStore().remove(source.id); + + // if before, put source first + if (before) { + this.getBlockStore().putSync(source) + } + + // now place all back + for (var j = 0; j < itemsToBeMoved.length; j++) { + store.put(itemsToBeMoved[j]) + } + + // if after, place source back + if (!before) { + this.getBlockStore().putSync(source) + } + return true; + // we move from root to lower item + } else if (!source.parentId && target.parentId && add == false) { + source.group = target.group; + + // we move from root to into root item + } else if (!source.parentId && !target.parentId && add) { + if (target.canAdd && target.canAdd()) { + source.group = null; + target.add(source, null, null) + } + return true; + + // we move within the same parent + } else if (source.parentId && target.parentId && add == false && source.parentId === target.parentId) { + var parent = this.getBlockById(source.parentId); + if (!parent) { + return false + } + var items = parent[parent._getContainer(source)]; + var cIndexSource = source.indexOf(items, source); + var cIndexTarget = source.indexOf(items, target); + var direction = cIndexSource > cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - (cIndexTarget + (before == true ? -1 : 1))) + for (var i = 0; i < distance - 1; i++) { + source.move(direction); + } + return true; + // we move within the different parents + } else if (source.parentId && target.parentId && add == false && source.parentId !== target.parentId) { + var sourceParent = this.getBlockById(source.parentId); + if (!sourceParent) { + return false + } + + var targetParent = this.getBlockById(target.parentId); + if (!targetParent) { + return false + } + + // remove it from the source parent and re-parent the source + if (sourceParent && sourceParent.removeBlock && targetParent.canAdd && targetParent.canAdd()) { + sourceParent.removeBlock(source, false); + targetParent.add(source, null, null) + } else { + return false + } + + // now proceed as in the case above : same parents + var items = targetParent[targetParent._getContainer(source)]; + if (items == null) { + console.error('weird : target parent has no item container') + } + var cIndexSource = targetParent.indexOf(items, source); + var cIndexTarget = targetParent.indexOf(items, target); + if (!cIndexSource || !cIndexTarget) { + console.error(' weird : invalid drop processing state, have no valid item indicies') + return + } + var direction = cIndexSource > cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - (cIndexTarget + (before == true ? -1 : 1))) + for (var i = 0; i < distance - 1; i++) { + targetParent.move(source, direction) + } + return true + } + + return false + } + + }) + dcl.chainAfter(Module, 'destroy') + return Module +}) \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/Statement.js b/packages/xblox/ref-control-freak/xblox/model/Statement.js new file mode 100644 index 00000000..d99d994b --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Statement.js @@ -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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/Targeted.js b/packages/xblox/ref-control-freak/xblox/model/Targeted.js new file mode 100644 index 00000000..8e27b315 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/Targeted.js @@ -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],{ + + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/code/CallMethod.js b/packages/xblox/ref-control-freak/xblox/model/code/CallMethod.js new file mode 100644 index 00000000..98cdfd3e --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/code/CallMethod.js @@ -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 ''; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/code/RunBlock.js b/packages/xblox/ref-control-freak/xblox/model/code/RunBlock.js new file mode 100644 index 00000000..5a5dba06 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/code/RunBlock.js @@ -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 ''; + } + + + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/code/RunScript.html b/packages/xblox/ref-control-freak/xblox/model/code/RunScript.html new file mode 100644 index 00000000..e275e0c8 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/code/RunScript.html @@ -0,0 +1,10 @@ +Runs an expression.
+ +Behaviour + +
+
+    //to abort execution (child blocks), return something negative as -1 or false.
+    return false;
+
+
\ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/code/RunScript.js b/packages/xblox/ref-control-freak/xblox/model/code/RunScript.js new file mode 100644 index 00000000..597d2377 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/code/RunScript.js @@ -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 = '' + this.getBlockIcon() + ' ' + this.name + ' :: '+''; + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/events/OnEvent.js b/packages/xblox/ref-control-freak/xblox/model/events/OnEvent.js new file mode 100644 index 00000000..c1cceaa2 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/events/OnEvent.js @@ -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 ''; + }, + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/events/OnKey.js b/packages/xblox/ref-control-freak/xblox/model/events/OnKey.js new file mode 100644 index 00000000..ca9ba058 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/events/OnKey.js @@ -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 ''; + }, + ///////////////////////////////////////////////////////////////////////////////////// + // + // 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){} + + + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/functions/CallBlock.js b/packages/xblox/ref-control-freak/xblox/model/functions/CallBlock.js new file mode 100644 index 00000000..2c4d545e --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/functions/CallBlock.js @@ -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 "" + this[field] +""; + }, + 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 = '' +this.scope.toFriendlyName(this,this.command) + ''; + } + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/functions/PauseBlock.js b/packages/xblox/ref-control-freak/xblox/model/functions/PauseBlock.js new file mode 100644 index 00000000..cb78702f --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/functions/PauseBlock.js @@ -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 "" + this[field] +""; + }, + 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 = '' +this.scope.toFriendlyName(this,this.command) + ''; + } + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/functions/SetProperties.js b/packages/xblox/ref-control-freak/xblox/model/functions/SetProperties.js new file mode 100644 index 00000000..1fc0a8c6 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/functions/SetProperties.js @@ -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 "" + this[field] +""; + }, + 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 = '' +this.scope.toFriendlyName(this,this.command) + ''; + } + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/functions/StopBlock.js b/packages/xblox/ref-control-freak/xblox/model/functions/StopBlock.js new file mode 100644 index 00000000..ff352145 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/functions/StopBlock.js @@ -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 "" + this[field] +""; + }, + 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 = '' +this.scope.toFriendlyName(this,this.command) + ''; + } + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/html/SetCSS.js b/packages/xblox/ref-control-freak/xblox/model/html/SetCSS.js new file mode 100644 index 00000000..d900bf74 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/html/SetCSS.js @@ -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 ''; + }, + 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(){ + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/html/SetState.js b/packages/xblox/ref-control-freak/xblox/model/html/SetState.js new file mode 100644 index 00000000..c91c392b --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/html/SetState.js @@ -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 ''; + }, + 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; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/html/SetStyle.js b/packages/xblox/ref-control-freak/xblox/model/html/SetStyle.js new file mode 100644 index 00000000..122bd1e3 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/html/SetStyle.js @@ -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 ''; + }, + ///////////////////////////////////////////////////////////////////////////////////// + // + // 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; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/logging/Log.js b/packages/xblox/ref-control-freak/xblox/model/logging/Log.js new file mode 100644 index 00000000..46d3ffee --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/logging/Log.js @@ -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 + " : " + " " + ' :: '; + if (this.message) { + result += this.message; + } + return result + ""; + }, + /*** + * 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 ''; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/logic/BreakBlock.js b/packages/xblox/ref-control-freak/xblox/model/logic/BreakBlock.js new file mode 100644 index 00000000..5d19d7dd --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/logic/BreakBlock.js @@ -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 '   ' + this.name + ''; + }, + /*** + * Solves the case block + * @param scope + * @param settings + */ + solve: function (scope, settings) { + this.onSuccess(this, settings); + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/logic/CaseBlock.js b/packages/xblox/ref-control-freak/xblox/model/logic/CaseBlock.js new file mode 100644 index 00000000..f322a6a9 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/logic/CaseBlock.js @@ -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 width + 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 '   ' + 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') : '') + ''; + }, + 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 + } + }) + ] + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/logic/Comparator.js b/packages/xblox/ref-control-freak/xblox/model/logic/Comparator.js new file mode 100644 index 00000000..d9b90eb3 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/logic/Comparator.js @@ -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: '' + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/logic/DefaultBlock.js b/packages/xblox/ref-control-freak/xblox/model/logic/DefaultBlock.js new file mode 100644 index 00000000..b5eaaf51 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/logic/DefaultBlock.js @@ -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 '   ' + this.name + ''; + }, + solve: function (scope, settings) { + this.onSuccess(this, settings); + return this._solve(scope, settings); + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/logic/ElseIfBlock.js b/packages/xblox/ref-control-freak/xblox/model/logic/ElseIfBlock.js new file mode 100644 index 00000000..a4e5f4c1 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/logic/ElseIfBlock.js @@ -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 "" + this.getBlockIcon('E') + this.name + " " + "" + (this.condition || "") + ""; + }, + // 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/logic/IfBlock.js b/packages/xblox/ref-control-freak/xblox/model/logic/IfBlock.js new file mode 100644 index 00000000..75a9d554 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/logic/IfBlock.js @@ -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 "" + this.getBlockIcon('E') + this.name + " " + "" + this.condition + ""; + }, + _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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/logic/SwitchBlock.js b/packages/xblox/ref-control-freak/xblox/model/logic/SwitchBlock.js new file mode 100644 index 00000000..c2952362 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/logic/SwitchBlock.js @@ -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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/loops/ForBlock.js b/packages/xblox/ref-control-freak/xblox/model/loops/ForBlock.js new file mode 100644 index 00000000..427132ed --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/loops/ForBlock.js @@ -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 width + comparator: null, + // modifier: xcf.model.Expression + // expression to be applied to the counter on every step. Expression: "" + modifier: null, + + //items: Array (xblox.model.Block) + // block to be executed while the condition compare width 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/loops/WhileBlock.js b/packages/xblox/ref-control-freak/xblox/model/loops/WhileBlock.js new file mode 100644 index 00000000..fda5a0ae --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/loops/WhileBlock.js @@ -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; + } + + + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/mqtt/Publish.js b/packages/xblox/ref-control-freak/xblox/model/mqtt/Publish.js new file mode 100644 index 00000000..d55da910 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/mqtt/Publish.js @@ -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 = '' + this.getBlockIcon() + ' ' + this.makeEditable('name', 'top', 'text', 'Enter a name', 'inline') + ' :: ' + ''; + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/mqtt/Subscribe.js b/packages/xblox/ref-control-freak/xblox/model/mqtt/Subscribe.js new file mode 100644 index 00000000..fbdf43cf --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/mqtt/Subscribe.js @@ -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 = '' + + this.getBlockIcon() + '' + this.makeEditable('topic','bottom','text','Enter a topic','inline') + ' :: '+''; + + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/network/SSH.js b/packages/xblox/ref-control-freak/xblox/model/network/SSH.js new file mode 100644 index 00000000..b6ae6fbb --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/network/SSH.js @@ -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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/server/RunServerMethod.js b/packages/xblox/ref-control-freak/xblox/model/server/RunServerMethod.js new file mode 100644 index 00000000..da9f4208 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/server/RunServerMethod.js @@ -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 ''; + }, + ///////////////////////////////////////////////////////////////////////////////////// + // + // 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; + } + + + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/server/ServerBlock.js b/packages/xblox/ref-control-freak/xblox/model/server/ServerBlock.js new file mode 100644 index 00000000..22850dea --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/server/ServerBlock.js @@ -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 ''; + }, + ///////////////////////////////////////////////////////////////////////////////////// + // + // 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/server/Shell.js b/packages/xblox/ref-control-freak/xblox/model/server/Shell.js new file mode 100644 index 00000000..0179ea93 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/server/Shell.js @@ -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 ''; + }, + ///////////////////////////////////////////////////////////////////////////////////// + // + // 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/simple.json b/packages/xblox/ref-control-freak/xblox/model/simple.json new file mode 100644 index 00000000..fbcded5e --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/simple.json @@ -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 + } +] \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/transform/BezierInterpolate.js b/packages/xblox/ref-control-freak/xblox/model/transform/BezierInterpolate.js new file mode 100644 index 00000000..8188be63 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/transform/BezierInterpolate.js @@ -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 ''; + } + }); + + bezInterpolate.createQuadraticBuilder = createQuadraticBuilder();//statics + + return bezInterpolate; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/transform/BezierTransform.js b/packages/xblox/ref-control-freak/xblox/model/transform/BezierTransform.js new file mode 100644 index 00000000..e37643bc --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/transform/BezierTransform.js @@ -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 ''; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/variables/Variable.js b/packages/xblox/ref-control-freak/xblox/model/variables/Variable.js new file mode 100644 index 00000000..eadb63b8 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/variables/Variable.js @@ -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 ' '; + }, + toText:function(){ + return "" + this.getBlockIcon() + this.makeEditable('name','right','text','Enter a unique name','inline') +""; + }, + 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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/variables/VariableAssignmentBlock.js b/packages/xblox/ref-control-freak/xblox/model/variables/VariableAssignmentBlock.js new file mode 100644 index 00000000..22583aa5 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/variables/VariableAssignmentBlock.js @@ -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 = '' +this.scope.toFriendlyName(this, this.variable)+''; + } + return this.getBlockIcon('C') + this.name + ' ' + _text + " to " + this.makeEditable("value",'bottom','text','Enter the string to send','inline') + ""; + }, + _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; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/model/variables/VariableSwitch.js b/packages/xblox/ref-control-freak/xblox/model/variables/VariableSwitch.js new file mode 100644 index 00000000..d79beb85 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/model/variables/VariableSwitch.js @@ -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; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/package.json b/packages/xblox/ref-control-freak/xblox/package.json new file mode 100644 index 00000000..a29dd67b --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/package.json @@ -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": "." + } +} diff --git a/packages/xblox/ref-control-freak/xblox/resources-debug.json b/packages/xblox/ref-control-freak/xblox/resources-debug.json new file mode 100644 index 00000000..28d8bb78 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/resources-debug.json @@ -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 + } + + ] +} diff --git a/packages/xblox/ref-control-freak/xblox/resources-release.json b/packages/xblox/ref-control-freak/xblox/resources-release.json new file mode 100644 index 00000000..10083d25 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/resources-release.json @@ -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 + } + ] +} + diff --git a/packages/xblox/ref-control-freak/xblox/run.js b/packages/xblox/ref-control-freak/xblox/run.js new file mode 100644 index 00000000..81758017 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/run.js @@ -0,0 +1,31 @@ +/** + * This file is used to reconfigure parts of the loader at runtime for this application. We’ve 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 aren’t 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 /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']); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/addCss.js b/packages/xblox/ref-control-freak/xblox/test/intern/addCss.js new file mode 100644 index 00000000..7f94cc2d --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/addCss.js @@ -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); + } + }; +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/all.js b/packages/xblox/ref-control-freak/xblox/test/intern/all.js new file mode 100644 index 00000000..493a4b5f --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/all.js @@ -0,0 +1,3 @@ +define([ + 'intern/node_modules/dojo/has!host-browser?./core/createDestroy' +], function () {}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/core/OnDemandList.js b/packages/xblox/ref-control-freak/xblox/test/intern/core/OnDemandList.js new file mode 100644 index 00000000..2c2b3fd6 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/core/OnDemandList.js @@ -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); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/core/_StoreMixin.js b/packages/xblox/ref-control-freak/xblox/test/intern/core/_StoreMixin.js new file mode 100644 index 00000000..3681f5c6 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/core/_StoreMixin.js @@ -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'); + }); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/core/addCssRule.js b/packages/xblox/ref-control-freak/xblox/test/intern/core/addCssRule.js new file mode 100644 index 00000000..c2cab6a7 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/core/addCssRule.js @@ -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'); + }); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/core/columns.js b/packages/xblox/ref-control-freak/xblox/test/intern/core/columns.js new file mode 100644 index 00000000..d7951694 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/core/columns.js @@ -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(); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/core/createDestroy.js b/packages/xblox/ref-control-freak/xblox/test/intern/core/createDestroy.js new file mode 100644 index 00000000..3041a031 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/core/createDestroy.js @@ -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'); + */ + }); + + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/core/setClass.js b/packages/xblox/ref-control-freak/xblox/test/intern/core/setClass.js new file mode 100644 index 00000000..29db3ae1 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/core/setClass.js @@ -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(); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/core/stores.js b/packages/xblox/ref-control-freak/xblox/test/intern/core/stores.js new file mode 100644 index 00000000..d3852c1c --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/core/stores.js @@ -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)); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/core/trackable.js b/packages/xblox/ref-control-freak/xblox/test/intern/core/trackable.js new file mode 100644 index 00000000..82ae0065 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/core/trackable.js @@ -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); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/extensions/ColumnHider.js b/packages/xblox/ref-control-freak/xblox/test/intern/extensions/ColumnHider.js new file mode 100644 index 00000000..89510a2c --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/extensions/ColumnHider.js @@ -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'); + }); + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/extensions/CompoundColumns.js b/packages/xblox/ref-control-freak/xblox/test/intern/extensions/CompoundColumns.js new file mode 100644 index 00000000..b127c6ca --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/extensions/CompoundColumns.js @@ -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'); + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/extensions/Pagination.js b/packages/xblox/ref-control-freak/xblox/test/intern/extensions/Pagination.js new file mode 100644 index 00000000..5b631a45 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/extensions/Pagination.js @@ -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); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional.js b/packages/xblox/ref-control-freak/xblox/test/intern/functional.js new file mode 100644 index 00000000..e899e1ed --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional.js @@ -0,0 +1,3 @@ +define([ + './functional/Keyboard' +], function () {}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor-OnDemand.html b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor-OnDemand.html new file mode 100644 index 00000000..1d7b8483 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor-OnDemand.html @@ -0,0 +1,89 @@ + + + + + Test Cell Editors + + + + +

OnDemandGrid with editors

+
+ + + + + + diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor.html b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor.html new file mode 100644 index 00000000..cb3c642f --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor.html @@ -0,0 +1,72 @@ + + + + + Test Cell Editors + + + + +

Grid with editors

+
+ + + + + + diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor.js b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor.js new file mode 100644 index 00000000..8a0298a7 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Editor.js @@ -0,0 +1,350 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + './util', + 'intern/dojo/node!leadfoot/helpers/pollUntil', + 'intern/dojo/node!leadfoot/keys', + 'require' +], function (test, assert, util, pollUntil, keys, require) { + // Number of visible rows in the grid. + // Check the data loaded in test file (editor.html) and rows visible + // when the page is loaded to ensure this is correct. + var GRID_ROW_COUNT = 3; + var rowSelectorPrefix = '#grid-row-'; + + var EditorCommand = util.createCommandConstructor({ + dismissViaEnter: function () { + // Presses the enter key and ends the current element context. + return new this.constructor(this, function () { + return this.parent.type(keys.ENTER); + }); + }, + dismissViaBlur: function () { + // Exits to the parent context and focuses an unrelated element. + return new this.constructor(this, function () { + return this.parent.end() + .findByTagName('h2') + .click(); + }); + } + }); + + test.suite('dgrid/editor functional tests', function () { + var gotoEnd; // Function defined when `before` logic runs + + // Functions performing operations to test the editor columns in the grid, + // passed to createDatachangeTest + + function testAlwaysOnEditor(command, rowIndex, dismissFunc) { + // Turn off whitespace check because it doesn't like [dismissFunc]() on its own line + /* jshint white: false */ + var startValue, + appendValue = 'abc'; + + // Click the cell's editor element to focus it + command = command.findByCssSelector(rowSelectorPrefix + rowIndex + ' .field-name input') + .click() + // Store the current cell value + .getProperty('value') + .then(function (cellValue) { + startValue = cellValue; + }); + // Type extra characters to change value + return gotoEnd(command) + .type(appendValue) + [dismissFunc]() + .end() + // Click another cell to blur the edited cell (and trigger saving and dgrid-datachange event) + .findByCssSelector(rowSelectorPrefix + rowIndex + ' .field-description') + .click() + // The test page has a dgrid-datachange event listener that will push the new value + // into a global array: datachangeStack + .execute('return datachangeStack.shift();') + .then(function (datachangeValue) { + assert.strictEqual(startValue + appendValue, datachangeValue, + 'Value in dgrid-datachange event (' + datachangeValue + + ') should equal edited value (' + startValue + appendValue + ')'); + }) + .end(); + } + + function testEditOnEditor(command, rowIndex, dismissFunc) { + // Turn off whitespace check because it doesn't like [dismissFunc]() on its own line + /* jshint white: false */ + + var cellSelector = rowSelectorPrefix + rowIndex + ' .field-description', + startValue, + appendValue = 'abc'; + + // Click the cell to activate the editor + command = command.findByCssSelector(cellSelector) + .click() + .end() + // Set context to the cell's editor + .findByCssSelector(cellSelector + ' input') + // Store the current cell value + .getProperty('value') + .then(function (cellValue) { + startValue = cellValue; + }); + // Type extra characters to change value + return gotoEnd(command) + .type(appendValue) + [dismissFunc]() + .end() + // The test page has a dgrid-datachange event listener that will push the new value + // into a global array: datachangeStack + .execute('return datachangeStack.shift();') + .then(function (datachangeValue) { + assert.strictEqual(startValue + appendValue, datachangeValue, + 'Value in dgrid-datachange event (' + datachangeValue + + ') should equal edited value (' + startValue + appendValue + ')'); + }); + } + + function createDatachangeTest(testFunc, dismissFunc, initFunction) { + // Generates test functions for enter/blur value registration tests + return function () { + this.async(60000); + var command = new EditorCommand(this.get('remote')); + + command = command.get(require.toUrl('./Editor.html')) + .then(pollUntil(function () { + return window.ready; + }, null, 5000)); + + if (initFunction) { + command = command.execute(initFunction); + } + + for (var rowIndex = 0; rowIndex < GRID_ROW_COUNT; rowIndex++) { + command = testFunc(command, rowIndex, dismissFunc); + } + + return command; + }; + } + + function createFocusTest(selector, initFunction) { + // Generates test functions for focus preservation tests + return function () { + var command = new EditorCommand(this.get('remote')), + rowIndex; + + function each(rowIndex) { + // Click the cell to activate and focus the editor + return command.findByCssSelector(rowSelectorPrefix + rowIndex + ' ' + selector) + .click() + .end() + .executeAsync(function (id, rowIdPrefix, done) { + /* global grid */ + function getRowId(node) { + // Retrieves ID of row based on an input node + while (node && node.id.slice(0, 9) !== rowIdPrefix) { + node = node.parentNode; + } + return node && node.id; + } + + var activeId = getRowId(document.activeElement); + grid.collection.emit('update', { target: grid.collection.get(id) }); + // Need to wait until next turn for refocus + setTimeout(function () { + done(activeId === getRowId(document.activeElement)); + }, 0); + }, [ rowIndex, rowSelectorPrefix.slice(1) ]) + .then(function (testPassed) { + assert.isTrue(testPassed, + 'Focused element before refresh should remain focused after refresh'); + }) + .findByTagName('h2') + .click() + .end(); + } + + command = command.get(require.toUrl('./Editor-OnDemand.html')) + .then(pollUntil(function () { + return window.ready; + }, null, 5000)); + + if (initFunction) { + command = command.execute(initFunction); + } + + for (rowIndex = 0; rowIndex < GRID_ROW_COUNT; rowIndex++) { + command = each(rowIndex); + } + + return command; + }; + } + + function createEscapeRevertTest(initFunction) { + return function () { + var command = new EditorCommand(this.get('remote')), + rowIndex; + + function each(rowIndex) { + var cellSelector = rowSelectorPrefix + rowIndex + ' .field-description', + startValue, + appendValue = 'abc'; + + // Click the cell to focus the editor + var newCommand = command.findByCssSelector(cellSelector) + .click() + .end() + // Get the initial value from the editor field + .findByCssSelector(cellSelector + ' input') + .getProperty('value') + .then(function (cellValue) { + startValue = cellValue; + }); + + // Append extra chars and verify the editor's value has updated + return gotoEnd(newCommand) + .type(appendValue) + .getProperty('value') + .then(function (cellValue) { + assert.notStrictEqual(startValue, cellValue, + 'Row ' + rowIndex + ' editor value should differ from the original'); + }) + // Send Escape and verify the value has reverted in the grid's data + .type(keys.ESCAPE) + .execute('return grid.row(' + rowIndex + ').data.description;') + .then(function (cellValue) { + assert.strictEqual(startValue, cellValue, + 'Row ' + rowIndex + ' editor value should equal the starting value after escape'); + }) + .end(); + } + + command = command.get(require.toUrl('./Editor.html')) + .then(pollUntil(function () { + return window.ready; + }, null, 5000)); + + if (initFunction) { + command = command.execute(initFunction); + } + + for (rowIndex = 0; rowIndex < GRID_ROW_COUNT; rowIndex++) { + command = each(rowIndex); + } + + return command; + }; + } + + function createAutosaveTest(initFunction) { + return function () { + var command = new EditorCommand(this.get('remote')), + appendValue = 'abc', + rowIndex; + + function each(rowIndex) { + var editedValue; + + // Click the cell editor and update the value + var newCommand = command.findByCssSelector(rowSelectorPrefix + rowIndex + ' .field-name input') + .click(); + return gotoEnd(newCommand) + .type(appendValue) + .getProperty('value') + .then(function (cellValue) { + editedValue = cellValue; + }) + .dismissViaBlur() + .end() + // Click elsewhere to trigger saving of edited cell + .findByTagName('h2') + .click() + .end() + // Wait for the save to complete before moving on to next iteration + .then(pollUntil(function () { + return window.saveComplete; + }, null, 5000)) + // Get the saved value from the test page and verify it + .execute('return gridSaveStack.shift();') + .then(function (savedValue) { + assert.strictEqual(editedValue, savedValue, + 'Row ' + rowIndex + ', column "name" saved value (' + savedValue + + ') should equal the entered value (' + editedValue + ')'); + }); + } + + command = command.get(require.toUrl('./Editor-OnDemand.html')) + .then(pollUntil(function () { + return window.ready; + }, null, 5000)); + + if (initFunction) { + command = command.execute(initFunction); + } + + for (rowIndex = 0; rowIndex < GRID_ROW_COUNT; rowIndex++) { + command = each(rowIndex); + } + + return command; + }; + } + + // Function passed to above functions to change grid column structure + // to test other types of editors + + function setTextBox() { + /* global setEditorToTextBox */ + setEditorToTextBox(); + } + + test.before(function () { + // In order to function properly on all platforms, we need to know + // what the proper character sequence is to go to the end of a text field. + // End key works generally everywhere except Mac OS X. + return util.isInputHomeEndSupported(this.get('remote')).then(function (isSupported) { + gotoEnd = isSupported ? function (command) { + return command.type(keys.END); + } : function (command) { + return command.type(keys.META + keys.ARROW_RIGHT); + }; + }); + }); + + test.test('escape reverts edited value', createEscapeRevertTest()); + test.test('escape reverts edited value - TextBox', createEscapeRevertTest(setTextBox)); + + // This combination works, though it's debatable whether it even should + test.test('enter registers edited value for always-on editor', + createDatachangeTest(testAlwaysOnEditor, 'dismissViaEnter')); + + test.test('enter registers edited value for editOn editor', + createDatachangeTest(testEditOnEditor, 'dismissViaEnter')); + + test.test('blur registers edited value for always-on editor', + createDatachangeTest(testAlwaysOnEditor, 'dismissViaBlur')); + + test.test('blur registers edited value for always-on editor - TextBox', + createDatachangeTest(testAlwaysOnEditor, 'dismissViaBlur', setTextBox)); + + test.test('blur registers edited value for editOn editor', + createDatachangeTest(testEditOnEditor, 'dismissViaBlur')); + + test.test('blur registers edited value for editOn editor - TextBox', + createDatachangeTest(testEditOnEditor, 'dismissViaBlur', setTextBox)); + + test.test('maintain focus on update for always-on editor', + createFocusTest('.field-name input')); + + test.test('maintain focus on update for always-on editor - TextBox', + createFocusTest('.field-name input', setTextBox)); + + test.test('maintain focus on update for editOn editor', + createFocusTest('.field-description')); + + test.test('maintain focus on update for editOn editor - TextBox', + createFocusTest('.field-description', setTextBox)); + + test.test('autoSave: true', createAutosaveTest()); + test.test('autoSave: true - TextBox', createAutosaveTest(setTextBox)); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/Keyboard.html b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Keyboard.html new file mode 100644 index 00000000..8c95b658 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Keyboard.html @@ -0,0 +1,81 @@ + + + + + Test Keyboard Mixin + + + +

Keyboard/focus-enabled List and Grids

+
+
+
+
+
+
+ + + + diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/Keyboard.js b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Keyboard.js new file mode 100644 index 00000000..a38b4755 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Keyboard.js @@ -0,0 +1,34 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'intern/dojo/node!leadfoot/helpers/pollUntil', + 'intern/dojo/node!leadfoot/keys', + 'require' +], function (test, assert, pollUntil, keys, require) { + function testUpDownKeys(gridId, cellNavigation) { + + return function () { + return true; + }; + } + + test.suite('Keyboard functional tests', function () { + + /* + test.before(function () { + // Get our html page. This page should load all necessary scripts + // since this functional test module runs on the server and can't load + // such scripts. Further, in the html page, set a global "ready" var + // to true to tell the runner to continue. + return this.get('remote') + .get(require.toUrl('./Keyboard.html')) + .then(pollUntil(function () { + return window.ready; + }, null, 5000)); + }); + */ + + test.test('grid (cellNavigation: true) -> up + down arrow keys', + testUpDownKeys('grid', true)); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/KeyboardTab.html b/packages/xblox/ref-control-freak/xblox/test/intern/functional/KeyboardTab.html new file mode 100644 index 00000000..e34aed42 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/KeyboardTab.html @@ -0,0 +1,71 @@ + + + + +Test Keyboard Mixin with Tab key + + + + + +
+
+ + + + + + diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/KeyboardTab.js b/packages/xblox/ref-control-freak/xblox/test/intern/functional/KeyboardTab.js new file mode 100644 index 00000000..98272e4c --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/KeyboardTab.js @@ -0,0 +1,98 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'intern/dojo/node!leadfoot/helpers/pollUntil', + 'intern/dojo/node!leadfoot/keys', + 'require' +], function (test, assert, pollUntil, keys, require) { + var tabKey = keys.TAB; + test.suite('Keyboard tab key functional tests', function () { + test.before(function () { + // Get our html page. This page should load all necessary scripts + // since this functional test module runs on the server and can't load + // such scripts. Further, in the html page, set a global "ready" var + // to true to tell the runner to continue. + return this.get('remote') + .get(require.toUrl('./KeyboardTab.html')) + .then(pollUntil(function () { + return window.ready; + }, null, 5000)); + }); + + test.test('grids with and without headers -> tab key', function () { + return this.get('remote') + .getActiveElement() + .getAttribute('id') + .then(function (id) { + assert.strictEqual(id, 'showHeaderButton', 'Focus is on the button: ' + id); + }) + .type(tabKey) + .end() + .getActiveElement() + .getAttribute('role').then(function (role) { + assert.strictEqual(role, 'columnheader', 'Focus is on a column header: ' + role); + }) + .type(tabKey) + .end() + .getActiveElement() + .getAttribute('role').then(function (role) { + assert.strictEqual(role, 'gridcell', 'Focus is on a grid cell: ' + role); + }) + .getVisibleText() + .then(function (text) { + assert.strictEqual(text, '0', 'The cell with focus contains 0: ' + text); + }) + .type(tabKey) + .end() + .getActiveElement() + .getAttribute('role').then(function (role) { + assert.strictEqual(role, 'gridcell', 'Focus is on a grid cell: ' + role); + }) + .getVisibleText() + .then(function (text) { + assert.strictEqual(text, '10', 'The cell with focus contains 10: ' + text); + }) + .end() + .findById('showHeaderButton') + .click() + .end() + .getActiveElement() + .getAttribute('id') + .then(function (id) { + assert.strictEqual(id, 'showHeaderButton', 'Focus is on the button: ' + id); + }) + .type(tabKey) + .end() + .getActiveElement() + .getAttribute('role').then(function (role) { + assert.strictEqual(role, 'columnheader', 'Focus is on a column header: ' + role); + }) + .type(tabKey) + .end() + .getActiveElement() + .getAttribute('role').then(function (role) { + assert.strictEqual(role, 'gridcell', 'Focus is on a grid cell: ' + role); + }) + .getVisibleText() + .then(function (text) { + assert.strictEqual(text, '0', 'The cell with focus contains 0: ' + text); + }) + .type(tabKey) + .end() + .getActiveElement() + .getAttribute('role').then(function (role) { + assert.strictEqual(role, 'columnheader', 'Focus is on a column header: ' + role); + }) + .type(tabKey) + .end() + .getActiveElement() + .getAttribute('role').then(function (role) { + assert.strictEqual(role, 'gridcell', 'Focus is on a grid cell: ' + role); + }) + .getVisibleText() + .then(function (text) { + assert.strictEqual(text, '10', 'The cell with focus contains 10: ' + text); + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/Selector.html b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Selector.html new file mode 100644 index 00000000..c4f95b7a --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Selector.html @@ -0,0 +1,110 @@ + + + + + Test dgrid row selector module + + + + + + + + + + + + + +
+

Extended selectionMode

+
+ +

Multiple selectionMode

+
+ +

Single selectionMode

+
+
+

Toggle selectionMode

+
+ +

None selectionMode

+
+
+ + + + + \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/Selector.js b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Selector.js new file mode 100644 index 00000000..b9e8c312 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Selector.js @@ -0,0 +1,196 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + './util', + 'intern/dojo/node!leadfoot/helpers/pollUntil', + 'intern/dojo/node!leadfoot/keys', + 'require' +], function (test, assert, util, pollUntil, keys, require) { + // The number of visible rows in each grid (clickable without scrolling) + // Look at the test page to determine this value + var NUM_VISIBLE_ROWS = 6; + + // The expected select state of each of the first NUM_VISIBLE_ROWS in each + // table after the first click loop. + // This could be calculated with a conditional statement, but the logic to so + // gets a bit tortuous so just lay it out explicitly: + var expectedSelectState = { + gridExtended: [1, 0, 1, 0, 1, 0], + gridSingle: [0, 0, 0, 0, 1, 0] + }; + expectedSelectState.gridMultiple = expectedSelectState.gridExtended; + expectedSelectState.gridToggle = expectedSelectState.gridExtended; + expectedSelectState.gridNone = expectedSelectState.gridExtended; + + test.suite('dgrid/selector functional tests', function () { + + var isShiftClickSupported; + + // Click the checkbox/radio in the first NUM_VISIBLE_ROWS of a grid. + // After each click the row will be tested for the "dgrid-selected" class. + function clickAndTestEachRow(remote, gridId) { + var rowSelector = '#' + gridId + '-row-', + rowIndex; + + function each(rowIndex) { + // Click the dgrid/selector checkbox/radio + return remote.findByCssSelector(rowSelector + rowIndex + ' .field-select input') + .click() + .end() + // Check the row for the "dgrid-selected" class + .findByCssSelector(rowSelector + rowIndex) + .getAttribute('class') + .then(function (classString) { + var classNames, + isSelected; + + if (rowIndex % 2) { + classNames = classString.split(' '); + isSelected = classNames.indexOf('dgrid-selected') !== -1; + assert.isFalse(isSelected, + gridId + ': Row ' + rowIndex + ' should NOT be selected'); + } + else { + assert.include(classString, 'dgrid-selected', + gridId + ': Row ' + rowIndex + ' should be selected after click'); + } + }) + .end(); + } + + for (rowIndex = 0; rowIndex < NUM_VISIBLE_ROWS; rowIndex++) { + // The code in this loop is async and might run after the loop has updated + // rowIndex, so run the code in a function with its own value + remote = each(rowIndex); + } + + return remote; + } + + // Click the checkbox/radio in the first row, then shift+click in the 5th. + function shiftClickAndTestRows(remote, gridId) { + var rowSelector = '#' + gridId + '-row-'; + + return remote.findByCssSelector(rowSelector + '0' + ' .field-select input') + .click() + .end() + .pressKeys(keys.SHIFT) + .findByCssSelector(rowSelector + '4' + ' .field-select input') + .click() + .pressKeys(keys.NULL) + .end(); + } + + // Click the "Select All" checkbox in the grid header + function selectAll(remote, gridId) { + var selector = '#' + gridId + ' .dgrid-header .field-select input'; + + return remote.findByCssSelector(selector) + .click() + .end(); + } + + function createRowSelectionTest(gridId, allowMultiple, selectTestFunction) { + return function () { + if (selectTestFunction === shiftClickAndTestRows && !isShiftClickSupported) { + return; + } + + var selector = '#' + gridId + '-row-', + remote = this.get('remote'), + rowIndex; + + function each(rowIndex) { + return remote.findByCssSelector(selector + rowIndex) + .getAttribute('class').then(function (classString) { + var classNames, + isSelected; + + if (expectedSelectState[gridId][rowIndex]) { + assert.include(classString, 'dgrid-selected', + gridId + ': Row ' + rowIndex + ' should still be selected'); + } + else { + classNames = classString.split(' '); + isSelected = classNames.indexOf('dgrid-selected') !== -1; + assert.isFalse(isSelected, + gridId + ': Row ' + rowIndex + ' should NOT be selected'); + } + }) + .end(); + } + + remote = selectTestFunction(remote, gridId); + + // Loop through all rows to verify selection state + for (rowIndex = 0; rowIndex < NUM_VISIBLE_ROWS; rowIndex++) { + // The code in this loop is async and might run after the loop has updated + // rowIndex, so run the code in a function with its own value + remote = each(rowIndex); + } + + return remote; + }; + } + + test.before(function () { + var remote = this.get('remote'); + return remote.get(require.toUrl('./Selector.html')) + .then(pollUntil(function () { + return window.ready; + }, null, 5000)) + .then(function () { + return util.isShiftClickSupported(remote).then(function (isSupported) { + isShiftClickSupported = isSupported; + if (!isSupported) { + console.warn('shift+click tests will be no-ops because ' + + 'this browser/WebDriver combination does not support shift+click.'); + } + }); + }); + }); + + test.beforeEach(function () { + // Clear selections from previous tests + return this.get('remote').execute(function () { + /* global gridExtended, gridMultiple, gridSingle, gridToggle, gridNone */ + gridExtended.clearSelection(); + gridMultiple.clearSelection(); + gridSingle.clearSelection(); + gridToggle.clearSelection(); + gridNone.clearSelection(); + }); + }); + + test.test('selectionMode: extended', + createRowSelectionTest('gridExtended', true, clickAndTestEachRow)); + test.test('selectionMode: multiple', + createRowSelectionTest('gridMultiple', true, clickAndTestEachRow)); + test.test('selectionMode: single', + createRowSelectionTest('gridSingle', false, clickAndTestEachRow)); + test.test('selectionMode: toggle', + createRowSelectionTest('gridToggle', true, clickAndTestEachRow)); + test.test('selectionMode: none', + createRowSelectionTest('gridNone', true, clickAndTestEachRow)); + + test.test('multiple selection with shift+click; selectionMode: extended', + createRowSelectionTest('gridExtended', true, shiftClickAndTestRows)); + test.test('multiple selection with shift+click; selectionMode: multiple', + createRowSelectionTest('gridMultiple', true, shiftClickAndTestRows)); + test.test('multiple selection with shift+click; selectionMode: single', + createRowSelectionTest('gridSingle', false, shiftClickAndTestRows)); + test.test('multiple selection with shift+click; selectionMode: toggle', + createRowSelectionTest('gridToggle', true, shiftClickAndTestRows)); + test.test('multiple selection with shift+click; selectionMode: none', + createRowSelectionTest('gridNone', true, shiftClickAndTestRows)); + + test.test('select all; selectionMode: extended', + createRowSelectionTest('gridExtended', true, selectAll)); + test.test('select all; selectionMode: multiple', + createRowSelectionTest('gridMultiple', true, selectAll)); + test.test('select all; selectionMode: toggle', + createRowSelectionTest('gridToggle', true, selectAll)); + test.test('select all; selectionMode: none', + createRowSelectionTest('gridNone', true, selectAll)); + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/Tree.html b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Tree.html new file mode 100644 index 00000000..24156e83 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Tree.html @@ -0,0 +1,72 @@ + + + + +Test dgrid/selector module + + + + + +
+ + + + + + \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/Tree.js b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Tree.js new file mode 100644 index 00000000..6baa6a9f --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/Tree.js @@ -0,0 +1,72 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'intern/dojo/node!leadfoot/helpers/pollUntil', + 'require' +], function (test, assert, pollUntil, require) { + // dgrid.css defines a 300ms transition duration for .dgrid-tree-container + // Add 20% for good measure + var TRANSITION_DELAY = 360; + + function createExpandTest(clickTarget, clickMethod) { + return function () { + var xOffset = 0; + // Nudge the y-offset of the cursor down a bit for good measure (default seems to be 0,0 in the target + // element; in Firefox the click has been known to hit just above the target element) + var remote = this.get('remote'); + + if (remote.environmentType.browserName === 'safari') { + console.warn('Warning: skipping a tree functional test because ' + + 'the safari web driver does not support mouseMoveTo.'); + return; + } + + // With the cell selector used in the double-click test the cursor will be positioned at the start of the + // cell, right over the expando + // Move the cursor a bit to the right so it's not on the expando + if (clickMethod === 'doubleClick') { + xOffset = 30; + } + + // Turn off whitespace check because it doesn't like [clickMethod]() on its own line + /* jshint white: false */ + return remote.findByCssSelector('#treeGrid-row-AF ' + clickTarget) + .moveMouseTo(xOffset, 8) + [clickMethod]() + .sleep(TRANSITION_DELAY) + .end() + .findByCssSelector('#treeGrid-row-SD') + .isDisplayed() + .then(function (isDisplayed) { + assert.ok(isDisplayed, 'Expanded rows should be visible'); + }) + .end() + .findByCssSelector('#treeGrid-row-AF ' + clickTarget) + .moveMouseTo(xOffset, 8) + [clickMethod]() + .sleep(TRANSITION_DELAY) + .end() + .findByCssSelector('#treeGrid-row-SD') + .isDisplayed() + .then(function (isDisplayed) { + assert.ok(!isDisplayed, 'Collapsed rows should not be visible'); + }) + .end(); + }; + } + + test.suite('dgrid/tree functional tests', function () { + + test.before(function () { + var remote = this.get('remote'); + + return remote.get(require.toUrl('./Tree.html')) + .then(pollUntil(function () { + return window.ready; + }, null, 5000)); + }); + + test.test('expand/collapse: click on expando node', createExpandTest('.dgrid-expando-icon', 'click')); + test.test('expand/collapse: double-click on cell node', createExpandTest('.dgrid-column-0', 'doubleClick')); + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/functional/util.js b/packages/xblox/ref-control-freak/xblox/test/intern/functional/util.js new file mode 100644 index 00000000..ef526027 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/functional/util.js @@ -0,0 +1,87 @@ +define([ + 'intern/dojo/node!leadfoot/helpers/pollUntil', + 'intern/dojo/node!leadfoot/keys', + 'intern/dojo/node!leadfoot/Command' +], function (pollUntil, keys, Command) { + return { + isShiftClickSupported: function (remote) { + // summary: + // Detects browser/WebDriver support of shift+click. + // This is known to not work in many versions of IE & FF with + // Selenium's drivers. + // remote: PromisedWebDriver + // A webdriver instance with a remote page already loaded + // returns: + // A promise that resolves to a boolean + + return remote.execute(function () { + window.isShiftClickSupported = false; + var button = document.createElement('button'); + button.id = 'shiftClickTestButton'; + button.onclick = function (event) { + window.shiftClickTestButtonClicked = true; + window.isShiftClickSupported = event.shiftKey; + }; + document.body.appendChild(button); + }) + .pressKeys(keys.SHIFT) + .findById('shiftClickTestButton') + .click() + .pressKeys(keys.NULL) + .end() + .then(pollUntil(function () { + return window.shiftClickTestButtonClicked; + }, null, 5000)) + .execute(function () { + document.body.removeChild(document.getElementById('shiftClickTestButton')); + return window.isShiftClickSupported; + }); + }, + + isInputHomeEndSupported: function (remote) { + // summary: + // Detects whether the given browser/OS combination supports + // using the home and end keys to move the caret in a textbox. + // remote: PromisedWebDriver + // A webdriver instance with a remote page already loaded + // returns: + // A promise that resolves to a boolean + + return remote.execute(function () { + var input = document.createElement('input'); + input.id = 'homeEndTestInput'; + input.value = '2'; + document.body.appendChild(input); + }) + .findById('homeEndTestInput') + .click() + .type(keys.END + '3' + keys.HOME + '1') + .end() + .execute(function () { + var input = document.getElementById('homeEndTestInput'), + value = input.value; + document.body.removeChild(input); + return value === '123'; + }); + }, + + createCommandConstructor: function (members) { + // summary: + // Creates a custom Command constructor extended with the + // provided members. Based on Leadfoot's Command documentation: + // http://theintern.github.io/leadfoot/Command.html + + function CustomCommand() { + Command.apply(this, arguments); + } + CustomCommand.prototype = Object.create(Command.prototype); + CustomCommand.prototype.constructor = CustomCommand; + + Object.keys(members).forEach(function (name) { + CustomCommand.prototype[name] = members[name]; + }); + + return CustomCommand; + } + }; +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/intern.js b/packages/xblox/ref-control-freak/xblox/test/intern/intern.js new file mode 100644 index 00000000..45c07b8c --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/intern.js @@ -0,0 +1,69 @@ +define([ + 'intern/dojo/has' +], function (has) { + return { + + // The port on which the instrumenting proxy will listen + proxyPort: 9000, + + // A fully qualified URL to the Intern proxy + proxyUrl: 'http://localhost:9000/', + + // Default desired capabilities for all environments. Individual capabilities can be overridden by any of the + // specified browser environments in the `environments` array below as well. See + // https://code.google.com/p/selenium/wiki/DesiredCapabilities for standard Selenium capabilities and + // https://saucelabs.com/docs/additional-config#desired-capabilities for Sauce Labs capabilities. + // Note that the `build` capability will be filled in with the current commit ID from the Travis CI environment + // automatically + capabilities: { + // Limit duration of each job to avoid waste of resources during hangs + 'max-duration': 600, + // Increase timeout if Sauce Labs receives no new commands + // (no commands are sent during non-functional unit tests) + 'idle-timeout': 180 + }, + + // Browsers to run integration testing against. Note that version numbers must be strings if used with Sauce + // OnDemand. Options that will be permutated are browserName, version, platform, and platformVersion; any other + // capabilities options specified for an environment will be copied as-is + environments: [ + { browserName: 'internet explorer', version: '11', platform: 'Windows 8.1' }, + { browserName: 'internet explorer', version: '10', platform: 'Windows 8' }, + { browserName: 'internet explorer', version: '9', platform: 'Windows 7' }, + { browserName: 'firefox', platform: [ 'Linux', 'OS X 10.6', 'Windows 7' ] }, + { browserName: 'chrome', platform: [ 'Linux', 'OS X 10.8', 'Windows 7' ] }, + { browserName: 'safari', version: '6', platform: 'OS X 10.8' } + ], + + // Maximum number of simultaneous integration tests that should be executed on the remote WebDriver service + maxConcurrency: 3, + + // If using Sauce Labs, keep your username and password in the SAUCE_USERNAME and SAUCE_ACCESS_KEY + // environment variables. + tunnel: 'SauceLabsTunnel', + + // Configuration options for the module loader; + // any AMD configuration options supported by the Dojo loader can be used here + loader: { + baseUrl: has('host-browser') ? '../../..' : '..', + // Packages that should be registered with the loader in each testing environment + packages: [ + { name: 'xide', location: 'xide' }, + { name: 'dojo', location: 'dojo' }, + { name: 'dijit', location: 'dijit' }, + { name: 'dgrid', location: 'dgrid' }, + { name: 'dstore', location: 'dstore' }, + { name: 'xblox', location: 'xblox' } + ] + }, + + // A regular expression matching URLs to files that should not be included in code coverage analysis + excludeInstrumentation: /^dojox?|^dijit|^dstore|\/node_modules\/|\/test\/|\/nls\//, + + // Non-functional test suite(s) to run in each browser + suites: [ 'xblox/test/intern/all' ], + + // Functional test suite(s) to run in each browser once non-functional tests are completed + functionalSuites: [ 'xblox/test/intern/functional' ] + }; +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/intern.local.js b/packages/xblox/ref-control-freak/xblox/test/intern/intern.local.js new file mode 100644 index 00000000..91838b74 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/intern.local.js @@ -0,0 +1,16 @@ +define([ + './intern' +], function (intern) { + intern.tunnel = 'NullTunnel'; + + + + intern.environments = [ + // Enter whichever browser you want to test here. + // (It is unwise to test more than one simultaneously on one host, + // due to potential for spurious focus test failures.) + { browserName: 'chrome' } + ]; + + return intern; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/mixins/ColumnSet.js b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/ColumnSet.js new file mode 100644 index 00000000..c96e98b7 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/ColumnSet.js @@ -0,0 +1,172 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'dojo/_base/declare', + 'dojo/aspect', + 'dojo/Deferred', + 'dojo/query', + 'dgrid/test/data/createSyncStore', + 'dgrid/test/data/createHierarchicalStore', + 'dgrid/OnDemandGrid', + 'dgrid/ColumnSet', + 'dgrid/Keyboard', + 'dgrid/Tree', + '../addCss!' +], function (test, assert, declare, aspect, Deferred, query, createSyncStore, createHierarchicalStore, + OnDemandGrid, ColumnSet, Keyboard, Tree) { + + var grid; + var handles; + + test.suite('ColumnSet mixin', function () { + test.before(function () { + var ColumnSetGrid = declare([ OnDemandGrid, Keyboard, ColumnSet ]); + grid = new ColumnSetGrid({ + columnSets: [ + [ + [ + { field: 'column1', label: 'Column 1' } + ] + ], + [ + [ + { field: 'column2', label: 'Column 2' }, + { field: 'column3', label: 'Column 3' } + ] + ] + ] + }); + + // Give the grid a specific width (as well as its columns) so that + // expected behaviors occur regardless of browser window dimensions + grid.domNode.style.width = '1000px'; + grid.styleColumnSet('0', 'width: 400px;'); + grid.styleColumn('1-0-0', 'width: 300px;'); + grid.styleColumn('1-0-1', 'width: 1000px;'); + document.body.appendChild(grid.domNode); + grid.startup(); + }); + + test.beforeEach(function () { + var data = []; + handles = []; + + for (var i = 0; i < 3; i++) { + data.push({ id: i + 1, column1: 'Column 1', column2: 'Column 2', column3: 'Column 3' }); + } + + grid.set('collection', createSyncStore({ data: data })); + }); + + test.afterEach(function () { + for (var i = handles.length; i--;) { + handles[i].remove(); + } + }); + + test.after(function () { + grid.destroy(); + }); + + test.test('Re-inserted rows should maintain previous column set scroll positions', function () { + var dfd = this.async(1000); + var scrollAmount = 250; + + function assertScroll(messageSuffix) { + var element = query('.dgrid-column-set-1 [data-dgrid-column-set-id="1"]', grid.row(1).element)[0]; + assert.strictEqual(element.scrollLeft, scrollAmount, + 'Column Set scrollLeft should equal ' + scrollAmount + messageSuffix); + } + + handles.push(aspect.after(grid, '_onColumnSetScroll', dfd.callback(function () { + assertScroll(' before put'); + grid.collection.putSync(grid.collection.getSync(1)); + assertScroll(' after put'); + }))); + + grid._scrollColumnSet('1', scrollAmount); + }); + + test.test('Horizontal scroll caused by cell focus should remain synchronized', function () { + var dfd = this.async(1000); + + handles.push(aspect.after(grid, '_onColumnSetScroll', dfd.callback(function () { + var element = query('.dgrid-column-set-1 [data-dgrid-column-set-id="1"]', grid.row(1).element)[0]; + assert.isTrue(element.scrollLeft > 0, + 'Column Set 1 should have scrolled in response to right cell being focused'); + }))); + + grid.focus(grid.cell(1, '1-0-1')); + }); + }); + + test.suite('ColumnSet + Tree mixins', function () { + test.beforeEach(function () { + var TreeColumnSetGrid = declare([ OnDemandGrid, ColumnSet, Tree ]); + var data = []; + + for (var i = 0; i < 5; i++) { + var parentId = '' + i; + data.push({ + id: parentId, + name: 'Root ' + i + }); + for (var k = 0; k < 5; k++) { + data.push({ + id: i + ':' + k, + parent: parentId, + name: 'Child ' + k, + hasChildren: false + }); + } + } + + var store = createHierarchicalStore({ + data: data + }); + + grid = new TreeColumnSetGrid({ + collection: store, + enableTreeTransitions: false, + columnSets: [ [ + [ { renderExpando: true, label: 'Name', field: 'name', sortable: false } ] + ] ] + }); + + document.body.appendChild(grid.domNode); + grid.startup(); + }); + + test.afterEach(function () { + for (var i = handles.length; i--;) { + handles[i].remove(); + } + grid.destroy(); + }); + + test.test('re-expand after horizontal scroll should restore correct scrollLeft', function () { + var scrollAmount = 250; + grid.styleColumn('0-0-0', 'width: 10000px;'); + + return grid.expand(0, true).then(function () { + return grid.expand(0, false); + }).then(function () { + var scrollDfd = new Deferred(); + handles.push(aspect.after(grid, '_onColumnSetScroll', function () { + scrollDfd.resolve(); + })); + + grid._scrollColumnSet('0', scrollAmount); + return scrollDfd.promise; + }).then(function () { + return grid.expand(0, true); + }).then(function () { + var element = query('.dgrid-column-set-0 [data-dgrid-column-set-id="0"]', + grid.row('0:0').element)[0]; + + assert.strictEqual(element.scrollLeft, scrollAmount, + 'Column Set should have expected scroll position for re-expanded rows'); + }); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Editor-radio.js b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Editor-radio.js new file mode 100644 index 00000000..cddbc721 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Editor-radio.js @@ -0,0 +1,157 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'dojo/_base/declare', + 'dojo/query', + 'dgrid/Grid', + 'dgrid/Editor', + 'dgrid/OnDemandGrid', + 'dstore/Memory' +], function (test, assert, declare, query, Grid, Editor, OnDemandGrid, Memory) { + + var PlainEditorGrid = declare([ Grid, Editor ]); + var OnDemandEditorGrid = declare([ OnDemandGrid, Editor ]); + var grid; + + test.suite('Grids with Editor mixin - radio editor', function () { + + test.afterEach(function () { + if (grid) { + grid.destroy(); + } + }); + + function createData() { + return [ + { id: 0, data1: 'text', data2: false }, + { id: 1, data1: 'text', data2: false }, + { id: 2, data1: 'text', data2: false } + ]; + } + + function createGrid(Ctor, options) { + grid = new Ctor(options); + document.body.appendChild(grid.domNode); + grid.startup(); + } + + function checkInputs() { + var inputs = query('.field-data2 .dgrid-input', grid.domNode); + // Make sure none are checked at first, then click on each one. + inputs.forEach(function (input) { + assert.isFalse(input.checked); + input.click(); + }); + // Make sure the last one is checked after all 3 have been clicked. + inputs.forEach(function (input, i) { + if (i === 2) { + assert.isTrue(input.checked); + } else { + assert.isFalse(input.checked); + } + }); + } + + test.test('Grid', function () { + createGrid(PlainEditorGrid, { + columns: [ + { + field: 'id', + label: 'ID' + }, + { + editor: 'text', + field: 'data1', + label: 'Data 1' + }, + { + editor: 'radio', + field: 'data2', + label: 'Data 2' + } + ] + }); + grid.renderArray(createData()); + + checkInputs(); + + assert.isFalse(grid.row(0).data.data2); + assert.isFalse(grid.row(1).data.data2); + assert.isTrue(grid.row(2).data.data2); + }); + + test.test('OnDemandGrid - autoSave', function () { + var dfd = this.async(1000); + var store = new Memory({ data: createData() }); + createGrid(OnDemandEditorGrid, { + columns: [ + { + field: 'id', + label: 'ID' + }, + { + editor: 'text', + field: 'data1', + label: 'Data 1', + autoSave: true + }, + { + editor: 'radio', + field: 'data2', + label: 'Data 2', + autoSave: true + } + ], + collection: store + }); + + checkInputs(); + + // Allow the click event to fire. + setTimeout(dfd.callback(function () { + assert.isFalse(store.getSync(0).data2); + assert.isFalse(store.getSync(1).data2); + assert.isTrue(store.getSync(2).data2); + }), 0); + }); + + test.test('OnDemandGrid - no autoSave', function () { + var dfd = this.async(1000); + var store = new Memory({ data: createData() }); + createGrid(OnDemandEditorGrid, { + columns: [ + { + field: 'id', + label: 'ID' + }, + { + editor: 'text', + field: 'data1', + label: 'Data 1' + }, + { + editor: 'radio', + field: 'data2', + label: 'Data 2' + } + ], + collection: store + }); + + checkInputs(); + + // Allow the click event to fire. + setTimeout(dfd.rejectOnError(function () { + assert.isFalse(store.getSync(0).data2); + assert.isFalse(store.getSync(1).data2); + assert.isFalse(store.getSync(2).data2); + + grid.save().then(dfd.callback(function () { + assert.isFalse(store.getSync(0).data2); + assert.isFalse(store.getSync(1).data2); + assert.isTrue(store.getSync(2).data2); + })); + }), 0); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Editor.js b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Editor.js new file mode 100644 index 00000000..4adf6cc1 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Editor.js @@ -0,0 +1,465 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'dojo/_base/declare', + 'dojo/aspect', + 'dojo/Deferred', + 'dojo/on', + 'dojo/promise/all', + 'dojo/query', + 'dojo/when', + 'xide/registry', + 'dijit/form/TextBox', + 'dgrid/Grid', + 'dgrid/OnDemandGrid', + 'dgrid/Editor', + 'dgrid/test/data/createSyncStore', + 'dgrid/test/data/orderedData' +], function (test, assert, declare, aspect, Deferred, on, all, query, when, registry, TextBox, + Grid, OnDemandGrid, Editor, createSyncStore, orderedData) { + + var testOrderedData = orderedData.items, + EditorGrid = declare([Grid, Editor]), + grid; + + test.suite('Editor mixin', function () { + + test.afterEach(function () { + if (grid) { + grid.destroy(); + } + }); + + test.test('canEdit - always-on (instance-per-row) editor', function () { + var results = {}; + var data = [ + {id: 1, data1: 'Data 1.a', data2: 'Data 2.a'}, + {id: 2, data1: 'Data 1.b', data2: 'Data 2.b'}, + {id: 3, data1: 'Data 1.c', data2: 'Data 2.c'} + ]; + grid = new EditorGrid({ + columns: [ + { + field: 'data1', + label: 'Data 1' + }, + { + editor: 'text', + field: 'data2', + label: 'Data 2', + canEdit: function (object, value) { + results[object.id] = value; + } + } + ] + }); + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(data); + + assert.strictEqual(results[1], 'Data 2.a', + 'canEdit should have been called (item 1)'); + assert.strictEqual(results[2], 'Data 2.b', + 'canEdit should have been called (item 2)'); + assert.strictEqual(results[3], 'Data 2.c', + 'canEdit should have been called (item 3)'); + }); + + test.test('canEdit - editOn (shared) editor', function () { + var results = {}; + var data = [ + {id: 1, data1: 'Data 1.a', data2: 'Data 2.a'}, + {id: 2, data1: 'Data 1.b', data2: 'Data 2.b'}, + {id: 3, data1: 'Data 1.c', data2: 'Data 2.c'} + ]; + grid = new EditorGrid({ + columns: [ + { + field: 'data1', + label: 'Data 1', + id: 'data1' + }, + { + editor: TextBox, + editOn: 'click', + field: 'data2', + label: 'Data 2', + id: 'data2', + canEdit: function (object, value) { + results[object.id] = value; + } + } + ] + }); + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(data); + + assert.isUndefined(results[1], + 'canEdit should not have been called yet for editOn editor (item 1)'); + assert.isUndefined(results[2], + 'canEdit should not have been called yet for editOn editor (item 2)'); + assert.isUndefined(results[3], + 'canEdit should not have been called yet for editOn editor (item 3)'); + + // Note: The "Data 2" column's canEdit method always returns false so none of the following + // grid.edit calls will return a promise and no editor will receive focus. + grid.edit(grid.cell(1, 'data2')); + assert.isUndefined(results[1], + 'canEdit should not have been called yet for editOn editor (item 1)'); + assert.strictEqual(results[2], 'Data 2.b', + 'canEdit should have been called for editOn editor (item 2)'); + assert.isUndefined(results[3], + 'canEdit should not have been called yet for editOn editor (item 3)'); + + grid.edit(grid.cell(0, 'data2')); + assert.strictEqual(results[1], 'Data 2.a', + 'canEdit should have been called for editOn editor (item 1)'); + assert.strictEqual(results[2], 'Data 2.b', + 'canEdit should have been called for editOn editor (item 2)'); + assert.isUndefined(results[3], + 'canEdit should not have been called yet for editOn editor (item 3)'); + + grid.edit(grid.cell(2, 'data2')); + assert.strictEqual(results[1], 'Data 2.a', + 'canEdit should have been called for editOn editor (item 1)'); + assert.strictEqual(results[2], 'Data 2.b', + 'canEdit should have been called for editOn editor (item 2)'); + assert.strictEqual(results[3], 'Data 2.c', + 'canEdit should have been called for editOn editor (item 3)'); + }); + + test.test('canEdit always-on editor - suppress on false', function () { + var rowCount, + cell, + matchedNodes, + dfd = this.async(); + + function canEdit(data) { + return data.order % 2; + } + + grid = new EditorGrid({ + columns: { + order: 'step', + name: { + editor: 'text', + label: 'Name', + canEdit: canEdit + }, + description: { + editor: 'text', + label: 'Description', + editOn: 'click', + canEdit: canEdit + } + } + }); + + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(testOrderedData); + rowCount = testOrderedData.length; + + function testRow(rowIndex) { + cell = grid.cell(rowIndex, 'name'); + when(grid.edit(cell)).then(dfd.rejectOnError(function () { + matchedNodes = query('input', cell.element); + if (canEdit(cell.row.data)) { + assert.strictEqual(1, matchedNodes.length, + 'Cell with canEdit=>true should have an editor element'); + } + else { + assert.strictEqual(0, matchedNodes.length, + 'Cell with canEdit=>false should not have an editor element'); + } + rowIndex++; + if (rowIndex < rowCount) { + testRow(rowIndex); + } + else { + dfd.resolve(); + } + })); + } + testRow(0); + + return dfd; + }); + + test.test('canEdit edit-on click editor - suppress on false', function () { + var rowCount, + cell, + matchedNodes, + dfd = this.async(); + + function canEdit(data) { + return data.order % 2; + } + + grid = new EditorGrid({ + columns: { + order: 'step', + name: { + editor: 'text', + label: 'Name', + canEdit: canEdit + }, + description: { + editor: 'text', + label: 'Description', + editOn: 'click', + canEdit: canEdit + } + } + }); + + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(testOrderedData); + rowCount = testOrderedData.length; + + function testRow(rowIndex) { + cell = grid.cell(rowIndex, 'description'); + when(grid.edit(cell)).then(dfd.rejectOnError(function () { + matchedNodes = query('input', cell.element); + if (canEdit(cell.row.data)) { + assert.strictEqual(1, matchedNodes.length, + 'Cell with canEdit=>true should have an editor element'); + } + else { + assert.strictEqual(0, matchedNodes.length, + 'Cell with canEdit=>false should not have an editor element'); + } + rowIndex++; + if (rowIndex < rowCount) { + testRow(rowIndex); + } + else { + dfd.resolve(); + } + })); + } + testRow(0); + + return dfd; + }); + + test.test('destroy editor widgets - native', function () { + var matchedNodes; + + matchedNodes = query('input'); + assert.strictEqual(0, matchedNodes.length, + 'Before grid is created there should be 0 input elements on the page'); + + grid = new EditorGrid({ + columns: { + order: 'step', + name: { + label: 'Name', + editor: 'text' + }, + description: { + label: 'Description', + editor: 'text', + editOn: 'click' + } + } + }); + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(testOrderedData); + + matchedNodes = query('input'); + assert.strictEqual(testOrderedData.length, matchedNodes.length, + 'There should be ' + testOrderedData.length + ' input elements for the grid\'s editors'); + + grid.destroy(); + + matchedNodes = query('input'); + assert.strictEqual(0, matchedNodes.length, + 'After grid is destroyed there should be 0 input elements on the page'); + }); + + test.test('destroy editor widgets - Dijit', function () { + assert.strictEqual(0, registry.length, + 'Before grid is created there should be 0 widgets on the page'); + + grid = new EditorGrid({ + columns: { + order: 'step', + name: { + label: 'Name', + editor: TextBox + }, + description: { + label: 'Description', + editor: TextBox, + editOn: 'click' + } + } + }); + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(testOrderedData); + + // Expected is data length + 1 due to widget for editOn editor + assert.strictEqual(testOrderedData.length + 1, registry.length, + 'There should be ' + (testOrderedData.length + 1) + ' widgets for the grid\'s editors'); + + grid.destroy(); + + assert.strictEqual(0, registry.length, + 'After grid is destroyed there should be 0 widgets on the page'); + }); + + test.test('editor widget startup called at appropriate time', function () { + var assertionMessage; + var AssertionTextBox = declare(TextBox, { + startup: function () { + if (this._started) { + return; + } + assert.isTrue(this.domNode.offsetHeight > 0, + assertionMessage + ': startup should not be called before widgets are in flow'); + this.inherited(arguments); + } + }); + + grid = new (declare([OnDemandGrid, Editor]))({ + columns: { + order: 'step', + name: { + label: 'Name', + editor: AssertionTextBox + }, + description: { + label: 'Description', + editor: AssertionTextBox, + editOn: 'click' + } + }, + collection: createSyncStore({ + data: testOrderedData, + idProperty: 'order' + }) + }); + document.body.appendChild(grid.domNode); + + assertionMessage = 'always-on'; + grid.startup(); + + // Assertions will automatically run for always-on editor; + // test activating an editOn editor and also test updating a row + assertionMessage = 'editOn + edit()'; + grid.edit(grid.cell(1, 'description')); + assertionMessage = 'editOn + Trackable'; + grid.collection.put(grid.collection.getSync(2)); + }); + + test.test('editor focus with always-on editor', function () { + var rowCount, + cell, + cellEditor, + dfd = this.async(); + + grid = new EditorGrid({ + columns: { + order: 'step', + name: { + label: 'Name', + editor: 'text' + }, + description: { + label: 'Description', + editor: 'text', + editOn: 'click' + } + } + }); + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(testOrderedData); + rowCount = testOrderedData.length; + + function testRow(rowIndex) { + // Test calling 'grid.edit()' in an always-on cell + cell = grid.cell(rowIndex, 'name'); + grid.edit(cell).then(dfd.rejectOnError(function (node) { + cellEditor = query('input', cell.element)[0]; + assert.strictEqual(cellEditor, node, + 'edit method\'s promise should return the active editor'); + assert.strictEqual(cellEditor, document.activeElement, + 'Editing a cell should make the cell\'s editor active'); + rowIndex++; + if (rowIndex < rowCount) { + testRow(rowIndex); + } + else { + dfd.resolve(); + } + })); + } + testRow(0); + + return dfd; + }); + + test.test('editor focus and show event with edit-on click editor', function () { + var rowCount, + cell, + cellEditor, + dfd = this.async(); + + grid = new EditorGrid({ + columns: { + order: 'step', + name: { + label: 'Name', + editor: 'text' + }, + description: { + label: 'Description', + editor: 'text', + editOn: 'click' + } + } + }); + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(testOrderedData); + rowCount = testOrderedData.length; + + function testRow(rowIndex) { + var dfdEvent = new Deferred(); + // Test calling 'grid.edit()' in an always-on cell + cell = grid.cell(rowIndex, 'description'); + // Respond to the "dgrid-editor-show" event to ensure the + // correct cell has an editor. This event actually fires + // synchronously, so we don't need to use this.async. + on.once(grid, 'dgrid-editor-show', dfd.rejectOnError(function (event) { + assert.strictEqual(cell.element, event.cell.element, + 'The activated cell should be being edited' + ); + dfdEvent.resolve(); + })); + // Don't move on to the next row until the editor has received focus and the show event has fired. + all([grid.edit(cell), dfdEvent]).then(dfd.rejectOnError(function () { + cellEditor = query('input', cell.element)[0]; + assert.strictEqual(cellEditor, document.activeElement, + 'Editing a cell should make the cell\'s editor active'); + rowIndex++; + if (rowIndex < rowCount) { + testRow(rowIndex); + } + else { + dfd.resolve(); + } + })); + } + testRow(0); + + return dfd; + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Keyboard.js b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Keyboard.js new file mode 100644 index 00000000..124095f9 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Keyboard.js @@ -0,0 +1,506 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'dgrid/OnDemandList', + 'dgrid/OnDemandGrid', + 'dgrid/Keyboard', + 'dgrid/ColumnSet', + 'dojo/_base/declare', + 'dojo/dom-construct', + 'dojo/on', + 'dojo/query', + 'dstore/Memory', + 'dgrid/test/data/createSyncStore', + 'dgrid/test/data/genericData' +], function (test, assert, OnDemandList, OnDemandGrid, Keyboard, ColumnSet, + declare, domConstruct, on, query, Memory, createSyncStore, genericData) { + var handles = [], + columns = { + col1: 'Column 1', + col3: 'Column 3', + col5: 'Column 5' + }, + testStore = createSyncStore({ data: genericData }), + item = testStore.getSync(1), + grid, + columnSet = [ + [ + [ + { label: 'Column 1', field: 'col1' }, + { label: 'Column 2', field: 'col2', sortable: false } + ], + [ + { label: 'Column 3', field: 'col3', colSpan: 2 } + ] + ], + [ + [ + { label: 'Column 1', field: 'col1', rowSpan: 2 }, + { label: 'Column 4', field: 'col4' } + ], + [ + { label: 'Column 5', field: 'col5' } + ] + ] + ]; + + // Common functions run after each test and suite + + function afterEach() { + grid.domNode.style.display = ''; + + for (var i = handles.length; i--;) { + handles[i].remove && handles[i].remove(); + } + handles = []; + } + + function after() { + // Destroy list or grid + grid.destroy(); + // Restore item that was removed for focus retention test + testStore.put(item); + } + + // Common test functions for grid w/ cellNavigation: false and list + + function testRowFocus() { + var rowId; + // listen for a dgrid-cellfocusin event + handles.push(on(document.body, 'dgrid-cellfocusin', function (e) { + assert.ok(e.row, 'dgrid-cellfocusin event got a non-null row value'); + rowId = e.row.id; + })); + // trigger a focus with no argument, which should focus the first row + grid.focus(); + assert.strictEqual(document.activeElement, query('.dgrid-row', grid.contentNode)[0], + 'focus() targeted the first row'); + assert.strictEqual(rowId, 0, + 'dgrid-cellfocusin event triggered on first row on focus() call'); + } + + function testRowFocusArgs() { + var rowId, target; + // listen for a dgrid-cellfocusin event + handles.push(on(document.body, 'dgrid-cellfocusin', function (e) { + assert.ok(e.row, 'dgrid-cellfocusin event got a non-null row value'); + rowId = e.row.id; + })); + // trigger a body focus with the second row as the target + target = query('.dgrid-row', grid.contentNode)[1]; + grid.focus(target); + // make sure we got the right row + assert.strictEqual(document.activeElement, target, + 'focus(...) targeted the expected row'); + assert.strictEqual(rowId, 1, + 'dgrid-cellfocusin event triggered on expected row'); + } + + function testRowBlur() { + var blurredRow, + targets = query('.dgrid-row', grid.contentNode); + + // call one focus event, followed by a subsequent focus event, + // thus triggering a dgrid-cellfocusout event + grid.focus(targets[0]); + + // listen for a dgrid-cellfocusout event + handles.push(on(document.body, 'dgrid-cellfocusout', function (e) { + blurredRow = e.row; + assert.ok(blurredRow, 'dgrid-cellfocusout event got a non-null row value'); + })); + + grid.focus(targets[1]); + // make sure our handler was called + assert.strictEqual(blurredRow && blurredRow.id, 0, + 'dgrid-cellfocusout event triggered on expected row'); + } + + function testRowUpdate() { + var element, elementId; + // Focus a row based on a store ID, then issue an update and make sure + // the same id is still focused + grid.focus(1); + + element = document.activeElement; + assert.ok(element && element.className && element.className.indexOf('dgrid-row') > -1, + 'focus(id) call focused a row'); + + elementId = element.id; + grid.collection.put(item); + assert.notStrictEqual(element, document.activeElement, + 'A different DOM element is focused after updating the item'); + assert.strictEqual(elementId, document.activeElement.id, + 'The item\'s new row is focused after updating the item'); + } + + function testRowRemove() { + var dfd = this.async(1000), + element, + nextElement; + + // Focus a row based on a store ID, then remove the item and + // make sure the corresponding cell is eventually focused + grid.focus(1); + + element = document.activeElement; + assert.ok(element && element.className && element.className.indexOf('dgrid-row') > -1, + 'focus(id) call focused a row'); + + nextElement = element.nextSibling; + grid.collection.remove(1); + + // The logic responsible for moving to the next row runs on next turn, + // since it operates as a fallback that is run only if a replacement + // is not immediately inserted. Therefore we need to execute our + // assertions on the next turn as well. + setTimeout(dfd.callback(function () { + assert.strictEqual(nextElement, document.activeElement, + 'The next row is focused after removing the item'); + }), 0); + + return dfd; + } + + function registerRowTests(name) { + test.afterEach(afterEach); + test.after(after); + + test.test(name + '.focus + no args', testRowFocus); + test.test(name + '.focus + args', testRowFocusArgs); + test.test('dgrid-cellfocusout event', testRowBlur); + test.test(name + '.focus + item update', testRowUpdate); + test.test(name + '.focus + item removal', testRowRemove); + } + + test.suite('Keyboard (Grid + cellNavigation:true)', function () { + test.before(function () { + grid = new (declare([OnDemandGrid, Keyboard]))({ + columns: columns, + sort: 'id', + collection: testStore + }); + document.body.appendChild(grid.domNode); + grid.startup(); + }); + + test.afterEach(afterEach); + test.after(after); + + test.test('grid.focus + no args', function () { + var colId; + // listen for a dgrid-cellfocusin event + handles.push(on(document.body, 'dgrid-cellfocusin', function (e) { + assert.ok(e.cell, 'dgrid-cellfocusin event got a non-null cell value'); + colId = e.cell.column.id; + })); + // trigger a focus with no argument, which should focus the first cell + grid.focus(); + assert.strictEqual(document.activeElement, query('.dgrid-cell', grid.contentNode)[0], + 'focus() targeted the first cell'); + assert.strictEqual(colId, 'col1', + 'dgrid-cellfocusin event triggered on first cell on focus() call'); + }); + + test.test('grid.focusHeader + no args', function () { + var colId; + // listen for a dgrid-cellfocusin event (header triggers same event) + handles.push(on(document.body, 'dgrid-cellfocusin', function (e) { + assert.ok(e.cell, 'dgrid-cellfocusin event got a non-null cell value'); + assert.ok(!e.row, 'dgrid-cellfocusin event for header got a falsy row value'); + colId = e.cell.column.id; + })); + // trigger a header focus with no argument, which should focus the first cell + grid.focusHeader(); + assert.strictEqual(document.activeElement, query('.dgrid-cell', grid.headerNode)[0], + 'focus() targeted the first header cell'); + assert.strictEqual(colId, 'col1', + 'dgrid-cellfocusin event triggered on first cell on focusHeader() call'); + }); + + test.test('grid.focus + args', function () { + var focusedCell, target; + // listen for a dgrid-cellfocusin event + handles.push(on(document.body, 'dgrid-cellfocusin', function (e) { + assert.ok(e.cell, 'dgrid-cellfocusin event got a non-null cell value'); + focusedCell = e.cell; + })); + // trigger a body focus with the second cell as the target + target = query('.dgrid-cell', grid.contentNode)[1]; + grid.focus(target); + assert.strictEqual(document.activeElement, target, + 'focus(...) targeted the expected cell'); + assert.ok(focusedCell, 'dgrid-cellfocusin event fired'); + assert.strictEqual(focusedCell.row.id, 0, + 'dgrid-cellfocusin event triggered on expected row'); + assert.strictEqual(focusedCell.column.id, 'col3', + 'dgrid-cellfocusin event triggered on second cell on focus(...) call'); + }); + + test.test('grid.focusHeader + args', function () { + var colId, target; + // listen for a dgrid-cellfocusin event (header triggers same event) + handles.push(on(document.body, 'dgrid-cellfocusin', function (e) { + assert.ok(e.cell, 'dgrid-cellfocusin event got a non-null cell value'); + assert.ok(!e.row, 'dgrid-cellfocusin event for header got a falsy row value'); + colId = e.cell.column.id; + })); + // trigger a focus on the first header cell + target = query('.dgrid-cell', grid.headerNode)[1]; + grid.focus(target); + assert.strictEqual(document.activeElement, target, + 'focusHeader(...) targeted the expected cell'); + assert.strictEqual(colId, 'col3', 'dgrid-cellfocusin event triggered on expected header cell'); + }); + + test.test('dgrid-cellfocusout event', function () { + var blurredCell, + blurredElementRowId, + targets = query('.dgrid-cell', grid.contentNode); + + // call one focus event, followed by a subsequent focus event, + // thus triggering a dgrid-cellfocusout event + grid.focus(targets[0]); + + // listen for a dgrid-cellfocusout event + handles.push(on(document.body, 'dgrid-cellfocusout', function (e) { + blurredElementRowId = grid.row(e.target).id; + blurredCell = e.cell; + assert.ok(blurredCell, 'dgrid-cellfocusout event got a non-null cell value'); + })); + + // Focus first cell in next row and make sure handler was called + grid.focus(targets[3]); + assert.ok(blurredCell, 'dgrid-cellfocusout event fired'); + assert.strictEqual(blurredElementRowId, 0, + 'dgrid-cellfocusout event fired from expected element'); + assert.strictEqual(blurredCell.row.id, 0, + 'dgrid-cellfocusout event.cell contains expected row'); + assert.strictEqual(blurredCell.column.id, 'col1', + 'dgrid-cellfocusout event.cell contains expected column'); + }); + + test.test('grid.focus - no args, empty store', function () { + grid.set('collection', new Memory({ data: [] })); + assert.doesNotThrow(function () { + grid.focus(); + }, null, 'grid.focus() on empty grid should not throw error'); + assert.strictEqual(document.activeElement, grid.contentNode, + 'grid.contentNode should be focused after grid.focus() on empty grid'); + }); + }); + + test.suite('Keyboard (Grid + cellNavigation:true + ColumnSet)', function () { + test.before(function () { + grid = new (declare([OnDemandGrid, ColumnSet, Keyboard]))({ + columnSets: columnSet, + sort: 'id', + collection: testStore + }); + document.body.appendChild(grid.domNode); + grid.startup(); + }); + + test.afterEach(afterEach); + test.after(after); + + test.test('grid.focusHeader + ColumnSet', function () { + var colSetId; + + handles.push(on(document.body, 'dgrid-cellfocusin', function (e) { + assert.isTrue('cell' in e, 'dgrid-cellfocusin event has a cell property'); + assert.isFalse('row' in e, 'dgrid-cellfocusin event does not have a row property'); + colSetId = e.cell.column.id; + })); + + grid.focus(); // first focus the content body + grid.focusHeader(); + assert.strictEqual(document.activeElement, query('.dgrid-cell', grid.headerNode)[0], + 'focusHeader() targeted the first header cell'); + assert.strictEqual(colSetId, '0-0-0', + 'dgrid-cellfocusin event triggered on first cell on focusHeader() call'); + }); + }); + + test.suite('Keyboard focus preservation', function () { + test.before(function () { + grid = new (declare([OnDemandGrid, Keyboard]))({ + columns: columns, + sort: 'id', + collection: testStore + }); + document.body.appendChild(grid.domNode); + grid.startup(); + }); + + test.afterEach(afterEach); + test.after(after); + + test.test('grid.focus + item update', function () { + var element; + // Focus a row based on a store ID + column ID, + // then issue an update and make sure the same id is still focused + grid.focus(grid.cell(1, 'col1')); + + element = document.activeElement; + assert.ok(element && element.className && element.className.indexOf('dgrid-cell') > -1, + 'focus(id) call focused a cell'); + + grid.collection.put(item); + assert.notStrictEqual(element, document.activeElement, + 'A different DOM element is focused after updating the item'); + assert.strictEqual(grid.cell(1, 'col1').element, document.activeElement, + 'The item\'s new cell is focused after updating the item'); + }); + + test.test('grid.focus + item update on hidden grid', function () { + var element; + // Focus a row based on a store ID + column ID, + // then issue an update and make sure the same id is still focused + grid.focus(grid.cell(1, 'col1')); + + element = document.activeElement; + assert.ok(element && element.className && element.className.indexOf('dgrid-cell') > -1, + 'focus(id) call focused a cell'); + + // Hide the grid + grid.domNode.style.display = 'none'; + + // Modify the item in the store + grid.collection.put(item); + assert.notStrictEqual(element, document.activeElement, + 'A different or no DOM element is focused after updating the item'); + }); + + test.test('grid.focus + item removal', function () { + var dfd = this.async(1000), + element, + nextElement; + + // Focus a cell based on a store ID, then remove the item and + // make sure the corresponding cell is eventually focused + grid.focus(grid.cell(1, 'col1')); + + element = document.activeElement; + assert.ok(element && element.className && element.className.indexOf('dgrid-cell') > -1, + 'focus(id) call focused a cell'); + + nextElement = grid.cell(2, 'col1').element; + grid.collection.remove(1); + + // The logic responsible for moving to the next row runs on next turn, + // since it operates as a fallback that is run only if a replacement + // is not immediately inserted. Therefore we need to execute our + // assertions on the next turn as well. + setTimeout(dfd.callback(function () { + assert.strictEqual(nextElement, document.activeElement, + 'The next row is focused after removing the item'); + }), 0); + + return dfd; + }); + }); + + test.suite('Keyboard focused node preservation after blur', function () { + var button; + + test.before(function () { + grid = new (declare([OnDemandGrid, Keyboard]))({ + columns: columns, + sort: 'id', + collection: testStore + }); + document.body.appendChild(grid.domNode); + grid.startup(); + + // Add a button as a target to move focus out of grid + button = domConstruct.create('button', null, document.body); + }); + + test.afterEach(afterEach); + test.after(function () { + after(); + domConstruct.destroy(button); + }); + + test.test('grid.focus + item update', function () { + var element; + grid.focus(grid.cell(1, 'col1')); + + element = document.activeElement; + assert.ok(element && element.className && element.className.indexOf('dgrid-cell') > -1, + 'focus(id) call focused a cell'); + + // Focus the button we added to move focus out of the grid + button.focus(); + + grid.collection.put(item); + grid.focus(); + assert.notStrictEqual(element, document.activeElement, + 'A different DOM element is focused after updating the item'); + assert.strictEqual(grid.cell(1, 'col1').element, document.activeElement, + 'The item\'s new cell is focused after updating the item'); + }); + + test.test('grid.focus + item removal', function () { + var dfd = this.async(1000), + element, + nextElement; + + grid.focus(grid.cell(1, 'col1')); + + element = document.activeElement; + assert.ok(element && element.className && element.className.indexOf('dgrid-cell') > -1, + 'focus(id) call focused a cell'); + + // Focus the button we added to move focus out of the grid + button.focus(); + + nextElement = grid.cell(2, 'col1').element; + grid.collection.remove(1); + + setTimeout(dfd.callback(function () { + assert.doesNotThrow(function () { + grid.focus(); + }, null, 'focus() after blur and item removal should not throw error'); + assert.strictEqual(nextElement, document.activeElement, + 'The next row is focused after calling focus()'); + }), 0); + + return dfd; + }); + }); + + test.suite('Keyboard (Grid + cellNavigation:false)', function () { + test.before(function () { + grid = new (declare([OnDemandGrid, Keyboard]))({ + cellNavigation: false, + columns: columns, + sort: 'id', + collection: testStore + }); + document.body.appendChild(grid.domNode); + grid.startup(); + }); + + registerRowTests('grid'); + }); + + test.suite('Keyboard (List)', function () { + test.before(function () { + grid = new (declare([OnDemandList, Keyboard]))({ + sort: 'id', + collection: testStore, + renderRow: function (item) { + var div = document.createElement('div'); + div.appendChild(document.createTextNode(item.col5)); + return div; + } + }); + document.body.appendChild(grid.domNode); + grid.startup(); + }); + + registerRowTests('list'); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Selection.js b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Selection.js new file mode 100644 index 00000000..f85b6ba9 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Selection.js @@ -0,0 +1,556 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'dojo/_base/declare', + 'dojo/_base/array', + 'dojo/_base/lang', + 'dojo/json', + 'dojo/dom-class', + 'dstore/Memory', + 'dstore/Trackable', + 'dgrid/Grid', + 'dgrid/OnDemandGrid', + 'dgrid/Selection', + 'dgrid/CellSelection', + 'dgrid/extensions/Pagination' +], function (test, assert, declare, arrayUtil, lang, JSON, domClass, Memory, Trackable, + Grid, OnDemandGrid, Selection, CellSelection, Pagination) { + + var mixins = { + Selection: Selection, + CellSelection: CellSelection + }, + notificationTests = {}, + grid, + TrackableMemory = declare([ Memory, Trackable ]); + + function _createTestData(size) { + var data = [], + aCode = 'A'.charCodeAt(0), + i; + size = size || 15; + for (i = 0; i < size; i++) { + data.push({ + id: i, + first: 'First' + String.fromCharCode(aCode + (i % 26)), + last: 'Last' + String.fromCharCode(aCode + 25 - (i % 26)) + }); + } + return data; + } + + function countProperties(object) { + var count = 0, + key; + + for (key in object) { + if (object.hasOwnProperty(key)) { + count++; + } + } + return count; + } + + function getColumns() { + return { + first: 'First Name', + last: 'Last Name' + }; + } + + arrayUtil.forEach([ 'Selection', 'CellSelection' ], function (name) { + var SelectionMixin = mixins[name]; + notificationTests[name + ' + update'] = function () { + var store = new TrackableMemory({ + data: _createTestData() + }); + + grid = new (declare([OnDemandGrid, SelectionMixin]))({ + columns: getColumns(), + collection: store, + sort: 'id' + }); + + document.body.appendChild(grid.domNode); + grid.startup(); + + // Using this long-winded approach for the purposes + // of the same logic working for both Selection and + // CellSelection + grid.select(grid.row(3)); + grid.select(grid.row(4)); + grid.select(grid.row(5)); + grid.select(grid.row(6)); + grid.select(grid.row(7)); + + var selection = grid.selection; + assert.strictEqual(countProperties(selection), 5, + 'Selection contains the expected number of items'); + assert.ok(selection[3] && selection[4] && selection[5] && + selection[6] && selection[7], + 'Selection contains the expected items'); + + store.put({ id: 5, first: 'Updated First', last: 'Updated Last'}); + store.put({ id: 99, first: 'New First', last: 'New Last'}); + + assert.ok(selection[3] && selection[4] && selection[5] && + selection[6] && selection[7], + 'Selection still contains the same items'); + + assert.ok(!selection[99], + 'Selection does not contain newly-added item'); + + store.remove(5); + assert.ok(selection[3] && selection[4] && !selection[5] && + selection[6] && selection[7], + 'Item 5 has been removed from the selection'); + + // Calling remove row does not notify the store so the selection is not updated. + grid.row(4).remove(); + assert.ok(selection[3] && selection[4] && !selection[5] && + selection[6] && selection[7], + 'Selection is unchanged when calling removeRow directly on a store-backed grid'); + + grid.destroy(); + }; + + notificationTests[name + ' + update + no store'] = function () { + grid = new (declare([Grid, SelectionMixin]))({ + columns: getColumns() + }); + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(_createTestData()); + + // Using this long-winded approach for the purposes + // of the same logic working for both Selection and + // CellSelection + grid.select(grid.row(3)); + grid.select(grid.row(4)); + grid.select(grid.row(5)); + grid.select(grid.row(6)); + grid.select(grid.row(7)); + + var selection = grid.selection; + assert.strictEqual(countProperties(selection), 5, + 'Selection contains the expected number of items'); + assert.ok(selection[3] && selection[4] && selection[5] && + selection[6] && selection[7], + 'Selection contains the expected items'); + + grid.row(4).remove(); + assert.strictEqual(countProperties(selection), 4, + 'Selection contains 1 fewer items after removal of selected item'); + assert.ok(selection[3] && !selection[4] && selection[5] && + selection[6] && selection[7], + 'Item 4 has been removed from the selection'); + + grid.row(1).remove(); + assert.strictEqual(countProperties(selection), 4, + 'Selection is unchanged after removal of unselected item'); + assert.ok(selection[3] && !selection[4] && selection[5] && + selection[6] && selection[7], + 'Selection is unchanged after removal of unselected item'); + + grid.row(3).remove(); + assert.strictEqual(countProperties(selection), 3, + 'Selection contains 1 fewer items after removal of selected item'); + assert.ok(!selection[3] && !selection[4] && selection[5] && + selection[6] && selection[7], + 'Item 3 has been removed from the selection'); + + grid.row(5).remove(); + grid.row(6).remove(); + grid.row(7).remove(); + assert.strictEqual(countProperties(selection), 0, + 'No items are selected after all selected items have been removed'); + + grid.destroy(); + }; + + notificationTests[name + ' + update + store + paging'] = function () { + // Create a selection, trigger paging, notify + var store = new TrackableMemory({ + data: _createTestData(100) + }); + + grid = new (declare([Grid, SelectionMixin, Pagination]))({ + collection: store, + columns: getColumns() + }); + + document.body.appendChild(grid.domNode); + grid.startup(); + + function checkStyles() { + // Checks to see if the rendered rows that are selected have the dgrid-selected style + // and no unselected rows have that style + var selection = grid.selection, + id, + rowElement, + rowObject, + isHighlighted, + shouldBeHighlighted; + + for (id in grid._rowIdToObject) { + rowElement = document.getElementById(id); + rowObject = grid._rowIdToObject[id]; + if (rowElement) { + if (name === 'Selection') { + isHighlighted = domClass.contains(rowElement, 'dgrid-selected'); + } + else { + isHighlighted = domClass.contains(grid.cell(rowObject.id, 'first').element, + 'dgrid-selected'); + } + shouldBeHighlighted = !!selection[rowObject.id]; + assert.strictEqual(isHighlighted, shouldBeHighlighted, + 'Expected ' + JSON.stringify(rowObject) + ' to' + + (shouldBeHighlighted ? '' : ' not') + ' be selected.'); + } + } + } + + function checkSelected(ids) { + var selection = grid.selection; + var numIds = ids.length; + checkStyles(); + assert.strictEqual(countProperties(selection), numIds, + 'Selection contains the expected number of items: ' + numIds); + assert.ok(arrayUtil.every(ids, function (id) { + return id in selection; + }), 'Selection contains the expected items'); + } + + var initSelection = [3, 4, 5, 23, 24, 25]; + arrayUtil.forEach(initSelection, function (id) { + grid.select(grid.row(id)); + }); + checkSelected(initSelection); + + grid.gotoPage(4); + checkSelected(initSelection); + + grid.gotoPage(1); + store.put({ id: 1, first: 'Updated First 1', last: 'Updated Last 1'}); + checkSelected(initSelection); + assert.isTrue(grid.cell(1, 'first').element.innerHTML.indexOf('Updated First 1') > -1); + store.put({ id: 4, first: 'Updated First 4', last: 'Updated Last 4'}); + checkSelected(initSelection); + assert.isTrue(grid.cell(4, 'first').element.innerHTML.indexOf('Updated First 4') > -1); + + store.put({ id: 24, first: 'Updated First', last: 'Updated Last'}); + checkSelected(initSelection); + + store.put({ id: 1999, first: 'New First', last: 'New Last'}); + checkSelected(initSelection); + + store.remove(2); + checkSelected(initSelection); + + store.remove(3); + checkSelected([4, 5, 23, 24, 25]); + + store.remove(25); + checkSelected([4, 5, 23, 24]); + + grid.destroy(); + }; + + notificationTests[name + ' events + store'] = function () { + // Create and remove selections, watch for events + var store = new TrackableMemory({ + data: _createTestData() + }); + + grid = new (declare([OnDemandGrid, SelectionMixin]))({ + columns: getColumns(), + collection: store, + sort: 'id' + }); + + document.body.appendChild(grid.domNode); + grid.startup(); + + var selectEventFired; + var deselectEventFired; + grid.on('dgrid-select', function () { + selectEventFired++; + }); + grid.on('dgrid-deselect', function () { + deselectEventFired++; + }); + + function testEvents() { + selectEventFired = 0; + deselectEventFired = 0; + + grid.select(3); + assert.strictEqual(selectEventFired, 1, 'Select event fired once: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 0, 'Deselect event not fired: ' + deselectEventFired); + + grid.deselect(3); + assert.strictEqual(selectEventFired, 1, 'Select event fired once: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 1, 'Deselect event fired once: ' + deselectEventFired); + + grid.select(3); + assert.strictEqual(selectEventFired, 2, 'Select event fired twice: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 1, 'Deselect event fired once: ' + deselectEventFired); + + grid.select(4); + assert.strictEqual(selectEventFired, 3, 'Select event fired three times: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 1, 'Deselect event fired once: ' + deselectEventFired); + + grid.deselect(3); + assert.strictEqual(selectEventFired, 3, 'Select event fired three times: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 2, 'Deselect event fired twice: ' + deselectEventFired); + + grid.deselect(4); + assert.strictEqual(selectEventFired, 3, 'Select event fired three times: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 3, 'Deselect event fired three times: ' + deselectEventFired); + } + + // Run the event tests + testEvents(); + // Change the store + store = new TrackableMemory({ + data: _createTestData() + }); + grid.set('collection', store); + // Run the tests again + testEvents(); + + grid.destroy(); + }; + + notificationTests[name + ' events + no store'] = function () { + // Create and remove selections, watch for events + var selectEventFired = 0, + deselectEventFired = 0; + + grid = new (declare([Grid, SelectionMixin]))({ + columns: getColumns() + }); + + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(_createTestData()); + + grid.on('dgrid-select', function () { + selectEventFired++; + }); + grid.on('dgrid-deselect', function () { + deselectEventFired++; + }); + + grid.select(3); + assert.strictEqual(selectEventFired, 1, 'Select event fired once: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 0, 'Deselect event not fired: ' + deselectEventFired); + + grid.deselect(3); + assert.strictEqual(selectEventFired, 1, 'Select event fired once: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 1, 'Deselect event fired once: ' + deselectEventFired); + + grid.select(3); + assert.strictEqual(selectEventFired, 2, 'Select event fired twice: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 1, 'Deselect event fired once: ' + deselectEventFired); + + grid.select(4); + assert.strictEqual(selectEventFired, 3, 'Select event fired three times: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 1, 'Deselect event fired once: ' + deselectEventFired); + + grid.deselect(3); + assert.strictEqual(selectEventFired, 3, 'Select event fired three times: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 2, 'Deselect event fired twice: ' + deselectEventFired); + + grid.deselect(4); + assert.strictEqual(selectEventFired, 3, 'Select event fired three times: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 3, 'Deselect event fired three times: ' + deselectEventFired); + + grid.destroy(); + }; + + notificationTests[name + ' events + no store + remove'] = function () { + // Create selections, remove rows, watch for events + var deselectEventFired = 0; + + grid = new (declare([Grid, SelectionMixin]))({ + columns: getColumns() + }); + + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderArray(_createTestData()); + + grid.on('dgrid-deselect', function () { + deselectEventFired++; + }); + + grid.select(3); + grid.select(4); + assert.strictEqual(deselectEventFired, 0, 'Deselect event not fired: ' + deselectEventFired); + + grid.row(3).remove(); + assert.strictEqual(deselectEventFired, 1, 'Deselect event fired once: ' + deselectEventFired); + + grid.row(5).remove(); + assert.strictEqual(deselectEventFired, 1, 'Deselect event not fired again: ' + deselectEventFired); + + grid.row(4).remove(); + assert.strictEqual(deselectEventFired, 2, 'Deselect event fired a second time: ' + deselectEventFired); + + grid.destroy(); + }; + + notificationTests[name + ' events + store + remove'] = function () { + // Create selections, remove data, watch for events + var store = new TrackableMemory({ + data: _createTestData() + }), + selectEventFired, + deselectEventFired; + + grid = new (declare([OnDemandGrid, SelectionMixin]))({ + columns: getColumns(), + collection: store, + sort: 'id' + }); + + document.body.appendChild(grid.domNode); + grid.startup(); + + grid.on('dgrid-select', function () { + selectEventFired++; + }); + grid.on('dgrid-deselect', function () { + deselectEventFired++; + }); + + function testEvents() { + selectEventFired = 0; + deselectEventFired = 0; + grid.select(3); + grid.select(4); + assert.strictEqual(deselectEventFired, 0, 'Deselect event not fired: ' + deselectEventFired); + + // Reset the select event counter. It should not fire on remove. + selectEventFired = 0; + + store.remove(3); + assert.strictEqual(selectEventFired, 0, 'Select event not fired: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 1, 'Deselect event fired once: ' + deselectEventFired); + + store.remove(5); + assert.strictEqual(selectEventFired, 0, 'Select event not fired: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 1, 'Deselect event not fired again: ' + deselectEventFired); + + store.remove(4); + assert.strictEqual(selectEventFired, 0, 'Select event not fired: ' + selectEventFired); + assert.strictEqual(deselectEventFired, 2, 'Deselect event fired a second time: ' + deselectEventFired); + } + + // Test the events + testEvents(); + // Change the store + store = new TrackableMemory({ + data: _createTestData() + }); + grid.set('collection', store); + // Test the events again + testEvents(); + + grid.destroy(); + }; + }); + + test.suite('Selection update handling', function () { + test.afterEach(function () { + grid.destroy(); + }); + + for (var name in notificationTests) { + test.test(name, notificationTests[name]); + } + }); + + test.suite('Selection events', function () { + var store = new TrackableMemory({ + data: _createTestData() + }); + var handles = []; + + test.beforeEach(function () { + grid = new (declare([OnDemandGrid, Selection]))({ + columns: getColumns(), + sort: 'id', + collection: store + }); + document.body.appendChild(grid.domNode); + grid.startup(); + }); + + test.afterEach(function () { + grid.destroy(); + for (var i = handles.length; i--;) { + handles[i].remove(); + } + handles = []; + }); + + test.test('programmatic row deselection event', function () { + var lastEventType, + expectedEventType = 'dgrid-deselect', + eventCount = 0; + + grid.select('1'); + + // Intentionally check for both select and deselect events - + // we should only receive a single deselect event + grid.on('dgrid-select, dgrid-deselect', function (event) { + lastEventType = event.type; + eventCount++; + }); + + grid.deselect('1'); + grid.deselect('1'); + + assert.equal(eventCount, 1); + assert.equal(expectedEventType, lastEventType); + }); + + test.test('programmatic row selection event', function () { + var lastEventType, + expectedEventType = 'dgrid-select', + eventCount = 0; + + grid.on('dgrid-select, dgrid-deselect', function (event) { + lastEventType = event.type; + eventCount++; + }); + + grid.select('1'); + grid.select('1'); + + assert.equal(eventCount, 1); + assert.equal(expectedEventType, lastEventType); + }); + + test.test('clearSelection() within event handler', function () { + var dfd = this.async(); + var numCalls = 0; + handles.push(grid.on('dgrid-select', dfd.rejectOnError(function (event) { + numCalls++; + assert.isTrue(numCalls < 2, 'dgrid-select handler should only fire once'); + // clearSelection will cause selection events to be fired, + // but that should not include the originally-queued event + grid.clearSelection(); + }))); + + grid.select('1'); + + // Since this test passes on 1 event firing but fails on multiple, + // resolve on a small timeout (since failure will occur instantaneously) + setTimeout(function () { dfd.resolve(); }, 100); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Selector.js b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Selector.js new file mode 100644 index 00000000..f2f96658 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Selector.js @@ -0,0 +1,96 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'dojo/_base/declare', + 'dojo/query', + 'dgrid/OnDemandGrid', + 'dgrid/Selection', + 'dgrid/Selector', + 'dgrid/test/data/testStore' +], function (test, assert, declare, query, OnDemandGrid, Selection, Selector, testStore) { + test.suite('selector column plugin', function () { + var grid; + + test.beforeEach(function () { + grid = new (declare([OnDemandGrid, Selection, Selector]))({ + collection: testStore, + columns: { + select: { selector: 'checkbox', label: 'Select' }, + col1: 'Column 1', + col2: 'Column 2', + col5: 'Column 5' + } + }); + document.body.appendChild(grid.domNode); + grid.startup(); + }); + + test.afterEach(function () { + grid.destroy(); + }); + + test.test('programmatic row selection', function () { + var rowNode, + checkboxNode, + rowCount = testStore.data.length, + rowIndex = 0, + expectedSelection = {}, + lastSelectedRow; + + grid.on('dgrid-select, dgrid-deselect', function (event) { + lastSelectedRow = event.rows[0].id; + }); + + // Test selecting every row in grid + for (rowIndex = 0; rowIndex < rowCount; rowIndex++) { + // initialize to invalid value + lastSelectedRow = -1; + + grid.select(rowIndex); + expectedSelection[rowIndex] = true; + assert.deepEqual(grid.selection, expectedSelection, + 'grid.selection object should match expected values'); + + rowNode = grid.row(rowIndex).element; + if (rowNode) { + // This checks if lastSelectedRow was updated by the event handler for dgrid-select + // The event is not fired for rows that are not in the DOM, so the test is in this conditional block + assert.strictEqual(lastSelectedRow, rowIndex, + 'dgrid-select event: last selected row should be ' + rowIndex + '; real: ' + lastSelectedRow); + + // Rows not in the DOM will also not have any checkbox/radio, so only run this test + // if rowNode exists + checkboxNode = query('.field-select input', rowNode)[0]; + assert.isTrue(checkboxNode.checked, + 'Checkbox for selected row ' + rowIndex + ' should be checked'); + } + } + + // Test deselecting every row in grid + for (rowIndex = 0; rowIndex < rowCount; rowIndex++) { + // initialize to invalid value + lastSelectedRow = -1; + + grid.deselect(rowIndex); + delete expectedSelection[rowIndex]; + assert.deepEqual(grid.selection, expectedSelection, + 'grid.selection object should match expected values'); + + rowNode = grid.row(rowIndex).element; + if (rowNode) { + // This checks if lastSelectedRow was updated by the event handler for dgrid-deselect + // The event is not fired for rows that are not in the DOM, so the test is in this conditional block + assert.strictEqual(lastSelectedRow, rowIndex, + 'dgrid-deselect event: last deselected row should be ' + rowIndex + '; real: ' + lastSelectedRow + ); + + // Rows not in the DOM will also not have any checkbox/radio, so only run this test + // if rowNode exists + checkboxNode = query('.field-select input', rowNode)[0]; + assert.isFalse(checkboxNode.checked, + 'Checkbox for selected row ' + rowIndex + ' should NOT be checked'); + } + } + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree-additional-filter.js b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree-additional-filter.js new file mode 100644 index 00000000..5dafea95 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree-additional-filter.js @@ -0,0 +1,147 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'dgrid/OnDemandGrid', + 'dgrid/Tree', + 'dojo/_base/declare', + 'dojo/_base/lang', + 'dojo/query', + 'dstore/Memory', + '../addCss!' +], function (test, assert, OnDemandGrid, Tree, declare, lang, query, Memory) { + + function formatName(name) { + return name.charAt(0).toUpperCase() + name.slice(1); + } + + test.suite('tree + additional filters', function () { + var grid1; + var grid2; + var store; + var noBlueStore; + var TreeGrid = declare([OnDemandGrid, Tree]); + var TreeStore = declare(Memory, { + constructor: function () { + this.root = this; + }, + + getChildren: function (parent) { + if (parent.contains) { + // Call filter from the original store to search all objects. + return this.root.filter({ type: parent.contains }); + } + }, + mayHaveChildren: function (obj) { + return obj.type === 'basket'; + } + }); + + function destroyGrid(grid) { + if (grid) { + grid.destroy(); + } + } + + test.before(function () { + var id = 0; + var iItemType, lItem; + var iColor, lColor; + var itemType, color; + var items = [ + { id: id++, name: 'Socks', type: 'basket', contains: 'sock' }, + { id: id++, name: 'Shirts', type: 'basket', contains: 'shirt' }, + { id: id++, name: 'Pants', type: 'basket', contains: 'pants' }, + { id: id++, name: 'Hats', type: 'basket', contains: 'hat' } + ]; + var itemTypes = [ 'sock', 'shirt', 'pants', 'hat' ]; + var colors = [ 'red', 'green', 'blue', 'white' ]; + for (iItemType = 0, lItem = itemTypes.length; iItemType < lItem; iItemType++) { + for (iColor = 0, lColor = colors.length; iColor < lColor; iColor++) { + itemType = itemTypes[iItemType]; + color = colors[iColor]; + items.push({ + id: id++, + name: formatName(color) + ' ' + formatName(itemType), + type: itemType, + color: color + }); + } + } + + store = new TreeStore({ + data: items + }); + + // Create a delegate of the original store with a new getChildren method. + // Make getChildren remove the blue items. + noBlueStore = lang.delegate(store, { + getChildren: function (parent) { + var children = this.root.getChildren(parent); + return children.filter(function (obj) { + return obj.color !== 'blue'; + }); + } + }); + }); + + test.beforeEach(function () { + grid1 = new TreeGrid({ + collection: store.filter({ type: 'basket' }), + columns: [ + {renderExpando: true, label: 'Name', field: 'name', sortable: false} + ] + }); + document.body.appendChild(grid1.domNode); + grid1.startup(); + + grid2 = new TreeGrid({ + collection: noBlueStore.filter({ type: 'basket' }), + columns: [ + {renderExpando: true, label: 'Name', field: 'name', sortable: false} + ] + }); + document.body.appendChild(grid2.domNode); + grid2.startup(); + }); + + test.afterEach(function () { + destroyGrid(grid1); + destroyGrid(grid2); + }); + + // Note: since the following tests are using a Memory store which is synchronous, there is no + // need to wait for the expand's promise to resolve. + test.test('expand tree with original store', function () { + assert.strictEqual(4, query('.dgrid-row', grid1.domNode).length, 'Grid should have 4 rows'); + grid1.expand(1); + assert.strictEqual(8, query('.dgrid-row', grid1.domNode).length, 'Grid should have 8 rows'); + }); + + test.test('expand tree with no-blue store', function () { + assert.strictEqual(4, query('.dgrid-row', grid2.domNode).length, 'Grid should have 4 rows'); + grid2.expand(3); + assert.strictEqual(7, query('.dgrid-row', grid2.domNode).length, 'Grid should have 7 rows'); + }); + + test.test('expand both trees', function () { + assert.strictEqual(4, query('.dgrid-row', grid1.domNode).length, 'Grid should have 4 rows'); + assert.strictEqual(4, query('.dgrid-row', grid2.domNode).length, 'Grid should have 4 rows'); + + grid1.expand(0); + assert.strictEqual(8, query('.dgrid-row', grid1.domNode).length, 'Grid should have 8 rows'); + assert.strictEqual(4, query('.dgrid-row', grid2.domNode).length, 'Grid should have 4 rows'); + + grid2.expand(3); + assert.strictEqual(8, query('.dgrid-row', grid1.domNode).length, 'Grid should have 8 rows'); + assert.strictEqual(7, query('.dgrid-row', grid2.domNode).length, 'Grid should have 7 rows'); + + grid1.expand(3); + assert.strictEqual(12, query('.dgrid-row', grid1.domNode).length, 'Grid should have 12 rows'); + assert.strictEqual(7, query('.dgrid-row', grid2.domNode).length, 'Grid should have 7 rows'); + + grid2.expand(0); + assert.strictEqual(12, query('.dgrid-row', grid1.domNode).length, 'Grid should have 12 rows'); + assert.strictEqual(10, query('.dgrid-row', grid2.domNode).length, 'Grid should have 10 rows'); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree-expand-promise.js b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree-expand-promise.js new file mode 100644 index 00000000..0e7751cb --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree-expand-promise.js @@ -0,0 +1,467 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'dgrid/Grid', + 'dgrid/OnDemandGrid', + 'dgrid/_StoreMixin', + 'dgrid/Tree', + 'dojo/_base/declare', + 'dojo/_base/lang', + 'dojo/_base/array', + 'dojo/Deferred', + 'dojo/on', + 'dstore/Memory', + 'dstore/Tree', + 'dstore/QueryResults', + 'dojo/query', + '../addCss!' +], function (test, assert, Grid, OnDemandGrid, _StoreMixin, Tree, declare, lang, arrayUtil, Deferred, on, + Memory, TreeStore, QueryResults, query) { + + test.suite('tree (expand + promise)', function () { + var grid, + SyncTreeStore = declare([ Memory, TreeStore ], { + mayHaveChildren: function () { + return true; + } + }), + AsyncTreeStore = declare(SyncTreeStore, { + // TreeStore with an asynchronous fetch method. + fetch: function () { + return asyncFetch.call(this); + }, + fetchRange: function (kwArgs) { + return asyncFetch.call(this, kwArgs); + }, + resolve: function () { + // Allows the test to control when the store query is resolved. + this.dfd.resolve(this.results); + }, + reject: function (error) { + // Allows the test to control when the store query is rejected. + this.dfd.reject(error); + } + }), + StoreMixinGrid = declare([Grid, _StoreMixin, Tree]), + syncStore = new SyncTreeStore({ data: createData() }), + asyncStore = new AsyncTreeStore({ data: createData() }); + + function asyncFetch(kwArgs) { + // Setting dfd on the prototype because collection chaining means we can't set it on an instance. + // It isn't great, but in practice, it is not much different than before. + var dfd = AsyncTreeStore.prototype.dfd = new Deferred(); + var results = AsyncTreeStore.prototype.results = kwArgs ? + this.fetchSync().slice(kwArgs.start, kwArgs.end) : this.fetchSync(); + results.totalLength = dfd.then(function () { + return results.length; + }); + return new QueryResults(dfd.promise); + } + + function createData() { + return [ + { id: 1, node: 'Node 1', value: 'Value 1', parent: null }, + { id: 2, node: 'Node 2', value: 'Value 2', parent: 1 }, + { id: 3, node: 'Node 3', value: 'Value 3', parent: 2 }, + { id: 4, node: 'Node 4', value: 'Value 4', parent: 2 }, + { id: 5, node: 'Node 5', value: 'Value 5', parent: null } + ]; + } + + function createGrid(store) { + grid = new (declare([ OnDemandGrid, Tree ]))({ + collection: store.getRootCollection(), + columns: [ + {renderExpando: true, field: 'node', label: 'Node'}, + {field: 'value', label: 'Value'} + ] + }); + document.body.appendChild(grid.domNode); + grid.startup(); + } + + function createNoRenderQueryGrid(store) { + grid = new StoreMixinGrid({ + collection: store.getRootCollection(), + columns: [ + {renderExpando: true, field: 'node', label: 'Node'}, + {field: 'value', label: 'Value'} + ] + }); + document.body.appendChild(grid.domNode); + grid.startup(); + grid.renderQueryResults(grid.collection.fetch()); + } + + function destroyGrid() { + if (grid) { + grid.destroy(); + grid = null; + } + } + + function createOnPromise(target, event) { + // Creates a promise based on an on.once call. + // Resolves to the event passed to the handler function. + var dfd = new Deferred(function () { + handle.remove(); + }), + handle = on.once(target, event, function (event) { + dfd.resolve(event); + }); + + return dfd.promise; + } + + function delayedResolve() { + setTimeout(function () { + grid.collection.resolve(); + }, 10); + } + + test.suite('tree + sync store', function () { + test.beforeEach(function () { + createGrid(syncStore); + }); + test.afterEach(destroyGrid); + + // Tests + test.test('expand + no callback', function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, 'Grid should have 2 rows'); + grid.expand(1); + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, 'Grid should have 3 rows'); + }); + + test.test('expand + callback', function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, 'Grid should have 2 rows'); + return grid.expand(1).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, 'Grid should have 3 rows'); + }); + }); + + test.test('expand + multiple callback', function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, 'Grid should have 2 rows'); + return grid.expand(1).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, 'Grid should have 3 rows'); + return grid.expand(2); + }).then(function () { + assert.strictEqual(5, query('.dgrid-row', grid.domNode).length, 'Grid should have 5 rows'); + return grid.expand(4); + }).then(function () { + assert.strictEqual(5, query('.dgrid-row', grid.domNode).length, 'Grid should have 5 rows'); + }); + }); + + test.test('duplicate expand + callback', function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, 'Grid should have 2 rows'); + return grid.expand(1).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, 'Grid should have 3 rows'); + return grid.expand(1); + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should have 3 rows (no query)'); + }); + }); + }); + + test.suite('tree + async store', function () { + test.beforeEach(function () { + createGrid(asyncStore); + }); + test.afterEach(destroyGrid); + + test.test('expand + callback', function () { + var promise = createOnPromise(grid, 'dgrid-refresh-complete').then(function () { + // Start testing when the grid is ready. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should have 2 rows'); + var promise = grid.expand(1); + + // Verify that the result is the same before the query resolves. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 2 rows before expand resolves'); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should have 3 rows'); + }); + + assert.strictEqual(0, query('.dgrid-row', grid.domNode).length, + 'Grid should have 0 rows before first async query resolves'); + // Resolve the grid's initial store query. + delayedResolve(); + return promise; + }); + + test.test('expand + multiple callback', function () { + var promise = createOnPromise(grid, 'dgrid-refresh-complete').then(function () { + // Start testing when the grid is ready. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should have 2 rows'); + var promise = grid.expand(1); + + // Verify that the result is the same before the query resolves. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 2 rows before expand resolves'); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should have 3 rows'); + var promise = grid.expand(2); + + // Verify that the result is the same before the query resolves. + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 3 rows before expand resolves'); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(5, query('.dgrid-row', grid.domNode).length, + 'Grid should have 5 rows'); + var promise = grid.expand(4); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(5, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 5 rows after expanding item with no children'); + }); + + assert.strictEqual(0, query('.dgrid-row', grid.domNode).length, + 'Grid should have 0 rows before first async query resolves'); + // Resolve the grid's initial store query. + delayedResolve(); + return promise; + }); + + test.test('duplicate expand + callback', function () { + var promise = createOnPromise(grid, 'dgrid-refresh-complete').then(function () { + // Start testing when the grid is ready. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should have 2 rows'); + var promise = grid.expand(1); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should have 3 rows'); + return grid.expand(1); + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 3 rows (no query)'); + }); + + assert.strictEqual(0, query('.dgrid-row', grid.domNode).length, + 'Grid should have 0 rows before first async query resolves'); + // Resolve the grid's initial store query. + delayedResolve(); + return promise; + }); + + test.test('expand + callback, rejecting', function () { + var errorCount = 0; + + grid.on('dgrid-error', function (event) { + event.preventDefault(); // Suppress log message + errorCount++; + }); + + var promise = createOnPromise(grid, 'dgrid-refresh-complete').then(function () { + // Start testing when the grid is ready. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should have 2 rows'); + var promise = grid.expand(1); + + // Verify that the result is the same before the query resolves. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 2 rows before expand resolves'); + setTimeout(function () { + grid.collection.reject('Rejected'); + }, 10); + return promise; + }).then(function () { + throw new Error('Promise should have been rejected'); + }, function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 2 rows after rejected promise'); + assert.strictEqual(1, errorCount, + 'The grid should have emitted a single error event'); + }); + + assert.strictEqual(0, query('.dgrid-row', grid.domNode).length, + 'Grid should have 0 rows before first async query resolves'); + // Resolve the grid's initial store query. + delayedResolve(); + return promise; + }); + }); + + test.suite('tree + no renderQuery + sync store', function () { + test.beforeEach(function () { + createNoRenderQueryGrid(syncStore); + }); + test.afterEach(destroyGrid); + + test.test('expand + callback', function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, 'Grid should have 2 rows'); + grid.expand(1); + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, 'Grid should have 3 rows'); + }); + + test.test('expand + callback', function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, 'Grid should have 2 rows'); + return grid.expand(1).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, 'Grid should have 3 rows'); + }); + }); + + test.test('expand + multiple callback', function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, 'Grid should have 2 rows'); + return grid.expand(1).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, 'Grid should have 3 rows'); + return grid.expand(2); + }).then(function () { + assert.strictEqual(5, query('.dgrid-row', grid.domNode).length, 'Grid should have 5 rows'); + return grid.expand(4); + }).then(function () { + assert.strictEqual(5, query('.dgrid-row', grid.domNode).length, 'Grid should have 5 rows'); + }); + }); + + test.test('duplicate expand + callback', function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, 'Grid should have 2 rows'); + return grid.expand(1).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, 'Grid should have 3 rows'); + return grid.expand(1); + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should have 3 rows (no query)'); + }); + }); + }); + + test.suite('tree + no renderQuery + async store', function () { + test.beforeEach(function () { + createNoRenderQueryGrid(asyncStore); + }); + + test.afterEach(destroyGrid); + + test.test('expand + callback', function () { + var promise = grid.collection.dfd.then(function () { + // Start testing when the initial query is done is ready. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should have 2 rows'); + var promise = grid.expand(1); + + // Verify that the result is the same before the query resolves. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 2 rows before expand resolves'); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should have 3 rows'); + }); + + assert.strictEqual(0, query('.dgrid-row', grid.domNode).length, + 'Grid should have 0 rows before first async query resolves'); + // Resolve the grid's initial store query. + delayedResolve(); + return promise; + }); + + test.test('expand + multiple callback', function () { + var promise = grid.collection.dfd.then(function () { + // Start testing when the initial query is done is ready. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should have 2 rows'); + var promise = grid.expand(1); + + // Verify that the result is the same before the query resolves. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 2 rows before expand resolves'); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should have 3 rows'); + var promise = grid.expand(2); + + // Verify that the result is the same before the query resolves. + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 3 rows before expand resolves'); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(5, query('.dgrid-row', grid.domNode).length, + 'Grid should have 5 rows'); + var promise = grid.expand(4); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(5, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 5 rows after expanding item with no children'); + }); + + assert.strictEqual(0, query('.dgrid-row', grid.domNode).length, + 'Grid should have 0 rows before first async query resolves'); + // Resolve the grid's initial store query. + delayedResolve(); + return promise; + }); + + test.test('duplicate expand + callback', function () { + var promise = grid.collection.dfd.then(function () { + // Start testing when the initial query is done is ready. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should have 2 rows'); + var promise = grid.expand(1); + delayedResolve(); + return promise; + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should have 3 rows'); + return grid.expand(1); + }).then(function () { + assert.strictEqual(3, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 3 rows (no query)'); + }); + + assert.strictEqual(0, query('.dgrid-row', grid.domNode).length, + 'Grid should have 0 rows before first async query resolves'); + // Resolve the grid's initial store query. + delayedResolve(); + return promise; + }); + + test.test('expand + callback, rejecting', function () { + var promise = grid.collection.dfd.then(function () { + // Start testing when the initial query is done is ready. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should have 2 rows'); + var promise = grid.expand(1); + + // Verify that the result is the same before the query resolves. + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 2 rows before expand resolves'); + setTimeout(function () { + grid.collection.reject('Rejected'); + }, 10); + return promise; + }).then(function () { + throw new Error('Promise should have been rejected'); + }, function () { + assert.strictEqual(2, query('.dgrid-row', grid.domNode).length, + 'Grid should still have 2 rows after rejected promise'); + }); + + assert.strictEqual(0, query('.dgrid-row', grid.domNode).length, + 'Grid should have 0 rows before first async query resolves'); + // Resolve the grid's initial store query. + delayedResolve(); + return promise; + }); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree.js b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree.js new file mode 100644 index 00000000..c8f69d5d --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/mixins/Tree.js @@ -0,0 +1,467 @@ +define([ + 'intern!tdd', + 'intern/chai!assert', + 'dgrid/OnDemandGrid', + 'dgrid/Editor', + 'dgrid/Tree', + 'dgrid/util/has-css3', + 'dgrid/util/misc', + 'dojo/_base/declare', + 'dojo/_base/lang', + 'dojo/aspect', + 'dojo/Deferred', + 'dojo/dom-class', + 'dojo/dom-construct', + 'dojo/dom-style', + 'dojo/on', + 'dojo/query', + 'dgrid/test/data/createHierarchicalStore', + '../addCss!' +], function (test, assert, OnDemandGrid, Editor, Tree, has, miscUtil, declare, lang, aspect, Deferred, + domClass, domConstruct, domStyle, on, query, createHierarchicalStore) { + + var grid, + testDelay = 15, + hasTransitionEnd = has('transitionend'); + + function createGrid(options) { + var data = [], + store, + treeColumnOptions, + i, + k, + GridConstructor; + + for (i = 0; i < 5; i++) { + var parentId = '' + i; + data.push({ + id: parentId, + value: 'Root ' + i + }); + for (k = 0; k < 100; k++) { + data.push({ + id: i + ':' + k, + parent: parentId, + value: 'Child ' + k, + hasChildren: false + }); + } + } + + store = createHierarchicalStore({ + data: data + }); + + treeColumnOptions = lang.mixin({ + renderExpando: true, + label: 'id', + field: 'id' + }, options && options.treeColumnOptions); + + if (options && options.useEditor) { + GridConstructor = declare([OnDemandGrid, Editor, Tree]); + if (!treeColumnOptions.editor) { + treeColumnOptions.editor = 'text'; + } + } + else { + GridConstructor = declare([OnDemandGrid, Tree]); + } + + grid = new GridConstructor(lang.mixin({ + sort: 'id', + collection: store, + columns: [ + treeColumnOptions, + { label: 'value', field: 'value'} + ] + }, options && options.gridOptions)); + document.body.appendChild(grid.domNode); + grid.startup(); + } + + function destroyGrid() { + grid.destroy(); + grid = null; + } + + function testRowExists(dataItemId, exists) { + // Tests existence of a row for a given item ID; + // if `exists` is false, tests for nonexistence instead + exists = exists !== false; + assert[exists ? 'isNotNull' : 'isNull'](document.getElementById(grid.id + '-row-' + dataItemId), + 'A row for ' + dataItemId + ' should ' + (exists ? '' : 'not ') + 'exist in the grid.'); + } + + function wait(delay) { + // Returns a promise resolving after the given number of ms (or testDelay by default) + var dfd = new Deferred(); + setTimeout(function () { + dfd.resolve(); + }, delay || testDelay); + return dfd.promise; + } + + // Define a function returning a promise resolving once children are expanded. + // On browsers which support CSS3 transitions, this occurs when transitionend fires; + // otherwise it occurs immediately. + var expand = hasTransitionEnd ? function (id) { + var dfd = new Deferred(); + + on.once(grid, hasTransitionEnd, function () { + dfd.resolve(); + }); + + grid.expand(id); + return dfd.promise; + } : function (id) { + var dfd = new Deferred(); + grid.expand(id); + dfd.resolve(); + return dfd.promise; + }; + + function scrollToEnd() { + var dfd = new Deferred(), + handle; + + handle = on.once(grid.bodyNode, 'scroll', miscUtil.debounce(function () { + dfd.resolve(); + })); + + grid.scrollTo({ y: grid.bodyNode.scrollHeight }); + + return dfd.promise; + } + + test.suite('Tree', function () { + test.suite('large family expansion', function () { + + test.beforeEach(function () { + createGrid(); + + // Firefox in particular seems to skip transitions sometimes + // if we don't wait a bit after creating and placing the grid + return wait(); + }); + + test.afterEach(destroyGrid); + + test.test('expand first row', function () { + return expand(0) + .then(function () { + testRowExists('0:0'); + testRowExists('0:99', false); + }); + }); + + test.test('expand first row + scroll to bottom', function () { + return expand(0) + .then(scrollToEnd) + .then(function () { + testRowExists('0:0'); + testRowExists('0:99'); + }); + }); + + test.test('expand last row', function () { + return expand(4).then(function () { + testRowExists('4:0'); + testRowExists('4:99', false); + }); + }); + + test.test('expand last row + scroll to bottom', function () { + return expand(4) + .then(scrollToEnd) + .then(function () { + testRowExists('4:0'); + testRowExists('4:99'); + }); + }); + + test.test('expand first and last rows + scroll to bottom', function () { + return expand(0) + .then(scrollToEnd) + .then(function () { + return expand(4); + }) + .then(scrollToEnd) + .then(function () { + testRowExists('4:0'); + testRowExists('4:99'); + }); + }); + + test.test('expand hidden', function () { + var dfd = this.async(1000); + + grid.domNode.style.display = 'none'; + grid.expand(0); + grid.domNode.style.display = 'block'; + + // Since the grid is not displayed the expansion will occur without a transitionend event + // However, DOM updates from the expand will not complete within the current stack frame + setTimeout(dfd.callback(function () { + var connected = grid.row(0).element.connected; + assert.isTrue(connected && connected.offsetHeight > 0, + 'Node should be expanded with non-zero height'); + }), 0); + }); + + // Test goal: ensure the expando icon is displayed consistent with the results of the store's + // "mayHaveChildren" method. + // Notes: + // * The store created in "createGrid" has a "mayHaveChildren" that returns true for nodes with no parentId + // * The expando icon (.dgrid-expando-icon) is always rendered, it is not visible if it lacks the class + // ".ui-icon" + test.test('mayHaveChildren', function () { + var rowObject; + var expandoNode; + var i; + var j; + + for (i = 0; i < 5; i++) { + rowObject = grid.row(i); + expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0]; + assert.isDefined(expandoNode, 'Parent node should have an expando icon; node id = ' + i); + + grid.expand(i, true, true); + + for (j = 0; j < 2; j++) { + rowObject = grid.row(i + ':' + j); + expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0]; + assert.isUndefined(expandoNode, + 'Child node should not have an expando icon; node id = ' + i + ':' + j); + } + + grid.expand(i, false, true); + } + }); + + // Test goal: ensure that rows are correctly expanded/collapsed on grid render in accordance with the + // grid's "shouldExpand" method + test.test('shouldExpand', function () { + var shouldExpand; + var i; + + grid.shouldExpand = function (rowObject) { + var shouldExpand = false; + + if (rowObject.data.parent === undefined) { + shouldExpand = rowObject.id % 2 === 0; + } + + return shouldExpand; + }; + grid.refresh(); + + for (i = 0; i < 5; i++) { + shouldExpand = i % 2 === 0; + + if (shouldExpand) { + assert.isTrue(grid._expanded[i], 'Row ' + i + ' should be expanded'); + } + else { + assert.isUndefined(grid._expanded[i], 'Row ' + i + ' should not be expanded'); + } + } + }); + + // Test goal: ensure that a custom "renderExpando" column method produces the expected DOM structure + test.test('renderExpando', function () { + var columns; + var rowObject; + var expandoNode; + var i; + + columns = grid.get('columns'); + columns[0].renderExpando = function () { + + // * Adds the "test-expando" class + // * Floats the expando at the opposite end of the cell + var node = grid._defaultRenderExpando.apply(this, arguments); + domClass.add(node, 'test-expando'); + domStyle.set(node, 'float', 'right'); + return node; + }; + grid.set('columns', columns); + + for (i = 0; i < 5; i++) { + rowObject = grid.row(i); + expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0]; + assert.isDefined(expandoNode, 'Row ' + i + ' should have an expando icon'); + assert.include(expandoNode.className, 'test-expando', + 'Row ' + i + '\'s expando icon should have the class "test-expando"'); + } + }); + + // Test goal: ensure the expando node is still rendered when the column has a custom "renderCell" method + test.test('renderCell', function () { + var rowObject; + var expandoNode; + var i; + + grid.destroy(); + + createGrid({ + treeColumnOptions: { + renderCell: function (object, value) { + var div = domConstruct.create('div', { className: 'testRenderCell' }); + div.appendChild(document.createTextNode(value)); + return div; + } + } + }); + + for (i = 0; i < 5; i++) { + rowObject = grid.row(i); + expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0]; + assert.isDefined(expandoNode, 'Row ' + i + ' should have an expando node'); + } + + grid.destroy(); + + createGrid({ + treeColumnOptions: { + renderCell: function (rowObject, cellValue, cellNode) { + domClass.add(cellNode, 'testRenderCell'); + cellNode.appendChild(document.createTextNode(cellValue)); + } + } + }); + + for (i = 0; i < 5; i++) { + rowObject = grid.row(i); + expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0]; + assert.isDefined(expandoNode, 'Row ' + i + ' should have an expando node'); + } + }); + + // Test goal: ensure the expando node is still rendered with the editor plugin + // Note: ordering is important: tree(editor()), not editor(tree()) + test.test('renderCell with editor', function () { + var rowObject; + var expandoNode; + var inputNode; + var i; + + grid.destroy(); + + createGrid({ + useEditor: true + }); + + for (i = 0; i < 5; i++) { + rowObject = grid.row(i); + expandoNode = query('.dgrid-expando-icon.ui-icon', rowObject.element)[0]; + assert.isDefined(expandoNode, 'Row ' + i + ' should have an expando node'); + inputNode = query('.dgrid-input', rowObject.element)[0]; + assert.isDefined(inputNode, 'Row ' + i + ' should have an input node'); + } + }); + }); + + test.suite('Tree + Trackable', function () { + test.beforeEach(createGrid); + test.afterEach(destroyGrid); + + test.test('child modification', function () { + return expand(0).then(function () { + testRowExists('0:0'); + assert.doesNotThrow(function () { + grid.collection.put({ + id: '0:0', + value: 'Modified', + parent: '0' + }); + }, null, 'Modification of child should not throw error'); + }); + }); + }); + + test.suite('Tree + Trackable + shouldTrackCollection: false', function () { + var handles = []; + + test.beforeEach(function () { + createGrid({ + gridOptions: { + shouldTrackCollection: false + } + }); + }); + + test.afterEach(function () { + for (var i = handles.length; i--;) { + handles[i].remove(); + } + handles = []; + destroyGrid(); + }); + + test.test('child add', function () { + return expand(0).then(function () { + testRowExists('0:0'); + grid.collection.add({ + id: '0:0.5', + value: 'New', + parent: '0' + }); + testRowExists('0:0.5', false); + }); + }); + + test.test('child put', function () { + return expand(0).then(function () { + var calls = 0; + + handles.push(aspect.before(grid, 'removeRow', function () { + calls++; + })); + + handles.push(aspect.before(grid, 'insertRow', function () { + calls++; + })); + + testRowExists('0:0'); + grid.collection.put({ + id: '0:0', + value: 'Modified', + parent: '0' + }); + assert.strictEqual(calls, 0, 'insertRow and removeRow should never be called'); + }); + }); + + test.test('child remove', function () { + return expand(0).then(function () { + testRowExists('0:0'); + grid.collection.remove('0:0'); + testRowExists('0:0'); + }); + }); + }); + + test.suite('treeIndentWidth', function () { + var treeIndentWidth = 20; + test.beforeEach(function () { + createGrid({ + gridOptions: { treeIndentWidth: treeIndentWidth } + }); + return wait(); + }); + + test.afterEach(destroyGrid); + + test.test('treeIndentWidth override', function () { + return expand(0).then(function () { + var row = grid.row('0:0'); + assert.ok(row, 'Expected child row exists'); + query('.dgrid-expando-icon', row.element).forEach(function (element) { + assert.strictEqual(element.style.marginLeft, treeIndentWidth + 'px'); + }); + }); + }); + }); + }); +}); diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/resources/setClass.html b/packages/xblox/ref-control-freak/xblox/test/intern/resources/setClass.html new file mode 100644 index 00000000..0fb0e8ef --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/resources/setClass.html @@ -0,0 +1,8 @@ + + + + + + +
stepnamewhat to do
\ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/test/intern/runTests.html b/packages/xblox/ref-control-freak/xblox/test/intern/runTests.html new file mode 100644 index 00000000..15ccc8e2 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/test/intern/runTests.html @@ -0,0 +1,11 @@ + + + + + dgrid Intern suite + + + + Redirecting to Intern runner. + + \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestBlock.js b/packages/xblox/ref-control-freak/xblox/tests/TestBlock.js new file mode 100644 index 00000000..d7d62376 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestBlock.js @@ -0,0 +1,59 @@ +/** + * @param Block {module:xblox/model/Block} + * @param types {module:xide/types/Types} + * @param utils {module:xide/utils} + * @param _ {module:xide/lodash} + * @param BlockFactory {module:xblox/factory/Blocks} + * @param Command {module:xcf/model/Command} + */ +function loaded(Block, types, utils, _, BlockFactory, Command) { + /* + * playground + */ + var ctx = window.sctx, + ACTION = types.ACTION, + root, + scope, + blockManager, + driverManager, + marantz; + + + if (ctx) { + + console.clear(); + + + var blockManager = ctx.getBlockManager(); + var scope = blockManager.toScope([]); + + /** + * @type {module:xide/types/BLOCK_CAPABILITIES} + */ + var BLOCK_CAPABILITIES = types.BLOCK_CAPABILITIES; + var TopMost = BlockFactory.createBlock(Block, { + scope: scope, + /** + * @type {module:xide/types/BLOCK_CAPABILITIES} + */ + capabilities:(BLOCK_CAPABILITIES.CHILDREN) + }); + + var canChildren = (TopMost.capabilities & BLOCK_CAPABILITIES.CHILDREN); + + console.log('can children ',canChildren); + + } +} + +/** @module xblox/types + * @description All the package's constants and enums in C style structures. + */ +define([ + 'xblox/model/Block', + 'xide/types', + 'xide/utils', + 'xide/lodash', + 'xblox/factory/Blocks', + 'xcf/model/Command' +],loaded); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestBlockGrid.js b/packages/xblox/ref-control-freak/xblox/tests/TestBlockGrid.js new file mode 100644 index 00000000..27a49c46 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestBlockGrid.js @@ -0,0 +1,578 @@ +/** @module xgrid/Base **/ +define([ + 'xide/utils', + 'xide/types', + 'xgrid/Grid', + 'xblox/views/BlockGrid', + 'xblox/views/ThumbRenderer', + 'xide/tests/TestUtils', + 'dojo/dom-construct', + 'xaction/DefaultActions', + './TestUtils', + 'module' +], function (utils, types, Grid, BlockGrid, ThumbRenderer, TestUtils, domConstruct, DefaultActions, BlockUtils, module) { + + var ACTION = types.ACTION; + console.clear(); + /*** + * + * playground + */ + var ctx = window.sctx; + + function doGridTest(grid, scope) { + + + /* + grid.refresh().then(function () { + //grid.runAction('View/Layout/Thumb'); + grid.runAction('View/Show/Toolbar'); + + grid.select([0]).then(function (e) { + }); + + }); + */ + + } + + + + + ThumbRenderer.prototype.runAction = function (action) { + if (action.command === 'Step/Edit') { + var collection = this.collection, + item = this.getSelectedItem(); + + if (item) { + + this.set('collection', collection.filter({ + parentId: item.id + })); + } + } + + return true; + } + ThumbRenderer.prototype.renderRow = function (obj) { + if (obj.renderRow) { + var _res = obj.renderRow.apply(this, [obj]); + if (_res) { + return _res; + } + } + + var thiz = this, + div = domConstruct.create('div', { + className: "tile widget" + }), + icon = obj.icon, + no_access = obj.read === false && obj.write === false, + isBack = obj.name == '..', + directory = obj.getChildren().length > 0, + useCSS = false, + imageClass = 'fa fa-folder fa-5x', + isImage = false, + blockIcon = obj.icon; + + this._doubleWidthThumbs = true; + + + var _d = obj.getChildren(); + + + + console.log('render row ' + obj.name + ' = ' + directory + ' blockIcon ' + blockIcon); + + + var iconStyle = 'text-shadow: 2px 2px 5px rgba(0,0,0,0.3);font-size: 72px;opacity: 0.7'; + var contentClass = 'icon'; + + + if (directory) { + imageClass = 'fa fa-5x fa-folder'; + useCSS = true; + + } else { + + imageClass = 'fa fa-5x ' + ( blockIcon || 'fa-play'); + + useCSS = true; + } + + var label = obj.name; + + var folderContent = ''; + + var _image = false; + if (_image) { + + var url = this.getImageUrl(obj); + if (url) { + obj.icon = url; + } else { + obj.icon = thiz.config.REPO_URL + '/' + obj.path; + } + + imageClass = ''; + contentClass = 'image'; + //folderContent = ''; + folderContent = '
' + + '' + + '
'; + + useCSS = true; + isImage = true; + + } + + + //var iconStyle='text-shadow: 2px 2px 5px rgba(0,0,0,0.3);left:40px;text-align:left;font-size: 72px;margin-top:-45px;opacity: 0.7'; + + + var label2 = label + '\n' + obj.modified; + + var html = '
' + + folderContent + + '
' + + + '
' + + '' + + label + + '' + + '
'; + + + if (isImage || this._doubleWidthThumbs) { + $(div).addClass('double'); + } + + if (useCSS) { + div.innerHTML = html; + return div; + } + + + if (directory) { + div.innerHTML = html; + } else { + div.innerHTML = ' 
' + obj.name + '
'; + } + return div; + } + + if (ctx) { + var blockManager = ctx.getBlockManager(); + var blockScope = BlockUtils.createScope(blockManager, [ + { + "_containsChildrenIds": [], + "id": "Shell-Block", + "description": "Runs a JSON-RPC-2.0 method on the server", + "name": "Run Server Method", + "method": "XShell::run", + "args": "ls", + "deferred": true, + "defaultServiceClass": "XShell", + "defaultServiceMethod": "run", + "declaredClass": "xblox.model.server.RunServerMethod", + "enabled": true, + "shareTitle": "", + "canDelete": true, + "order": 0, + "type": "added", + "group": "click" + } + ]); + var mainView = ctx.mainView; + + if (mainView) { + + var parent = TestUtils.createTab(null, null, module.id); + + var grid, + store = blockScope.blockStore, + gridArgs = { + ctx: ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect: true, + collection: store.filter({ + group: "click" + }), + permissions: [ + //ACTION.EDIT, + ACTION.RENAME, + ACTION.RELOAD, + ACTION.DELETE, + ACTION.CLIPBOARD, + ACTION.LAYOUT, + ACTION.COLUMNS, + ACTION.SELECTION, + //ACTION.PREVIEW, + ACTION.SAVE, + ACTION.SEARCH, + ACTION.DOWNLOAD, + 'Step/Run', + 'Step/Move Up', + 'Step/Move Down', + 'Step/Edit', + ACTION.TOOLBAR + + ], + getBlockActions: function (permissions) { + + var result = [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + VISIBILITY = types.ACTION_VISIBILITY, + thiz = this, + container = thiz.domNode, + actionStore = thiz.getActionStore(); + + function addAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable) { + + var action = null; + mixin = mixin || {}; + utils.mixin(mixin, {owner: thiz}); + + if (mixin.addPermission || DefaultActions.hasAction(permissions, command)) { + + if (!handler) { + handler = function (action) { + console.log('log run action', arguments); + var who = this; + if (who.runAction) { + who.runAction.apply(who, [action]); + } + } + } + + if (!mixin.tooltip && keycombo) { + + if (_.isString(keycombo)) { + keycombo = [keycombo]; + } + + + mixin.tooltip = keycombo.join('
').toUpperCase(); + + } + + + action = DefaultActions.createAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable, thiz.domNode); + result.push(action); + return action; + } + } + + + + + var rootAction = 'Block/Insert'; + var defaultMixin = { addPermission: true }; + + result.push(thiz.createAction({ + label: 'New Block', + command: rootAction, + icon: 'fa-plus', + tab: 'Home', + group: 'Step', + keycombo: ['alt up'], + mixin: defaultMixin + })); + result.push(thiz.createAction({ + label: 'Save', + command: 'File/Save', + icon: 'fa-save', + tab: 'Home', + group: 'File', + keycombo: ['ctrl '], + mixin: defaultMixin + })); + result.push(thiz.createAction({ + label: 'Save As', + command: 'File/Save As', + icon: 'fa-save', + tab: 'Home', + group: 'File', + mixin: defaultMixin + })); + + var newBlockActions = this.getAddActions(); + var levelName = ''; + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + result.push(thiz.createAction({ + label: levelName, + command: path, + icon: item.iconClass, + tab: 'Home', + group: 'Step', + mixin:defaultMixin + })); + + /* + addAction(levelName, path, item.iconClass, null, 'Home', 'Insert', 'item|view', null, null, { + item: item, + addPermission: true + }, null, null); + */ + + + if (isContainer) { + addItems(path, item.items); + } + } + + } + + addItems(rootAction, newBlockActions); + + + + function _selection() { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return null; + } + var item = selection[0]; + if (!item) { + console.error('have no item'); + return null; + } + return selection; + } + + function _canMove() { + + var selection = _selection(); + if (!selection) { + return true; + } + return selection[0].canMove(item, this.command === 'Step/Move Up' ? -1 : 1); + + } + + + function canParent() { + + var selection = thiz.getSelection(); + if (!selection) { + return true; + } + var item = selection[0]; + if (!item) { + //console.error('canParent:have no item!!!',this); + return true; + } + if (this.command === 'Step/Move Left') { + return !item.getParent(); + } else { + return item.getParent(); + } + } + + function isItem() { + var selection = _selection(); + if (!selection) { + return true; + } + return false; + } + + function canMove() { + var selection = _selection(); + + if (!selection) { + return true; + } + + var item = selection[0]; + + if (this.command === 'Step/Move Up') { + return !item.canMove(null, -1); + } else { + return !item.canMove(null, 1); + } + + return false; + } + + result.push(thiz.createAction({ + label: 'Run', + command: 'Step/Run', + icon: 'fa-play', + tab: 'Home', + group: 'Step', + keycombo: ['r'], + shouldDisable: isItem + })); + + ////////////////////////////////////////////////////////////////// + // + // Step - Move + // + + result.push(thiz.createAction({ + label: 'MoveUp', + command: 'Step/Move Up', + icon: 'fa-arrow-up', + tab: 'Home', + group: 'Step', + keycombo: ['alt up'], + shouldDisable: canMove + })); + + result.push(thiz.createAction({ + label: 'MoveDown', + command: 'Step/Move Down', + icon: 'fa-arrow-down', + tab: 'Home', + group: 'Step', + keycombo: ['alt down'], + shouldDisable: canMove, + mixin: defaultMixin + })); + + result.push(thiz.createAction({ + label: 'MoveLeft', + command: 'Step/Move Left', + icon: 'fa-arrow-left', + tab: 'Home', + group: 'Step', + keycombo: ['alt left'], + shouldDisable: canMove, + mixin: defaultMixin + })); + + result.push(thiz.createAction({ + label: 'MoveRight', + command: 'Step/Move Right', + icon: 'fa-arrow-right', + tab: 'Home', + group: 'Step', + keycombo: ['alt right'], + shouldDisable: canParent, + mixin: defaultMixin + })); + + + //Step enable/disable + /* + addAction('On', 'Step/Enable', 'fa-toggle-off', ['alt d'], 'Home', 'Step', 'item|view', null, null, + { + addPermission:true, + onCreate: function (action) { + action.setVisibility(types.ACTION_VISIBILITY.RIBBON, { + widgetClass: declare.classFactory('_Checked', [ToggleButton,_ActionValueWidgetMixin], null, {} ,null), + widgetArgs: { + icon1: 'fa-toggle-on', + icon2: 'fa-toggle-off', + delegate: thiz, + checked:true, + iconClass:'fa-toggle-on' + } + }); + } + },null, function(){ + return thiz.getSelection().length==0; + }); + */ + + result.push(thiz.createAction({ + label: 'On', + command: 'Step/Enable', + icon: 'fa-toggle-off', + tab: 'Home', + group: 'Step', + keycombo: ['alt d'], + shouldDisable: isItem, + mixin: defaultMixin + })); + + + result.push(this.createAction({ + label: 'Edit', + command: 'Step/Edit', + icon: ACTION_ICON.EDIT, + keycombo: ['f4', 'enter', 'dblclick'], + tab: 'Home', + group: 'Step', + shouldDisable: isItem + })); + + /* + addAction('Properties', 'Step/Properties', 'fa-gears', ['alt enter'], 'Home', 'Step', 'item|view', null, null, + { + addPermission: true, + onCreate: function (action) { + action.handle=false; + action.setVisibility(types.ACTION_VISIBILITY.RIBBON, { + widgetClass: declare.classFactory('_Checked', [ToggleButton, _ActionValueWidgetMixin], null, {}, null), + widgetArgs: { + icon1: 'fa-toggle-on', + icon2: 'fa-toggle-off', + delegate: thiz, + checked: false, + iconClass: 'fa-toggle-off' + } + }); + } + }, null, function () { + return thiz.getSelection().length == 0; + }); + */ + result.push(thiz.createAction({ + label: 'Properties', + command: 'Step/Properties', + icon: 'fa-arrow-up', + tab: 'Home', + group: 'Step', + keycombo: ['alt enter'], + shouldDisable: isItem, + mixin: defaultMixin + })); + + this._emit('onAddActions', { + actions: result, + addAction: addAction, + permissions: permissions, + store: actionStore + }); + + //console.dir(_.pluck(result,'command')); + + + return result; + } + }; + + grid = utils.addWidget(BlockGrid, gridArgs, null, parent, true); + ctx.getWindowManager().registerView(grid, true); + + var blocks = blockScope.allBlocks(); + + + + setTimeout(function () { + mainView.resize(); + grid.resize(); + doGridTest(grid, blockScope); + }, 1000); + + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestBlockGridClass.js b/packages/xblox/ref-control-freak/xblox/tests/TestBlockGridClass.js new file mode 100644 index 00000000..2ff617b7 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestBlockGridClass.js @@ -0,0 +1,257 @@ +/** @module xgrid/Base **/ +define([ + 'xdojo/declare', + 'xide/utils', + 'xgrid/Grid', + 'xblox/views/BlockGrid', + 'xide/tests/TestUtils', + './TestUtils', + 'module' + + + +], function (declare,utils, Grid, BlockGrid, + TestUtils,BlockUtils,module + ) { + + + /*** + * playground + */ + var ctx = window.sctx; + + console.clear(); + console.log('do grid test2'); + + function doGridTest(grid,scope){ + + + + + + grid.refresh().then(function(){ + grid.select([0]).then(function(e){ + //grid.runAction('Step/Run'); + }); + }); + + } + + var did = false; + + + function format(value,block,who){ + + + if(!did){ + var items = block._store.storage.fullData; + items = items.filter(function(item){ + return item.group ==='click'; + }); + console.dir(_.pluck(items,'name')); + console.dir(_.pluck(items,'group')); + did=true; + } + + function index(item,dir){ + + var item = this, + parent = item.getParent(), + items = null, + group = item.group, + store = this._store; + + if (parent) { + items = parent[parent._getContainer(item)]; + items = items.filter(function(item){ + return item.group ===group; + }); + if (!items || items.length < 2 || !this.containsItem(items, item)) { + return false; + } + return this.indexOf(items, item); + + }else{ + items = store.storage.fullData; + items = items.filter(function(item){ + return item.group ===group; + }); + return this.indexOf(items, item); + } + } + + var parent = block.getParent(); + + var _index = index.apply(block,[block,1]); + if(parent){ + var parents =block.numberOfParents(); + var _out=' '; + for (var i = 0; i < parents; i++) { + _out+=' ' + } + _out += ''; + + for (var i = 0; i < parents; i++) { + _out+='-' + } + _out += ' '; + _index=_out + _index; + } + + console.log('index ' + block.name + ' : ' + _index); + + return _index; + } + xide.utils.fff= format; + + + + + if (ctx) { + + var blockManager = ctx.getBlockManager(); + + + var blockScope = BlockUtils.createScope(blockManager,[],[ + { + "_containsChildrenIds": [], + "id": "Shell-Block", + "description": "Runs a JSON-RPC-2.0 method on the server", + "name": "Run Server Method", + "method": "XShell::run", + "args": "ls", + "deferred": true, + "defaultServiceClass": "XShell", + "defaultServiceMethod": "run", + "declaredClass": "xblox.model.server.RunServerMethod", + "enabled": true, + "shareTitle": "", + "canDelete": true, + "order": 0, + "type": "added", + "group": "click" + } + ]); + + var mainView = ctx.mainView; + + + + + + if (mainView) { + + var parent = TestUtils.createTab(null,null,module.id); + var grid, + store = blockScope.blockStore, + + gridArgs = { + ctx:ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect:true, + collection: store.filter({ + group: "click" + }) + + }; + var gridClass = declare('sdfdf ' + new Date().getTime(),BlockGrid,{ + postMixInProperties: function() { + + + var _has = false; + + var self = this; + _.each(this.columns, function (col) { + if (col.field == 'order') { + _has = true; + } + }); + + if (!_has && this.blockGroup !== 'basicVariables') { + + this.columns.unshift({ + //renderExpando: true, + label: "Line", + field: "order", + sortable: false, + formatter: function(val,b){ + return xide.utils.fff(val,b,self); + } + }); + } + this.inherited(arguments); + }, + formatOrder:function(value,block){ + + + + /* + if(block) { + var row = this.row(block); + if (row && row.element) { + return 'rendered'; + } else { + console.log('not rendered yet2'); + return 'asdfsd'; + } + + + + }else{ + console.log('-have no block'); + } + + if(this._map) { + if (this._map[block.id]) { + return this._map[block.id]; + } + }else{ + //console.log('have no map'); + } + */ + return '0'; + + } + }); + + + grid = utils.addWidget(gridClass,gridArgs,null,parent,true); + + + ctx.getWindowManager().registerView(grid,true); + + + + + setTimeout(function () { + mainView.resize(); + grid.resize(); + }, 1000); + + + function test() { + doGridTest(grid,blockScope); + return; + + } + + function test2() { + return; + } + + setTimeout(function () { + test(); + }, 1000); + + + setTimeout(function () { + test2(); + }, 2000); + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestBlockGridClassLineNumber.js b/packages/xblox/ref-control-freak/xblox/tests/TestBlockGridClassLineNumber.js new file mode 100644 index 00000000..f089302d --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestBlockGridClassLineNumber.js @@ -0,0 +1,256 @@ +/** @module xgrid/Base **/ +define([ + 'xdojo/declare', + 'xide/utils', + 'xgrid/Grid', + 'xblox/views/BlockGrid', + 'xide/tests/TestUtils', + './TestUtils', + 'module' + + + +], function (declare,utils, Grid, BlockGrid, + TestUtils,BlockUtils,module + ) { + + + /*** + * playground + */ + var ctx = window.sctx; + + console.clear(); + console.log('do grid test2'); + + function doGridTest(grid,scope){ + + + + + grid.refresh().then(function(){ + grid.select([0]).then(function(e){ + //grid.runAction('Step/Run'); + }); + }); + + } + + var did = false; + + + function format(value,block,who){ + + + if(!did){ + var items = block._store.storage.fullData; + items = items.filter(function(item){ + return item.group ==='click'; + }); + console.dir(_.pluck(items,'name')); + console.dir(_.pluck(items,'group')); + did=true; + } + + function index(item,dir){ + + var item = this, + parent = item.getParent(), + items = null, + group = item.group, + store = this._store; + + if (parent) { + items = parent[parent._getContainer(item)]; + items = items.filter(function(item){ + return item.group ===group; + }); + if (!items || items.length < 2 || !this.containsItem(items, item)) { + return false; + } + return this.indexOf(items, item); + + }else{ + items = store.storage.fullData; + items = items.filter(function(item){ + return item.group ===group; + }); + return this.indexOf(items, item); + } + } + + var parent = block.getParent(); + + var _index = index.apply(block,[block,1]); + if(parent){ + var parents =block.numberOfParents(); + var _out=' '; + for (var i = 0; i < parents; i++) { + _out+=' ' + } + _out += ''; + + for (var i = 0; i < parents; i++) { + _out+='-' + } + _out += ' '; + _index=_out + _index; + } + + console.log('index ' + block.name + ' : ' + _index); + + return _index; + } + xide.utils.fff= format; + + + + + if (ctx) { + + var blockManager = ctx.getBlockManager(); + + + var blockScope = BlockUtils.createScope(blockManager,[],[ + { + "_containsChildrenIds": [], + "id": "Shell-Block", + "description": "Runs a JSON-RPC-2.0 method on the server", + "name": "Run Server Method", + "method": "XShell::run", + "args": "ls", + "deferred": true, + "defaultServiceClass": "XShell", + "defaultServiceMethod": "run", + "declaredClass": "xblox.model.server.RunServerMethod", + "enabled": true, + "shareTitle": "", + "canDelete": true, + "order": 0, + "type": "added", + "group": "click" + } + ]); + + var mainView = ctx.mainView; + + + + + + if (mainView) { + + var parent = TestUtils.createTab(null,null,module.id); + var grid, + store = blockScope.blockStore, + + gridArgs = { + ctx:ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect:true, + collection: store.filter({ + group: "click" + }) + + }; + var gridClass = declare('sdfdf ' + new Date().getTime(),BlockGrid,{ + postMixInProperties: function() { + + + var _has = false; + + var self = this; + _.each(this.columns, function (col) { + if (col.field == 'order') { + _has = true; + } + }); + + if (!_has && this.blockGroup !== 'basicVariables') { + + this.columns.unshift({ + //renderExpando: true, + label: "Line", + field: "order", + sortable: false, + formatter: function(val,b){ + return xide.utils.fff(val,b,self); + } + }); + } + this.inherited(arguments); + }, + formatOrder:function(value,block){ + + + + /* + if(block) { + var row = this.row(block); + if (row && row.element) { + return 'rendered'; + } else { + console.log('not rendered yet2'); + return 'asdfsd'; + } + + + + }else{ + console.log('-have no block'); + } + + if(this._map) { + if (this._map[block.id]) { + return this._map[block.id]; + } + }else{ + //console.log('have no map'); + } + */ + return '0'; + + } + }); + + + grid = utils.addWidget(gridClass,gridArgs,null,parent,true); + + + ctx.getWindowManager().registerView(grid,true); + + + + + setTimeout(function () { + mainView.resize(); + grid.resize(); + }, 1000); + + + function test() { + doGridTest(grid,blockScope); + return; + + } + + function test2() { + return; + } + + setTimeout(function () { + test(); + }, 1000); + + + setTimeout(function () { + test2(); + }, 2000); + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestBlocksFileEditor.js b/packages/xblox/ref-control-freak/xblox/tests/TestBlocksFileEditor.js new file mode 100644 index 00000000..d1653a6e --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestBlocksFileEditor.js @@ -0,0 +1,959 @@ +define([ + 'require', + 'xdojo/has', + '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', + 'xdocker/Docker2', + 'xide/tests/TestUtils', + 'xblox/tests/TestUtils', + 'module' +], function (require,has,dcl,lang,declare, factory, utils, Variable, Deferred,_CIDialog,types,_LayoutMixin,BlockGrid,ActionProvider,_TabContainer,Container,Docker, + TestUtils,BTestUtils,module) { + + var ACTION = types.ACTION; + console.clear(); + + + /** + * the layouter + */ + var LayoutClass = dcl(_LayoutMixin.dcl,{ + rootPanel:null, + _lastTab:null, + getDocker:function(){ + if(!this._docker){ + this._docker = Docker.createDefault(this.containerNode); + this.add(this._docker); + } + return this._docker; + }, + getRootContainer:function(args){ + + if(this.rootPanel){ + return this.rootPanel; + } + + this.reparent = true; + var docker = this.getDocker(); + var DOCKER = types.DOCKER; + var defaultTabArgs = { + icon:false, + closeable:true, + moveable:true, + tabOrientation:DOCKER.TAB.TOP, + location:DOCKER.DOCK.STACKED + + }; + this.rootPanel = docker.addTab(null,utils.mixin(defaultTabArgs,args)); + return this.rootPanel; + }, + createLayout:function(){ + if(!this.rootPanel) { + this.rootPanel = this.getRootContainer({ + title: 'test' + }); + } + return this.rootPanel; + }, + startup:function(){ + + }, + getPropertyStruct:function(){ + return this.propertyStruct; + }, + setPropertyStruct:function(struct){ + this.propertyStruct = struct; + }, + getGroupContainer: function () { + return this.rootPanel; + }, + createGroupView: function (groupContainer, group,scope,closable,iconClass,selected,args) { + var DOCKER = types.DOCKER; + var defaultTabArgs = { + icon:iconClass, + closeable:true, + moveable:true, + title:group, + target:this._lastTab||this.rootPanel, + tabOrientation:DOCKER.TAB.TOP, + location:DOCKER.DOCK.STACKED + + }; + + var tab = this.getDocker().addTab(null,utils.mixin(defaultTabArgs,args)); + + this._lastTab = tab; + + /* + 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; + } + }); + 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,LayoutClass,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, + registerGrids:true, + visibileTab:null, + setVisibleTab:function(tab){ + this.visibileTab = tab; + }, + getVisibleTab:function(){ + return this.visibileTab; + }, + 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'); + console.log('add group',contentPane); + /* + 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; + + if(this._lastTab && !this.__right){ + this.__right = this.getRightPanel(null,null,'DefaultFixed',{ + target:this._lastTab + }); + } + var gridArgs = { + __right:this.__right, + ctx:this.ctx, + blockScope: scope, + blockGroup: group, + attachDirect:true, + resizeToParent:true, + collection: store.filter({ + group: group + }), + _parent:container, + getPropertyStruct:this.getPropertyStruct, + setPropertyStruct:this.setPropertyStruct + }; + + extra && lang.mixin(gridArgs, extra); + var view = utils.addWidget(gridBaseClass,gridArgs,null,container,false); + + if(!view.__editorActions){ + view.__editorActions=true; + this.addGridActions(view,view); + } + container.grid = view; + view._on('selectionChanged',function(evt){ + thiz._emit('selectionChanged',evt); + }); + + view._on('onAfterAction',function(e){ + thiz.onGridAction(e); + }); + container.on(types.DOCKER.EVENT.VISIBILITY_CHANGED, function (visible) { + if(visible){ + thiz.setVisibleTab(container); + view.onShow(); + var right = thiz.getRightPanel(); + var splitter = right.getSplitter(); + if (!splitter.isCollapsed()) { + view.showProperties(view.getSelection()[0]); + } + if(view.__last){ + view._restoreSelection(); + view.focus(view.row(view.__last.focused)); + } + }else{ + view.__last = view._preserveSelection(); + } + if(visible && !this.grid._started){} + }); + this.registerGrids && this.ctx.getWindowManager().registerView(view,true); + if(!container._widgets){ + container._widgets=[]; + } + container.add(view); + return view; + }, + getDeviceVariablesAsEventOptions:function(startIntend){ + var options = []; + var _item = function(label,value,intend,selected,displayValue){ + + var string="" +label + ""; + var pre = ""; + if(intend>0){ + for (var i = 0; i < intend; i++) { + pre+=" "; + pre+=" "; + pre+=" "; + } + } + 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 ? "" +label + "" : label; + var pre = ""; + if(intend>0){ + for (var i = 0; i < intend; i++) { + pre+=" "; + pre+=" "; + pre+=" "; + } + } + 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,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                        ', + 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; + }, + 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); + */ + }, + renderGroup:function(group,blockScope,gridBaseClass){ + blockScope = blockScope || this.blockScope; + var 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(null, 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, group, blockScope, gridViewConstructurArgs,gridBaseClass); + contentPane.blockView = view; + !this.activeGrid && (this.activeGrid=view); + return view; + }, + renderGroups: function (_array, blockScope) { + this._isCreating = true; + this.activeGrid = null; + + this.setPropertyStruct({ + currentCIView:null, + targetTop:null, + _lastItem:null, + id:utils.createUUID() + }); + 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); + } + } + + if(this._lastTab) { + this._lastTab.select(); + } + //this.onShowGrid() + }, + onSave: function (groupedBlockView) { + this.save(); + }, + save: function () { + if (this.blockScope) { + 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'); + } + } + }); + + var ctx = window.sctx; + var mainView = ctx.mainView; + + + if (mainView) { + var blockManager = ctx.getBlockManager(); + var blockScope = BTestUtils.createScope(blockManager,null); + var parent = TestUtils.createTab('BlockGrid-Test', null, module.id); + var editor = utils.addWidget(Module,{ + blockManager:blockManager, + blockScope:blockScope, + gridClass:GridClass, + ctx:ctx + + },null,parent,true); + editor.initWithScope(blockScope); + + } + + + return Module; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestClass.js b/packages/xblox/ref-control-freak/xblox/tests/TestClass.js new file mode 100644 index 00000000..b9b7ec83 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestClass.js @@ -0,0 +1,1337 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + 'dojo/dom-class', + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/ThumbRenderer', + 'xide/views/_ActionMixin', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dijit/form/RadioButton', + 'xide/widgets/Ribbon', + 'xide/editor/Registry', + 'xaction/DefaultActions', + 'xaction/Action', + "xblox/widgets/BlockGridRowEditor", + + 'dgrid/Editor', + + + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + + 'xide/factory', + + + 'dijit/Menu', + + 'xide/data/Reference', + + 'dijit/form/DropDownButton', + 'dijit/MenuItem', + + 'xdocker/Docker2', + + "xide/views/CIViewMixin", + + 'xide/layout/TabContainer', + + + "dojo/has!host-browser?xblox/views/BlockEditDialog", + + 'xblox/views/BlockGrid', + + 'xgrid/DnD', + 'xblox/views/BlocksGridDndSource', + 'xblox/widgets/DojoDndMixin', + + + 'xide/registry', + + 'dojo/topic', + 'xide/tests/TestUtils', + './TestUtils', + 'module' + + +], function (declare, domClass,types, + utils, ListRenderer, TreeRenderer, ThumbRenderer, + _ActionMixin, + Grid, MultiRenderer, RadioButton, Ribbon, Registry, DefaultActions, Action, BlockGridRowEditor, + Editor,Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, factory,Menu,Reference,DropDownButton, + MenuItem,Docker,CIViewMixin,TabContainer, + + BlockEditDialog, + BlockGrid, + Dnd,BlocksGridDndSource,DojoDndMixin, + registry,topic,TestUtils,BlockUtils,module + ) { + + + + /** + * + */ + var _layoutMixin = { + _docker:null, + _parent:null, + __right:null, + getDocker:function(container){ + + if(!this._docker){ + + var _dst = container || this._domNode.parentNode, + thiz = this; + + thiz._docker = Docker.createDefault(_dst); + thiz._oldParent = thiz._parent; + + var parent = thiz._docker.addPanel('DefaultFixed', types.DOCKER.TOP, null, { + w: '100%', + title:' ' + }); + + dojo.place(thiz._domNode,parent.containerNode); + + thiz._docker.$container.css('top',0); + thiz._parent = parent; + + parent._parent.showTitlebar(false); + + +/* + var right = this._docker.addPanel('Collapsible', types.DOCKER.RIGHT, parent, { + w: '20%', + title:'Properties' + }); + + parent._parent.showTitlebar(false);*/ + + + + + + //parent._parent.$center.css('top',0); + + } + + return thiz._docker; + }, + getRightPanel:function(){ + + if(this.__right){ + return this.__right; + } + + var docker = this.getDocker(); + + + var right = docker.addPanel('Collapsible', types.DOCKER.RIGHT, this._parent, { + w: '300px', + title:' ' + }); + + + + right._parent.showTitlebar(false); + + var splitter = right.getSplitter(); + + splitter.pos(0.6); + + + this.__right = right; + + return right; + } + }; + + var DojoDndMixin = declare("xblox.widgets.DojoDndMixin",null,{ + dropEvent:"/dnd/drop", + dragEvent:"/dnd/start", + overEvent:"/dnd/source/over", + _eDrop:false, + isDragging:false, + didInit:false, + /*** + * Pre-Process DND Events + * @param node + * @param targetArea + * @param indexChild + */ + _calcNewItemList:function(items){ + var res = []; + if(items){ + + for(var i=0 ; i move nodes! + this.onDrop(source, nodes, copy, target); + } + this.onDndCancel(); + } + }); + + function getFileActions(permissions) { + + + + var result = [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + VISIBILITY = types.ACTION_VISIBILITY, + thiz = this, + actionStore = thiz.getActionStore(); + + + return []; + + function addAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable) { + + var action = null; + if (DefaultActions.hasAction(permissions, command)) { + + mixin = mixin || {}; + + utils.mixin(mixin, {owner: thiz}); + + if (!handler) { + + handler = function (action) { + console.log('log run action', arguments); + var who = this; + if (who.runAction) { + who.runAction.apply(who, [action]); + } + } + } + action = DefaultActions.createAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable, thiz.domNode); + + result.push(action); + return action; + + } + } + + /* + var rootAction = 'Block/Insert'; + permissions.push(rootAction); + addAction('Block', rootAction, 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + dummy: true, + onCreate: function (action) { + action.setVisibility(VISIBILITY.CONTEXT_MENU, { + label: 'Add' + }); + + } + }, null, null); + permissions.push('Block/Insert Variable'); + + + addAction('Variable', 'Block/Insert Variable', 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + }, null, null); + */ + + /* + permissions.push('Clipboard/Paste/New'); + addAction('New ', 'Clipboard/Paste/New', 'el-icon-plus-sign', null, 'Home', 'Clipboard', 'item|view', null, null, { + }, null, null);*/ + + + var newBlockActions = this.getAddActions(); + var addActions = []; + var levelName = ''; + + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + permissions.push(path); + + addAction(levelName, path, item.iconClass, null, 'Home', 'Insert', 'item|view', null, null, {}, null, null); + + + if (isContainer) { + addItems(path, item.items); + } + + + } + + } + //console.clear(); + //addItems(rootAction, newBlockActions); + //return result; + + + //run + function canMove(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + var canMove = item.canMove(item, this.command === 'Step/Move Up' ? -1 : 1); + + return !canMove; + + } + + + function canParent(selection, reference, visibility) { + + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + if(!item){ + console.warn('bad item',selection); + return false; + } + + if(this.command === 'Step/Move Left'){ + return !item.getParent(); + }else{ + return item.getParent(); + } + /* + var canMove = item.canMove(item, this.command === 'Step/Move Left' ? -1 : 1); + return !canMove;*/ + + return true; + + } + + function isItem(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + return false; + + } + + /** + * run + */ + + addAction('Run', 'Step/Run', 'el-icon-play', ['space'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + widgetArgs:{ + label: ' ', + style:'font-size:25px!important;' + } + }); + + } + }, null, isItem); + permissions.push('Step/Run/From here'); + + /** + * run + */ + + addAction('Run from here', 'Step/Run/From here', 'el-icon-play', ['ctrl space'], 'Home', 'Step', 'item', null, null, { + + onCreate: function (action) { + + } + }, null, isItem); + + + + /** + * move + */ + + addAction('Move Up', 'Step/Move Up', 'fa-arrow-up', ['alt up'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canMove); + + + addAction('Move Down', 'Step/Move Down', 'fa-arrow-down', ['alt down'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + } + }, null, canMove); + /* + + + permissions.push('Step/Edit'); + addAction('Edit', 'Step/Edit', ACTION_ICON.EDIT, ['f4', 'enter'], 'Home', 'Step', 'item', null, null, null, null, isItem); + */ + /////////////////////////////////////////////////// + // + // Editors + // + /////////////////////////////////////////////////// + + permissions.push('Step/Move Left'); + permissions.push('Step/Move Right'); + + addAction('Move Left', 'Step/Move Left', 'fa-arrow-left', ['alt left'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canParent); + + addAction('Move Right', 'Step/Move Right', 'fa-arrow-right', ['alt right'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + + } + }, null, canParent); + + + return result; + + } + + function doPost(view){ + return; + + var right = view.getRightPanel(); + var splitter = right.getSplitter(); + + //var ori = splitter.orientation(); + //splitter.orientation(false); + //spl + + //splitter.collapse(); + //splitter.expand(); + splitter.minimize = function(){ + + this._isToggledMin = true; + + //save pos + if(this._isToggledMin){ + this._savedPosMin = this.pos(); + } + + var togglePosValue = this._isToggledMin ? 1 : 0; + + if(togglePosValue==1){ + //togglePosValue = this._savedPosMin; + } + + + //console.log('toggle ' + togglePosValue + ' cvalue ' + this.pos()); + + + this.pos(togglePosValue); + + } + //splitter.minimize(); + + } + + function clipboardTest(){ + + this.select(['root2'],null,true,{ + focus:true + }); + + var actionStore = this.getActionStore(); + + var copy = actionStore.getSync('Clipboard/Copy'); + var paste = actionStore.getSync('Clipboard/Paste'); + + console.clear(); + + + this.runAction(copy); + this.runAction(paste); + + + + //this.refresh(); + + + } + + function unparent(){ + + this.select(['sub0'],null,true,{ + focus:true + }); + this.runAction('Step/Move Left'); + this.refresh(); + } + + function reparent(){ + + this.select(['root3'],null,true,{ + focus:true + }); + this.runAction('Step/Move Right'); + this.refresh(); + } + + function expand(){ + + + this.select(['root3'],null,true,{ + focus:true + }); + + //clipboardTest.apply(this); + + //reparent.apply(this); + + + return; + + + + //var row = grid.row('root'); + //var _t = this._normalize('root'); + //this.expand(_t); + //var _expanded = this.isExpanded(_t); + //debugger; + //this.isRendered('root'); + var store = this.collection; + //var item = store.getSync('sub0'); + //this._expandTo(item); + + var root = this.collection.getSync('click'); + + + this.select(['root2','root3'],null,true,{ + focus:true + }); + + + var actionStore = this.getActionStore(); + + var moveLeft = actionStore.getSync('Step/Move Left'); + + + var moveUp = actionStore.getSync('Step/Move Up'); + + var moveDown = actionStore.getSync('Step/Move Down'); + + var items = this.collection.storage.fullData; + //console.log('before move',items); + //this.printRootOrder(); + this.runAction(moveUp); + //console.log('after move',this.collection.storage.fullData); + //this._place('root','root2','below'); + //this.printRootOrder(); + + + var thiz = this; + + setTimeout(function(){ + + //thiz.refresh(); + //this.runAction(moveDown); + //thiz.refreshRoot(); + thiz.printRootOrder(); + },1500); + + //this.runAction(moveLeft); + } + + + + var _gridBase = { + startup:function(){ + + this.inherited(arguments); + + this._on('selectionChanged',function(evt){ + + var selection = evt.selection; + return; + }) + } + }; + + /*** + * playground + */ + var _lastGrid = window._lastGrid; + var ctx = window.sctx, + ACTION = types.ACTION, + root; + + function fixScope(scope){ + + /** + * + * @param source + * @param target + * @param before + * @param add: comes from 'hover' state + * @returns {boolean} + */ + scope.moveTo = function(source,target,before,add){ + + console.log('scope::move, add: ' +add,arguments); + + if(!add){ + debugger; + } + /** + * treat first the special case of adding an item + */ + if(add){ + + //remove it from the source parent and re-parent the source + if(target.canAdd && target.canAdd()){ + + var sourceParent = this.getBlockById(source.parentId); + if(sourceParent){ + sourceParent.removeBlock(source,false); + } + target.add(source,null,null); + return; + }else{ + console.error('cant reparent'); + return false; + } + } + + + //for root level move + if(!target.parentId && add==false){ + + //console.error('root level move'); + + //if source is part of something, we remove it + var sourceParent = this.getBlockById(source.parentId); + if(sourceParent && sourceParent.removeBlock){ + sourceParent.removeBlock(source,false); + source.parentId=null; + source.group=target.group; + } + + var itemsToBeMoved=[]; + var groupItems = this.getBlocks({ + group:target.group + }); + + var rootLevelIndex=[]; + var store = this.getBlockStore(); + + var sourceIndex = store.storage.index[source.id]; + var targetIndex = store.storage.index[target.id]; + for(var i = 0; i= targetIndex : itemIndex <= targetIndex; + if(add){ + itemsToBeMoved.push(groupItems[i]); + rootLevelIndex.push(store.storage.index[groupItems[i].id]); + } + } + } + + //remove them the store + for(var j = 0; j cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before ==true ? -1 : 1))); + for(var i = 0 ; i < distance -1; i++){ + parent.move(source,direction); + } + return true; + + // we move within the different parents + }else if( source.parentId && target.parentId && add==false && source.parentId !== target.parentId){ console.log('same parent!'); + + console.error('we move within the different parents'); + //collect data + + var sourceParent = this.getBlockById(source.parentId); + if(!sourceParent){ + console.error(' couldnt find source parent '); + return false; + } + + var targetParent = this.getBlockById(target.parentId); + if(!targetParent){ + console.error(' couldnt find target parent '); + return false; + } + + + //remove it from the source parent and re-parent the source + if(sourceParent && sourceParent.removeBlock && targetParent.canAdd && targetParent.canAdd()){ + sourceParent.removeBlock(source,false); + targetParent.add(source,null,null); + }else{ + console.error('cant reparent'); + return false; + } + + //now proceed as in the case above : same parents + var items = targetParent[targetParent._getContainer(source)]; + if(items==null){ + console.error('weird : target parent has no item container'); + } + var cIndexSource = targetParent.indexOf(items,source); + var cIndexTarget = targetParent.indexOf(items,target); + if(!cIndexSource || !cIndexTarget){ + console.error(' weird : invalid drop processing state, have no valid item indicies'); + return; + } + var direction = cIndexSource > cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before ==true ? -1 : 1))); + for(var i = 0 ; i < distance -1; i++){ + targetParent.move(source,direction); + } + return true; + } + + return false; + }; + + return scope; + + var topLevelBlocks = []; + var blocks = scope.getBlocks({ + parentId:null + }); + + + var grouped = _.groupBy(blocks,function(block){ + return block.group; + }); + + function createDummyBlock(id,scope){ + + var block = { + "_containsChildrenIds": [ + "items" + ], + "group": null, + "id": id, + "items": [ + + ], + "name": id, + "method": "----group block ----", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": false, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + + }; + + return scope.blockFromJson(block); + + } + + for(var group in grouped){ + + var groupBlock = createDummyBlock(group,scope); + var blocks = grouped[group]; + _.each(blocks,function(block){ + groupBlock['items'].push(block); + + + + if(!block.parentId && block.group /*&& block.id !== group*/) { + block.parent = groupBlock; + block.parentId = groupBlock.id; + } + }); + } + + console.clear(); + var root = scope.getBlockById('root'); + //console.dir(root.getParent()); + + return scope; + } + + function createScope() { + + var data = { + "blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + + { + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + } + ], + "variables": [] + }; + + return fixScope(blockManager.toScope(data)); + } + + if (ctx) { + + + var blockManager = ctx.getBlockManager(); + + function createGridClass() { + + var renderers = [TreeRenderer]; + //, ThumbRenderer, TreeRenderer + + var multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + + + var _gridClass = Grid.createGridClass('driverTreeView', + { + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + //CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + //TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + SPLIT:{ + CLASS:declare('splitMixin',null,_layoutMixin) + }, + DND:{ + CLASS:Dnd//declare('splitMixin',Dnd,_dndMixin) + } + + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + EDITOR: Editor, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + + + + return declare('gridFinal', BlockGrid, _gridBase); + + //return BlockGrid; + } + var blockScope = createScope('docs'); + var mainView = ctx.mainView; + if (mainView) { + var parent = TestUtils.createTab('BlockGrid-Test',null,module.id); + + + var actions = [], + thiz = this, + ACTION_TYPE = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + grid, + ribbon; + + var store = blockScope.blockStore; + + + var _gridClass = createGridClass(); + + + var gridArgs = { + ctx:ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect:true, + collection: store.filter({ + group: "click" + }), + //dndConstructor: SharedDndGridSource, + //dndConstructor:Dnd.GridSource, + dndParams: { + allowNested: true, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return true;//source !== this; // Don't self-accept. + }, + isSource: true + } + }; + + + grid = utils.addWidget(_gridClass,gridArgs,null,parent,true); + + var blocks = blockScope.allBlocks(); + + var root = blocks[0]; + + var actionStore = grid.getActionStore(); + var toolbar = mainView.getToolbar(); + var _defaultActions = []; + _defaultActions = _defaultActions.concat(getFileActions.apply(grid, [grid.permissions])); + grid.addActions(_defaultActions); + + + + if (!toolbar) { + + + } else { + toolbar.addActionEmitter(grid); + toolbar.setActionEmitter(grid); + } + + doPost(grid); + + setTimeout(function () { + mainView.resize(); + grid.resize(); + }, 1000); + + + function test() { + return; + + } + + function test2() { + return; + } + + setTimeout(function () { + test(); + }, 1000); + + + setTimeout(function () { + test2(); + }, 2000); + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestClass2.js b/packages/xblox/ref-control-freak/xblox/tests/TestClass2.js new file mode 100644 index 00000000..309c6d24 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestClass2.js @@ -0,0 +1,374 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dgrid/Editor', + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + 'xdocker/Docker2', + 'xblox/views/BlockGrid', + 'xide/tests/TestUtils', + 'module' + +], function (declare, types, + utils, ListRenderer, TreeRenderer, Grid, MultiRenderer, Editor,Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, Docker,BlockGrid, + TestUtils,module + ) { + var _layoutMixin = { + _docker:null, + _parent:null, + __right:null, + getDocker:function(container){ + + if(!this._docker){ + + var _dst = container || this._domNode.parentNode, + thiz = this; + + thiz._docker = Docker.createDefault(_dst); + thiz._oldParent = thiz._parent; + + var parent = thiz._docker.addPanel('DefaultFixed', types.DOCKER.TOP, null, { + w: '100%', + title:' ' + }); + + dojo.place(thiz._domNode,parent.containerNode); + + thiz._docker.$container.css('top',0); + thiz._parent = parent; + + parent._parent.showTitlebar(false); + + +/* + var right = this._docker.addPanel('Collapsible', types.DOCKER.RIGHT, parent, { + w: '20%', + title:'Properties' + }); + + parent._parent.showTitlebar(false);*/ + + + + + + //parent._parent.$center.css('top',0); + + } + + return thiz._docker; + }, + getRightPanel:function(){ + + if(this.__right){ + return this.__right; + } + + var docker = this.getDocker(); + + + var right = docker.addPanel('Collapsible', types.DOCKER.RIGHT, this._parent, { + w: '300px', + title:' ' + }); + + + + right._parent.showTitlebar(false); + + var splitter = right.getSplitter(); + + splitter.pos(0.6); + + + this.__right = right; + + return right; + } + }; + + var _gridBase = { + + getTypeMap:function(){ + + }, + startup:function(){ + this.inherited(arguments); + this._on('selectionChanged',function(evt){ + + var selection = evt.selection; + return; + }) + } + }; + + /*** + * playground + */ + var ctx = window.sctx, + root; + + function fixScope(scope){ + /** + * + * @param source + * @param target + * @param before + * @param add: comes from 'hover' state + * @returns {boolean} + */ + + + return scope; + } + + function createScope() { + + var data = { + "blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + + { + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + } + ], + "variables": [] + }; + + return fixScope(blockManager.toScope(data)); + } + + if (ctx) { + + + var blockManager = ctx.getBlockManager(); + + function createGridClass() { + + var renderers = [TreeRenderer]; + //, ThumbRenderer, TreeRenderer + + var multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + var _gridClass = Grid.createGridClass('driverTreeView',{ + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + //CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + //TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + SPLIT:{ + CLASS:declare('splitMixin',null,_layoutMixin) + } + + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + EDITOR: Editor, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + return declare('gridFinal', BlockGrid, _gridBase); + } + + + var blockScope = createScope('docs'); + + var mainView = ctx.mainView; + + if (mainView) { + + var parent = TestUtils.createTab('BlockGrid-Test',null,module.id); + + + var thiz = this, + grid; + var store = blockScope.blockStore; + var _gridClass = createGridClass(); + var gridArgs = { + ctx:ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect:true, + collection: store.filter({ + group: "click" + }) + }; + + + grid = utils.addWidget(_gridClass,gridArgs,null,parent,true); + + var blocks = blockScope.allBlocks(); + + setTimeout(function () { + mainView.resize(); + grid.resize(); + }, 1000); + + + function test() { + return; + + } + + function test2() { + return; + } + + setTimeout(function () { + test(); + }, 1000); + + + setTimeout(function () { + test2(); + }, 2000); + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestCopyPaste.js b/packages/xblox/ref-control-freak/xblox/tests/TestCopyPaste.js new file mode 100644 index 00000000..778a394e --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestCopyPaste.js @@ -0,0 +1,1294 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'xaction/DefaultActions', + 'dgrid/Editor', + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + 'xdocker/Docker2', + 'xblox/views/BlockGrid', + 'xgrid/DnD', + 'xblox/views/BlocksGridDndSource', + 'xblox/widgets/DojoDndMixin', + 'xide/registry', + 'dojo/topic', + 'xide/tests/TestUtils', + 'module' + +], function (declare, types, + utils, ListRenderer, TreeRenderer, Grid, MultiRenderer, DefaultActions, Editor, Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, Docker, BlockGrid, + Dnd, BlocksGridDndSource, DojoDndMixin, + registry, topic, TestUtils, module) { + + + /** + * + */ + var _layoutMixin = { + _docker: null, + _parent: null, + __right: null, + getDocker: function (container) { + + if (!this._docker) { + + var _dst = container || this._domNode.parentNode, + thiz = this; + + thiz._docker = Docker.createDefault(_dst); + thiz._oldParent = thiz._parent; + + var parent = thiz._docker.addPanel('DefaultFixed', types.DOCKER.TOP, null, { + w: '100%', + title: ' ' + }); + + dojo.place(thiz._domNode, parent.containerNode); + + thiz._docker.$container.css('top', 0); + thiz._parent = parent; + + parent._parent.showTitlebar(false); + + + /* + var right = this._docker.addPanel('Collapsible', types.DOCKER.RIGHT, parent, { + w: '20%', + title:'Properties' + }); + + parent._parent.showTitlebar(false);*/ + + + //parent._parent.$center.css('top',0); + + } + + return thiz._docker; + }, + getRightPanel: function () { + + if (this.__right) { + return this.__right; + } + + var docker = this.getDocker(); + + + var right = docker.addPanel('Collapsible', types.DOCKER.RIGHT, this._parent, { + w: '300px', + title: ' ' + }); + + + right._parent.showTitlebar(false); + + var splitter = right.getSplitter(); + + splitter.pos(0.6); + + + this.__right = right; + + return right; + } + }; + + function getFileActions(permissions) { + + + var result = [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + VISIBILITY = types.ACTION_VISIBILITY, + thiz = this, + actionStore = thiz.getActionStore(); + + + return []; + + function addAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable) { + + var action = null; + if (DefaultActions.hasAction(permissions, command)) { + + mixin = mixin || {}; + + utils.mixin(mixin, {owner: thiz}); + + if (!handler) { + + handler = function (action) { + console.log('log run action', arguments); + var who = this; + if (who.runAction) { + who.runAction.apply(who, [action]); + } + } + } + action = DefaultActions.createAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable, thiz.domNode); + + result.push(action); + return action; + + } + } + + /* + var rootAction = 'Block/Insert'; + permissions.push(rootAction); + addAction('Block', rootAction, 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + dummy: true, + onCreate: function (action) { + action.setVisibility(VISIBILITY.CONTEXT_MENU, { + label: 'Add' + }); + + } + }, null, null); + permissions.push('Block/Insert Variable'); + + + addAction('Variable', 'Block/Insert Variable', 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + }, null, null); + */ + + /* + permissions.push('Clipboard/Paste/New'); + addAction('New ', 'Clipboard/Paste/New', 'el-icon-plus-sign', null, 'Home', 'Clipboard', 'item|view', null, null, { + }, null, null);*/ + + + var newBlockActions = this.getAddActions(); + var addActions = []; + var levelName = ''; + + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + permissions.push(path); + + addAction(levelName, path, item.iconClass, null, 'Home', 'Insert', 'item|view', null, null, {}, null, null); + + + if (isContainer) { + addItems(path, item.items); + } + + + } + + } + + //console.clear(); + //addItems(rootAction, newBlockActions); + //return result; + + + //run + function canMove(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + var canMove = item.canMove(item, this.command === 'Step/Move Up' ? -1 : 1); + + return !canMove; + + } + + + function canParent(selection, reference, visibility) { + + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + if (!item) { + console.warn('bad item', selection); + return false; + } + + if (this.command === 'Step/Move Left') { + return !item.getParent(); + } else { + return item.getParent(); + } + /* + var canMove = item.canMove(item, this.command === 'Step/Move Left' ? -1 : 1); + return !canMove;*/ + + return true; + + } + + function isItem(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + return false; + + } + + /** + * run + */ + + addAction('Run', 'Step/Run', 'el-icon-play', ['space'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + widgetArgs: { + label: ' ', + style: 'font-size:25px!important;' + } + }); + + } + }, null, isItem); + permissions.push('Step/Run/From here'); + + /** + * run + */ + + addAction('Run from here', 'Step/Run/From here', 'el-icon-play', ['ctrl space'], 'Home', 'Step', 'item', null, null, { + + onCreate: function (action) { + + } + }, null, isItem); + + + /** + * move + */ + + addAction('Move Up', 'Step/Move Up', 'fa-arrow-up', ['alt up'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canMove); + + + addAction('Move Down', 'Step/Move Down', 'fa-arrow-down', ['alt down'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + } + }, null, canMove); + /* + + + permissions.push('Step/Edit'); + addAction('Edit', 'Step/Edit', ACTION_ICON.EDIT, ['f4', 'enter'], 'Home', 'Step', 'item', null, null, null, null, isItem); + */ + /////////////////////////////////////////////////// + // + // Editors + // + /////////////////////////////////////////////////// + + permissions.push('Step/Move Left'); + permissions.push('Step/Move Right'); + + addAction('Move Left', 'Step/Move Left', 'fa-arrow-left', ['alt left'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canParent); + + addAction('Move Right', 'Step/Move Right', 'fa-arrow-right', ['alt right'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + + } + }, null, canParent); + + + return result; + + } + + function clipboardTest() { + + + /* + this.select(['root2'], null, true, { + focus: true, + expand:false + }); + */ + + this._on('selectionChanged',function(e){ + + + var selection = e.selection; + if(selection && selection[0]) { + + + var item = selection[0]; + var scope = item.scope; + + var targetParent = scope.getBlockById(item.parentId) || {}; + +// console.log('selected '+item.id,item); + console.log('selected ' + item.name + ' belongs to ' + targetParent.name + ' | ' + item.id +' == ' + targetParent.id); + var items = item.items; + //console.log('items', items); + + + } + + }); + + this.refresh(); + + var actionStore = this.getActionStore(); + var copy = actionStore.getSync(types.ACTION.CLIPBOARD_COPY); + var paste = actionStore.getSync(types.ACTION.CLIPBOARD_PASTE); + + //console.clear(); + + return; + + this.runAction(copy); + this.runAction(paste); + + } + + function unparent() { + + this.select(['sub0'], null, true, { + focus: true + }); + this.runAction('Step/Move Left'); + this.refresh(); + } + + function reparent() { + + this.select(['root3'], null, true, { + focus: true + }); + this.runAction('Step/Move Right'); + this.refresh(); + } + + function expand() { + + + this.select(['root3'], null, true, { + focus: true + }); + + //clipboardTest.apply(this); + + //reparent.apply(this); + + + return; + + + //var row = grid.row('root'); + //var _t = this._normalize('root'); + //this.expand(_t); + //var _expanded = this.isExpanded(_t); + //debugger; + //this.isRendered('root'); + var store = this.collection; + //var item = store.getSync('sub0'); + //this._expandTo(item); + + var root = this.collection.getSync('click'); + + + this.select(['root2', 'root3'], null, true, { + focus: true + }); + + + var actionStore = this.getActionStore(); + + var moveLeft = actionStore.getSync('Step/Move Left'); + + + var moveUp = actionStore.getSync('Step/Move Up'); + + var moveDown = actionStore.getSync('Step/Move Down'); + + var items = this.collection.storage.fullData; + //console.log('before move',items); + //this.printRootOrder(); + this.runAction(moveUp); + //console.log('after move',this.collection.storage.fullData); + //this._place('root','root2','below'); + //this.printRootOrder(); + + + var thiz = this; + + setTimeout(function () { + + //thiz.refresh(); + //this.runAction(moveDown); + //thiz.refreshRoot(); + thiz.printRootOrder(); + }, 1500); + + //this.runAction(moveLeft); + } + + + var blocks = [{"_containsChildrenIds":[],"group":"click4","id":"root4","description":"Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
","name":"Root - 4","method":"console.log(this);","args":"","deferred":false,"declaredClass":"xblox.model.code.RunScript","enabled":true,"shareTitle":"","order":0,"icon":"fa-code","outlet":0,"type":"added"},{"_containsChildrenIds":["elseIfBlocks"],"group":"click","condition":"[value]=='PW'","id":"bbb6f653-3c92-0b0d-2ed2-e952103680a7","elseIfBlocks":["ac0e58b2-5016-a150-f228-af93590c2700"],"declaredClass":"xblox.model.logic.IfBlock","name":"if","icon":"","outlet":0,"enabled":true,"shareTitle":"","description":"No Description","order":0,"type":"added"},{"_containsChildrenIds":["items"],"name":"else if","items":["0db30720-1889-81c4-3a88-2b3b3df14a3a"],"dstField":"elseIfBlocks","parentId":"bbb6f653-3c92-0b0d-2ed2-e952103680a7","id":"ac0e58b2-5016-a150-f228-af93590c2700","declaredClass":"xblox.model.logic.ElseIfBlock","condition":"","icon":"","outlet":0,"enabled":true,"shareTitle":"","description":"No Description","order":0,"type":"added"},{"_containsChildrenIds":[],"parentId":"ac0e58b2-5016-a150-f228-af93590c2700","id":"0db30720-1889-81c4-3a88-2b3b3df14a3a","declaredClass":"xblox.model.code.RunScript","name":"Run Script","method":"","args":"","deferred":false,"icon":"fa-code","outlet":0,"enabled":true,"shareTitle":"","order":0,"type":"added","description":"No Description"},{"_containsChildrenIds":[],"group":"click","id":"2879e1f2-d973-ebdf-00ea-a8c3b9fe05eb","declaredClass":"xblox.model.code.RunScript","name":"Run Script","method":"","args":"","deferred":false,"icon":"fa-code","outlet":0,"enabled":true,"shareTitle":"","order":0,"type":"added"}]; + + var _gridBase = { + onDrop: function (source, target, before, grid, targetState, insert) { + + + //debugger; + var ctrArgs = source.ctrArgs || {}; + var proto = source.proto; + + var add = !before == true && target.parentId == null; + var newBlock = null; + var isNewItem = false; + + console.log('grid::onDrop2',arguments); + + //prepare args + if (source.ctrArgs) {//comes from factory + ctrArgs.scope = ctrArgs.scope || target.scope; + ctrArgs.group = ctrArgs.group || target.group; + ctrArgs.parentId = ctrArgs.parentId || target.id; + isNewItem = true; + } + + if (isNewItem) { + //new item at root level + if (target.parentId == null && !insert) { + ctrArgs.parentId = null; + + newBlock = factory.createBlock(proto, ctrArgs);//root block + + } else if (insert && target.canAdd && target.canAdd() != null) {//new item at item level + newBlock = target.add(proto, ctrArgs, null); + } + } else { + + + + //real item move, before or after + if (targetState === 'Moved') { + + if (source.scope && target.scope && source.scope == target.scope) { + + source.group = null; + var moved = target.scope.moveTo(source, target, before, insert); + return source; + } + } + } + }, + paste: function (items, owner, cut) { + + + if (!items) { + return; + } + var target = this.getSelection()[0], + _flatten, + source, + thiz = this, + scope = thiz.blockScope; + + if (!owner) { + return;//@TODO : support + } + + + scope.__flatten = function(blocks){ + var result = []; + for(var b in blocks){ + + var block = blocks[b]; + + if(block.keys==null){ + continue; + } + + var found = _.find(result,{ + id:block.id + }) + + if(found){ + //console.error('already in array : ' +found.name); + }else { + result.push(block); + } + + for(var prop in block){ + if (prop == 'ctrArgs') { + continue; + } + //flatten children to ids. Skip "parent" field + if (prop !== 'parent') { + + var value = block[prop]; + if (this.isBlock(value)){ + // if the field is a single block container, store the child block's id + //result.push(value); + found = _.find(result,{ + id:value.id + }) + if(found){ + //console.error('already in array : ' +value.name); + }else { + result.push(value); + } + } else if (this.areBlocks(value)){ + + //console.log('found sub blocks in ' + prop + ' ' + block.name); + + for(var i = 0; i < value.length ; i++){ + var sBlock = value[i]; + + //result.push(sBlock); + found = _.find(result,{ + id:sBlock.id + }) + + if(found){ + //console.error('already in array : ' +sBlock.name); + }else { + result.push(sBlock); + } + result = result.concat(this.flatten([sBlock])); + } + } + } + } + } + + //console.log('un : ' , _.uniqBy(result, 'id')); + + //console.log('found in total '+result.length + ' blocks ',result); + + result = _.uniq(result,false,function(item){ + return item.id; + }); + + return result; + } + + + + + + //special case when paste on nothing + if (!target) { + //save parentIds + for (var i = 0; i < items.length; i++) { + var obj = items[i]; + if (obj.parentId) { + obj._parentOri = '' + obj.parentId; + obj.parentId = null; + } + } + //flatten them + _flatten = scope.flatten(items); + + var _flat2 = _.uniq(_flatten,false,function(item){ + return item.id + }); + //clone them + var _clones = scope.cloneBlocks2(_flat2, this.newRootItemGroup); + + var _flattenClones = scope.flatten(_clones); + var firstItem = null; + for (var i = 0; i < _clones.length; i++) { + var clone = _clones[i]; + if (!firstItem) { + firstItem = clone; + } + } + //restore parentIds + for (var i = 0; i < items.length; i++) { + var obj = items[i]; + if (obj._parentOri) {//restore + obj.parentId = obj._parentOri; + delete obj['_parentOri']; + } + } + return _clones; + } + + var grid = owner; + + var srcScope = items[0].scope; + var dstScope = target.scope; + if (srcScope != dstScope) { + return; + } + var insert = target.canAdd() || false; + var parent = srcScope.getBlockById(target.parentId); + if (!parent) { + parent = target; + } + var targetState = 'Moved'; + var before = false; + + _flatten = scope.flatten(items); + + _flatten = _.uniq(_flatten,false,function(item){ + return item.id + }); + + items = srcScope.cloneBlocks2(_flatten); + var newItems = []; + + + + + for (var i = 0; i < items.length; i++) { + + source = items[i]; + // + //Case source=target + // + if (source == target) { + console.log('source=target!'); + } + + if(source.parentId){ + newItems.push(source); + }else{ + source.parentId = parent.id; + parent.add(source); + var nItem = this.onDrop(source, target, before, grid, targetState, insert); + newItems.push(nItem); + } + } + return newItems; + } + }; + + /*** + * playground + */ + var _lastGrid = window._lastGrid; + var ctx = window.sctx, + ACTION = types.ACTION, + root; + + function fixScope(scope) { + + /** + * + * @param source + * @param target + * @param before + * @param add: comes from 'hover' state + * @returns {boolean} + */ + scope.moveTo = function (source, target, before, add) { + + console.log('scope::move, add: ' + add, arguments); + + if (!add) { + debugger; + } + /** + * treat first the special case of adding an item + */ + if (add) { + + //remove it from the source parent and re-parent the source + if (target.canAdd && target.canAdd()) { + + var sourceParent = this.getBlockById(source.parentId); + if (sourceParent) { + sourceParent.removeBlock(source, false); + } + target.add(source, null, null); + return; + } else { + console.error('cant reparent'); + return false; + } + } + + + //for root level move + if (!target.parentId && add == false) { + + //console.error('root level move'); + + //if source is part of something, we remove it + var sourceParent = this.getBlockById(source.parentId); + if (sourceParent && sourceParent.removeBlock) { + sourceParent.removeBlock(source, false); + source.parentId = null; + source.group = target.group; + } + + var itemsToBeMoved = []; + var groupItems = this.getBlocks({ + group: target.group + }); + + var rootLevelIndex = []; + var store = this.getBlockStore(); + + var sourceIndex = store.storage.index[source.id]; + var targetIndex = store.storage.index[target.id]; + for (var i = 0; i < groupItems.length; i++) { + + var item = groupItems[i]; + //keep all root-level items + + if (groupItems[i].parentId == null && //must be root + groupItems[i] != source// cant be source + ) { + + var itemIndex = store.storage.index[item.id]; + var add = before ? itemIndex >= targetIndex : itemIndex <= targetIndex; + if (add) { + itemsToBeMoved.push(groupItems[i]); + rootLevelIndex.push(store.storage.index[groupItems[i].id]); + } + } + } + + //remove them the store + for (var j = 0; j < itemsToBeMoved.length; j++) { + store.remove(itemsToBeMoved[j].id); + } + + //remove source + this.getBlockStore().remove(source.id); + + //if before, put source first + if (before) { + this.getBlockStore().putSync(source); + } + + //now place all back + for (var j = 0; j < itemsToBeMoved.length; j++) { + store.put(itemsToBeMoved[j]); + } + + //if after, place source back + if (!before) { + this.getBlockStore().putSync(source); + } + + return true; + + //we move from root to lower item + } else if (!source.parentId && target.parentId && add == false) { + source.group = target.group; + if (target) { + + } + + //we move from root to into root item + } else if (!source.parentId && !target.parentId && add) { + + console.error('we are adding an item into root root item'); + if (target.canAdd && target.canAdd()) { + source.group = null; + target.add(source, null, null); + } + return true; + + // we move within the same parent + } else if (source.parentId && target.parentId && add == false && source.parentId === target.parentId) { + console.error('we move within the same parents'); + var parent = this.getBlockById(source.parentId); + if (!parent) { + console.error(' couldnt find parent '); + return false; + } + + var maxSteps = 20; + var items = parent[parent._getContainer(source)]; + + var cIndexSource = source.indexOf(items, source); + var cIndexTarget = source.indexOf(items, target); + var direction = cIndexSource > cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before == true ? -1 : 1))); + for (var i = 0; i < distance - 1; i++) { + parent.move(source, direction); + } + return true; + + // we move within the different parents + } else if (source.parentId && target.parentId && add == false && source.parentId !== target.parentId) { + console.log('same parent!'); + + console.error('we move within the different parents'); + //collect data + + var sourceParent = this.getBlockById(source.parentId); + if (!sourceParent) { + console.error(' couldnt find source parent '); + return false; + } + + var targetParent = this.getBlockById(target.parentId); + if (!targetParent) { + console.error(' couldnt find target parent '); + return false; + } + + + //remove it from the source parent and re-parent the source + if (sourceParent && sourceParent.removeBlock && targetParent.canAdd && targetParent.canAdd()) { + sourceParent.removeBlock(source, false); + targetParent.add(source, null, null); + } else { + console.error('cant reparent'); + return false; + } + + //now proceed as in the case above : same parents + var items = targetParent[targetParent._getContainer(source)]; + if (items == null) { + console.error('weird : target parent has no item container'); + } + var cIndexSource = targetParent.indexOf(items, source); + var cIndexTarget = targetParent.indexOf(items, target); + if (!cIndexSource || !cIndexTarget) { + console.error(' weird : invalid drop processing state, have no valid item indicies'); + return; + } + var direction = cIndexSource > cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before == true ? -1 : 1))); + for (var i = 0; i < distance - 1; i++) { + targetParent.move(source, direction); + } + return true; + } + + return false; + }; + + return scope; + + var topLevelBlocks = []; + var blocks = scope.getBlocks({ + parentId: null + }); + + + var grouped = _.groupBy(blocks, function (block) { + return block.group; + }); + + function createDummyBlock(id, scope) { + + var block = { + "_containsChildrenIds": [ + "items" + ], + "group": null, + "id": id, + "items": [], + "name": id, + "method": "----group block ----", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": false, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + + }; + + return scope.blockFromJson(block); + + } + + for (var group in grouped) { + + var groupBlock = createDummyBlock(group, scope); + var blocks = grouped[group]; + _.each(blocks, function (block) { + groupBlock['items'].push(block); + + + if (!block.parentId && block.group /*&& block.id !== group*/) { + block.parent = groupBlock; + block.parentId = groupBlock.id; + } + }); + } + + console.clear(); + var root = scope.getBlockById('root'); + //console.dir(root.getParent()); + + return scope; + } + + function createScope() { + + var data = { + "___blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + + { + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + } + ], + "blocksz": [ + { + "_containsChildrenIds": [], + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "shareTitle": "", + "order": 0, + "icon": "fa-code", + "outlet": 0, + "type": "added" + }, { + "_containsChildrenIds": ["items", "consequent", "elseIfBlocks"], + "group": "click", + "condition": "[value]=='PW'", + "id": "29986b6f-fc18-d2b5-0efa-c4aed858b4df", + "items": ["a2077337-2d29-60a9-d222-821f6a59463b"], + "consequent": ["2fed8943-f1bb-9129-ef89-9064832ea6eb"], + "elseIfBlocks": ["a2077337-2d29-60a9-d222-821f6a59463b"], + "declaredClass": "xblox.model.logic.IfBlock", + "autoCreateElse": true, + "name": "if", + "icon": "", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "order": 0, + "type": "added" + }, + { + "_containsChildrenIds": [], + "parentId": "29986b6f-fc18-d2b5-0efa-c4aed858b4df", + "id": "2fed8943-f1bb-9129-ef89-9064832ea6eb", + "declaredClass": "xblox.model.code.RunScript", + "name": "Run Script", + "method": "ELSE-OK", + "args": "", + "deferred": false, + "icon": "fa-code", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "order": 0 + }, { + "_containsChildrenIds": ["items"], + "name": "else if", + "items": ["66b32440-2809-4c30-8552-36508b6805da"], + "dstField": "elseIfBlocks", + "parentId": "29986b6f-fc18-d2b5-0efa-c4aed858b4df", + "id": "a2077337-2d29-60a9-d222-821f6a59463b", + "declaredClass": "xblox.model.logic.ElseIfBlock", + "condition": "", + "icon": "", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "order": 0 + }, { + "_containsChildrenIds": [], + "parentId": "a2077337-2d29-60a9-d222-821f6a59463b", + "id": "66b32440-2809-4c30-8552-36508b6805da", + "declaredClass": "xblox.model.code.RunScript", + "name": "Run Script", + "method": "Else-IF", + "args": "", + "deferred": false, + "icon": "fa-code", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "order": 0, + "type": "added" + }], + blocks: blocks, + "variables": [] + + }; + + + return fixScope(blockManager.toScope(data)); + } + + function createGridClass() { + + var renderers = [TreeRenderer]; + var multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + var _gridClass = Grid.createGridClass('driverTreeView', + { + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + //CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + //TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + SPLIT: { + CLASS: declare('splitMixin', null, _layoutMixin) + } + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + return declare('gridFinal', BlockGrid, _gridBase); + } + + if (ctx) { + + + var blockManager = ctx.getBlockManager(); + + + var blockScope = createScope('docs'); + var mainView = ctx.mainView; + if (mainView) { + var parent = TestUtils.createTab('BlockGrid-Test', null, module.id); + var actions = [], + thiz = this, + ACTION_TYPE = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + grid, + ribbon; + + var store = blockScope.blockStore; + var _gridClass = createGridClass(); + var gridArgs = { + ctx: ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect: true, + collection: store.filter({ + group: "click" + }), + dndParams: { + allowNested: true, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return true;//source !== this; // Don't self-accept. + }, + isSource: true + } + }; + + + grid = utils.addWidget(_gridClass, gridArgs, null, parent, true); + + var blocks = blockScope.allBlocks(); + var root = blocks[0]; + var actionStore = grid.getActionStore(); + var toolbar = mainView.getToolbar(); + var _defaultActions = []; + _defaultActions = _defaultActions.concat(getFileActions.apply(grid, [grid.permissions])); + grid.addActions(_defaultActions); + if (!toolbar) { + } else { + toolbar.addActionEmitter(grid); + toolbar.setActionEmitter(grid); + } + setTimeout(function () { + mainView.resize(); + grid.resize(); + }, 1000); + + setTimeout(function () { + clipboardTest.apply(grid); + }, 1000); + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestDNDClass.js b/packages/xblox/ref-control-freak/xblox/tests/TestDNDClass.js new file mode 100644 index 00000000..3bb53d7d --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestDNDClass.js @@ -0,0 +1,716 @@ +define([ + "xdojo/declare", + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dgrid/Editor', + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + 'xdocker/Docker2', + 'xblox/views/BlockGrid', + 'xide/tests/TestUtils', + 'xblox/BlockActions', + 'xide/views/_LayoutMixin', + "xide/widgets/_Widget", + + 'dojo/dnd/Source', + 'dojo/dnd/Manager', + 'dojo/_base/NodeList', + 'module', + 'dojo/when', + 'dojo/has!touch?dgrid/util/touch', + 'dojo/topic', + 'xdojo/has', + 'dojo/_base/array', + 'dojo/aspect', + 'dojo/dom-class', + 'xide/factory', + 'xgrid/DnD', + 'dojo/Deferred', + 'xide/registry', + 'xblox/views/ThumbRenderer', + 'dojo/dnd/Manager', + 'dojo/dom-geometry', + "dojo/dnd/common", + "xblox/views/DnD" + +], function (declare, types, + utils, ListRenderer, TreeRenderer, Grid, MultiRenderer, Editor, Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, Docker, BlockGrid, + TestUtils, BlockActions, _LayoutMixin, _Widget, DnDSource, + DnDManager, NodeList, module, when, touchUtil, topic, has, arrayUtil, + aspect, domClass, factory, DnD, Deferred, registry, ThumbRenderer, Manager, domGeom, dnd,BlocksDnD) { + + + + + //console.clear(); + /** + * flow - drop + * 1. SharedDndGridSource:onDrop -> + * 1.1. SharedDndGridSource:onDropInternal or SharedDndGridSource:onDropExternal + * 1.2 SharedDndGridSource:onDropInternal + * 1.3 DojoDndMixin#onDrop + * 1.4 Grid#onDrop + */ + //var Source = DnD.GridSource; + + /** + * + * @class module:xblox/views/BlockGridDnDSource + * @extends module:xgrid/DnD/GridDnDSource + */ + var BlockGridDnDSource = declare(DnD.GridSource, { + declaredClass: 'xblox/views/BlockGridDnDSource', + _lastTargetId: null, + _lastTargetCrossCounter: 0, + /** + * Assigns a class to the current target anchor based on "before" status + * @param before {boolean} insert before, if true, after otherwise + * @param center {boolean} + * @param canDrop {boolean} + * @private + */ + _markTargetAnchor: function (before, center, canDrop) { + if (this.current == this.targetAnchor && this.before === before && this.center === center) { + return; + } + if (this.targetAnchor) { + this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After"); + this._removeItemClass(this.targetAnchor, "Disallow"); + this._removeItemClass(this.targetAnchor, "Center"); + } + this.targetAnchor = this.current; + this.targetBox = null; + this.before = before; + this.center = center; + this._before = before; + this._center = center; + + if (this.targetAnchor) { + if (center) { + this._removeItemClass(this.targetAnchor, "Before"); + this._removeItemClass(this.targetAnchor, "After"); + this._addItemClass(this.targetAnchor, 'Center'); + } + !center && this._addItemClass(this.targetAnchor, this.before ? "Before" : "After"); + center && canDrop === false && this._addItemClass(this.targetAnchor, "Disallow"); + } + }, + /** + * @param sourceSource {module:dojo/dnd/Source} + * @param nodes {HTMLElement[]} + * @param copy {boolean} + * @param targetItem {module:dojo/dnd/Target} + */ + onDropExternal: function (sourceSource, nodes, copy, targetItem) { + // Note: this default implementation expects that two grids do not + // share the same store. There may be more ideal implementations in the + // case of two grids using the same store (perhaps differentiated by + // query), dragging to each other. + var grid = this.grid, + store = this.grid.collection, + sourceGrid = sourceSource.grid; + + // TODO: bail out if sourceSource.getObject isn't defined? + nodes.forEach(function (node, i) { + when(sourceSource.getObject(node), function (object) { + // Copy object, if supported by store; otherwise settle for put + // (put will relocate an existing item with the same id). + // Note that we use store.copy if available even for non-copy dnd: + // since this coming from another dnd source, always behave as if + // it is a new store item if possible, rather than replacing existing. + grid._trackError(function () { + return store[store.copy ? 'copy' : 'put'](object, { + beforeId: targetItem ? store.getIdentity(targetItem) : null + }).then(function () { + if (!copy) { + if (sourceGrid) { + // Remove original in the case of inter-grid move. + // (Also ensure dnd source is cleaned up properly) + var id = sourceGrid.collection.getIdentity(object); + !i && sourceSource.selectNone(); // Deselect all, one time + sourceSource.delItem(node.id); + return sourceGrid.collection.remove(id); + } + else { + sourceSource.deleteSelectedNodes(); + } + } + }); + }); + }); + }); + } + }); + + function debugStoreItem(item) { + if (item) { + var parent = item.getParent() || {}; + console.log("Item : " + item.name + ' | ' + parent.name); + } + } + + /** + * + * @enum {int} module:xblox/types/DROP_TYPE + * @memberOf module:xide/types + */ + var DROP_TYPE = { + BEFORE: 1, + CENTER: 2 + }; + /** + * @class module:xblox/views/DojoDndMixin + */ + var DojoDndMixin = declare("xblox.widgets.DojoDndMixin", null, { + declaredClass: 'xblox.widgets.DojoDndMixin', + dropEvent: "/dnd/drop", + dragEvent: "/dnd/start", + overEvent: "/dnd/source/over", + /** + * Final + * @param sources {module:xblox/model/Block[]} + * @param target {module:xblox/model/Block} + * @param sourceGrid {module:xgrid/Base| module:dgrid/List} + * @param params {object} + * @param params.SAME_STORE {object} + * @param copy {boolean} + * @private + */ + __onDrop: function (sources, target, sourceGrid, params, copy) { + var into = params.center === true; + _.each(sources, function (source) { + //happens when dragged on nothing + if (source == target) { + return; + } + if (into) { + if (target.canAdd(source) !== false) { + target.scope.moveTo(source, target, params.before, into); + } + } else { + var sourceParent = source.getParent(); + if (sourceParent) { + var targetParent = target.getParent(); + if (!targetParent) { + //move to root - end + source.group = this.blockGroup; + sourceParent.removeBlock(source, false); + source._store.putSync(source); + } else { + + //we dropped at the end within the same tree + if (targetParent == source.getParent()) { + target.scope.moveTo(source, target, params.before, into); + } + } + } + } + source.refresh(); + }, this); + + this.set('collection', this.collection.filter({ + group: "click" + })) + + this.select(sources[0], null, true, { + focus: true, + append: false, + expand: true, + delay: 2 + }); + }, + /** + * + * @param source {module:dojo/dnd/Source} + * @returns {module:xblox/model/Block} + * @returns {module:xgrid/Base} + * @private + */ + _sourceToModel: function (source, grid) { + var result = null; + if (source) { + + var anchor = source._targetAnchor || source.anchor || source; + grid = grid || source.grid || this; + if (!anchor || !anchor.ownerDocument) { + return null; + } + var row = grid.row(anchor); + if (row) { + return row.data; + } + if (anchor) { + result = grid.collection.getSync(anchor.id.slice(grid.id.length + 5)); + if (!result) { + result = grid.row(anchor); + } + } + } + return result; + }, + __dndNodesToModel: function (nodes) { + return _.map(nodes, function (n) { + return (this.row(n) || {}).data; + }, this); + }, + startup:function(){ + if(this._started){ + return; + } + this.subscribe(this.dropEvent, function (source, nodes, copy, target) { + if (source.grid == this) { + var rows = this.__dndNodesToModel(nodes); + target = target || {}; + var _target = this._sourceToModel(target, target.grid); + + if (!_target || _.isEmpty(rows)) { + return; + } + var DND_PARAMS = { + SAME_STORE: rows[0]._store == _target._store, + before: target._before, + center: target._center + } + if (rows && _target) { + this.__onDrop(rows, _target, source.grid, DND_PARAMS, copy); + } + } + }); + + return this.inherited(arguments); + + } + }); + + // Mix in Selection for more resilient dnd handling, particularly when part + // of the selection is scrolled out of view and unrendered (which we + // handle below). + DnD = declare('xblox.dnd', [DojoDndMixin], { + // dndSourceType: String + // Specifies the type which will be set for DnD items in the grid, + // as well as what will be accepted by it by default. + dndSourceType: 'dgrid-row', + + // dndParams: Object + // Object containing params to be passed to the DnD Source constructor. + dndParams: null, + + // dndConstructor: Function + // Constructor from which to instantiate the DnD Source. + // Defaults to the GridSource constructor defined/exposed by this module. + dndConstructor: BlockGridDnDSource, + postMixInProperties: function () { + this.inherited(arguments); + // ensure dndParams is initialized + this.dndParams = utils.mixin({accept: [this.dndSourceType]}, this.dndParams); + }, + postCreate: function () { + this.inherited(arguments); + // Make the grid's content a DnD source/target. + var Source = this.dndConstructor || BlockGridDnDSource; + var dndParams = utils.mixin(this.dndParams, { + // add cross-reference to grid for potential use in inter-grid drop logic + grid: this, + dropParent: this.contentNode + }); + if (typeof this.expand === 'function') { + // If the Tree mixin is being used, allowNested needs to be set to true for DnD to work properly + // with the child rows. Without it, child rows will always move to the last child position. + dndParams.allowNested = true; + } + this.dndSource = new Source(this.bodyNode, dndParams); + + // Set up select/deselect handlers to maintain references, in case selected + // rows are scrolled out of view and unrendered, but then dragged. + var selectedNodes = this.dndSource._selectedNodes = {}; + + function selectRow(row) { + selectedNodes[row.id] = row.element; + } + + function deselectRow(row) { + delete selectedNodes[row.id]; + // Re-sync dojo/dnd UI classes based on deselection + // (unfortunately there is no good programmatic hook for this) + domClass.remove(row.element, 'dojoDndItemSelected dojoDndItemAnchor'); + } + + this.on('dgrid-select', function (event) { + arrayUtil.forEach(event.rows, selectRow); + }); + this.on('dgrid-deselect', function (event) { + arrayUtil.forEach(event.rows, deselectRow); + }); + + aspect.after(this, 'destroy', function () { + delete this.dndSource._selectedNodes; + selectedNodes = null; + this.dndSource.destroy(); + }, true); + }, + insertRow: function (object) { + // override to add dojoDndItem class to make the rows draggable + var row = this.inherited(arguments), + type = typeof this.getObjectDndType === 'function' ? + this.getObjectDndType(object) : [this.dndSourceType]; + + domClass.add(row, 'dojoDndItem'); + this.dndSource.setItem(row.id, { + data: object, + type: type instanceof Array ? type : [type] + }); + return row; + }, + removeRow: function (rowElement) { + this.dndSource.delItem(this.row(rowElement)); + this.inherited(arguments); + } + }); + + DnD.GridSource = BlockGridDnDSource; + + function debugSource(source) { + console.log('\t Source : ' + source.declaredClass + ' | before: ' + source._before + ' | ContainerState = ' + source.containerState + ' | TargetState =' + source.targetState + ' | SourceState ' + source.sourceState + ' | center = ' + source._center, source); + } + function debugTarget(source) { + console.log('\t Target : ' + source.declaredClass + ' | before: ' + source._before + ' | ContainerState = ' + source.containerState + ' | TargetState =' + source.targetState + ' | SourceState ' + source.sourceState + ' | center = ' + source._center, source); + } + + + + + + /** + * @class module:xblox/views/SharedDndGridSource + * @extends module:xblox/views/BlockGridDnDSource + * @extends module:xblox/views/DojoDndMixin + */ + var SharedDndGridSource = declare([DnD.GridSource], { + autoSync: true, + skipForm: true, + selfAccept: false, + onDndDrop: function (source, nodes, copy, target) { + console.error('ondrop'); + if (this == target) { + // this one is for us => move nodes! + this.onDrop(source, nodes, copy, target); + this.grid.refresh(); + } + this.onDndCancel(); + } + }); + + + /*** + * playground + */ + var ctx = window.sctx, + root; + + function createScope() { + + var data = { + "blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "icon": 'fa-bell text-fatal', + "_scenario": "update" + }, + + { + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + 'icon': 'fa-play' + + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "group": "click", + "id": "6a12d54a-f5af-aaf8-0abb-bff866211fc4", + "declaredClass": "xblox.model.logic.SwitchBlock", + "name": "Switch", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "order": 0, + "type": "added" + }, + { + "_containsChildrenIds": [], + "group": "click", + "id": "8e1c9d2a-d2fe-356f-d484-82e27c793dc9", + "declaredClass": "xblox.model.variables.VariableSwitch", + "name": "Switch on Variable", + "icon": "", + "variable": "PowerState", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "order": 0, + "type": "added" + } + ], + "variables": [] + }; + return blockManager.toScope(data); + } + + if (ctx) { + + var blockManager = ctx.getBlockManager(); + + + function createGridClass() { + + var renderers = [TreeRenderer, ThumbRenderer]; + + var multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + var _gridClass = Grid.createGridClass('driverTreeView', { + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + SPLIT: { + CLASS: _LayoutMixin + }, + Dnd: { + CLASS: DnD + }, + Base: { + CLASS: declare('Base', null, BlockGrid.IMPLEMENTATION) + }, + BLOCK_ACTIONS: { + CLASS: BlockActions + }, + WIDGET: { + CLASS: _Widget + } + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + EDITOR: Editor, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + //return declare('gridFinal', BlockGrid, _gridClass); + return _gridClass; + } + + var blockScope = createScope('docs'); + var mainView = ctx.mainView; + if (mainView) { + + + var parent = TestUtils.createTab('BlockGrid-Test-Source', null, module.id); + var target = TestUtils.createTab('BlockGrid-Test-Target', null, module.id + '_target', parent, types.DOCKER.TAB.BOTTOM, types.DOCKER.DOCK.BOTTOM); + + var thiz = this, + grid, grid2; + + var store = blockScope.blockStore; + + var _gridClass = createGridClass(); + + var dndParams = { + allowNested: true, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return true;//source !== this; // Don't self-accept. + }, + isSource: true + }; + + + var gridArgs = { + ctx: ctx, + /** + * @type {module:xblox/views/SharedDndGridSource} + */ + dndConstructor: SharedDndGridSource, // use extension defined above + blockScope: blockScope, + blockGroup: 'click', + dndParams: dndParams, + name: "Grid - 1", + collection: store.filter({ + group: "click" + }) + }; + + + var gridArgs2 = { + ctx: ctx, + /** + * @type {module:xblox/views/SharedDndGridSource} + */ + dndConstructor: SharedDndGridSource, // use extension defined above + blockScope: blockScope, + blockGroup: 'click', + attachDirect: true, + name: "Grid - 2", + collection: store.filter({ + group: "click" + }) + }; + + grid = utils.addWidget(_gridClass, gridArgs, null, parent, true); + + grid2 = utils.addWidget(_gridClass, gridArgs2, null, target, true); + + + + ctx.getWindowManager().registerView(grid); + grid2 && ctx.getWindowManager().registerView(grid2); + + + setTimeout(function () { + mainView.resize(); + grid.resize(); + }, 1000); + + + function test() { + return; + } + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestDNDPaletteClass.js b/packages/xblox/ref-control-freak/xblox/tests/TestDNDPaletteClass.js new file mode 100644 index 00000000..f63f8280 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestDNDPaletteClass.js @@ -0,0 +1,1039 @@ +define([ + "xdojo/declare", + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dgrid/Editor', + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + 'xdocker/Docker2', + 'xblox/views/BlockGrid', + 'xide/tests/TestUtils', + 'xblox/BlockActions', + 'xide/views/_LayoutMixin', + "xide/widgets/_Widget", + 'dojo/dnd/Source', + 'dojo/dnd/Manager', + 'dojo/_base/NodeList', + 'module', + 'dojo/when', + 'dojo/has!touch?dgrid/util/touch', + 'dojo/topic', + 'xdojo/has', + 'dojo/_base/array', + 'dojo/aspect', + 'dojo/dom-class', + 'xide/factory', + 'xgrid/DnD', + 'dojo/Deferred', + 'xide/registry', + 'xblox/views/ThumbRenderer', + 'dojo/dnd/Manager', + 'dojo/dom-geometry', + "dojo/dnd/common", + "xblox/views/DnD", + 'dojo/dom-construct', + + 'xgrid/Selection', + "xide/widgets/_Widget", + 'xgrid/KeyboardNavigation', + 'xaction/ActionStore', + 'xide/factory', + 'xlang/i18' + +], function (declare, types, + utils, ListRenderer, TreeRenderer, Grid, MultiRenderer, Editor, Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, Docker, BlockGrid, + TestUtils, BlockActions, _LayoutMixin, _Widget, DnDSource, + DnDManager, NodeList, module, when, touchUtil, topic, has, arrayUtil, + aspect, domClass, factory, DnD, Deferred, registry, ThumbRenderer, Manager, domGeom, dnd,BlocksDnD, + domConstruct,Selection, _XWidget, KeyboardNavigation, ActionStore, factory, i18) { + + + var ThumbClass = declare('xfile.ThumbRenderer2', [ThumbRenderer], { + thumbSize: "400", + resizeThumb: true, + __type: 'thumb', + deactivateRenderer: function () { + $(this.domNode.parentNode).removeClass('metro'); + $(this.domNode).css('padding', ''); + this.isThumbGrid = false; + + }, + activateRenderer: function () { + $(this.contentNode).css('padding', '8px'); + this.isThumbGrid = true; + this.refresh(); + }, + /** + * Override renderRow + * @param obj + * @returns {*} + */ + renderRow: function (obj) { + var div = domConstruct.create('div', { + className: "tile widget form-control", + style: 'margin-left:16px;margin-top:8px;margin-right:8px;width:150px;height:initial;max-width:200px;float:left;padding:8px' + }), + icon = obj.icon, + label = obj.title, + imageClass = obj.icon || 'fa fa-folder fa-2x'; + + var iconStyle = 'float:left; margin-left:3px;margin-top:3px;margin-right:3px;text-shadow: 2px 2px 5px rgba(0,0,0,0.3);font-size: inherit;opacity: 0.4'; + var folderContent = ''; + + div.innerHTML = + '
' + + folderContent + + '' + label + '' + + '
'; + return div; + } + }); + var GroupRenderer = declare('xfile.GroupRenderer', TreeRenderer, { + itemClass: ThumbClass, + groupClass: TreeRenderer, + renderRow: function (obj) { + return (!obj.group ? this.itemClass : this.groupClass).prototype.renderRow.apply(this, arguments); + } + }); + var Implementation = {}, + renderers = [GroupRenderer, ThumbClass, TreeRenderer], + + multiRenderer = declare.classFactory('xgrid/MultiRendererGroups', {}, renderers, MultiRenderer.Implementation); + + + + + + + + + + + console.clear(); + /** + * flow - drop + * 1. SharedDndGridSource:onDrop -> + * 1.1. SharedDndGridSource:onDropInternal or SharedDndGridSource:onDropExternal + * 1.2 SharedDndGridSource:onDropInternal + * 1.3 DojoDndMixin#onDrop + * 1.4 Grid#onDrop + */ + /** + * + * @class module:xblox/views/BlockGridDnDSource + * @extends module:xgrid/DnD/GridDnDSource + */ + var BlockGridDnDSource = declare(DnD.GridSource, { + declaredClass: 'xblox/views/BlockGridDnDSource', + _lastTargetId: null, + _lastTargetCrossCounter: 0, + /** + * Assigns a class to the current target anchor based on "before" status + * @param before {boolean} insert before, if true, after otherwise + * @param center {boolean} + * @param canDrop {boolean} + * @private + */ + _markTargetAnchor: function (before, center, canDrop) { + if (this.current == this.targetAnchor && this.before === before && this.center === center) { + return; + } + if (this.targetAnchor) { + this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After"); + this._removeItemClass(this.targetAnchor, "Disallow"); + this._removeItemClass(this.targetAnchor, "Center"); + } + this.targetAnchor = this.current; + this.targetBox = null; + this.before = before; + this.center = center; + this._before = before; + this._center = center; + + if (this.targetAnchor) { + if (center) { + this._removeItemClass(this.targetAnchor, "Before"); + this._removeItemClass(this.targetAnchor, "After"); + this._addItemClass(this.targetAnchor, 'Center'); + } + !center && this._addItemClass(this.targetAnchor, this.before ? "Before" : "After"); + center && canDrop === false && this._addItemClass(this.targetAnchor, "Disallow"); + } + }, + /** + * + * @param nodes {HTMLElement[]} + * @param copy {boolean} + * @param targetItem {module:xide/data/Model} + */ + onDropInternal: function (nodes, copy, targetItem) { + var grid = this.grid, + store = grid.collection, + targetSource = this, + anchor = targetSource._targetAnchor, + targetRow, + nodeRow; + + if (anchor) { // (falsy if drop occurred in empty space after rows) + targetRow = this._center ? anchor : this.before ? anchor.previousSibling : anchor.nextSibling; + } + // Don't bother continuing if the drop is really not moving anything. + // (Don't need to worry about edge first/last cases since dropping + // directly on self doesn't fire onDrop, but we do have to worry about + // dropping last node into empty space beyond rendered rows.) + nodeRow = grid.row(nodes[0]); + if (!copy && (targetRow === nodes[0] || (!targetItem && nodeRow && grid.down(nodeRow).element === nodes[0]))) { + return; + } + var rows = grid.__dndNodesToModel(nodes); + var _target = grid._sourceToModel(targetSource, grid); + var DND_PARAMS = { + before: this._before, + center: this._center, + targetGrid:grid + }; + + if(this._center){ + grid.__onDrop(rows, _target, grid, DND_PARAMS, copy); + } + nodes.forEach(function (node) { + when(targetSource.getObject(node), function (object) { + var id = store.getIdentity(object); + // For copy DnD operations, copy object, if supported by store; + // otherwise settle for put anyway. + // (put will relocate an existing item with the same id, i.e. move). + grid._trackError(function () { + return store[copy && store.copy ? 'copy' : 'put'](object, { + beforeId: targetItem ? store.getIdentity(targetItem) : null + }).then(function () { + + // Self-drops won't cause the dgrid-select handler to re-fire, + // so update the cached node manually + if (targetSource._selectedNodes[id]) { + targetSource._selectedNodes[id] = grid.row(id).element; + } + !this.center && grid.__onDrop(rows, _target, grid, DND_PARAMS, copy); + }); + }); + }); + }); + }, + /** + * @param sourceSource {module:dojo/dnd/Source} + * @param nodes {HTMLElement[]} + * @param copy {boolean} + * @param targetItem {module:dojo/dnd/Target} + */ + onDropExternal: function (sourceSource, nodes, copy, targetItem) { + // Note: this default implementation expects that two grids do not + // share the same store. There may be more ideal implementations in the + // case of two grids using the same store (perhaps differentiated by + // query), dragging to each other. + var grid = this.grid, + store = this.grid.collection, + sourceGrid = sourceSource.grid, + targetSource = this; + + function done(grid,object){ + grid.refresh(); + return grid.select(object, null, true, { + focus: true, + append: false, + expand: true, + delay: 2 + },'mouse'); + } + + nodes.forEach(function (node, i) { + when(sourceSource.getObject(node), function (object) { + var DND_PARAMS = { + before: targetSource._before, + center: targetSource._center, + targetGrid:grid + }; + if(object.toDropObject){ + object = object.toDropObject(object,DND_PARAMS.center ? sourceGrid._sourceToModel(targetSource) : targetItem,DND_PARAMS,copy); + if(DND_PARAMS.center){ + return done(grid,object); + } + } + // Copy object, if supported by store; otherwise settle for put + // (put will relocate an existing item with the same id). + // Note that we use store.copy if available even for non-copy dnd: + // since this coming from another dnd source, always behave as if + // it is a new store item if possible, rather than replacing existing. + grid._trackError(function () { + return store[store.copy ? 'copy' : 'put'](object, { + beforeId: targetItem ? store.getIdentity(targetItem) : null + }).then(function () { + if (!copy) { + if (sourceGrid) { + // Remove original in the case of inter-grid move. + // (Also ensure dnd source is cleaned up properly) + var id = sourceGrid.collection.getIdentity(object); + !i && sourceSource.selectNone(); // Deselect all, one time + sourceSource.delItem(node.id); + done(grid,object); + return sourceGrid.collection.remove(id); + } + else { + sourceSource.deleteSelectedNodes(); + } + } + }); + }); + }); + }); + } + }); + + function debugStoreItem(item) { + if (item) { + var parent = item.getParent() || {}; + console.log("Item : " + item.name + ' | ' + parent.name); + } + } + /** + * @class module:xblox/views/DojoDndMixin + */ + var DojoDndMixin = declare("xblox.widgets.DojoDndMixin", null, { + declaredClass: 'xblox.widgets.DojoDndMixin', + dropEvent: "/dnd/drop", + dragEvent: "/dnd/start", + overEvent: "/dnd/source/over", + /** + * Final + * @param sources {module:xblox/model/Block[]} + * @param target {module:xblox/model/Block} + * @param sourceGrid {module:xgrid/Base| module:dgrid/List} + * @param params {object} + * @param params.SAME_STORE {object} + * @param copy {boolean} + * @private + */ + __onDrop: function (sources, target, sourceGrid, params, copy) { + var into = params.center === true; + _.each(sources, function (source) { + if(source.toDropObject){ + source = source.toDropObject(source,target,params,copy); + } + //happens when dragged on nothing + if (source == target) { + return; + } + if (into) { + if (target.canAdd(source) !== false) { + target.scope.moveTo(source, target, params.before, into); + } + } else { + var sourceParent = source.getParent(); + if (sourceParent) { + var targetParent = target.getParent(); + if (!targetParent) { + //move to root - end + source.group = this.blockGroup; + sourceParent.removeBlock(source, false); + source._store.putSync(source); + } else { + //we dropped at the end within the same tree + if (targetParent == source.getParent()) { + target.scope.moveTo(source, target, params.before, into); + } + } + } + } + source.refresh(); + }, this); + + this.set('collection', this.collection.filter({ + group: "click" + })); + + this.select(sources[0], null, true, { + focus: true, + append: false, + expand: true, + delay: 2 + },'mouse'); + + }, + /** + * + * @param source {module:dojo/dnd/Source} + * @returns {module:xblox/model/Block} + * @returns {module:xgrid/Base} + * @private + */ + _sourceToModel: function (source, grid) { + var result = null; + if (source) { + + var anchor = source._targetAnchor || source.anchor || source; + grid = grid || source.grid || this; + if (!anchor || !anchor.ownerDocument) { + return null; + } + var row = grid.row(anchor); + if (row) { + return row.data; + } + if (anchor) { + result = grid.collection.getSync(anchor.id.slice(grid.id.length + 5)); + if (!result) { + result = grid.row(anchor); + } + } + } + return result; + }, + __dndNodesToModel: function (nodes) { + return _.map(nodes, function (n) { + return (this.row(n) || {}).data; + }, this); + }, + startup:function(){ + if(this._started){ + return; + } + //this.dropEvent = null; + /* + this.dropEvent && this.subscribe(this.dropEvent, function (source, nodes, copy, target) { + target = target || {}; + var sourceGrid = source.grid; + var targetGrid = target.grid; + if(targetGrid==this || sourceGrid==this){ + var rows = sourceGrid.__dndNodesToModel(nodes); + var _target = targetGrid._sourceToModel(target, target.grid); + if (!_target || _.isEmpty(rows)) { + return; + } + var DND_PARAMS = { + SAME_STORE: rows[0]._store == _target._store, + before: target._before, + center: target._center, + targetGrid:targetGrid + }; + if (rows && _target) { + targetGrid.__onDrop(rows, _target, sourceGrid, DND_PARAMS, copy); + } + } + }); + */ + return this.inherited(arguments); + } + }); + // Mix in Selection for more resilient dnd handling, particularly when part + // of the selection is scrolled out of view and unrendered (which we + // handle below). + DnD = declare('xblox.dnd', [DojoDndMixin], { + // dndSourceType: String + // Specifies the type which will be set for DnD items in the grid, + // as well as what will be accepted by it by default. + dndSourceType: 'dgrid-row', + // dndParams: Object + // Object containing params to be passed to the DnD Source constructor. + dndParams: null, + // dndConstructor: Function + // Constructor from which to instantiate the DnD Source. + // Defaults to the GridSource constructor defined/exposed by this module. + dndConstructor: BlockGridDnDSource, + postMixInProperties: function () { + this.inherited(arguments); + // ensure dndParams is initialized + this.dndParams = utils.mixin({accept: [this.dndSourceType]}, this.dndParams); + }, + postCreate: function () { + this.inherited(arguments); + // Make the grid's content a DnD source/target. + var Source = this.dndConstructor || BlockGridDnDSource; + var dndParams = utils.mixin(this.dndParams, { + // add cross-reference to grid for potential use in inter-grid drop logic + grid: this, + dropParent: this.contentNode + }); + if (typeof this.expand === 'function') { + // If the Tree mixin is being used, allowNested needs to be set to true for DnD to work properly + // with the child rows. Without it, child rows will always move to the last child position. + dndParams.allowNested = true; + } + this.dndSource = new Source(this.bodyNode, dndParams); + + // Set up select/deselect handlers to maintain references, in case selected + // rows are scrolled out of view and unrendered, but then dragged. + var selectedNodes = this.dndSource._selectedNodes = {}; + + function selectRow(row) { + selectedNodes[row.id] = row.element; + } + + function deselectRow(row) { + delete selectedNodes[row.id]; + // Re-sync dojo/dnd UI classes based on deselection + // (unfortunately there is no good programmatic hook for this) + domClass.remove(row.element, 'dojoDndItemSelected dojoDndItemAnchor'); + } + + this.on('dgrid-select', function (event) { + arrayUtil.forEach(event.rows, selectRow); + }); + this.on('dgrid-deselect', function (event) { + arrayUtil.forEach(event.rows, deselectRow); + }); + + aspect.after(this, 'destroy', function () { + delete this.dndSource._selectedNodes; + selectedNodes = null; + this.dndSource.destroy(); + }, true); + }, + insertRow: function (object) { + // override to add dojoDndItem class to make the rows draggable + var row = this.inherited(arguments), + type = typeof this.getObjectDndType === 'function' ? + this.getObjectDndType(object) : [this.dndSourceType]; + + domClass.add(row, 'dojoDndItem'); + this.dndSource.setItem(row.id, { + data: object, + type: type instanceof Array ? type : [type] + }); + return row; + }, + removeRow: function (rowElement) { + this.dndSource.delItem(this.row(rowElement)); + this.inherited(arguments); + } + }); + + DnD.GridSource = BlockGridDnDSource; + + function debugSource(source) { + console.log('\t Source : ' + source.declaredClass + ' | before: ' + source._before + ' | ContainerState = ' + source.containerState + ' | TargetState =' + source.targetState + ' | SourceState ' + source.sourceState + ' | center = ' + source._center, source); + } + function debugTarget(source) { + console.log('\t Target : ' + source.declaredClass + ' | before: ' + source._before + ' | ContainerState = ' + source.containerState + ' | TargetState =' + source.targetState + ' | SourceState ' + source.sourceState + ' | center = ' + source._center, source); + } + + + /** + * @class module:xblox/views/SharedDndGridSource + * @extends module:xblox/views/BlockGridDnDSource + * @extends module:xblox/views/DojoDndMixin + */ + var SharedDndGridSource = declare([DnD.GridSource], { + autoSync: true, + skipForm: true, + selfAccept: false, + onDndDrop: function (source, nodes, copy, target) { + if (this == target) { + // this one is for us => move nodes! + this.onDrop(source, nodes, copy, target); + this.grid.refresh(); + } + this.onDndCancel(); + } + }); + + + + var GridClass = Grid.createGridClass('xfile.views.Grid', Implementation, { + SELECTION: { + CLASS: Selection + }, + KEYBOARD_SELECTION: true, + CONTEXT_MENU: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + WIDGET: { + CLASS: _XWidget + }, + KEYBOARD_NAVIGATION: { + CLASS: KeyboardNavigation + }, + Dnd: { + CLASS: DnD + } + }, + { + RENDERER: multiRenderer + }, + { + renderers: renderers, + selectedRenderer: GroupRenderer + } + ); + var GridCSourcelass = declare('ActionGrid', GridClass, { + formatColumn: function (field, value, obj) { + var renderer = this.selectedRenderer ? this.selectedRenderer.prototype : this; + if (renderer.formatColumn_) { + var result = renderer.formatColumn.apply(arguments); + if (result) { + return result; + } + } + if (obj.renderColumn_) { + var rendered = obj.renderColumn.apply(this, arguments); + if (rendered) { + return rendered; + } + } + switch (field) { + case "title": { + value = obj.group ? ('' + value + '
') : value; + } + } + return value; + }, + _columns: {}, + postMixInProperties: function () { + var state = this.state; + if (state) { + if (state._columns) { + this._columns = state._columns; + } + } + this.columns = this.getColumns(); + var newBlockActions = this.getAddActions(); + var levelName = ''; + var result = []; + var BLOCK_INSERT_ROOT_COMMAND = ''; + var defaultMixin = {addPermission: true}; + var rootAction = BLOCK_INSERT_ROOT_COMMAND; + var thiz = this; + + function addItems(commandPrefix, items) { + for (var i = 0; i < items.length; i++) { + var item = items[i]; + levelName = item.name; + var path = commandPrefix ? commandPrefix + '/' + levelName : levelName; + var isContainer = !_.isEmpty(item.items); + result.push(thiz.createAction({ + label: levelName, + command: path, + icon: item.iconClass, + tab: 'Home', + mixin: utils.mixin({ + item: item, + parent: isContainer ? "root" : commandPrefix, + quick: true, + title: i18.localize(levelName), + group: isContainer, + icon: item.iconClass, + toDropObject:function(item,targetItem,params){ + var blockArgs = item.item; + var ctrArgs = blockArgs.ctrArgs; + ctrArgs.id = null; + ctrArgs.items = null; + var proto = blockArgs.proto; + var parent = params.center === true && targetItem ? targetItem : null; + if(parent) { + var block = factory.createBlock(proto, ctrArgs);//root block + return parent.add(block, null, null); + + }else{ + ctrArgs.group = params.targetGrid.blockGroup; + } + return factory.createBlock(proto, ctrArgs);//root block + } + }, defaultMixin) + + })); + + if (isContainer) { + addItems(path, item.items); + } + } + } + + addItems(rootAction, newBlockActions); + var actionStore = new ActionStore({ + parentProperty: 'parent', + parentField: 'parent', + idProperty: 'command', + mayHaveChildren: function (parent) { + if (parent._mayHaveChildren === false) { + return false; + } + return true; + } + }); + actionStore.setData(result); + var all = actionStore.query(null); + _.each(all, function (item) { + item._store = actionStore; + }); + var $node = $(this.domNode); + $node.addClass('xFileGrid metro welcomeGrid'); + $node.css('height', '30px'); + this.collection = actionStore.filter({ + parent: "root" + }); + + return this.inherited(arguments); + }, + getColumns: function (formatters) { + var self = this; + this.columns = [ + { + renderExpando: true, + label: 'Name', + field: 'title', + sortable: true, + formatter: function (value, obj) { + return self.formatColumn('title', value, obj); + }, + hidden: false + } + ]; + return this.columns; + }, + /** + * + * @param item + * @returns {Array} + */ + getAddActions: function (item) { + var thiz = this; + item = item || {}; + var items = factory.getAllBlocks(this.blockScope, this, item, this.blockGroup, false); + //tell everyone + thiz.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items, + view: this, + owner: this, + item: item, + group: this.blockGroup, + scope: this.blockScope + }); + + thiz._emit(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items, + view: this, + owner: this + }); + return items; + + + }, + dropEvent:null, + startup: function () { + //this.collection = actionStore; + this._showHeader(false); + var res = this.inherited(arguments); + return res; + } + }); + + /*** + * playground + */ + var ctx = window.sctx, + root; + + function createScope() { + + var data = { + "blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "icon": 'fa-bell text-fatal', + "_scenario": "update" + }, + + { + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + 'icon': 'fa-play' + + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "group": "click", + "id": "6a12d54a-f5af-aaf8-0abb-bff866211fc4", + "declaredClass": "xblox.model.logic.SwitchBlock", + "name": "Switch", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "order": 0, + "type": "added" + }, + { + "_containsChildrenIds": [], + "group": "click", + "id": "8e1c9d2a-d2fe-356f-d484-82e27c793dc9", + "declaredClass": "xblox.model.variables.VariableSwitch", + "name": "Switch on Variable", + "icon": "", + "variable": "PowerState", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "order": 0, + "type": "added" + } + ], + "variables": [] + }; + return blockManager.toScope(data); + } + + if (ctx) { + + var blockManager = ctx.getBlockManager(); + + + function createGridClass() { + + var renderers = [TreeRenderer, ThumbRenderer]; + + var multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + var _gridClass = Grid.createGridClass('driverTreeView', { + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + SPLIT: { + CLASS: _LayoutMixin + }, + Dnd: { + CLASS: DnD + }, + Base: { + CLASS: declare('Base', null, BlockGrid.IMPLEMENTATION) + }, + BLOCK_ACTIONS: { + CLASS: BlockActions + }, + WIDGET: { + CLASS: _Widget + } + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + EDITOR: Editor, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + //return declare('gridFinal', BlockGrid, _gridClass); + return _gridClass; + } + + var blockScope = createScope('docs'); + var mainView = ctx.mainView; + if (mainView) { + var parent = TestUtils.createTab('BlockGrid-Test-Source', null, module.id); + var target = TestUtils.createTab('BlockGrid-Test-Target', null, module.id + '_target', parent, types.DOCKER.TAB.BOTTOM, types.DOCKER.DOCK.BOTTOM); + var thiz = this, + grid, grid2; + + var store = blockScope.blockStore; + + var _gridClass = createGridClass(); + var dndParams = { + allowNested: true, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return true;//source !== this; // Don't self-accept. + }, + isSource: true + }; + var gridArgs = { + ctx: ctx, + /** + * @type {module:xblox/views/SharedDndGridSource} + */ + dndConstructor: SharedDndGridSource, // use extension defined above + blockScope: blockScope, + blockGroup: 'click', + dndParams: dndParams, + name: "Grid - 1", + collection: store.filter({ + group: "click" + }) + }; + var gridArgs2 = { + options: utils.clone(types.DEFAULT_GRID_OPTIONS), + ctx: ctx + }; + + var gridArgs2 = { + ctx: ctx, + /** + * @type {module:xblox/views/SharedDndGridSource} + */ + dndConstructor: SharedDndGridSource, // use extension defined above + blockScope: blockScope, + attachDirect: true, + name: "Grid - 2", + dndParams: { + allowNested: false, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return false;//source !== this; // Don't self-accept. + }, + isSource: true + } + }; + + grid = utils.addWidget(_gridClass, gridArgs, null, parent, true); + + //grid2 = utils.addWidget(_gridClass, gridArgs2, null, target, true); + + grid2 = utils.addWidget(GridCSourcelass, gridArgs2, null, target, true); + + + ctx.getWindowManager().registerView(grid); + //grid2 && ctx.getWindowManager().registerView(grid2); + + + setTimeout(function () { + mainView.resize(); + grid.resize(); + }, 1000); + + + function test() { + return; + } + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestNewPallete.js b/packages/xblox/ref-control-freak/xblox/tests/TestNewPallete.js new file mode 100644 index 00000000..d907ee82 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestNewPallete.js @@ -0,0 +1,437 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dgrid/Editor', + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + 'xdocker/Docker2', + 'xblox/views/BlockGrid', + 'xide/tests/TestUtils', + 'module', + 'xgrid/ThumbRenderer', + 'dojo/dom-construct', + 'xgrid/Selection', + "xide/widgets/_Widget", + 'xgrid/KeyboardNavigation', + 'xaction/ActionStore', + 'xide/factory', + 'xlang/i18' +], function (declare, types, + utils, ListRenderer, TreeRenderer, Grid, MultiRenderer, Editor, Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, Docker, BlockGrid, + TestUtils, module, ThumbRenderer, domConstruct, + Selection, _XWidget, KeyboardNavigation, ActionStore, factory, i18) { + + + console.clear(); + + var ThumbClass = declare('xfile.ThumbRenderer2', [ThumbRenderer], { + thumbSize: "400", + resizeThumb: true, + __type: 'thumb', + deactivateRenderer: function () { + $(this.domNode.parentNode).removeClass('metro'); + $(this.domNode).css('padding', ''); + this.isThumbGrid = false; + + }, + activateRenderer: function () { + $(this.contentNode).css('padding', '8px'); + this.isThumbGrid = true; + this.refresh(); + }, + /** + * Override renderRow + * @param obj + * @returns {*} + */ + renderRow: function (obj) { + var div = domConstruct.create('div', { + className: "tile widget form-control", + style: 'margin-left:16px;margin-top:8px;margin-right:8px;width:150px;height:initial;max-width:200px;float:left;padding:8px' + }), + icon = obj.icon, + label = obj.title, + imageClass = obj.icon || 'fa fa-folder fa-2x'; + + var iconStyle = 'float:left; margin-left:3px;margin-top:3px;margin-right:3px;text-shadow: 2px 2px 5px rgba(0,0,0,0.3);font-size: inherit;opacity: 0.4'; + var folderContent = ''; + + div.innerHTML = + '
' + + folderContent + + '' + label + '' + + '
'; + return div; + } + }); + var GroupRenderer = declare('xfile.GroupRenderer', TreeRenderer, { + itemClass: ThumbClass, + groupClass: TreeRenderer, + renderRow: function (obj) { + return (!obj.group ? this.itemClass : this.groupClass).prototype.renderRow.apply(this, arguments); + } + }); + var Implementation = {}, + renderers = [GroupRenderer, ThumbClass, TreeRenderer], + multiRenderer = declare.classFactory('xgrid/MultiRendererGroups', {}, renderers, MultiRenderer.Implementation); + + var GridClass = Grid.createGridClass('xfile.views.Grid', Implementation, { + SELECTION: { + CLASS: Selection + }, + KEYBOARD_SELECTION: true, + CONTEXT_MENU: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + WIDGET: { + CLASS: _XWidget + }, + KEYBOARD_NAVIGATION: { + CLASS: KeyboardNavigation + } + }, + { + RENDERER: multiRenderer + }, + { + renderers: renderers, + selectedRenderer: GroupRenderer + } + ); + /*** + * playground + */ + var ctx = window.sctx, + root; + + + function fixScope(scope) { + /** + * + * @param source + * @param target + * @param before + * @param add: comes from 'hover' state + * @returns {boolean} + */ + + + return scope; + } + + function createScope() { + + var data = { + "blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + + { + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + } + ], + "variables": [] + }; + + return fixScope(blockManager.toScope(data)); + } + + if (ctx) { + var blockManager = ctx.getBlockManager(); + + + var blockScope = createScope('docs'); + + var mainView = ctx.mainView; + + if (mainView) { + + var parent = TestUtils.createTab('BlockGrid-Test', null, module.id); + var thiz = this, + grid; + + GridClass = declare('ActionGrid', GridClass, { + formatColumn: function (field, value, obj) { + var renderer = this.selectedRenderer ? this.selectedRenderer.prototype : this; + if (renderer.formatColumn_) { + var result = renderer.formatColumn.apply(arguments); + if (result) { + return result; + } + } + if (obj.renderColumn_) { + var rendered = obj.renderColumn.apply(this, arguments); + if (rendered) { + return rendered; + } + } + switch (field) { + case "title": { + value = obj.group ? ('' + value + '
') : value; + } + } + return value; + }, + _columns: {}, + postMixInProperties: function () { + var state = this.state; + if (state) { + if (state._columns) { + this._columns = state._columns; + } + } + this.columns = this.getColumns(); + var newBlockActions = this.getAddActions(); + var levelName = ''; + var result = []; + var BLOCK_INSERT_ROOT_COMMAND = ''; + var defaultMixin = {addPermission: true}; + var rootAction = BLOCK_INSERT_ROOT_COMMAND; + var thiz = this; + + function addItems(commandPrefix, items) { + for (var i = 0; i < items.length; i++) { + var item = items[i]; + levelName = item.name; + var path = commandPrefix ? commandPrefix + '/' + levelName : levelName; + var isContainer = !_.isEmpty(item.items); + result.push(thiz.createAction({ + label: levelName, + command: path, + icon: item.iconClass, + tab: 'Home', + mixin: utils.mixin({ + item: item, + parent: isContainer ? "root" : commandPrefix, + quick: true, + title: i18.localize(levelName), + group: isContainer, + icon: item.iconClass + }, defaultMixin) + })); + + if (isContainer) { + addItems(path, item.items); + } + } + } + + addItems(rootAction, newBlockActions); + var actionStore = new ActionStore({ + parentProperty: 'parent', + parentField: 'parent', + idProperty: 'command', + mayHaveChildren: function (parent) { + if (parent._mayHaveChildren === false) { + return false; + } + return true; + } + }); + + + actionStore.setData(result); + var all = actionStore.query(null); + _.each(all, function (item) { + item._store = actionStore; + }); + var $node = $(this.domNode); + $node.addClass('xFileGrid metro welcomeGrid'); + //$node.css('padding', '16px'); + $node.css('height', '30px'); + this.collection = actionStore.filter({ + parent: "root" + }); + + return this.inherited(arguments); + }, + getColumns: function (formatters) { + var self = this; + this.columns = [ + { + renderExpando: true, + label: 'Name', + field: 'title', + sortable: true, + formatter: function (value, obj) { + return self.formatColumn('title', value, obj); + }, + hidden: false + } + ]; + return this.columns; + }, + /** + * + * @param item + * @returns {Array} + */ + getAddActions: function (item) { + var thiz = this; + item = item || {}; + var items = factory.getAllBlocks(this.blockScope, this, item, this.blockGroup, false); + //tell everyone + thiz.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items, + view: this, + owner: this, + item: item, + group: this.blockGroup, + scope: this.blockScope + }); + + thiz._emit(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items, + view: this, + owner: this + }); + return items; + + }, + startup: function () { + //this.collection = actionStore; + this._showHeader(false); + var res = this.inherited(arguments); + return res; + } + }); + + var _gridClass = GridClass; + var gridArgs = { + options: utils.clone(types.DEFAULT_GRID_OPTIONS), + ctx: ctx + }; + + grid = utils.addWidget(_gridClass, gridArgs, null, parent, true); + var blocks = blockScope.allBlocks(); + setTimeout(function () { + mainView.resize(); + grid.resize(); + }, 1000); + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestReload.js b/packages/xblox/ref-control-freak/xblox/tests/TestReload.js new file mode 100644 index 00000000..beee6015 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestReload.js @@ -0,0 +1,103 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + 'dojo/dom-class', + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/ThumbRenderer', + 'xide/views/_ActionMixin', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dijit/form/RadioButton', + 'xide/widgets/Ribbon', + 'xide/editor/Registry', + 'xaction/DefaultActions', + 'xaction/Action', + "xblox/widgets/BlockGridRowEditor", + + 'dgrid/Editor', + + + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + + 'xide/factory', + + + 'dijit/Menu', + + 'xide/data/Reference', + + 'dijit/form/DropDownButton', + 'dijit/MenuItem', + + 'xide/docker/Docker', + + + "xide/views/CIViewMixin", + + 'xide/layout/TabContainer', + + + "dojo/has!host-browser?xblox/views/BlockEditDialog", + + 'xblox/views/BlockGrid', + + 'xgrid/DnD', + 'xblox/views/BlocksGridDndSource', + 'xblox/widgets/DojoDndMixin', + + + 'xide/registry', + + 'dojo/topic', + 'xide/tests/TestUtils', + './TestUtils', + 'module' + + +], function (declare, domClass,types, + utils, ListRenderer, TreeRenderer, ThumbRenderer, + _ActionMixin, + Grid, MultiRenderer, RadioButton, Ribbon, Registry, DefaultActions, Action, BlockGridRowEditor, + Editor,Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, factory,Menu,Reference,DropDownButton, + MenuItem,Docker,CIViewMixin,TabContainer, + + BlockEditDialog, + BlockGrid, + Dnd,BlocksGridDndSource,DojoDndMixin, + registry,topic,TestUtils,BlockUtils,module + ) { + + + var ctx = window.sctx; + + if(ctx){ + + var blockManager = ctx.getBlockManager(); + + + + var scope = BlockUtils.createScope(blockManager); + + + + + + } + + + + var Module = declare('xblox.tests.TestUtils',null,{}); + + + return Module; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/TestUtils.js b/packages/xblox/ref-control-freak/xblox/tests/TestUtils.js new file mode 100644 index 00000000..187f9b88 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/TestUtils.js @@ -0,0 +1,1077 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + "xide/utils", + "xide/types", + "xblox/views/BlockGrid" + +], function (declare,utils,types,BlockGrid) { + + var Module = declare('xblox.tests.TestUtils',null,{}); + + function createScope(blockManager,extraBlocks) { + extraBlocks = extraBlocks || []; + var data = { + "blocks": extraBlocks.concat([ + { + "_containsChildrenIds": [], + "group": "click", + "id": "5fc8ea23-bac4-3a4e-0f37-b9ed6f4b340f", + "declaredClass": "xblox.model.variables.VariableSwitch", + "name": "Switch on Variable", + "icon": "", + "variable": "PowerState", + "isCommand": false, + "enabled": true, + "shareTitle": "", + "allowActionOverride": true, + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added" + }, + { + "_containsChildrenIds": [], + "name": "PowerState", + "send": "nada", + "group": "Variables", + "id": "31c98cdd-02a8-3af1-3a49-11955c0fad48", + "declaredClass": "xcf.model.Variable", + "gui": "off", + "cmd": "off", + "save": false, + "target": "None", + "type": "added", + "value": "off", + "register": true, + "readOnly": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + }, + { + "group": "click", + "id": "root5", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 5", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + } + ]) + }; + return blockManager.toScope(data); + } + + function createBlockGrid(ctx,parent,mixin,startup){ + + var ACTION = types.ACTION; + var blockManager = ctx.getBlockManager(); + var blockScope = createScope(blockManager, [ + { + "_containsChildrenIds": [], + "id": "83de87c0-f8c7-74da-161d-8e9cf51d67b1", + "name": "value", + "value": "MVMAX 98", + "type": "added", + "group": "processVariables", + "gui": false, + "cmd": false, + "declaredClass": "xcf.model.Variable", + "save": false, + "target": "None", + "register": true, + "readOnly": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [], + "name": "PowerOn", + "send": "PWON", + "group": "basic", + "id": "53a10527-709b-4c7d-7a90-37f58f17c8db", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "text-success fa-circle", + "interval": "0", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "PowerOff", + "send": "PWSTANDBY", + "group": "basic", + "id": "84961334-9cd2-d384-25dc-a6b943e8cb8e", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": "-1", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "text-danger fa-power-off", + "interval": "0", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "VolumeUp", + "send": "return 'MV' + (Math.abs(this.getVariable('Volume')) +2);", + "group": "basic", + "id": "6d0c5e0e-5c04-bb98-44a0-705c8269de07", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": "-1", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "fa-arrow-up", + "interval": "0", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "Volume", + "type": "added", + "value": 59, + "enumType": "VariableType", + "save": false, + "initialize": "", + "group": "basicVariables", + "id": "3403a69e-252a-30dc-b130-40a028d1cde4", + "register": true, + "readOnly": false, + "declaredClass": "xcf.model.Variable", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "icon": "fa-automobile", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "VolumeDown", + "send": "return 'MV' + (this.getVariable('Volume') - 2);", + "group": "basic", + "id": "69f6d4fb-4300-0498-9bbf-27554f5f1fa4", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": "-1", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "fa-arrow-down", + "interval": "0", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "Ping", + "group": "basic", + "id": "a5423bf7-7b99-023d-c637-363fbf9a7f18", + "declaredClass": "xcf.model.Command", + "startup": false, + "send": "pw?", + "interval": "1000", + "waitForResponse": false, + "icon": "fa-bell", + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "auto": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [ + "items" + ], + "name": "Fade-Volume-Down", + "group": "conditional", + "id": "1f969cc6-89c4-f559-e824-daf7dfff35cf", + "items": [ + "c6bc0ef4-5b85-b543-4df7-3c00dd73a9eb" + ], + "declaredClass": "xcf.model.Command", + "startup": false, + "send": "", + "interval": 0, + "icon": "fa-exclamation", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [ + "items" + ], + "condition": "[Volume]>20", + "parentId": "1f969cc6-89c4-f559-e824-daf7dfff35cf", + "id": "c6bc0ef4-5b85-b543-4df7-3c00dd73a9eb", + "declaredClass": "xblox.model.loops.WhileBlock", + "loopLimit": 1500, + "name": "While", + "wait": "10", + "icon": "", + "enabled": true, + "shareTitle": "", + "description": "No Description

sdfsdf", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true, + "items": [ + "0caac742-956e-de6b-6b53-36fb8174e5e0" + ], + "_timer": 3564 + }, + { + "_containsChildrenIds": [ + "items" + ], + "name": "Fade-Volume-Up", + "group": "conditional", + "id": "48a83acf-f2ef-ccdf-44a7-621fb635e3c4", + "declaredClass": "xcf.model.Command", + "startup": false, + "send": "", + "interval": "0", + "icon": "fa-exclamation", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "items": [ + "0a561903-9422-f97d-8a76-0757177a7471" + ], + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [ + "items" + ], + "condition": "[Volume]<80", + "parentId": "48a83acf-f2ef-ccdf-44a7-621fb635e3c4", + "id": "0a561903-9422-f97d-8a76-0757177a7471", + "declaredClass": "xblox.model.loops.WhileBlock", + "loopLimit": 1500, + "name": "While", + "wait": "10", + "icon": "", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true, + "items": [ + "660a553b-ff5d-ca79-c114-955800c8de76" + ], + "_timer": 2740 + }, + { + "_containsChildrenIds": [], + "name": "PowerState", + "send": "nada", + "group": "basicVariables", + "id": "31c98cdd-02a8-3af1-3a49-11955c0fad48", + "declaredClass": "xcf.model.Variable", + "gui": "off", + "cmd": "off", + "save": false, + "target": "None", + "type": "added", + "value": "off", + "register": true, + "readOnly": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [], + "name": "Ping - Volume", + "group": "basic", + "id": "dec01610-0355-f038-71c8-a46b2cde5fd2", + "declaredClass": "xcf.model.Command", + "startup": false, + "send": "MV?", + "interval": "1000", + "waitForResponse": false, + "icon": "fa-exclamation", + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "auto": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "VolumeNormal", + "send": "MV?", + "group": "basic", + "id": "963944c2-c0b0-fe8a-504e-1b8bedd2a3cf", + "declaredClass": "xcf.model.Command", + "startup": true, + "auto": true, + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "interval": "2000", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [ + "items" + ], + "group": "conditionalProcess", + "id": "43062d7e-6cfd-040c-34bf-7a01c428c057", + "items": [ + "1c8b8909-5fb8-2df5-2937-03799f54c3df", + "ac702535-d89b-3072-63c1-6774fe7b1a87" + ], + "declaredClass": "xblox.model.variables.VariableSwitch", + "name": "Switch on Variable", + "icon": "", + "variable": "31c98cdd-02a8-3af1-3a49-11955c0fad48", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "comparator": "==", + "expression": "on", + "id": "1c8b8909-5fb8-2df5-2937-03799f54c3df", + "declaredClass": "xblox.model.logic.CaseBlock", + "name": "Case", + "icon": "", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "parentId": "43062d7e-6cfd-040c-34bf-7a01c428c057", + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "comparator": "==", + "expression": "off", + "id": "ac702535-d89b-3072-63c1-6774fe7b1a87", + "declaredClass": "xblox.model.logic.CaseBlock", + "name": "Case", + "icon": "", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "parentId": "43062d7e-6cfd-040c-34bf-7a01c428c057", + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "group": "conditionalProcess", + "condition": "[Volume]>60", + "id": "4d6f219e-fb31-6184-f238-447a8b0ff616", + "declaredClass": "xblox.model.logic.IfBlock", + "autoCreateElse": true, + "name": "if", + "icon": "", + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "Volume-Loud", + "group": "basicVariables", + "id": "b16bfc09-e449-d514-dc98-10b8afbb14f8", + "declaredClass": "xcf.model.Variable", + "gui": "off", + "cmd": "off", + "save": false, + "target": "None", + "value": "65", + "register": true, + "readOnly": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [], + "name": "No Title", + "group": "basic", + "id": "12cdb13e-9c72-2079-8791-87402763c720", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": false, + "send": "return 'MV' + ([Volume] + 20);", + "interval": "4000", + "waitForResponse": false, + "icon": "fa-exclamation", + "isCommand": false, + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "Test", + "group": "basicVariables", + "id": "487b9e80-ca4c-c096-0950-91aad9dd4612", + "declaredClass": "xcf.model.Variable", + "gui": "off", + "cmd": "off", + "save": false, + "target": "None", + "value": "return [Volume] + 2;", + "register": true, + "readOnly": false, + "isCommand": false, + "enabled": true, + "shareTitle": "", + "allowActionOverride": true, + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "fa-play" + }, + { + "_containsChildrenIds": [], + "condition": "", + "parentId": "c6bc0ef4-5b85-b543-4df7-3c00dd73a9eb", + "id": "0caac742-956e-de6b-6b53-36fb8174e5e0", + "declaredClass": "xblox.model.functions.CallBlock", + "command": "69f6d4fb-4300-0498-9bbf-27554f5f1fa4", + "icon": "", + "_timeout": 100, + "isCommand": false, + "enabled": true, + "shareTitle": "", + "allowActionOverride": true, + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added" + }, + { + "_containsChildrenIds": [], + "id": "Shell-Block", + "description": "Runs a JSON-RPC-2.0 method on the server", + "name": "Run Server Method", + "method": "XShell::run", + "args": "ls", + "deferred": true, + "defaultServiceClass": "XShell", + "defaultServiceMethod": "run", + "declaredClass": "xblox.model.server.RunServerMethod", + "enabled": true, + "shareTitle": "", + "canDelete": true, + "order": 0, + "type": "added", + "group": "click" + } + ]); + + var grid, + store = blockScope.blockStore, + gridArgs = utils.mixin({ + ctx: ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect: true, + collection: store.filter({ + group: "click" + }), + _permissions: [ + ACTION.CLIPBOARD, + 'Step/Properties', + ACTION.TOOLBAR, + //ACTION.EDIT, + ACTION.RENAME, + ACTION.RELOAD, + ACTION.DELETE, + ACTION.CLIPBOARD, + ACTION.LAYOUT, + ACTION.COLUMNS, + ACTION.SELECTION, + //ACTION.PREVIEW, + ACTION.SAVE, + ACTION.SEARCH, + 'Step/Run', + 'Step/Move Up', + 'Step/Move Down', + 'Step/Edit' + //ACTION.TOOLBAR + + + ], + __getBlockActions: function (permissions) { + + var result = [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + VISIBILITY = types.ACTION_VISIBILITY, + thiz = this, + container = thiz.domNode, + actionStore = thiz.getActionStore(); + + function addAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable) { + + var action = null; + mixin = mixin || {}; + utils.mixin(mixin, {owner: thiz}); + + if (mixin.addPermission || DefaultActions.hasAction(permissions, command)) { + + if (!handler) { + handler = function (action) { + console.log('log run action', arguments); + var who = this; + if (who.runAction) { + who.runAction.apply(who, [action]); + } + } + } + + if (!mixin.tooltip && keycombo) { + + if (_.isString(keycombo)) { + keycombo = [keycombo]; + } + + + mixin.tooltip = keycombo.join('
').toUpperCase(); + + } + + + action = DefaultActions.createAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable, thiz.domNode); + result.push(action); + return action; + } + } + + + + + var rootAction = 'Block/Insert'; + var defaultMixin = { addPermission: true }; + + result.push(thiz.createAction({ + label: 'New Block', + command: rootAction, + icon: 'fa-plus', + tab: 'Home', + group: 'Step', + keycombo: ['alt up'], + mixin: defaultMixin + })); + result.push(thiz.createAction({ + label: 'Save', + command: 'File/Save', + icon: 'fa-save', + tab: 'Home', + group: 'File', + keycombo: ['ctrl '], + mixin: defaultMixin + })); + result.push(thiz.createAction({ + label: 'Save As', + command: 'File/Save As', + icon: 'fa-save', + tab: 'Home', + group: 'File', + mixin: defaultMixin + })); + + var newBlockActions = this.getAddActions(); + var levelName = ''; + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + result.push(thiz.createAction({ + label: levelName, + command: path, + icon: item.iconClass, + tab: 'Home', + group: 'Step', + mixin:defaultMixin + })); + + /* + addAction(levelName, path, item.iconClass, null, 'Home', 'Insert', 'item|view', null, null, { + item: item, + addPermission: true + }, null, null); + */ + + + if (isContainer) { + addItems(path, item.items); + } + } + + } + + addItems(rootAction, newBlockActions); + + + + function _selection() { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return null; + } + var item = selection[0]; + if (!item) { + console.error('have no item'); + return null; + } + return selection; + } + + function _canMove() { + + var selection = _selection(); + if (!selection) { + return true; + } + return selection[0].canMove(item, this.command === 'Step/Move Up' ? -1 : 1); + + } + + + function canParent() { + + var selection = thiz.getSelection(); + if (!selection) { + return true; + } + var item = selection[0]; + if (!item) { + //console.error('canParent:have no item!!!',this); + return true; + } + if (this.command === 'Step/Move Left') { + return !item.getParent(); + } else { + return item.getParent(); + } + } + + function isItem() { + var selection = _selection(); + if (!selection) { + return true; + } + return false; + } + + function canMove() { + var selection = _selection(); + + if (!selection) { + return true; + } + + var item = selection[0]; + + if (this.command === 'Step/Move Up') { + return !item.canMove(null, -1); + } else { + return !item.canMove(null, 1); + } + + return false; + } + + result.push(thiz.createAction({ + label: 'Run', + command: 'Step/Run', + icon: 'fa-play', + tab: 'Home', + group: 'Step', + keycombo: ['r'], + shouldDisable: isItem + })); + + ////////////////////////////////////////////////////////////////// + // + // Step - Move + // + + result.push(thiz.createAction({ + label: 'MoveUp', + command: 'Step/Move Up', + icon: 'fa-arrow-up', + tab: 'Home', + group: 'Step', + keycombo: ['alt up'], + shouldDisable: canMove + })); + + result.push(thiz.createAction({ + label: 'MoveDown', + command: 'Step/Move Down', + icon: 'fa-arrow-down', + tab: 'Home', + group: 'Step', + keycombo: ['alt down'], + shouldDisable: canMove, + mixin: defaultMixin + })); + + result.push(thiz.createAction({ + label: 'MoveLeft', + command: 'Step/Move Left', + icon: 'fa-arrow-left', + tab: 'Home', + group: 'Step', + keycombo: ['alt left'], + shouldDisable: canMove, + mixin: defaultMixin + })); + + result.push(thiz.createAction({ + label: 'MoveRight', + command: 'Step/Move Right', + icon: 'fa-arrow-right', + tab: 'Home', + group: 'Step', + keycombo: ['alt right'], + shouldDisable: canParent, + mixin: defaultMixin + })); + + + //Step enable/disable + /* + addAction('On', 'Step/Enable', 'fa-toggle-off', ['alt d'], 'Home', 'Step', 'item|view', null, null, + { + addPermission:true, + onCreate: function (action) { + action.setVisibility(types.ACTION_VISIBILITY.RIBBON, { + widgetClass: declare.classFactory('_Checked', [ToggleButton,_ActionValueWidgetMixin], null, {} ,null), + widgetArgs: { + icon1: 'fa-toggle-on', + icon2: 'fa-toggle-off', + delegate: thiz, + checked:true, + iconClass:'fa-toggle-on' + } + }); + } + },null, function(){ + return thiz.getSelection().length==0; + }); + */ + + result.push(thiz.createAction({ + label: 'On', + command: 'Step/Enable', + icon: 'fa-toggle-off', + tab: 'Home', + group: 'Step', + keycombo: ['alt d'], + shouldDisable: isItem, + mixin: defaultMixin + })); + + + result.push(this.createAction({ + label: 'Edit', + command: 'Step/Edit', + icon: ACTION_ICON.EDIT, + keycombo: ['f4', 'enter', 'dblclick'], + tab: 'Home', + group: 'Step', + shouldDisable: isItem + })); + + /* + addAction('Properties', 'Step/Properties', 'fa-gears', ['alt enter'], 'Home', 'Step', 'item|view', null, null, + { + addPermission: true, + onCreate: function (action) { + action.handle=false; + action.setVisibility(types.ACTION_VISIBILITY.RIBBON, { + widgetClass: declare.classFactory('_Checked', [ToggleButton, _ActionValueWidgetMixin], null, {}, null), + widgetArgs: { + icon1: 'fa-toggle-on', + icon2: 'fa-toggle-off', + delegate: thiz, + checked: false, + iconClass: 'fa-toggle-off' + } + }); + } + }, null, function () { + return thiz.getSelection().length == 0; + }); + */ + result.push(thiz.createAction({ + label: 'Properties', + command: 'Step/Properties', + icon: 'fa-arrow-up', + tab: 'Home', + group: 'Step', + keycombo: ['alt enter'], + shouldDisable: isItem, + mixin: defaultMixin + })); + + this._emit('onAddActions', { + actions: result, + addAction: addAction, + permissions: permissions, + store: actionStore + }); + + //console.dir(_.pluck(result,'command')); + + + return result; + } + },mixin); + + grid = utils.addWidget(BlockGrid, gridArgs, null, parent, startup!==false ? true : false); + + return grid; + + + + } + + Module.createScope = createScope; + Module.createBlockGrid = createBlockGrid; + + return Module; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/bak/Test.js b/packages/xblox/ref-control-freak/xblox/tests/bak/Test.js new file mode 100644 index 00000000..7f2fd641 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/bak/Test.js @@ -0,0 +1,1656 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'xide/widgets/Ribbon', + 'xaction/DefaultActions', + "xblox/widgets/BlockGridRowEditor", + + 'dgrid/Editor', + + + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + + 'xide/factory', + + + 'dijit/Menu', + + 'xide/data/Reference', + + + + + 'xide/docker/Docker', + + + "xide/views/CIViewMixin", + + 'xide/layout/TabContainer', + + + "dojo/has!host-browser?xblox/views/BlockEditDialog" + + +], function (declare, types, + utils, ListRenderer, TreeRenderer, Grid, MultiRenderer, Ribbon, DefaultActions, BlockGridRowEditor, + Editor,Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, factory,Menu,Reference, + Docker,CIViewMixin,TabContainer, + + BlockEditDialog) { + + function patchToolbar(ribbon) { + + return ; + + ribbon._renderAction = function (action, where, widgetClass, showLabel) { + + var thiz = this; + + + //if (action && action.show !== false) { + + var actionVisibility = action.getVisibility != null ? action.getVisibility(this.visibility) : {}; + + //case when visibility is not an object, upgrade visibility to an object + if (!_.isObject(actionVisibility) && action.setVisibility) { + actionVisibility = {}; + + action.setVisibility(this.visibility, actionVisibility); + + } + + //pre-rendered widget + if (actionVisibility._widget) { + + utils.addChild(where, actionVisibility._widget); + + domClass.add(actionVisibility._widget.domNode, utils.replaceAll('/', '-', action.command)); + + actionVisibility.widget = actionVisibility._widget;//for clean up + + thiz._onDidCreateWidget(action, actionVisibility, actionVisibility._widget); + + return; + } + + //render only a widget for this action if not already done and the actions isn't a widget itself=dirty yet + if (!actionVisibility.widget && !action.domNode) { + + if (!action.label) {//huh, really? @TODO: get rid of label needs in action:render + } else { + actionVisibility.widget = thiz._addMenuItem(action, where, actionVisibility.widgetClass || widgetClass || thiz.widgetClass, showLabel); + actionVisibility.widget.visibility = thiz.visibility; + } + } else { + console.log('have already rendered action',action); + } + + return actionVisibility.widget; + + + } + + + /** + * + * @param action + * @param where + * @param widgetClass + * @param showLabel + * @returns {*} + */ + ribbon.renderAction = function (action, where, widgetClass, showLabel) { + + + /** + * Collect variables + */ + + var thiz = this, + _items = action.getChildren ? action.getChildren() : action.items, + _hasItems = _items && _items.length, + isContainer = _hasItems, + _expand = thiz.getVisibilityField(action, 'expand') == true, + _forceSub = action.forceSubs, + + widget = thiz.getVisibilityField(action, 'widget'), + parentAction = action.getParent ? action.getParent() : null, + parentWidget = parentAction ? thiz.getVisibilityField(parentAction, 'widget') : null, + actionVisibility = action.getVisibility != null ? action.getVisibility(this.visibility) : {}; + + + + + /** + * Cleanup variables, sanitize + */ + + //case when visibility is not an object, upgrade visibility to an object + if (!_.isObject(actionVisibility) && action.setVisibility) { + actionVisibility = {}; + action.setVisibility(this.visibility, actionVisibility); + } + + //further variables + var label = actionVisibility.label || action.label, + + widgetArgs = { + iconClass: action.icon, + label:label, + item:action + }; + + if(actionVisibility.widgetArgs){ + utils.mixin(widgetArgs,actionVisibility.widgetArgs); + } + + + + /** + * Determine widget class to use, resolve + */ + widgetClass = + //override + actionVisibility.widgetClass || + //argument + widgetClass || + //visibility's default + thiz.widgetClass; + + + // case one: isContainer = true + if(!widget) { + + if (isContainer) { + + if(_expand){ + return null; + } + + + console.log('render container ' + action.command); + + if(!parentWidget) { + + var menu = utils.addWidget(DropDownButton, widgetArgs, this, where, true, null, [Reference]); + + var pSubMenu = utils.addWidget(Menu, { + item: action + }, this, null, true); + + menu.dropDown = pSubMenu; + actionVisibility.widget = pSubMenu; + return menu; + + }else{ + + //parent widget there + + var pSubMenu = new Menu({parentMenu: parentWidget}); + + var popup = new dijit.PopupMenuItem({ + label: label, + popup: pSubMenu, + iconClass: action.icon + }); + + parentWidget.addChild(popup); + + actionVisibility.widget = pSubMenu; + return pSubMenu; + + } + } + if(parentWidget){ + + where = parentWidget; + widgetClass = MenuItem; + } + }else{ + console.log('widget already rendered!'); + } + + + console.log('render action ' + action.command, { + widgetClass: widgetClass ? widgetClass.prototype.declaredClass : 'no class', + hasItems: _hasItems, + expand: _expand, + widget: widget, + parentAction: parentAction, + parentWidget: parentWidget, + where: where + }); + + + if (!actionVisibility.widget && !action.domNode) { + + if (!action.label) {//huh, really? @TODO: get rid of label needs in action:render + } else { + actionVisibility.widget = thiz._addMenuItem(action, where, widgetClass, showLabel); + actionVisibility.widget.visibility = thiz.visibility; + } + } else { + console.log('have already rendered action',action); + } + + + /*var _lastWidget = thiz._renderAction(action, where, widgetClass, showLabel);*/ + + + return actionVisibility.widget; + + } + + /** + * Renders a set of actions into is appropriate widgets. This utilizes this._renderAction and + * puts the action's widget into its visibility store for global access. + * + * @param actions {xaction/Action[]} + * @param where {dijit/_Widget} + * @param widgetClass {Module} + * @private + */ + ribbon._renderActions = function (actions, where, widgetClass, showLabel, separatorClass, force) { + + + + //track the last action group for adding a separator + var _lastGroup = null, + thiz = this, + widgets = [], + _lastWidget; + + if (!actions) { + console.error('strange, have no actions!'); + return; + } + + for (var i = 0; i < actions.length; i++) { + + var action = actions[i], + sAction = action._store ? action._store.getSync(action.command) : null; + + if (sAction && sAction != action) { + console.log('weird!'); + } + if (sAction) { + action = sAction; + } + + if (!action) { + console.error('invalid action!'); + continue; + } + + + + + //pick command group + if (i == 0 && !_lastGroup && action.group) { + _lastGroup = action.group; + } + + //skip if action[visibility].show is false + if (thiz.getVisibilityField(action, 'show') === false) { + continue; + } + + var _items = action.getChildren ? action.getChildren() : action.items, + _hasItems = _items && _items.length, + _expand = thiz.getVisibilityField(action, 'expand'), + _forceSub = action.forceSubs; + + + + + //action has its own set of actions + + /* + if ((_hasItems || _forceSub ) && !thiz.getVisibilityField(action, 'widget')) { + if (_expand == true) { + + } else { + thiz.renderSubActions(action, where); + } + continue; + } + */ + + + force = force != null ? force : this.forceRenderSubActions; + + + /* + //check action for a parent widget + var parent = action.getParent ? action.getParent() : null; + if (parent) { + + var parentWidget = thiz.getVisibilityField(parent, 'widget'); + + if (parentWidget && force !== true && _expand !== true) { + console.log('skip action, has parent and force not TRUE'); + continue; + } + } + */ + + + //console.log('render action: '+action.command,[actions,action]); + + /*if (action && action.show !== false) {*/ + + + // Insert separator as soon there is a 'group' and the group actually changed + /* + if (!_.isEmpty(action.group) && action.group !== _lastGroup) { + + var children = where.getChildren(); + + if (children.length > 0 && children[children.length - 1].prototype != thiz.separatorClass.prototype) { + + var separator = utils.addWidget(separatorClass || thiz.separatorClass, {}, null, where, true); + + var widgetArgs = thiz.getVisibilityField(action, 'widgetArgs'); + if (widgetArgs && widgetArgs.style && widgetArgs.style.indexOf('float:right') != -1) { + domStyle.set(separator.domNode, { + float: 'right' + }); + } + + } + _lastGroup = action.group; + } + */ + + + _lastWidget = thiz.renderAction(action, where, widgetClass, showLabel); + + + /* + _lastWidget = thiz._renderAction(action, where, widgetClass, showLabel); + if (_lastWidget) { + widgets.push(_lastWidget); + } + */ + + /*}*/ + } + + return widgets; + } + + } + + /** + * + */ + var _layoutMixin = { + _docker:null, + _parent:null, + __right:null, + /** + * @param container + * @returns {module:xdocker/Docker2} + */ + getDocker:function(container){ + + if(!this._docker){ + + var _dst = container || this._domNode.parentNode, + thiz = this; + + thiz._docker = Docker.createDefault(_dst); + thiz._oldParent = thiz._parent; + + var parent = thiz._docker.addPanel('DefaultFixed', types.DOCKER.TOP, null, { + w: '100%', + title:' ' + }); + + dojo.place(thiz._domNode,parent.containerNode); + + thiz._docker.$container.css('top',0); + thiz._parent = parent; + + parent._parent.showTitlebar(false); + + +/* + var right = this._docker.addPanel('Collapsible', types.DOCKER.RIGHT, parent, { + w: '20%', + title:'Properties' + }); + + parent._parent.showTitlebar(false);*/ + + + + + + //parent._parent.$center.css('top',0); + + } + + return thiz._docker; + }, + getRightPanel:function(){ + + if(this.__right){ + return this.__right; + } + + var docker = this.getDocker(); + + + var right = docker.addPanel('Collapsible', types.DOCKER.RIGHT, this._parent, { + w: '300px', + title:' ' + }); + + + + right._parent.showTitlebar(false); + + var splitter = right.getSplitter(); + + splitter.pos(0.6); + + + this.__right = right; + + return right; + } + } + + var _gridBase = { + currentCIView:null, + targetTop:null, + lastSelectedTopTabTitle:'General', + editBlock:function(item,changedCB,select){ + + + var selection = this.getSelection(), + item=null; + if(selection.length == 1){ + item = selection[0]; + } + + if(!item){ + return; + } + + if(!item.canEdit()){ + return; + } + var title='Edit ', + thiz=this; + if(item.title){ + title+=item.title; + }else if(item.name){ + title+=item.name; + } + try { + var actionDialog = new BlockEditDialog({ + item: item, + title: title, + style: 'width:400px', + resizeable: true, + delegate: { + onOk: function (dlg, data) { + + //item.scope.expression. + + if (changedCB) { + changedCB(item); + } + + /** + * triggers to refresh block grid views + */ + thiz.publish(types.EVENTS.ON_BLOCK_PROPERTY_CHANGED,{ + item:item + }); + + /** + * update block tree view! + */ + + if(select!==false) { + + thiz.onBlockSelected({ + item: item, + owner: this.currentCIView + }); + } + + } + } + }); + }catch(e){ + //debugger; + } + actionDialog.show(); + actionDialog.resize(); + }, + execute: function (block) { + + if (!block || !block.scope) { + console.error('have no scope'); + return; + } + try { + var result = block.scope.solveBlock(block, { + highlight: true, + force: true + }); + } catch (e) { + console.error(' excecuting block - ' + block.name + ' failed! : ' + e); + console.error(printStackTrace().join('\n\n')); + } + return true; + }, + move: function (dir) { + + var item = this.getSelection()[0]; + + if (!item || !item.parentId) { + + console.log('cant move, no selection or parentId', item); + + return; + + } + + //parent.items = items.swap(item,upperItem); + + console.log('move ', item); + + + try { + item.move(item, dir); + //this.onItemAction(true); + console.log('move up to' + item.name); + } catch (e) { + debugger; + } + + + + }, + runAction: function (action) { + + switch (action.command) { + case 'Step/Run': + { + return this.execute(this.getSelection()[0]); + } + case 'Step/Move Up': + { + return this.move(-1); + } + case 'Step/Move Down': + { + return this.move(1); + } + case 'Step/Edit': + { + return this.editBlock(); + } + } + + + }, + getAddActions: function (item) { + + var thiz = this; + + + var items = []; + this.showAllBlocks = true; + if (this.showAllBlocks || item) { + + var variables = this.blockScope.getVariables(); + var variableItems = []; + /* + for (var i = 0; i < variables.length; i++) { + variableItems.push({ + name: variables[i].title, + target: item, + iconClass: 'el-icon-compass', + proto: VariableAssignmentBlock, + item: variables[i], + ctrArgs: { + variable: variables[i].title, + scope: this.blockScope, + value: '0' + } + }); + }*/ + items = factory.getAllBlocks(this.blockScope, this, item, this.blockGroup, false); + items.push({ + name: 'Set Variable', + target: item, + iconClass: 'el-icon-pencil-alt', + items: variableItems + }); + + //tell everyone + factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items + }); + return items; + } + + + /* + if (!item) { + + if (this.newRootItemFunction) { + items.push({ + name: this.newRootItemLabel, + iconClass: this.newRootItemIcon, + handler: function () { + thiz.newRootItemFunction(); + thiz.onItemAction(); + } + }); + return items; + } + } + */ + + }, + showProperties:function(item){ + + + var block = item, + thiz = this; + + var right = this.getRightPanel(); + + if(item == this._lastItem){ + return; + } + + this._lastItem = item; + + var tabContainer = this.targetTop; + + + if(!tabContainer) { + + tabContainer = utils.addWidget(TabContainer, { + delegate: this, + tabStrip: true, + tabPosition: "top", + attachParent: true, + style: "width:100%;height:100%;overflow-x:hidden;", + allowSplit: false + }, null,right.containerNode, true); + this.targetTop =tabContainer; + } + + if (tabContainer.selectedChildWidget) { + this.lastSelectedTopTabTitle = tabContainer.selectedChildWidget.title; + }else{ + this.lastSelectedTopTabTitle = 'General'; + } + + + + _.each(tabContainer.getChildren(),function(tab){ + tabContainer.removeChild(tab); + }); + + if (this.currentCIView){ + this.currentCIView.empty(); + } + + + + + // clear tab - container + /* + if (this.currentCIView && this.currentCIView.tabs) { + + if(this.currentCIView.tabContainer._destroyed){ + this.currentCIView=null; + }else { + this.currentCIView.empty(); + + try { + for (var i = 0; i < this.currentCIView.tabs.length; i++) { + this.targetTop.removeChild(this.currentCIView.tabs[i]); + } + } catch (e) { + //should not happen! + console.error('clear failed: ' + e, e); + console.trace(); + } + } + } + */ + + if(!block.getFields){ + console.log('have no fields',block); + return; + } + + var cis = block.getFields(); + for (var i = 0; i < cis.length; i++) { + cis[i].vertical = true; + } + + + + + + + + var ciView = new CIViewMixin({ + tabContainer: this.targetTop, + delegate: this, + viewStyle: 'padding:0px;', + autoSelectLast: true, + item: block, + source: this.callee, + options: { + groupOrder: { + 'General': 0, + 'Advanced': 1, + 'Description': 2 + } + }, + cis: cis + }); + ciView.initWithCIS(cis); + + this.currentCIView = ciView; + + if(block.onFieldsRendered){ + block.onFieldsRendered(block,cis); + } + + ciView._on('valueChanged',function(evt){ + console.log('ci value changed ',evt); + }); + + + var containers = this.targetTop.getChildren(); + var descriptionView = null; + for (var i = 0; i < containers.length; i++) { + + // @TODO : why is that not set? + containers[i].parentContainer = this.targetTop; + + // track description container for re-rooting below + if (containers[i].title === 'Description') { + descriptionView = containers[i]; + } + + if(this.targetTop.selectedChildWidget.title!==this.lastSelectedTopTabTitle) { + if (containers[i].title === this.lastSelectedTopTabTitle) { + this.targetTop.selectChild(containers[i]); + } + } + } + + + this._docker.resize(); + + + + /* + if(this.targetBottom && this.targetBottom._destroyed){ + utils.destroy(this.targetBottom); + this.targetBottom=null; + } + + + if(!this.targetBottom) { + // Re root description view into right bottom panel + this.targetBottom = this.getRightBottomTarget(true, false); + } + */ + + + +/* + for (var i = 0; i < cis.length; i++) { + if(cis[i].select === true && cis[i].view){ + this.targetTop.selectChild(cis[i].view); + } + } + */ + + + + /* + if (descriptionView && this.targetBottom) { + this.targetBottom.destroyDescendants(false); + this.currentCIView.tabs.remove(descriptionView); + descriptionView.parentContainer.removeChild(descriptionView); + this.targetBottom.addChild(descriptionView); + var main = this.getLayoutRightMain(false, false); + main.resize(); + } + */ + + + if (this.blockTreeView) { + + } else { + /* + if(this.renderNewTab) { + + var main = this.ctx.getApplication().mainView; + if (main) { + main._openRight(); + } + this.blockTreeView = this.createBlockTreeView(this.targetTop, 'New'); + } + */ + } + + + + }, + startup:function(){ + + this.inherited(arguments); + + this._on('selectionChanged',function(evt){ + + var selection = evt.selection, + thiz = this; + + if(selection && selection[0]){ + thiz.showProperties(selection[0]); + } + }); + + } + }; + + /*** + * playground + */ + var _lastGrid = window._lastGrid; + var ctx = window.sctx, + parent, + ACTION = types.ACTION, + root; + + + var _actions = [ + ACTION.RENAME + ]; + + /** + * + */ + function getFileActions(permissions) { + + + var result = [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + VISIBILITY = types.ACTION_VISIBILITY, + thiz = this, + actionStore = thiz.getActionStore(); + + + function addAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable) { + + var action = null; + if (DefaultActions.hasAction(permissions, command)) { + + mixin = mixin || {}; + + utils.mixin(mixin, {owner: thiz}); + + if (!handler) { + + handler = function (action) { + console.log('log run action', arguments); + var who = this; + if (who.runAction) { + who.runAction.apply(who, [action]); + } + } + } + action = DefaultActions.createAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable, thiz.domNode); + + result.push(action); + return action; + + } + } + + + var rootAction = 'Block/Insert'; + permissions.push(rootAction); + addAction('Block', rootAction, 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + dummy: true, + onCreate: function (action) { + action.setVisibility(VISIBILITY.CONTEXT_MENU, { + label: 'Add' + }); + + } + }, null, null); + + permissions.push('Block/Insert Variable'); + + + addAction('Variable', 'Block/Insert Variable', 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + }, null, null); + /* + permissions.push('Clipboard/Paste/New'); + addAction('New ', 'Clipboard/Paste/New', 'el-icon-plus-sign', null, 'Home', 'Clipboard', 'item|view', null, null, { + }, null, null);*/ + + + var newBlockActions = this.getAddActions(); + var addActions = []; + var levelName = ''; + + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + permissions.push(path); + + addAction(levelName, path, item.iconClass, null, 'Home', 'Insert', 'item|view', null, null, {}, null, null); + + + if (isContainer) { + addItems(path, item.items); + } + + + } + + } + + + + //console.clear(); + + addItems(rootAction, newBlockActions); + + + //console.dir(newBlockActions); + + + /* + for (var i = 0; i < newBlockActions.length; i++) { + + + //top level items + var item = newBlockActions[i]; + + if(_.isEmpty(item.items)){ + continue; + } + + levelName = item.name; + + var path = rootAction + '/' + levelName; + + permissions.push(path); + + addAction(levelName,path,item.iconClass,null,'Home','Insert','item|view',null,null,{ + dummy:true + },null,null); + + //now the items + + + + addItems(path,item.items); + + + }*/ + + + console.log('new items', newBlockActions); + + //return result; + + //run + function canMove(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + var canMove = item.canMove(item, this.command === 'Step/Move Up' ? -1 : 1); + return !canMove; + + } + + + function isItem(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + return false; + + } + + /** + * run + */ + addAction('Run', 'Step/Run', 'el-icon-play', ['space'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + /* + action.setVisibility(types.ACTION_VISIBILITY_ALL, { + label: '' + });*/ + } + }, null, isItem); + + permissions.push('Step/Run/From here'); + + /** + * run + */ + addAction('Run from here', 'Step/Run/From here', 'el-icon-play', ['ctrl space'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + /* + action.setVisibility(types.ACTION_VISIBILITY_ALL, { + label: '' + }); + */ + } + }, null, isItem); + + + /** + * move + */ + addAction('Move Up', 'Step/Move Up', 'fa-arrow-up', ['ctrl up'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canMove); + + addAction('Move Down', 'Step/Move Down', 'fa-arrow-down', ['ctrl down'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + } + }, null, canMove); + + + + permissions.push('Step/Edit'); + + addAction('Edit', 'Step/Edit', ACTION_ICON.EDIT, ['f4', 'enter'], 'Home', 'Step', 'item', null, null, null, null, isItem); + /////////////////////////////////////////////////// + // + // Editors + // + /////////////////////////////////////////////////// + + return result; + + } + + if (ctx) { + + + var blockManager = ctx.getBlockManager(); + + + var doTest = true; + + + //var xfileCtx = ctx.getXFileContext(); + function createScope(mount) { + + var data = { + "blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Run Script", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + } + ], + "variables": [] + }; + + return blockManager.toScope(data); + } + + + var ribbon = ctx.mainView.getToolbar(); + patchToolbar(ribbon); + + + function createGridClass() { + + var renderers = [TreeRenderer]; + + //, ThumbRenderer, TreeRenderer + + + var multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + + + + + + + var _gridClass = Grid.createGridClass('driverTreeView', + { + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + SPLIT:{ + CLASS:declare('splitMixin',null,_layoutMixin) + } + + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + EDITOR: Editor, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + + + return declare('gridFinal', _gridClass, _gridBase); + + } + + var blockScope = createScope('docs'); + + + var mainView = ctx.mainView; + + var docker = mainView.getDocker(); + + + if (mainView) { + + + if (_lastGrid) { + + docker.removePanel(_lastGrid); + } + + var parent = docker.addTab(null, { + title: 'blox', + icon: 'fa-folder' + }); + + + //var themeRoot = require.toUrl('xide/docker'); + + //var docker = Docker.createDefault(master.containerNode); + + + window._lastGrid = parent; + + + +/* + parent = docker.addPanel('DefaultFixed', types.DOCKER.TOP, null, { + w: '80%', + title:'' + }); + + + var right = docker.addPanel('Collapsible', types.DOCKER.LEFT, parent, { + w: '20%', + title:'Properties' + }); + */ + + + var actions = [], + thiz = this, + ACTION_TYPE = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + grid, + ribbon; + + + var store = blockScope.blockStore; + + + var _gridClass = createGridClass(); + + /* + grid = new _gridClass({ + + blockScope: blockScope, + blockGroup: 'click', + shouldShowAction: function (action) { + return true; + }, + collection: store.filter({ + group: "click" + }), + tabOrder: { + 'Home': 100, + 'View': 50 + }, + groupOrder: { + + + 'Clipboard': 110, + 'File': 100, + 'Step': 80, + 'Open': 70, + 'Organize': 60, + 'Insert': 10, + 'Select': 0 + }, + showHeader: true, + options: utils.clone(types.DEFAULT_GRID_OPTIONS), + _parent: parent, + columns: [ + { + renderExpando: true, label: "Name", field: "name", sortable: false, editor: BlockGridRowEditor + } + ] + }, parent.containerNode); + */ + + var gridArgs = { + ctx:ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect:true, + shouldShowAction: function (action) { + return true; + }, + collection: store.filter({ + group: "click" + }), + tabOrder: { + 'Home': 100, + 'View': 50 + }, + groupOrder: { + + + 'Clipboard': 110, + 'File': 100, + 'Step': 80, + 'Open': 70, + 'Organize': 60, + 'Insert': 10, + 'Select': 0 + }, + showHeader: true, + options: utils.clone(types.DEFAULT_GRID_OPTIONS), + columns: [ + { + renderExpando: true, label: "Name", field: "name", sortable: false, editor: BlockGridRowEditor + } + ] + }; + + grid = utils.addWidget(_gridClass,gridArgs,null,parent,true); + //var grid2 = utils.addWidget(_gridClass,gridArgs,null,right,true); + + + + + + + + grid.startup(); + // grid2.startup(); + + grid.select(['root'], null, true, { + focus: true + }); + var _actions = [ + //ACTION.EDIT, + ACTION.RENAME, + /*ACTION.DOWNLOAD,*/ + ACTION.RELOAD, + ACTION.DELETE, + /*ACTION.NEW_FILE, + ACTION.NEW_DIRECTORY,*/ + ACTION.CLIPBOARD, + ACTION.LAYOUT, + ACTION.COLUMNS, + ACTION.SELECTION, + ACTION.PREVIEW, + 'Step/Run', + 'Step/Move Up', + 'Step/Move Down' + + ]; + /** + * struct + * + + - if .... + - sub0 + - Run-Script + - subsub0 + - else + - sub1 + + - else-if + - sub2 + + + + + */ + + var blocks = blockScope.allBlocks(); + var root = blocks[0]; + + + grid._on('selectionChanged', function (evt) { + + var selection = evt.selection; + var r0 = selection[0]; + var same = root == r0; + + + }); + + + var _defaultActions = DefaultActions.getDefaultActions(_actions, grid); + _defaultActions = _defaultActions.concat(getFileActions.apply(grid, [_actions])); + grid.addActions(_defaultActions); + + + /* + _defaultActions = _defaultActions.concat(grid.getFileActions(_actions)); + grid.addActions(_defaultActions); + + */ + + + var actionStore = grid.getActionStore(); + var toolbar = mainView.getToolbar(); + + if (!toolbar) { + window._lastRibbon = ribbon = toolbar = utils.addWidget(Ribbon, { + store: actionStore, + flat: true, + currentActionEmitter: grid + }, this, mainView.layoutTop, true); + + } else { + toolbar.addActionEmitter(grid); + toolbar.setActionEmitter(grid); + } + + setTimeout(function () { + mainView.resize(); + grid.resize(); + + }, 1000); + + + function test() { + + + + //grid.getRightPanel(); + + + + + //console.clear(); + //c1.set('value', true); + try { + // copy.set('value', true, true); + } catch (e) { + //debugger; + + } + + + grid.refresh(); + + return; + + } + + function test2() { + + return; + + + /* + var c2 = actionStore.getSync('View/Columns/Show Size'); + c2._originEvent='change'; + var _c = c1.get('value'); + */ + + + //console.log('c2 value : ' + _c); + + + try { + //copy.set('value', false, true); + c2.set('value', false); + + + } catch (e) { + //debugger; + } + + return; + + console.log('-------------------------------------------------------------------------'); + + var item = store.getSync('id3'); + var item1 = store.getSync('id1'); + var item2 = store.getSync('id2'); + + + //console.dir(grid._rows); + + + /* + grid.select(item); + grid.focus(item);*/ + + + //grid.select([item,item1,item2]); + //grid.select(item); + grid.select([item1, item2], null, true, { + append: true, + focus: false, + silent: true + + }); + + + /*var isToolbared = grid.hasFeature('TOOLBAR'); + console.warn('has Toolbar ');*/ + + + /* + var nameProperty = item.property('label'); + + var urlProperty = item.property('url'); + + + nameProperty.observe(function () { + console.log('horray!', arguments); + }); + + urlProperty.observe(function () { + console.log('horray url', arguments); + }); + + item.set('label', 'new label'); + item.set('url', null); + + //store.notify(item,'id3'); + store.emit('update', {target: item}); + */ + + + /*grid.refresh();*/ + //console.log('item ', item); + + /* + grid.select(store.getSync('id99'), null, true, { + append: true, + focus: true, + silent:true + });*/ + + var item99 = store.getSync('id99'); + var item98 = store.getSync('id98'); + + grid.select([item99, item98], null, true, { + append: true, + focus: true, + silent: false + }); + + + //console.log('is selected ' + grid.isSelected(item98)); + + //console.dir(grid.getRows(true)); + + /*console.dir(grid.getPrevious(item99, false, true));*/ + /* + console.dir(grid.getSelection(function(item){ + return item.id!='id99'; + }));*/ + + + //console.log(grid.getFocused()); + + /*store.removeSync('id3');*/ + } + + setTimeout(function () { + test(); + }, 4000); + setTimeout(function () { + test2(); + }, 2000); + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/bak/TestClass.js b/packages/xblox/ref-control-freak/xblox/tests/bak/TestClass.js new file mode 100644 index 00000000..c9fa0b70 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/bak/TestClass.js @@ -0,0 +1,1938 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + 'dojo/dom-class', + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/ThumbRenderer', + 'xide/views/_ActionMixin', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dijit/form/RadioButton', + 'xide/widgets/Ribbon', + 'xide/editor/Registry', + 'xaction/DefaultActions', + 'xaction/Action', + "xblox/widgets/BlockGridRowEditor", + + 'dgrid/Editor', + + + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + + 'xide/factory', + + + 'dijit/Menu', + + 'xide/data/Reference', + + 'dijit/form/DropDownButton', + 'dijit/MenuItem', + + 'xide/docker/Docker', + + + "xide/views/CIViewMixin", + + 'xide/layout/TabContainer', + + + "dojo/has!host-browser?xblox/views/BlockEditDialog", + + 'xblox/views/BlockGrid', + + 'xgrid/DnD', + 'xblox/views/BlocksGridDndSource', + 'xblox/widgets/DojoDndMixin', + + + 'xide/registry', + + 'dojo/topic' + + +], function (declare, domClass,types, + utils, ListRenderer, TreeRenderer, ThumbRenderer, + _ActionMixin, + Grid, MultiRenderer, RadioButton, Ribbon, Registry, DefaultActions, Action, BlockGridRowEditor, + Editor,Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, factory,Menu,Reference,DropDownButton, + MenuItem,Docker,CIViewMixin,TabContainer, + + BlockEditDialog, + BlockGrid, + Dnd,BlocksGridDndSource,DojoDndMixin, + + registry,topic + ) { + + + function patchToolbar(ribbon) { + + var _mixin = { + + } + + utils.mixin(ribbon,_mixin); + } + /** + * + */ + var _layoutMixin = { + _docker:null, + _parent:null, + __right:null, + getDocker:function(container){ + + if(!this._docker){ + + var _dst = container || this._domNode.parentNode, + thiz = this; + + thiz._docker = Docker.createDefault(_dst); + thiz._oldParent = thiz._parent; + + var parent = thiz._docker.addPanel('DefaultFixed', types.DOCKER.TOP, null, { + w: '100%', + title:' ' + }); + + dojo.place(thiz._domNode,parent.containerNode); + + thiz._docker.$container.css('top',0); + thiz._parent = parent; + + parent._parent.showTitlebar(false); + + +/* + var right = this._docker.addPanel('Collapsible', types.DOCKER.RIGHT, parent, { + w: '20%', + title:'Properties' + }); + + parent._parent.showTitlebar(false);*/ + + + + + + //parent._parent.$center.css('top',0); + + } + + return thiz._docker; + }, + getRightPanel:function(){ + + if(this.__right){ + return this.__right; + } + + var docker = this.getDocker(); + + + var right = docker.addPanel('Collapsible', types.DOCKER.RIGHT, this._parent, { + w: '300px', + title:' ' + }); + + + + right._parent.showTitlebar(false); + + var splitter = right.getSplitter(); + + splitter.pos(0.6); + + + this.__right = right; + + return right; + } + } + + + var _statusMixin = { + } + + /** + * Dnd impl. + */ + var _dndMixin = { + + } + + + + + var DojoDndMixin = declare("xblox.widgets.DojoDndMixin",null,{ + dropEvent:"/dnd/drop", + dragEvent:"/dnd/start", + overEvent:"/dnd/source/over", + _eDrop:false, + isDragging:false, + didInit:false, + /*** + * Pre-Process DND Events + * @param node + * @param targetArea + * @param indexChild + */ + _calcNewItemList:function(items){ + var res = []; + if(items){ + + for(var i=0 ; i move nodes! + this.onDrop(source, nodes, copy, target); + } + this.onDndCancel(); + } + }); + + + function getFileActions(permissions) { + + + + var result = [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + VISIBILITY = types.ACTION_VISIBILITY, + thiz = this, + actionStore = thiz.getActionStore(); + + + return []; + + function addAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable) { + + var action = null; + if (DefaultActions.hasAction(permissions, command)) { + + mixin = mixin || {}; + + utils.mixin(mixin, {owner: thiz}); + + if (!handler) { + + handler = function (action) { + console.log('log run action', arguments); + var who = this; + if (who.runAction) { + who.runAction.apply(who, [action]); + } + } + } + action = DefaultActions.createAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable, thiz.domNode); + + result.push(action); + return action; + + } + } + + /* + var rootAction = 'Block/Insert'; + permissions.push(rootAction); + addAction('Block', rootAction, 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + dummy: true, + onCreate: function (action) { + action.setVisibility(VISIBILITY.CONTEXT_MENU, { + label: 'Add' + }); + + } + }, null, null); + permissions.push('Block/Insert Variable'); + + + addAction('Variable', 'Block/Insert Variable', 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + }, null, null); + */ + + /* + permissions.push('Clipboard/Paste/New'); + addAction('New ', 'Clipboard/Paste/New', 'el-icon-plus-sign', null, 'Home', 'Clipboard', 'item|view', null, null, { + }, null, null);*/ + + + var newBlockActions = this.getAddActions(); + var addActions = []; + var levelName = ''; + + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + permissions.push(path); + + addAction(levelName, path, item.iconClass, null, 'Home', 'Insert', 'item|view', null, null, {}, null, null); + + + if (isContainer) { + addItems(path, item.items); + } + + + } + + } + //console.clear(); + //addItems(rootAction, newBlockActions); + //return result; + + + //run + function canMove(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + var canMove = item.canMove(item, this.command === 'Step/Move Up' ? -1 : 1); + + return !canMove; + + } + + + function canParent(selection, reference, visibility) { + + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + if(!item){ + console.warn('bad item',selection); + return false; + } + + if(this.command === 'Step/Move Left'){ + return !item.getParent(); + }else{ + return item.getParent(); + } + /* + var canMove = item.canMove(item, this.command === 'Step/Move Left' ? -1 : 1); + return !canMove;*/ + + return true; + + } + + function isItem(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + return false; + + } + + /** + * run + */ + + addAction('Run', 'Step/Run', 'el-icon-play', ['space'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + widgetArgs:{ + label: ' ', + style:'font-size:25px!important;' + } + }); + + } + }, null, isItem); + permissions.push('Step/Run/From here'); + + /** + * run + */ + + addAction('Run from here', 'Step/Run/From here', 'el-icon-play', ['ctrl space'], 'Home', 'Step', 'item', null, null, { + + onCreate: function (action) { + + } + }, null, isItem); + + + + /** + * move + */ + + addAction('Move Up', 'Step/Move Up', 'fa-arrow-up', ['alt up'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canMove); + + + addAction('Move Down', 'Step/Move Down', 'fa-arrow-down', ['alt down'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + } + }, null, canMove); + /* + + + permissions.push('Step/Edit'); + addAction('Edit', 'Step/Edit', ACTION_ICON.EDIT, ['f4', 'enter'], 'Home', 'Step', 'item', null, null, null, null, isItem); + */ + /////////////////////////////////////////////////// + // + // Editors + // + /////////////////////////////////////////////////// + + permissions.push('Step/Move Left'); + permissions.push('Step/Move Right'); + + addAction('Move Left', 'Step/Move Left', 'fa-arrow-left', ['alt left'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canParent); + + addAction('Move Right', 'Step/Move Right', 'fa-arrow-right', ['alt right'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + + } + }, null, canParent); + + + return result; + + } + + function doPost(view){ + + var right = view.getRightPanel(); + var splitter = right.getSplitter(); + //var ori = splitter.orientation(); + //splitter.orientation(false); + //spl + + //splitter.collapse(); + //splitter.expand(); + splitter.minimize = function(){ + + this._isToggledMin = true; + + //save pos + if(this._isToggledMin){ + this._savedPosMin = this.pos(); + } + + var togglePosValue = this._isToggledMin ? 1 : 0; + + if(togglePosValue==1){ + //togglePosValue = this._savedPosMin; + } + + + //console.log('toggle ' + togglePosValue + ' cvalue ' + this.pos()); + + + this.pos(togglePosValue); + + } + //splitter.minimize(); + + } + + function clipboardTest(){ + + this.select(['root2'],null,true,{ + focus:true + }); + + var actionStore = this.getActionStore(); + + var copy = actionStore.getSync('Clipboard/Copy'); + var paste = actionStore.getSync('Clipboard/Paste'); + + console.clear(); + + + this.runAction(copy); + this.runAction(paste); + + //this.refresh(); + + + } + + function unparent(){ + + this.select(['sub0'],null,true,{ + focus:true + }); + this.runAction('Step/Move Left'); + this.refresh(); + } + + function reparent(){ + + this.select(['root3'],null,true,{ + focus:true + }); + this.runAction('Step/Move Right'); + this.refresh(); + } + + + function expand(){ + + + this.select(['root3'],null,true,{ + focus:true + }); + + //clipboardTest.apply(this); + + //reparent.apply(this); + + + return; + + + + //var row = grid.row('root'); + //var _t = this._normalize('root'); + //this.expand(_t); + //var _expanded = this.isExpanded(_t); + //debugger; + //this.isRendered('root'); + var store = this.collection; + //var item = store.getSync('sub0'); + //this._expandTo(item); + + var root = this.collection.getSync('click'); + + + this.select(['root2','root3'],null,true,{ + focus:true + }); + + + var actionStore = this.getActionStore(); + + var moveLeft = actionStore.getSync('Step/Move Left'); + + + var moveUp = actionStore.getSync('Step/Move Up'); + + var moveDown = actionStore.getSync('Step/Move Down'); + + var items = this.collection.storage.fullData; + //console.log('before move',items); + //this.printRootOrder(); + this.runAction(moveUp); + //console.log('after move',this.collection.storage.fullData); + //this._place('root','root2','below'); + //this.printRootOrder(); + + + var thiz = this; + + setTimeout(function(){ + + //thiz.refresh(); + //this.runAction(moveDown); + //thiz.refreshRoot(); + thiz.printRootOrder(); + },1500); + + //this.runAction(moveLeft); + } + + + var _gridBase = { + _place:function(dst,ref,direction,items){ + + + var store = this.collection; + + ref = _.isString(ref) ? store.getSync(ref) : ref; + dst = _.isString(dst) ? store.getSync(dst) : dst; + + items = items || store.storage.fullData; + + direction = direction == -1 ? 0 : 1; + + function index(what){ + for (var j = 0; j < items.length; j++) { + if(what.id===items[j].id){ + return j; + } + } + }; + + function compare(left,right){ + return index(left) - index(right); + } + + items.remove(dst); + if(direction==-1){ + direction=0; + } + items.splice( Math.max(index(ref) + direction,0), 0, dst); + + store._reindex(); + }, + __reParentBlock:function(dir){ + + + var item = this.getSelection()[0]; + + if (!item /*|| !item.parentId*/) { + console.log('cant move, no selection or parentId', item); + return; + } + //console.log('reparenting block',item); + + var thiz = this; + //thiz._preserveSelection(); + + var store = item._store; + + item._next = function(dir){ + + var _dstIndex = 0; + var step = 1; + + var items = this._store.storage.fullData; + + //find item + 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(); + }; + + item.__unparent = function () { + + var item = this; + + if (!item) { + return false; + } + var parent = item.getParent(); + + var items = null; + + var newParent = null; + + if(parent){ + newParent = parent.getParent(); + } + + + + if(newParent){ + + item.group = newParent.group; + + if(newParent.id == thiz.blockGroup){ + item.group = newParent.id; + } + item.parentId = newParent.id; + }else{ + + //var items = store.storage.fullData; + + //console.log('next down - parent ',parent.next(items,1)); + + if (parent && parent.removeBlock) { + parent.removeBlock(item,false); + } + item.group = thiz.blockGroup; + item.parentId = null; + item.parent = null; + } + + /* + var scope = thiz.blockScope; + var t = item._store.getSync('sub0');*/ + + item._place(null,-1,null); + item._place(null,-1,null); + + + //item._place(null,-1,null); + + //console.log('new parent : ' + thiz.blockGroup ,scope); + }; + + item.reparent = function () { + + var item = this; + + if (!item) { + return false; + } + var parent = item.getParent(); + + if(parent){ + }else{ + + var _next = item.next(null,1) || item.next(null,-1); + if(_next){ + item.group=null; + _next._add(item); + thiz.refreshRoot(); + } + } + } + + if(dir==-1) { + item.unparent(thiz.blockGroup); + }else{ + item.reparent(); + } + thiz.deselectAll(); + this.refreshRoot(); + this.refresh(); + setTimeout(function(){ + thiz.select(item,null,true,{ + focus:true + }); + },20); + + + }, + /** + * + * @param mixed + * @param toRow {object} preserve super + * @param select {boolean} preserve super + * @param options {object} + * @param options.focus {boolean} + * @param options.silent {boolean} + * @param options.append {boolean} + */ + /** + * @param menuItem + */ + addItem: function (item,action) { + + + if (!item) { + return; + } + var target = this.getSelection()[0], + thiz = this; + var proto = item.proto; + var ctorArgs = item.ctrArgs; + var where = null; + var newBlock = null; + + + //cache problem: + if(!target){ + var _item = this.getSelection()[0]; + if(_item){ + target=_item; + } + } + + + if(ctorArgs) { + if (ctorArgs.id) { + console.error('addItem:: new block constructor args had id!!!'); + ctorArgs.id = utils.createUUID(); + } + if (ctorArgs.parent) { + ctorArgs.parent = null; + } + if (ctorArgs.parentId) { + ctorArgs.parentId = null; + } + if (ctorArgs.items) { + console.error('addItem:: new block constructor args had items!!!'); + ctorArgs.items = []; + } + } + + if (target && proto && ctorArgs) { + + if (target.owner && target.dstField) { + where = target.dstField; + target = target.owner; + } + + ctorArgs['parentId'] = target.id; + ctorArgs['group'] = null; + ctorArgs['parent'] = target;//should be obselete + + newBlock = target.add(proto, ctorArgs, where); + newBlock.parent = target; + + } else if (!target && proto && ctorArgs) { + + if(ctorArgs.group==="No Group" && this.newRootItemGroup){ + ctorArgs.group = this.newRootItemGroup; + } + if (!ctorArgs.group && this.newRootItemGroup) { + ctorArgs.group = this.newRootItemGroup; + } + if (!ctorArgs.group && this.blockGroup) { + ctorArgs.group = this.blockGroup; + } + + if (ctorArgs.group && this.newRootItemGroup && ctorArgs.group !==this.newRootItemGroup) { + ctorArgs.group = this.newRootItemGroup; + } + + newBlock = factory.createBlock(proto, ctorArgs);//root block + } + + if (newBlock && newBlock.postCreate) { + newBlock.postCreate(); + } + //this.onItemAction(); + + if(!newBlock){ + console.error('didnt create block'); + return; + } + + + var store = this.getStore(target); + + var storeItem = store.getSync(newBlock[store.idProperty]); + if(!storeItem){ + console.error('new block not in store'); + store.putSync(newBlock); + } + + try { + scope._emit(types.EVENTS.ON_ITEM_ADDED, { + item: newBlock, + owner: scope + }); + }catch(e){ + + console.error('error emitting',e); + debugger; + } + + this._itemChanged('added',newBlock,store); + + }, + refreshItem:function(item){}, + _itemChanged:function(type,item,store){ + + store = store || this.getStore(item); + + var thiz = this; + + function _refreshParent(item,silent){ + + var parent = item.getParent(); + if(parent) { + var args = { + target: parent + }; + if(silent){ + this._muteSelectionEvents=true; + } + store.emit('update', args); + if(silent){ + this._muteSelectionEvents=false; + } + }else{ + thiz.refresh(); + } + } + + + + + function select(item){ + thiz.select(item,null,true,{ + focus:true + }); + } + + + switch (type){ + + case 'added':{ + //_refreshParent(item); + this.refresh(); + select(item); + break; + } + + + case 'moved':{ + //_refreshParent(item,true); + //this.refresh(); + //select(item); + break; + } + + + + case 'deleted':{ + + var parent = item.getParent(); + if(parent) { + + var _prev = item.getPreviousBlock() || item.getNextBlock() || parent; + + + var _container = parent.getContainer(); + if(_container){ + _.each(_container,function(child){ + if(child.id == item.id) { + _container.remove(child); + } + }); + } + this.refresh(); + + setTimeout(function(){ + if(_prev) { + select(_prev); + } + },1); + + + } + + break; + } + } + + }, + startup:function(){ + + this.inherited(arguments); + + + this._on('selectionChanged',function(evt){ + + var selection = evt.selection; + + //console.log('selection changed',selection); + + return; + + if(selection[0]){ + + selection[0].canParent = function (item, dir) { + + try { + item = item || this; + + if (!item) { + return false; + } + var parent = item.getParent(); + var 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; + } + + } catch (e) { + debugger; + } + return true; + } + + + + + + return; + selection[0].canMove = function (item, dir) { + + try { + item = item || this; + + if (!item) { + return false; + } + var parent = item.getParent(); + var 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; + } + + } catch (e) { + debugger; + } + return true; + } + + } + + }) + } + }; + + /*** + * playground + */ + var _lastGrid = window._lastGrid; + var ctx = window.sctx, + ACTION = types.ACTION, + root; + + + var _actions = [ + ACTION.RENAME + ]; + + function fixScope(scope){ + + /** + * + * @param source + * @param target + * @param before + * @param add: comes from 'hover' state + * @returns {boolean} + */ + scope.moveTo = function(source,target,before,add){ + + + + + console.log('scope::move, add: ' +add,arguments); + + if(!add){ + debugger; + } + /** + * treat first the special case of adding an item + */ + if(add){ + + //remove it from the source parent and re-parent the source + if(target.canAdd && target.canAdd()){ + + var sourceParent = this.getBlockById(source.parentId); + if(sourceParent){ + sourceParent.removeBlock(source,false); + } + target.add(source,null,null); + return; + }else{ + console.error('cant reparent'); + return false; + } + } + + + //for root level move + if(!target.parentId && add==false){ + + //console.error('root level move'); + + //if source is part of something, we remove it + var sourceParent = this.getBlockById(source.parentId); + if(sourceParent && sourceParent.removeBlock){ + sourceParent.removeBlock(source,false); + source.parentId=null; + source.group=target.group; + } + + var itemsToBeMoved=[]; + var groupItems = this.getBlocks({ + group:target.group + }); + + var rootLevelIndex=[]; + var store = this.getBlockStore(); + + var sourceIndex = store.storage.index[source.id]; + var targetIndex = store.storage.index[target.id]; + for(var i = 0; i= targetIndex : itemIndex <= targetIndex; + if(add){ + itemsToBeMoved.push(groupItems[i]); + rootLevelIndex.push(store.storage.index[groupItems[i].id]); + } + } + } + + //remove them the store + for(var j = 0; j cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before ==true ? -1 : 1))); + for(var i = 0 ; i < distance -1; i++){ + parent.move(source,direction); + } + return true; + + // we move within the different parents + }else if( source.parentId && target.parentId && add==false && source.parentId !== target.parentId){ console.log('same parent!'); + + console.error('we move within the different parents'); + //collect data + + var sourceParent = this.getBlockById(source.parentId); + if(!sourceParent){ + console.error(' couldnt find source parent '); + return false; + } + + var targetParent = this.getBlockById(target.parentId); + if(!targetParent){ + console.error(' couldnt find target parent '); + return false; + } + + + //remove it from the source parent and re-parent the source + if(sourceParent && sourceParent.removeBlock && targetParent.canAdd && targetParent.canAdd()){ + sourceParent.removeBlock(source,false); + targetParent.add(source,null,null); + }else{ + console.error('cant reparent'); + return false; + } + + //now proceed as in the case above : same parents + var items = targetParent[targetParent._getContainer(source)]; + if(items==null){ + console.error('weird : target parent has no item container'); + } + var cIndexSource = targetParent.indexOf(items,source); + var cIndexTarget = targetParent.indexOf(items,target); + if(!cIndexSource || !cIndexTarget){ + console.error(' weird : invalid drop processing state, have no valid item indicies'); + return; + } + var direction = cIndexSource > cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before ==true ? -1 : 1))); + for(var i = 0 ; i < distance -1; i++){ + targetParent.move(source,direction); + } + return true; + } + + return false; + }; + + return scope; + + var topLevelBlocks = []; + var blocks = scope.getBlocks({ + parentId:null + }); + + + var grouped = _.groupBy(blocks,function(block){ + return block.group; + }); + + function createDummyBlock(id,scope){ + + var block = { + "_containsChildrenIds": [ + "items" + ], + "group": null, + "id": id, + "items": [ + + ], + "name": id, + "method": "----group block ----", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": false, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + + }; + + return scope.blockFromJson(block); + + } + + for(var group in grouped){ + + var groupBlock = createDummyBlock(group,scope); + var blocks = grouped[group]; + _.each(blocks,function(block){ + groupBlock['items'].push(block); + + + + if(!block.parentId && block.group /*&& block.id !== group*/) { + block.parent = groupBlock; + block.parentId = groupBlock.id; + } + }); + } + + console.clear(); + var root = scope.getBlockById('root'); + //console.dir(root.getParent()); + + return scope; + } + + + + function createScope() { + + var data = { + "blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + + { + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + } + ], + "variables": [] + }; + + return fixScope(blockManager.toScope(data)); + } + + if (ctx) { + + + var blockManager = ctx.getBlockManager(); + + + function createGridClass() { + + var renderers = [TreeRenderer]; + //, ThumbRenderer, TreeRenderer + + var multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + + + var _gridClass = Grid.createGridClass('driverTreeView', + { + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + //CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + //TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + SPLIT:{ + CLASS:declare('splitMixin',null,_layoutMixin) + }, + DND:{ + CLASS:Dnd//declare('splitMixin',Dnd,_dndMixin) + } + + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + EDITOR: Editor, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + + + + return declare('gridFinal', BlockGrid, _gridBase); + + //return BlockGrid; + } + + var blockScope = createScope('docs'); + + + var mainView = ctx.mainView; + + var docker = mainView.getDocker(); + + + if (mainView) { + + + if (_lastGrid) { + + docker.removePanel(_lastGrid); + } + + var parent = docker.addTab(null, { + title: 'blox', + icon: 'fa-folder' + }); + window._lastGrid = parent; + + + + + var actions = [], + thiz = this, + ACTION_TYPE = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + grid, + ribbon; + + + var store = blockScope.blockStore; + + + var _gridClass = createGridClass(); + + + + + var gridArgs = { + ctx:ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect:true, + collection: store.filter({ + group: "click" + }), + dndConstructor: SharedDndGridSource, + //dndConstructor:Dnd.GridSource, + dndParams: { + allowNested: true, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return true;//source !== this; // Don't self-accept. + }, + isSource: true + } + }; + + + + grid = utils.addWidget(_gridClass,gridArgs,null,parent,true); + + + var blocks = blockScope.allBlocks(); + + var root = blocks[0]; + + + var actionStore = grid.getActionStore(); + var toolbar = mainView.getToolbar(); + + var _defaultActions = []; + + _defaultActions = _defaultActions.concat(getFileActions.apply(grid, [grid.permissions])); + + grid.addActions(_defaultActions); + + + + if (!toolbar) { + window._lastRibbon = ribbon = toolbar = utils.addWidget(Ribbon, { + store: actionStore, + flat: true, + currentActionEmitter: grid + }, this, mainView.layoutTop, true); + + } else { + toolbar.addActionEmitter(grid); + toolbar.setActionEmitter(grid); + } + + doPost(grid); + + + setTimeout(function () { + mainView.resize(); + grid.resize(); + + }, 1000); + + + function test() { + + expand.apply(grid); + + return; + + } + + function test2() { + + return; + + /*var isToolbared = grid.hasFeature('TOOLBAR'); + console.warn('has Toolbar ');*/ + + + /* + var nameProperty = item.property('label'); + + var urlProperty = item.property('url'); + + + nameProperty.observe(function () { + console.log('horray!', arguments); + }); + + urlProperty.observe(function () { + console.log('horray url', arguments); + }); + + item.set('label', 'new label'); + item.set('url', null); + + //store.notify(item,'id3'); + store.emit('update', {target: item}); + */ + + + /*grid.refresh();*/ + //console.log('item ', item); + + /* + grid.select(store.getSync('id99'), null, true, { + append: true, + focus: true, + silent:true + });*/ + + var item99 = store.getSync('id99'); + var item98 = store.getSync('id98'); + + grid.select([item99, item98], null, true, { + append: true, + focus: true, + silent: false + }); + + + //console.log('is selected ' + grid.isSelected(item98)); + + //console.dir(grid.getRows(true)); + + /*console.dir(grid.getPrevious(item99, false, true));*/ + /* + console.dir(grid.getSelection(function(item){ + return item.id!='id99'; + }));*/ + + + //console.log(grid.getFocused()); + + /*store.removeSync('id3');*/ + } + + setTimeout(function () { + test(); + }, 1000); + + + setTimeout(function () { + test2(); + }, 2000); + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/bak/TestEditor.js b/packages/xblox/ref-control-freak/xblox/tests/bak/TestEditor.js new file mode 100644 index 00000000..ee94c74a --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/bak/TestEditor.js @@ -0,0 +1,1777 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + "dojo/_base/lang", + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/ThumbRenderer', + 'xide/views/_ActionMixin', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dijit/form/RadioButton', + 'xide/widgets/Ribbon', + 'xide/editor/Registry', + 'xaction/DefaultActions', + 'xaction/Action', + "xblox/widgets/BlockGridRowEditor", + + 'dgrid/Editor', + + + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + + 'xide/factory', + + + 'dijit/Menu', + + 'xide/data/Reference', + + 'dijit/form/DropDownButton', + 'dijit/MenuItem', + + 'xide/docker/Docker', + + + "xide/views/CIViewMixin", + + 'xide/layout/TabContainer', + + + "dojo/has!host-browser?xblox/views/BlockEditDialog", + + 'xblox/views/BlockGrid', + + 'xide/layout/ContentPane', + + 'xide/views/_LayoutMixin' + + +], function (declare,lang, types, + utils, ListRenderer, TreeRenderer, ThumbRenderer, + _ActionMixin, + Grid, MultiRenderer, RadioButton, Ribbon, Registry, DefaultActions, Action, BlockGridRowEditor, + Editor,Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, factory,Menu,Reference,DropDownButton, + MenuItem,Docker,CIViewMixin,TabContainer, + + BlockEditDialog, + BlockGrid, + ContentPane, + _LayoutMixin + ) { + + + var editorClass = declare("xblox.views.BlocksFileEditor", [ContentPane,_LayoutMixin],{ + getActionStore:function(){ + + + + var tContainer = this.getGroupContainer(); + + if(tContainer.selectedChildWidget){ + return tContainer.selectedChildWidget.grid.getActionStore(); + } + + return null; + + }, + ////////////////////////////////////////////////////////// + // + // 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, + /** + * Current Blox Scope {xblox.model.Scope} + */ + blockScope: null, + groupContainer: null, + canAddGroups:true, + gridClass:BlockGrid, + 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; + + gridBaseClass = gridBaseClass || this.gridClass; + + + var store = group ==='Variables' ? scope.variableStore : scope.blockStore; + + + var gridArgs = { + _docker:this.getDocker(), + __right:this.getRightPanel(), + ctx:this.ctx, + blockScope: scope, + blockGroup: group, + attachDirect:true, + shouldShowAction: function (action) { + return true; + }, + collection: store.filter({ + group: group + }) + }; + + var args = { + blockGroup: group, + title: group, + blockScope: scope, + ctx: this.ctx, + delegate: this, + showAllBlocks: true, + open: true, + lazy: true, + titlePane: false, + canToggle: false, + gridParams: { + cssClass: 'bloxGridView' + }, + gridBaseClass:gridBaseClass + }; + + if (extra) { + lang.mixin(gridArgs, extra); + } + + var view = utils.addWidget(gridBaseClass,gridArgs,null,container,true); + + container.grid = view; + + view._on('selectionChanged',function(evt){ + thiz._emit('selectionChanged',evt); + }); + + return view; + }, + getDeviceVariablesAsEventOptions:function(startIntend){ + + var options = []; + var _item = function(label,value,intend,selected,displayValue){ + + + var string="" +label + ""; + var pre = ""; + if(intend>0){ + for (var i = 0; i < intend; i++) { + pre+=" "; + pre+=" "; + pre+=" "; + } + } + 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="" +label + ""; + var pre = ""; + + if(intend>0){ + for (var i = 0; i < intend; i++) { + pre+=" "; + pre+=" "; + pre+=" "; + } + } + 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: '
Select the event for the new group:
', + 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(); + } + }, + getTabContainer:function(){ + return this.groupContainer; + }, + ////////////////////////////////////////////////////////// + // + // 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; + + 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 : + */ + }, + ////////////////////////////////////////////////////////// + // + // utils + // + destroyWidgets: function () { + utils.destroy(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.blockView.resize(); + } + } + }, 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, 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); + } + } + + }); + + + var _gridBase = { + currentCIView:null, + targetTop:null, + lastSelectedTopTabTitle:'General', + editBlock:function(item,changedCB,select){ + + + var selection = this.getSelection(), + item=null; + if(selection.length == 1){ + item = selection[0]; + } + + if(!item){ + return; + } + + if(!item.canEdit()){ + return; + } + var title='Edit ', + thiz=this; + if(item.title){ + title+=item.title; + }else if(item.name){ + title+=item.name; + } + try { + var actionDialog = new BlockEditDialog({ + item: item, + title: title, + style: 'width:400px', + resizeable: true, + delegate: { + onOk: function (dlg, data) { + + //item.scope.expression. + + if (changedCB) { + changedCB(item); + } + + /** + * triggers to refresh block grid views + */ + thiz.publish(types.EVENTS.ON_BLOCK_PROPERTY_CHANGED,{ + item:item + }); + + /** + * update block tree view! + */ + + if(select!==false) { + + thiz.onBlockSelected({ + item: item, + owner: this.currentCIView + }); + } + + } + } + }); + }catch(e){ + //debugger; + } + actionDialog.show(); + actionDialog.resize(); + }, + execute: function (block) { + + if (!block || !block.scope) { + console.error('have no scope'); + return; + } + try { + var result = block.scope.solveBlock(block, { + highlight: true, + force: true + }); + } catch (e) { + console.error(' excecuting block - ' + block.name + ' failed! : ' + e); + console.error(printStackTrace().join('\n\n')); + } + return true; + }, + move: function (dir) { + + var item = this.getSelection()[0]; + + if (!item || !item.parentId) { + + console.log('cant move, no selection or parentId', item); + + return; + + } + + //parent.items = items.swap(item,upperItem); + + console.log('move ', item); + + + try { + item.move(item, dir); + //this.onItemAction(true); + console.log('move up to' + item.name); + } catch (e) { + debugger; + } + + + + }, + runAction: function (action) { + + switch (action.command) { + case 'Step/Run': + { + return this.execute(this.getSelection()[0]); + } + case 'Step/Move Up': + { + return this.move(-1); + } + case 'Step/Move Down': + { + return this.move(1); + } + case 'Step/Edit': + { + return this.editBlock(); + } + } + + + }, + getAddActions: function (item) { + + var thiz = this; + + + var items = []; + this.showAllBlocks = true; + if (this.showAllBlocks || item) { + + var variables = this.blockScope.getVariables(); + var variableItems = []; + /* + for (var i = 0; i < variables.length; i++) { + variableItems.push({ + name: variables[i].title, + target: item, + iconClass: 'el-icon-compass', + proto: VariableAssignmentBlock, + item: variables[i], + ctrArgs: { + variable: variables[i].title, + scope: this.blockScope, + value: '0' + } + }); + }*/ + items = factory.getAllBlocks(this.blockScope, this, item, this.blockGroup, false); + items.push({ + name: 'Set Variable', + target: item, + iconClass: 'el-icon-pencil-alt', + items: variableItems + }); + + //tell everyone + factory.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items + }); + return items; + } + + + /* + if (!item) { + + if (this.newRootItemFunction) { + items.push({ + name: this.newRootItemLabel, + iconClass: this.newRootItemIcon, + handler: function () { + thiz.newRootItemFunction(); + thiz.onItemAction(); + } + }); + return items; + } + } + */ + + }, + showProperties:function(item){ + + + var block = item, + thiz = this; + + var right = this.getRightPanel(); + + if(item == this._lastItem){ + return; + } + + this._lastItem = item; + + var tabContainer = this.targetTop; + + + if(!tabContainer) { + + tabContainer = utils.addWidget(TabContainer, { + delegate: this, + tabStrip: true, + tabPosition: "top", + attachParent: true, + style: "width:100%;height:100%;overflow-x:hidden;", + allowSplit: false + }, null,right.containerNode, true); + this.targetTop =tabContainer; + } + + if (tabContainer.selectedChildWidget) { + this.lastSelectedTopTabTitle = tabContainer.selectedChildWidget.title; + }else{ + this.lastSelectedTopTabTitle = 'General'; + } + + + + _.each(tabContainer.getChildren(),function(tab){ + tabContainer.removeChild(tab); + }); + + if (this.currentCIView){ + this.currentCIView.empty(); + } + + + + + // clear tab - container + /* + if (this.currentCIView && this.currentCIView.tabs) { + + if(this.currentCIView.tabContainer._destroyed){ + this.currentCIView=null; + }else { + this.currentCIView.empty(); + + try { + for (var i = 0; i < this.currentCIView.tabs.length; i++) { + this.targetTop.removeChild(this.currentCIView.tabs[i]); + } + } catch (e) { + //should not happen! + console.error('clear failed: ' + e, e); + console.trace(); + } + } + } + */ + + if(!block.getFields){ + console.log('have no fields',block); + return; + } + + var cis = block.getFields(); + for (var i = 0; i < cis.length; i++) { + cis[i].vertical = true; + } + + + + + + + + var ciView = new CIViewMixin({ + tabContainer: this.targetTop, + delegate: this, + viewStyle: 'padding:0px;', + autoSelectLast: true, + item: block, + source: this.callee, + options: { + groupOrder: { + 'General': 0, + 'Advanced': 1, + 'Description': 2 + } + }, + cis: cis + }); + ciView.initWithCIS(cis); + + this.currentCIView = ciView; + + if(block.onFieldsRendered){ + block.onFieldsRendered(block,cis); + } + + ciView._on('valueChanged',function(evt){ + console.log('ci value changed ',evt); + }); + + + var containers = this.targetTop.getChildren(); + var descriptionView = null; + for (var i = 0; i < containers.length; i++) { + + // @TODO : why is that not set? + containers[i].parentContainer = this.targetTop; + + // track description container for re-rooting below + if (containers[i].title === 'Description') { + descriptionView = containers[i]; + } + + if(this.targetTop.selectedChildWidget.title!==this.lastSelectedTopTabTitle) { + if (containers[i].title === this.lastSelectedTopTabTitle) { + this.targetTop.selectChild(containers[i]); + } + } + } + + + this._docker.resize(); + + + + /* + if(this.targetBottom && this.targetBottom._destroyed){ + utils.destroy(this.targetBottom); + this.targetBottom=null; + } + + + if(!this.targetBottom) { + // Re root description view into right bottom panel + this.targetBottom = this.getRightBottomTarget(true, false); + } + */ + + + +/* + for (var i = 0; i < cis.length; i++) { + if(cis[i].select === true && cis[i].view){ + this.targetTop.selectChild(cis[i].view); + } + } + */ + + + + /* + if (descriptionView && this.targetBottom) { + this.targetBottom.destroyDescendants(false); + this.currentCIView.tabs.remove(descriptionView); + descriptionView.parentContainer.removeChild(descriptionView); + this.targetBottom.addChild(descriptionView); + var main = this.getLayoutRightMain(false, false); + main.resize(); + } + */ + + + if (this.blockTreeView) { + + } else { + /* + if(this.renderNewTab) { + + var main = this.ctx.getApplication().mainView; + if (main) { + main._openRight(); + } + this.blockTreeView = this.createBlockTreeView(this.targetTop, 'New'); + } + */ + } + + + + }, + startup:function(){ + + this.inherited(arguments); + + this._on('selectionChanged',function(evt){ + + var selection = evt.selection, + thiz = this; + + if(selection && selection[0]){ + thiz.showProperties(selection[0]); + } + + + + }); + + } + }; + + /*** + * playground + */ + var _lastGrid = window._lastGrid; + var ctx = window.sctx, + parent, + ACTION = types.ACTION, + root; + + + function getFileActions(permissions) { + + + var result = [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + VISIBILITY = types.ACTION_VISIBILITY, + thiz = this, + actionStore = thiz.getActionStore(); + + + function addAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable) { + + var action = null; + if (DefaultActions.hasAction(permissions, command)) { + + mixin = mixin || {}; + + utils.mixin(mixin, {owner: thiz}); + + if (!handler) { + + handler = function (action) { + console.log('log run action', arguments); + var who = this; + if (who.runAction) { + who.runAction.apply(who, [action]); + } + } + } + action = DefaultActions.createAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable, thiz.domNode); + + result.push(action); + return action; + + } + } + + + var rootAction = 'Block/Insert'; + permissions.push(rootAction); + addAction('Block', rootAction, 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + dummy: true, + onCreate: function (action) { + action.setVisibility(VISIBILITY.CONTEXT_MENU, { + label: 'Add' + }); + + } + }, null, null); + + permissions.push('Block/Insert Variable'); + + + addAction('Variable', 'Block/Insert Variable', 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + }, null, null); + /* + permissions.push('Clipboard/Paste/New'); + addAction('New ', 'Clipboard/Paste/New', 'el-icon-plus-sign', null, 'Home', 'Clipboard', 'item|view', null, null, { + }, null, null);*/ + + + var newBlockActions = this.getAddActions(); + var addActions = []; + var levelName = ''; + + + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + permissions.push(path); + + addAction(levelName, path, item.iconClass, null, 'Home', 'Insert', 'item|view', null, null, {}, null, null); + + + if (isContainer) { + addItems(path, item.items); + } + + + } + + } + + + + //console.clear(); + + addItems(rootAction, newBlockActions); + + + //console.dir(newBlockActions); + + + /* + for (var i = 0; i < newBlockActions.length; i++) { + + + //top level items + var item = newBlockActions[i]; + + if(_.isEmpty(item.items)){ + continue; + } + + levelName = item.name; + + var path = rootAction + '/' + levelName; + + permissions.push(path); + + addAction(levelName,path,item.iconClass,null,'Home','Insert','item|view',null,null,{ + dummy:true + },null,null); + + //now the items + + + + addItems(path,item.items); + + + }*/ + + + console.log('new items', newBlockActions); + + //return result; + + //run + function canMove(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + var canMove = item.canMove(item, this.command === 'Step/Move Up' ? -1 : 1); + return !canMove; + + } + + + function isItem(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + return false; + + } + + /** + * run + */ + addAction('Run', 'Step/Run', 'el-icon-play', ['space'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + /* + action.setVisibility(types.ACTION_VISIBILITY_ALL, { + label: '' + });*/ + } + }, null, isItem); + + permissions.push('Step/Run/From here'); + + /** + * run + */ + addAction('Run from here', 'Step/Run/From here', 'el-icon-play', ['ctrl space'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + /* + action.setVisibility(types.ACTION_VISIBILITY_ALL, { + label: '' + }); + */ + } + }, null, isItem); + + + /** + * move + */ + addAction('Move Up', 'Step/Move Up', 'fa-arrow-up', ['ctrl up'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canMove); + + addAction('Move Down', 'Step/Move Down', 'fa-arrow-down', ['ctrl down'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + } + }, null, canMove); + + + + + permissions.push('Step/Edit'); + + addAction('Edit', 'Step/Edit', ACTION_ICON.EDIT, ['f4', 'enter'], 'Home', 'Step', 'item', null, null, null, null, isItem); + /////////////////////////////////////////////////// + // + // Editors + // + /////////////////////////////////////////////////// + + return result; + + } + + function createScope() { + + var data = { + "blocks": [ + { + "_containsChildrenIds": [], + "id": "root2", + "name": "On Key", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "group": "My Group" + }, + { + + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Run Script", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "shareTitle": "", + "canDelete": true, + "order": 0 + + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + + "order": 0 + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0 + } + ], + "variables": [] + }; + + return blockManager.toScope(data); + } + if (ctx) { + + + + + var blockManager = ctx.getBlockManager(); + var doTest = true; + + var ribbon = ctx.mainView.getToolbar(); + + function createGridClass() { + return BlockGrid; + } + + + + var blockScope = createScope('docs'); + + + var mainView = ctx.mainView; + + var docker = mainView.getDocker(); + + + if (mainView) { + + + if (_lastGrid) { + + docker.removePanel(_lastGrid); + } + + + var parent = docker.addTab(null, { + title: 'blocks - editor', + icon: 'fa-folder' + }); + + window._lastGrid = parent; + + + var actions = [], + thiz = this, + ACTION_TYPE = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + grid, + ribbon; + + + var store = blockScope.blockStore; + var _gridClass = createGridClass(); + + + + var editor = utils.addWidget(editorClass,{ + gridClass:_gridClass, + ctx:ctx + },null,parent,true); + + + + editor.initWithScope(blockScope); + + + + + + /* + grid = new _gridClass({ + + blockScope: blockScope, + blockGroup: 'click', + shouldShowAction: function (action) { + return true; + }, + collection: store.filter({ + group: "click" + }), + tabOrder: { + 'Home': 100, + 'View': 50 + }, + groupOrder: { + + + 'Clipboard': 110, + 'File': 100, + 'Step': 80, + 'Open': 70, + 'Organize': 60, + 'Insert': 10, + 'Select': 0 + }, + showHeader: true, + options: utils.clone(types.DEFAULT_GRID_OPTIONS), + _parent: parent, + columns: [ + { + renderExpando: true, label: "Name", field: "name", sortable: false, editor: BlockGridRowEditor + } + ] + }, parent.containerNode); + */ + + var gridArgs = { + ctx:ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect:true, + shouldShowAction: function (action) { + return true; + }, + collection: store.filter({ + group: "click" + }), + tabOrder: { + 'Home': 100, + 'View': 50 + }, + groupOrder: { + + + 'Clipboard': 110, + 'File': 100, + 'Step': 80, + 'Open': 70, + 'Organize': 60, + 'Insert': 10, + 'Select': 0 + }, + options: utils.clone(types.DEFAULT_GRID_OPTIONS), + columns: [ + { + renderExpando: true, label: "Name", field: "name", sortable: false, editor: BlockGridRowEditor + } + ] + }; + + //grid = utils.addWidget(_gridClass,gridArgs,null,parent,true); + + //var grid2 = utils.addWidget(_gridClass,gridArgs,null,right,true); + +/* + + grid.startup(); + // grid2.startup(); + + grid.select(['root'], null, true, { + focus: true + }); + */ + /* + var _actions = [ + //ACTION.EDIT, + ACTION.RENAME, + ACTION.RELOAD, + ACTION.DELETE, + //ACTION.NEW_FILE, + //ACTION.NEW_DIRECTORY, + ACTION.CLIPBOARD, + ACTION.LAYOUT, + ACTION.COLUMNS, + ACTION.SELECTION, + ACTION.PREVIEW, + 'Step/Run', + 'Step/Move Up', + 'Step/Move Down' + + ]; + + var blocks = blockScope.allBlocks(); + var root = blocks[0];*/ + + + /* + var _defaultActions = DefaultActions.getDefaultActions(_actions, grid); + _defaultActions = _defaultActions.concat(getFileActions.apply(grid, [_actions])); + grid.addActions(_defaultActions);*/ + + var grid = editor; + + + //var actionStore = grid.getActionStore(); + var toolbar = mainView.getToolbar(); + + + + + if (!toolbar) { + window._lastRibbon = ribbon = toolbar = utils.addWidget(Ribbon, { + store: actionStore, + flat: true, + currentActionEmitter: grid + }, this, mainView.layoutTop, true); + + } else { + toolbar.addActionEmitter(grid); + + toolbar.setActionEmitter(grid); + } + + + setTimeout(function () { + mainView.resize(); + //grid.resize(); + + }, 1000); + + + function test() { + + return; + + } + + function test2() { + + } + + setTimeout(function () { + test(); + }, 4000); + setTimeout(function () { + test2(); + }, 2000); + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/tests/bak/TestGrid.js b/packages/xblox/ref-control-freak/xblox/tests/bak/TestGrid.js new file mode 100644 index 00000000..224c0980 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/tests/bak/TestGrid.js @@ -0,0 +1,2007 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + 'dojo/dom-class', + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/ThumbRenderer', + 'xide/views/_ActionMixin', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dijit/form/RadioButton', + 'xide/widgets/Ribbon', + 'xide/editor/Registry', + 'xaction/DefaultActions', + 'xaction/Action', + "xblox/widgets/BlockGridRowEditor", + + 'dgrid/Editor', + + + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + + 'xide/factory', + + + 'dijit/Menu', + + 'xide/data/Reference', + + 'dijit/form/DropDownButton', + 'dijit/MenuItem', + + 'xide/docker/Docker', + + + "xide/views/CIViewMixin", + + 'xide/layout/TabContainer', + + + "dojo/has!host-browser?xblox/views/BlockEditDialog", + + 'xblox/views/BlockGrid', + + 'xgrid/DnD', + 'xblox/views/BlocksGridDndSource', + 'xblox/widgets/DojoDndMixin', + + + 'xide/registry', + + 'dojo/topic' + + +], function (declare, domClass,types, + utils, ListRenderer, TreeRenderer, ThumbRenderer, + _ActionMixin, + Grid, MultiRenderer, RadioButton, Ribbon, Registry, DefaultActions, Action, BlockGridRowEditor, + Editor,Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, factory,Menu,Reference,DropDownButton, + MenuItem,Docker,CIViewMixin,TabContainer, + + BlockEditDialog, + BlockGrid, + Dnd,BlocksGridDndSource,DojoDndMixin, + + registry,topic + ) { + + + function patchToolbar(ribbon) { + + var _mixin = { + + } + + utils.mixin(ribbon,_mixin); + } + /** + * + */ + var _layoutMixin = { + _docker:null, + _parent:null, + __right:null, + getDocker:function(container){ + + if(!this._docker){ + + var _dst = container || this._domNode.parentNode, + thiz = this; + + thiz._docker = Docker.createDefault(_dst); + thiz._oldParent = thiz._parent; + + var parent = thiz._docker.addPanel('DefaultFixed', types.DOCKER.TOP, null, { + w: '100%', + title:' ' + }); + + dojo.place(thiz._domNode,parent.containerNode); + + thiz._docker.$container.css('top',0); + thiz._parent = parent; + + parent._parent.showTitlebar(false); + + +/* + var right = this._docker.addPanel('Collapsible', types.DOCKER.RIGHT, parent, { + w: '20%', + title:'Properties' + }); + + parent._parent.showTitlebar(false);*/ + + + + + + //parent._parent.$center.css('top',0); + + } + + return thiz._docker; + }, + getRightPanel:function(){ + + if(this.__right){ + return this.__right; + } + + var docker = this.getDocker(); + + + var right = docker.addPanel('Collapsible', types.DOCKER.RIGHT, this._parent, { + w: '300px', + title:' ' + }); + + + + right._parent.showTitlebar(false); + + var splitter = right.getSplitter(); + + splitter.pos(0.6); + + + this.__right = right; + + return right; + } + } + + + var _statusMixin = { + } + + /** + * Dnd impl. + */ + var _dndMixin = { + + } + + + + + var DojoDndMixin = declare("xblox.widgets.DojoDndMixin",null,{ + dropEvent:"/dnd/drop", + dragEvent:"/dnd/start", + overEvent:"/dnd/source/over", + _eDrop:false, + isDragging:false, + didInit:false, + /*** + * Pre-Process DND Events + * @param node + * @param targetArea + * @param indexChild + */ + _calcNewItemList:function(items){ + var res = []; + if(items){ + + for(var i=0 ; i move nodes! + this.onDrop(source, nodes, copy, target); + } + this.onDndCancel(); + } + }); + + + function getFileActions(permissions) { + + + + var result = [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + VISIBILITY = types.ACTION_VISIBILITY, + thiz = this, + actionStore = thiz.getActionStore(); + + + return []; + + function addAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable) { + + var action = null; + if (DefaultActions.hasAction(permissions, command)) { + + mixin = mixin || {}; + + utils.mixin(mixin, {owner: thiz}); + + if (!handler) { + + handler = function (action) { + console.log('log run action', arguments); + var who = this; + if (who.runAction) { + who.runAction.apply(who, [action]); + } + } + } + action = DefaultActions.createAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable, thiz.domNode); + + result.push(action); + return action; + + } + } + + /* + var rootAction = 'Block/Insert'; + permissions.push(rootAction); + addAction('Block', rootAction, 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + dummy: true, + onCreate: function (action) { + action.setVisibility(VISIBILITY.CONTEXT_MENU, { + label: 'Add' + }); + + } + }, null, null); + permissions.push('Block/Insert Variable'); + + + addAction('Variable', 'Block/Insert Variable', 'el-icon-plus-sign', null, 'Home', 'Insert', 'item|view', null, null, { + }, null, null); + */ + + /* + permissions.push('Clipboard/Paste/New'); + addAction('New ', 'Clipboard/Paste/New', 'el-icon-plus-sign', null, 'Home', 'Clipboard', 'item|view', null, null, { + }, null, null);*/ + + + var newBlockActions = this.getAddActions(); + var addActions = []; + var levelName = ''; + + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + permissions.push(path); + + addAction(levelName, path, item.iconClass, null, 'Home', 'Insert', 'item|view', null, null, {}, null, null); + + + if (isContainer) { + addItems(path, item.items); + } + + + } + + } + //console.clear(); + //addItems(rootAction, newBlockActions); + //return result; + + + //run + function canMove(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + var canMove = item.canMove(item, this.command === 'Step/Move Up' ? -1 : 1); + + return !canMove; + + } + + + function canParent(selection, reference, visibility) { + + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + + var item = selection[0]; + if(!item){ + console.warn('bad item',selection); + return false; + } + + if(this.command === 'Step/Move Left'){ + return !item.getParent(); + }else{ + return item.getParent(); + } + /* + var canMove = item.canMove(item, this.command === 'Step/Move Left' ? -1 : 1); + return !canMove;*/ + + return true; + + } + + function isItem(selection, reference, visibility) { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return true; + } + return false; + + } + + /** + * run + */ + + addAction('Run', 'Step/Run', 'el-icon-play', ['space'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + widgetArgs:{ + label: ' ', + style:'font-size:25px!important;' + } + }); + + } + }, null, isItem); + permissions.push('Step/Run/From here'); + + /** + * run + */ + + addAction('Run from here', 'Step/Run/From here', 'el-icon-play', ['ctrl space'], 'Home', 'Step', 'item', null, null, { + + onCreate: function (action) { + + } + }, null, isItem); + + + + /** + * move + */ + + addAction('Move Up', 'Step/Move Up', 'fa-arrow-up', ['alt up'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canMove); + + + addAction('Move Down', 'Step/Move Down', 'fa-arrow-down', ['alt down'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + } + }, null, canMove); + /* + + + permissions.push('Step/Edit'); + addAction('Edit', 'Step/Edit', ACTION_ICON.EDIT, ['f4', 'enter'], 'Home', 'Step', 'item', null, null, null, null, isItem); + */ + /////////////////////////////////////////////////// + // + // Editors + // + /////////////////////////////////////////////////// + + permissions.push('Step/Move Left'); + permissions.push('Step/Move Right'); + + addAction('Move Left', 'Step/Move Left', 'fa-arrow-left', ['alt left'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + } + }, null, canParent); + + addAction('Move Right', 'Step/Move Right', 'fa-arrow-right', ['alt right'], 'Home', 'Step', 'item', null, null, { + onCreate: function (action) { + action.setVisibility(VISIBILITY.RIBBON, { + label: '' + }); + + + } + }, null, canParent); + + + return result; + + } + + function doPost(view){ + + var right = view.getRightPanel(); + var splitter = right.getSplitter(); + //var ori = splitter.orientation(); + //splitter.orientation(false); + //spl + + //splitter.collapse(); + //splitter.expand(); + splitter.minimize = function(){ + + this._isToggledMin = true; + + //save pos + if(this._isToggledMin){ + this._savedPosMin = this.pos(); + } + + var togglePosValue = this._isToggledMin ? 1 : 0; + + if(togglePosValue==1){ + //togglePosValue = this._savedPosMin; + } + + + //console.log('toggle ' + togglePosValue + ' cvalue ' + this.pos()); + + + this.pos(togglePosValue); + + } + //splitter.minimize(); + + } + + function clipboardTest(){ + + this.select(['root2'],null,true,{ + focus:true + }); + + var actionStore = this.getActionStore(); + + var copy = actionStore.getSync('Clipboard/Copy'); + var paste = actionStore.getSync('Clipboard/Paste'); + + console.clear(); + + + this.runAction(copy); + this.runAction(paste); + + //this.refresh(); + + + } + + function unparent(){ + + this.select(['sub0'],null,true,{ + focus:true + }); + this.runAction('Step/Move Left'); + this.refresh(); + } + + function reparent(){ + + this.select(['root3'],null,true,{ + focus:true + }); + this.runAction('Step/Move Right'); + this.refresh(); + } + + + function expand(){ + + + this.select(['root3'],null,true,{ + focus:true + }); + + //clipboardTest.apply(this); + + //reparent.apply(this); + + + return; + + + + //var row = grid.row('root'); + //var _t = this._normalize('root'); + //this.expand(_t); + //var _expanded = this.isExpanded(_t); + //debugger; + //this.isRendered('root'); + var store = this.collection; + //var item = store.getSync('sub0'); + //this._expandTo(item); + + var root = this.collection.getSync('click'); + + + this.select(['root2','root3'],null,true,{ + focus:true + }); + + + var actionStore = this.getActionStore(); + + var moveLeft = actionStore.getSync('Step/Move Left'); + + + var moveUp = actionStore.getSync('Step/Move Up'); + + var moveDown = actionStore.getSync('Step/Move Down'); + + var items = this.collection.storage.fullData; + //console.log('before move',items); + //this.printRootOrder(); + this.runAction(moveUp); + //console.log('after move',this.collection.storage.fullData); + //this._place('root','root2','below'); + //this.printRootOrder(); + + + var thiz = this; + + setTimeout(function(){ + + //thiz.refresh(); + //this.runAction(moveDown); + //thiz.refreshRoot(); + thiz.printRootOrder(); + },1500); + + //this.runAction(moveLeft); + } + + + var _gridBase = { + _place:function(dst,ref,direction,items){ + + + var store = this.collection; + + ref = _.isString(ref) ? store.getSync(ref) : ref; + dst = _.isString(dst) ? store.getSync(dst) : dst; + + items = items || store.storage.fullData; + + direction = direction == -1 ? 0 : 1; + + function index(what){ + for (var j = 0; j < items.length; j++) { + if(what.id===items[j].id){ + return j; + } + } + }; + + function compare(left,right){ + return index(left) - index(right); + } + + items.remove(dst); + if(direction==-1){ + direction=0; + } + items.splice( Math.max(index(ref) + direction,0), 0, dst); + + store._reindex(); + }, + __reParentBlock:function(dir){ + + + var item = this.getSelection()[0]; + + if (!item /*|| !item.parentId*/) { + console.log('cant move, no selection or parentId', item); + return; + } + //console.log('reparenting block',item); + + var thiz = this; + //thiz._preserveSelection(); + + var store = item._store; + + item._next = function(dir){ + + var _dstIndex = 0; + var step = 1; + + var items = this._store.storage.fullData; + + //find item + 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(); + }; + + item.__unparent = function () { + + var item = this; + + if (!item) { + return false; + } + var parent = item.getParent(); + + var items = null; + + var newParent = null; + + if(parent){ + newParent = parent.getParent(); + } + + + + if(newParent){ + + item.group = newParent.group; + + if(newParent.id == thiz.blockGroup){ + item.group = newParent.id; + } + item.parentId = newParent.id; + }else{ + + //var items = store.storage.fullData; + + //console.log('next down - parent ',parent.next(items,1)); + + if (parent && parent.removeBlock) { + parent.removeBlock(item,false); + } + item.group = thiz.blockGroup; + item.parentId = null; + item.parent = null; + } + + /* + var scope = thiz.blockScope; + var t = item._store.getSync('sub0');*/ + + item._place(null,-1,null); + item._place(null,-1,null); + + + //item._place(null,-1,null); + + //console.log('new parent : ' + thiz.blockGroup ,scope); + }; + + item.reparent = function () { + + var item = this; + + if (!item) { + return false; + } + var parent = item.getParent(); + + if(parent){ + }else{ + + var _next = item.next(null,1) || item.next(null,-1); + if(_next){ + item.group=null; + _next._add(item); + thiz.refreshRoot(); + } + } + } + + if(dir==-1) { + item.unparent(thiz.blockGroup); + }else{ + item.reparent(); + } + thiz.deselectAll(); + this.refreshRoot(); + this.refresh(); + setTimeout(function(){ + thiz.select(item,null,true,{ + focus:true + }); + },20); + + + }, + /** + * + * @param mixed + * @param toRow {object} preserve super + * @param select {boolean} preserve super + * @param options {object} + * @param options.focus {boolean} + * @param options.silent {boolean} + * @param options.append {boolean} + */ + /** + * @param menuItem + */ + addItem: function (item,action) { + + + if (!item) { + return; + } + var target = this.getSelection()[0], + thiz = this; + var proto = item.proto; + var ctorArgs = item.ctrArgs; + var where = null; + var newBlock = null; + + + //cache problem: + if(!target){ + var _item = this.getSelection()[0]; + if(_item){ + target=_item; + } + } + + + if(ctorArgs) { + if (ctorArgs.id) { + console.error('addItem:: new block constructor args had id!!!'); + ctorArgs.id = utils.createUUID(); + } + if (ctorArgs.parent) { + ctorArgs.parent = null; + } + if (ctorArgs.parentId) { + ctorArgs.parentId = null; + } + if (ctorArgs.items) { + console.error('addItem:: new block constructor args had items!!!'); + ctorArgs.items = []; + } + } + + if (target && proto && ctorArgs) { + + if (target.owner && target.dstField) { + where = target.dstField; + target = target.owner; + } + + ctorArgs['parentId'] = target.id; + ctorArgs['group'] = null; + ctorArgs['parent'] = target;//should be obselete + + newBlock = target.add(proto, ctorArgs, where); + newBlock.parent = target; + + } else if (!target && proto && ctorArgs) { + + if(ctorArgs.group==="No Group" && this.newRootItemGroup){ + ctorArgs.group = this.newRootItemGroup; + } + if (!ctorArgs.group && this.newRootItemGroup) { + ctorArgs.group = this.newRootItemGroup; + } + if (!ctorArgs.group && this.blockGroup) { + ctorArgs.group = this.blockGroup; + } + + if (ctorArgs.group && this.newRootItemGroup && ctorArgs.group !==this.newRootItemGroup) { + ctorArgs.group = this.newRootItemGroup; + } + + newBlock = factory.createBlock(proto, ctorArgs);//root block + } + + if (newBlock && newBlock.postCreate) { + newBlock.postCreate(); + } + //this.onItemAction(); + + if(!newBlock){ + console.error('didnt create block'); + return; + } + + + var store = this.getStore(target); + + var storeItem = store.getSync(newBlock[store.idProperty]); + if(!storeItem){ + console.error('new block not in store'); + store.putSync(newBlock); + } + + try { + scope._emit(types.EVENTS.ON_ITEM_ADDED, { + item: newBlock, + owner: scope + }); + }catch(e){ + + console.error('error emitting',e); + debugger; + } + + this._itemChanged('added',newBlock,store); + + }, + refreshItem:function(item){}, + _itemChanged:function(type,item,store){ + + store = store || this.getStore(item); + + var thiz = this; + + function _refreshParent(item,silent){ + + var parent = item.getParent(); + if(parent) { + var args = { + target: parent + }; + if(silent){ + this._muteSelectionEvents=true; + } + store.emit('update', args); + if(silent){ + this._muteSelectionEvents=false; + } + }else{ + thiz.refresh(); + } + } + + + + + function select(item){ + thiz.select(item,null,true,{ + focus:true + }); + } + + + switch (type){ + + case 'added':{ + //_refreshParent(item); + this.refresh(); + select(item); + break; + } + + + case 'moved':{ + //_refreshParent(item,true); + //this.refresh(); + //select(item); + break; + } + + + + case 'deleted':{ + + var parent = item.getParent(); + if(parent) { + + var _prev = item.getPreviousBlock() || item.getNextBlock() || parent; + + + var _container = parent.getContainer(); + if(_container){ + _.each(_container,function(child){ + if(child.id == item.id) { + _container.remove(child); + } + }); + } + this.refresh(); + + setTimeout(function(){ + if(_prev) { + select(_prev); + } + },1); + + + } + + break; + } + } + + }, + startup:function(){ + + this.inherited(arguments); + + + this._on('selectionChanged',function(evt){ + + var selection = evt.selection; + + //console.log('selection changed',selection); + + return; + + if(selection[0]){ + + selection[0].canParent = function (item, dir) { + + try { + item = item || this; + + if (!item) { + return false; + } + var parent = item.getParent(); + var 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; + } + + } catch (e) { + debugger; + } + return true; + } + + + + + + return; + selection[0].canMove = function (item, dir) { + + try { + item = item || this; + + if (!item) { + return false; + } + var parent = item.getParent(); + var 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; + } + + } catch (e) { + debugger; + } + return true; + } + + } + + }) + } + }; + + /*** + * playground + */ + var _lastGrid = window._lastGrid; + var ctx = window.sctx, + ACTION = types.ACTION, + root; + + + var _actions = [ + ACTION.RENAME + ]; + + function fixScope(scope){ + + /** + * + * @param source + * @param target + * @param before + * @param add: comes from 'hover' state + * @returns {boolean} + */ + scope.moveTo = function(source,target,before,add){ + + + + + console.log('scope::move, add: ' +add,arguments); + + if(!add){ + debugger; + } + /** + * treat first the special case of adding an item + */ + if(add){ + + //remove it from the source parent and re-parent the source + if(target.canAdd && target.canAdd()){ + + var sourceParent = this.getBlockById(source.parentId); + if(sourceParent){ + sourceParent.removeBlock(source,false); + } + target.add(source,null,null); + return; + }else{ + console.error('cant reparent'); + return false; + } + } + + + //for root level move + if(!target.parentId && add==false){ + + //console.error('root level move'); + + //if source is part of something, we remove it + var sourceParent = this.getBlockById(source.parentId); + if(sourceParent && sourceParent.removeBlock){ + sourceParent.removeBlock(source,false); + source.parentId=null; + source.group=target.group; + } + + var itemsToBeMoved=[]; + var groupItems = this.getBlocks({ + group:target.group + }); + + var rootLevelIndex=[]; + var store = this.getBlockStore(); + + var sourceIndex = store.storage.index[source.id]; + var targetIndex = store.storage.index[target.id]; + for(var i = 0; i= targetIndex : itemIndex <= targetIndex; + if(add){ + itemsToBeMoved.push(groupItems[i]); + rootLevelIndex.push(store.storage.index[groupItems[i].id]); + } + } + } + + //remove them the store + for(var j = 0; j cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before ==true ? -1 : 1))); + for(var i = 0 ; i < distance -1; i++){ + parent.move(source,direction); + } + return true; + + // we move within the different parents + }else if( source.parentId && target.parentId && add==false && source.parentId !== target.parentId){ console.log('same parent!'); + + console.error('we move within the different parents'); + //collect data + + var sourceParent = this.getBlockById(source.parentId); + if(!sourceParent){ + console.error(' couldnt find source parent '); + return false; + } + + var targetParent = this.getBlockById(target.parentId); + if(!targetParent){ + console.error(' couldnt find target parent '); + return false; + } + + + //remove it from the source parent and re-parent the source + if(sourceParent && sourceParent.removeBlock && targetParent.canAdd && targetParent.canAdd()){ + sourceParent.removeBlock(source,false); + targetParent.add(source,null,null); + }else{ + console.error('cant reparent'); + return false; + } + + //now proceed as in the case above : same parents + var items = targetParent[targetParent._getContainer(source)]; + if(items==null){ + console.error('weird : target parent has no item container'); + } + var cIndexSource = targetParent.indexOf(items,source); + var cIndexTarget = targetParent.indexOf(items,target); + if(!cIndexSource || !cIndexTarget){ + console.error(' weird : invalid drop processing state, have no valid item indicies'); + return; + } + var direction = cIndexSource > cIndexTarget ? -1 : 1; + var distance = Math.abs(cIndexSource - ( cIndexTarget + (before ==true ? -1 : 1))); + for(var i = 0 ; i < distance -1; i++){ + targetParent.move(source,direction); + } + return true; + } + + return false; + }; + + return scope; + + var topLevelBlocks = []; + var blocks = scope.getBlocks({ + parentId:null + }); + + + var grouped = _.groupBy(blocks,function(block){ + return block.group; + }); + + function createDummyBlock(id,scope){ + + var block = { + "_containsChildrenIds": [ + "items" + ], + "group": null, + "id": id, + "items": [ + + ], + "name": id, + "method": "----group block ----", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": false, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + + }; + + return scope.blockFromJson(block); + + } + + for(var group in grouped){ + + var groupBlock = createDummyBlock(group,scope); + var blocks = grouped[group]; + _.each(blocks,function(block){ + groupBlock['items'].push(block); + + + + if(!block.parentId && block.group /*&& block.id !== group*/) { + block.parent = groupBlock; + block.parentId = groupBlock.id; + } + }); + } + + console.clear(); + var root = scope.getBlockById('root'); + //console.dir(root.getParent()); + + return scope; + } + + + function createScope() { + + var data = { + "blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + + { + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + } + ], + "variables": [] + }; + + return fixScope(blockManager.toScope(data)); + } + + if (ctx) { + + + var blockManager = ctx.getBlockManager(); + + + function createGridClass() { + + var renderers = [TreeRenderer]; + + //, ThumbRenderer, TreeRenderer + + + var multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + + + + + var _gridClass = Grid.createGridClass('driverTreeView', + { + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + //CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + //TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + SPLIT:{ + CLASS:declare('splitMixin',null,_layoutMixin) + }, + DND:{ + CLASS:Dnd//declare('splitMixin',Dnd,_dndMixin) + } + + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + EDITOR: Editor, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + + + + return declare('gridFinal', BlockGrid, _gridBase); + + //return BlockGrid; + } + + var blockScope = createScope('docs'); + + + var mainView = ctx.mainView; + + var docker = mainView.getDocker(); + + + if (mainView) { + + + if (_lastGrid) { + + docker.removePanel(_lastGrid); + } + + var parent = docker.addTab(null, { + title: 'blox', + icon: 'fa-folder' + }); + window._lastGrid = parent; + + + + + var actions = [], + thiz = this, + ACTION_TYPE = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + grid, + ribbon; + + + var store = blockScope.blockStore; + + + var _gridClass = createGridClass(); + + + + + var gridArgs = { + ctx:ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect:true, + collection: store.filter({ + group: "click" + }), + dndConstructor: SharedDndGridSource, + //dndConstructor:Dnd.GridSource, + dndParams: { + allowNested: true, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return true;//source !== this; // Don't self-accept. + }, + isSource: true + } + }; + + + + grid = utils.addWidget(_gridClass,gridArgs,null,parent,true); + + + + /* + grid.select(['sub1'], null, true, { + focus: true + }); + */ + + + + + + + + + + + + + + var blocks = blockScope.allBlocks(); + + var root = blocks[0]; + + + var actionStore = grid.getActionStore(); + var toolbar = mainView.getToolbar(); + + var _defaultActions = []; + + _defaultActions = _defaultActions.concat(getFileActions.apply(grid, [grid.permissions])); + + grid.addActions(_defaultActions); + + + + if (!toolbar) { + window._lastRibbon = ribbon = toolbar = utils.addWidget(Ribbon, { + store: actionStore, + flat: true, + currentActionEmitter: grid + }, this, mainView.layoutTop, true); + + } else { + toolbar.addActionEmitter(grid); + toolbar.setActionEmitter(grid); + } + + doPost(grid); + + + setTimeout(function () { + mainView.resize(); + grid.resize(); + + }, 1000); + + + function test() { + + expand.apply(grid); + + return; + + } + + function test2() { + + return; + + + /* + var c2 = actionStore.getSync('View/Columns/Show Size'); + c2._originEvent='change'; + var _c = c1.get('value'); + */ + + + //console.log('c2 value : ' + _c); + + + try { + //copy.set('value', false, true); + c2.set('value', false); + + + } catch (e) { + //debugger; + } + + return; + + console.log('-------------------------------------------------------------------------'); + + var item = store.getSync('id3'); + var item1 = store.getSync('id1'); + var item2 = store.getSync('id2'); + + + //console.dir(grid._rows); + + + /* + grid.select(item); + grid.focus(item);*/ + + + //grid.select([item,item1,item2]); + //grid.select(item); + grid.select([item1, item2], null, true, { + append: true, + focus: false, + silent: true + + }); + + + /*var isToolbared = grid.hasFeature('TOOLBAR'); + console.warn('has Toolbar ');*/ + + + /* + var nameProperty = item.property('label'); + + var urlProperty = item.property('url'); + + + nameProperty.observe(function () { + console.log('horray!', arguments); + }); + + urlProperty.observe(function () { + console.log('horray url', arguments); + }); + + item.set('label', 'new label'); + item.set('url', null); + + //store.notify(item,'id3'); + store.emit('update', {target: item}); + */ + + + /*grid.refresh();*/ + //console.log('item ', item); + + /* + grid.select(store.getSync('id99'), null, true, { + append: true, + focus: true, + silent:true + });*/ + + var item99 = store.getSync('id99'); + var item98 = store.getSync('id98'); + + grid.select([item99, item98], null, true, { + append: true, + focus: true, + silent: false + }); + + + //console.log('is selected ' + grid.isSelected(item98)); + + //console.dir(grid.getRows(true)); + + /*console.dir(grid.getPrevious(item99, false, true));*/ + /* + console.dir(grid.getSelection(function(item){ + return item.id!='id99'; + }));*/ + + + //console.log(grid.getFocused()); + + /*store.removeSync('id3');*/ + } + + setTimeout(function () { + test(); + }, 1000); + + + setTimeout(function () { + test2(); + }, 2000); + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/types/Types.js b/packages/xblox/ref-control-freak/xblox/types/Types.js new file mode 100644 index 00000000..b2f1dcea --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/types/Types.js @@ -0,0 +1,231 @@ +/** @module xblox/types **/ +define([ + 'xide/types/Types', + 'xide/utils' +], function (types, utils) { + + /** + * The block's capabilities. This will be evaluated in the interface but also + * by the run-time (speed ups). + * + * @enum {integer} module:xide/types/BLOCK_CAPABILITIES + * @memberOf module:xide/types + */ + var 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 + */ + var 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 + */ + var 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 + } + + types.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 + */ + types.BLOCK_OUTLET = { + NONE: 0x00000000, + PROGRESS: 0x00000001, + ERROR: 0x00000002, + PAUSED: 0x00000004, + FINISH: 0x00000008, + STOPPED: 0x00000010 + }; + + utils.mixin(types.EVENTS, { + ON_RUN_BLOCK: 'onRunBlock', + ON_RUN_BLOCK_FAILED: 'onRunBlockFailed', + ON_RUN_BLOCK_SUCCESS: 'onRunBlockSuccess', + ON_BLOCK_SELECTED: 'onItemSelected', + ON_BLOCK_UNSELECTED: 'onBlockUnSelected', + ON_BLOCK_EXPRESSION_FAILED: 'onExpressionFailed', + ON_BUILD_BLOCK_INFO_LIST: 'onBuildBlockInfoList', + ON_BUILD_BLOCK_INFO_LIST_END: 'onBuildBlockInfoListEnd', + ON_BLOCK_PROPERTY_CHANGED: 'onBlockPropertyChanged', + ON_SCOPE_CREATED: 'onScopeCreated', + ON_VARIABLE_CHANGED: 'onVariableChanged', + ON_CREATE_VARIABLE_CI: 'onCreateVariableCI' + }); + + + types.BlockType = { + AssignmentExpression: 'AssignmentExpression', + ArrayExpression: 'ArrayExpression', + BlockStatement: 'BlockStatement', + BinaryExpression: 'BinaryExpression', + BreakStatement: 'BreakStatement', + CallExpression: 'CallExpression', + CatchClause: 'CatchClause', + ConditionalExpression: 'ConditionalExpression', + ContinueStatement: 'ContinueStatement', + DoWhileStatement: 'DoWhileStatement', + DebuggerStatement: 'DebuggerStatement', + EmptyStatement: 'EmptyStatement', + ExpressionStatement: 'ExpressionStatement', + ForStatement: 'ForStatement', + ForInStatement: 'ForInStatement', + FunctionDeclaration: 'FunctionDeclaration', + FunctionExpression: 'FunctionExpression', + Identifier: 'Identifier', + IfStatement: 'IfStatement', + Literal: 'Literal', + LabeledStatement: 'LabeledStatement', + LogicalExpression: 'LogicalExpression', + MemberExpression: 'MemberExpression', + NewExpression: 'NewExpression', + ObjectExpression: 'ObjectExpression', + Program: 'Program', + Property: 'Property', + ReturnStatement: 'ReturnStatement', + SequenceExpression: 'SequenceExpression', + SwitchStatement: 'SwitchStatement', + SwitchCase: 'SwitchCase', + ThisExpression: 'ThisExpression', + ThrowStatement: 'ThrowStatement', + TryStatement: 'TryStatement', + UnaryExpression: 'UnaryExpression', + UpdateExpression: 'UpdateExpression', + VariableDeclaration: 'VariableDeclaration', + VariableDeclarator: 'VariableDeclarator', + WhileStatement: 'WhileStatement', + WithStatement: 'WithStatement' + }; + types.BLOCK_CAPABILITIES = BLOCK_CAPABILITIES; + types.EXECUTION_STATE = EXECUTION_STATE; + types.RUN_FLAGS = RUN_FLAGS; + + + return types; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/utils/TestUtils.js b/packages/xblox/ref-control-freak/xblox/utils/TestUtils.js new file mode 100644 index 00000000..0d790d70 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/utils/TestUtils.js @@ -0,0 +1,1083 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + "xide/utils", + "xide/types", + "xblox/views/BlockGrid", + "xaction/DefaultActions" + +], function (declare,utils,types,BlockGrid) { + + var Module = declare('xblox.utils.TestUtils',null,{}); + + function createScope(blockManager,extraBlocks,clear,mixin) { + extraBlocks = extraBlocks || []; + var defaultBlocks = clear === true ? [] : [ + { + "_containsChildrenIds": [], + "group": "click", + "id": "5fc8ea23-bac4-3a4e-0f37-b9ed6f4b340f", + "declaredClass": "xblox.model.variables.VariableSwitch", + "name": "Switch on Variable", + "icon": "", + "variable": "PowerState", + "isCommand": false, + "enabled": true, + "shareTitle": "", + "allowActionOverride": true, + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added" + }, + { + "_containsChildrenIds": [], + "name": "PowerState", + "send": "nada", + "group": "Variables", + "id": "31c98cdd-02a8-3af1-3a49-11955c0fad48", + "declaredClass": "xcf.model.Variable", + "gui": "off", + "cmd": "off", + "save": false, + "target": "None", + "type": "added", + "value": "off", + "register": true, + "readOnly": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + { + "group": "click", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + }, + { + "group": "click", + "id": "root5", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 5", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + } + ]; + + var data = { + "blocks": extraBlocks.concat(defaultBlocks) + }; + + + + var scope = blockManager.toScope(data); + utils.mixin(scope,mixin); + return scope; + } + + function createBlockGrid(ctx,parent,mixin,startup){ + + var ACTION = types.ACTION; + var blockManager = ctx.getBlockManager(); + var blockScope = createScope(blockManager, [ + { + "_containsChildrenIds": [], + "id": "83de87c0-f8c7-74da-161d-8e9cf51d67b1", + "name": "value", + "value": "MVMAX 98", + "type": "added", + "group": "processVariables", + "gui": false, + "cmd": false, + "declaredClass": "xcf.model.Variable", + "save": false, + "target": "None", + "register": true, + "readOnly": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [], + "name": "PowerOn", + "send": "PWON", + "group": "basic", + "id": "53a10527-709b-4c7d-7a90-37f58f17c8db", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "text-success fa-circle", + "interval": "0", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "PowerOff", + "send": "PWSTANDBY", + "group": "basic", + "id": "84961334-9cd2-d384-25dc-a6b943e8cb8e", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": "-1", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "text-danger fa-power-off", + "interval": "0", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "VolumeUp", + "send": "return 'MV' + (Math.abs(this.getVariable('Volume')) +2);", + "group": "basic", + "id": "6d0c5e0e-5c04-bb98-44a0-705c8269de07", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": "-1", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "fa-arrow-up", + "interval": "0", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "Volume", + "type": "added", + "value": 59, + "enumType": "VariableType", + "save": false, + "initialize": "", + "group": "basicVariables", + "id": "3403a69e-252a-30dc-b130-40a028d1cde4", + "register": true, + "readOnly": false, + "declaredClass": "xcf.model.Variable", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "icon": "fa-automobile", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "VolumeDown", + "send": "return 'MV' + (this.getVariable('Volume') - 2);", + "group": "basic", + "id": "69f6d4fb-4300-0498-9bbf-27554f5f1fa4", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": "-1", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "fa-arrow-down", + "interval": "0", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "Ping", + "group": "basic", + "id": "a5423bf7-7b99-023d-c637-363fbf9a7f18", + "declaredClass": "xcf.model.Command", + "startup": false, + "send": "pw?", + "interval": "1000", + "waitForResponse": false, + "icon": "fa-bell", + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "auto": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [ + "items" + ], + "name": "Fade-Volume-Down", + "group": "conditional", + "id": "1f969cc6-89c4-f559-e824-daf7dfff35cf", + "items": [ + "c6bc0ef4-5b85-b543-4df7-3c00dd73a9eb" + ], + "declaredClass": "xcf.model.Command", + "startup": false, + "send": "", + "interval": 0, + "icon": "fa-exclamation", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [ + "items" + ], + "condition": "[Volume]>20", + "parentId": "1f969cc6-89c4-f559-e824-daf7dfff35cf", + "id": "c6bc0ef4-5b85-b543-4df7-3c00dd73a9eb", + "declaredClass": "xblox.model.loops.WhileBlock", + "loopLimit": 1500, + "name": "While", + "wait": "10", + "icon": "", + "enabled": true, + "shareTitle": "", + "description": "No Description

sdfsdf", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true, + "items": [ + "0caac742-956e-de6b-6b53-36fb8174e5e0" + ], + "_timer": 3564 + }, + { + "_containsChildrenIds": [ + "items" + ], + "name": "Fade-Volume-Up", + "group": "conditional", + "id": "48a83acf-f2ef-ccdf-44a7-621fb635e3c4", + "declaredClass": "xcf.model.Command", + "startup": false, + "send": "", + "interval": "0", + "icon": "fa-exclamation", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "items": [ + "0a561903-9422-f97d-8a76-0757177a7471" + ], + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [ + "items" + ], + "condition": "[Volume]<80", + "parentId": "48a83acf-f2ef-ccdf-44a7-621fb635e3c4", + "id": "0a561903-9422-f97d-8a76-0757177a7471", + "declaredClass": "xblox.model.loops.WhileBlock", + "loopLimit": 1500, + "name": "While", + "wait": "10", + "icon": "", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true, + "items": [ + "660a553b-ff5d-ca79-c114-955800c8de76" + ], + "_timer": 2740 + }, + { + "_containsChildrenIds": [], + "name": "PowerState", + "send": "nada", + "group": "basicVariables", + "id": "31c98cdd-02a8-3af1-3a49-11955c0fad48", + "declaredClass": "xcf.model.Variable", + "gui": "off", + "cmd": "off", + "save": false, + "target": "None", + "type": "added", + "value": "off", + "register": true, + "readOnly": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [], + "name": "Ping - Volume", + "group": "basic", + "id": "dec01610-0355-f038-71c8-a46b2cde5fd2", + "declaredClass": "xcf.model.Command", + "startup": false, + "send": "MV?", + "interval": "1000", + "waitForResponse": false, + "icon": "fa-exclamation", + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "auto": false, + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "VolumeNormal", + "send": "MV?", + "group": "basic", + "id": "963944c2-c0b0-fe8a-504e-1b8bedd2a3cf", + "declaredClass": "xcf.model.Command", + "startup": true, + "auto": true, + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "interval": "2000", + "waitForResponse": false, + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [ + "items" + ], + "group": "conditionalProcess", + "id": "43062d7e-6cfd-040c-34bf-7a01c428c057", + "items": [ + "1c8b8909-5fb8-2df5-2937-03799f54c3df", + "ac702535-d89b-3072-63c1-6774fe7b1a87" + ], + "declaredClass": "xblox.model.variables.VariableSwitch", + "name": "Switch on Variable", + "icon": "", + "variable": "31c98cdd-02a8-3af1-3a49-11955c0fad48", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "comparator": "==", + "expression": "on", + "id": "1c8b8909-5fb8-2df5-2937-03799f54c3df", + "declaredClass": "xblox.model.logic.CaseBlock", + "name": "Case", + "icon": "", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "parentId": "43062d7e-6cfd-040c-34bf-7a01c428c057", + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "comparator": "==", + "expression": "off", + "id": "ac702535-d89b-3072-63c1-6774fe7b1a87", + "declaredClass": "xblox.model.logic.CaseBlock", + "name": "Case", + "icon": "", + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "parentId": "43062d7e-6cfd-040c-34bf-7a01c428c057", + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "group": "conditionalProcess", + "condition": "[Volume]>60", + "id": "4d6f219e-fb31-6184-f238-447a8b0ff616", + "declaredClass": "xblox.model.logic.IfBlock", + "autoCreateElse": true, + "name": "if", + "icon": "", + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "Volume-Loud", + "group": "basicVariables", + "id": "b16bfc09-e449-d514-dc98-10b8afbb14f8", + "declaredClass": "xcf.model.Variable", + "gui": "off", + "cmd": "off", + "save": false, + "target": "None", + "value": "65", + "register": true, + "readOnly": false, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "isCommand": false, + "allowActionOverride": true, + "icon": "fa-play" + }, + { + "_containsChildrenIds": [], + "name": "No Title", + "group": "basic", + "id": "12cdb13e-9c72-2079-8791-87402763c720", + "declaredClass": "xcf.model.Command", + "startup": false, + "auto": false, + "send": "return 'MV' + ([Volume] + 20);", + "interval": "4000", + "waitForResponse": false, + "icon": "fa-exclamation", + "isCommand": false, + "enabled": false, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "allowActionOverride": true + }, + { + "_containsChildrenIds": [], + "name": "Test", + "group": "basicVariables", + "id": "487b9e80-ca4c-c096-0950-91aad9dd4612", + "declaredClass": "xcf.model.Variable", + "gui": "off", + "cmd": "off", + "save": false, + "target": "None", + "value": "return [Volume] + 2;", + "register": true, + "readOnly": false, + "isCommand": false, + "enabled": true, + "shareTitle": "", + "allowActionOverride": true, + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added", + "icon": "fa-play" + }, + { + "_containsChildrenIds": [], + "condition": "", + "parentId": "c6bc0ef4-5b85-b543-4df7-3c00dd73a9eb", + "id": "0caac742-956e-de6b-6b53-36fb8174e5e0", + "declaredClass": "xblox.model.functions.CallBlock", + "command": "69f6d4fb-4300-0498-9bbf-27554f5f1fa4", + "icon": "", + "_timeout": 100, + "isCommand": false, + "enabled": true, + "shareTitle": "", + "allowActionOverride": true, + "description": "No Description", + "canDelete": true, + "order": 0, + "type": "added" + }, + { + "_containsChildrenIds": [], + "id": "Shell-Block", + "description": "Runs a JSON-RPC-2.0 method on the server", + "name": "Run Server Method", + "method": "XShell::run", + "args": "ls", + "deferred": true, + "defaultServiceClass": "XShell", + "defaultServiceMethod": "run", + "declaredClass": "xblox.model.server.RunServerMethod", + "enabled": true, + "shareTitle": "", + "canDelete": true, + "order": 0, + "type": "added", + "group": "click" + } + ]); + + var grid, + store = blockScope.blockStore, + gridArgs = utils.mixin({ + ctx: ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect: true, + collection: store.filter({ + group: "click" + }), + permissions: [ + ACTION.CLIPBOARD, + 'Step/Properties', + ACTION.TOOLBAR, + //ACTION.EDIT, + ACTION.RENAME, + ACTION.RELOAD, + ACTION.DELETE, + ACTION.CLIPBOARD, + ACTION.LAYOUT, + ACTION.COLUMNS, + ACTION.SELECTION, + //ACTION.PREVIEW, + ACTION.SAVE, + ACTION.SEARCH, + 'Step/Run', + 'Step/Move Up', + 'Step/Move Down', + 'Step/Edit' + //ACTION.TOOLBAR + ], + __getBlockActions: function (permissions) { + + var result = [], + ACTION = types.ACTION, + ACTION_ICON = types.ACTION_ICON, + VISIBILITY = types.ACTION_VISIBILITY, + thiz = this, + container = thiz.domNode, + actionStore = thiz.getActionStore(); + + function addAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable) { + + var action = null; + mixin = mixin || {}; + utils.mixin(mixin, {owner: thiz}); + + if (mixin.addPermission || DefaultActions.hasAction(permissions, command)) { + + if (!handler) { + handler = function (action) { + console.log('log run action', arguments); + var who = this; + if (who.runAction) { + who.runAction.apply(who, [action]); + } + } + } + + if (!mixin.tooltip && keycombo) { + + if (_.isString(keycombo)) { + keycombo = [keycombo]; + } + + + mixin.tooltip = keycombo.join('
').toUpperCase(); + + } + + + action = DefaultActions.createAction(label, command, icon, keycombo, tab, group, filterGroup, onCreate, handler, mixin, shouldShow, shouldDisable, thiz.domNode); + result.push(action); + return action; + } + } + + + + + var rootAction = 'Block/Insert'; + var defaultMixin = { addPermission: true }; + + result.push(thiz.createAction({ + label: 'New Block', + command: rootAction, + icon: 'fa-plus', + tab: 'Home', + group: 'Step', + keycombo: ['alt up'], + mixin: defaultMixin + })); + result.push(thiz.createAction({ + label: 'Save', + command: 'File/Save', + icon: 'fa-save', + tab: 'Home', + group: 'File', + keycombo: ['ctrl '], + mixin: defaultMixin + })); + result.push(thiz.createAction({ + label: 'Save As', + command: 'File/Save As', + icon: 'fa-save', + tab: 'Home', + group: 'File', + mixin: defaultMixin + })); + + var newBlockActions = this.getAddActions(); + var levelName = ''; + + function addItems(commandPrefix, items) { + + for (var i = 0; i < items.length; i++) { + var item = items[i]; + + levelName = item.name; + + var path = commandPrefix + '/' + levelName; + var isContainer = !_.isEmpty(item.items); + + result.push(thiz.createAction({ + label: levelName, + command: path, + icon: item.iconClass, + tab: 'Home', + group: 'Step', + mixin:defaultMixin + })); + + /* + addAction(levelName, path, item.iconClass, null, 'Home', 'Insert', 'item|view', null, null, { + item: item, + addPermission: true + }, null, null); + */ + + + if (isContainer) { + addItems(path, item.items); + } + } + + } + + addItems(rootAction, newBlockActions); + + + + function _selection() { + var selection = thiz.getSelection(); + if (!selection || !selection.length) { + return null; + } + var item = selection[0]; + if (!item) { + console.error('have no item'); + return null; + } + return selection; + } + + function _canMove() { + + var selection = _selection(); + if (!selection) { + return true; + } + return selection[0].canMove(item, this.command === 'Step/Move Up' ? -1 : 1); + + } + + + function canParent() { + + var selection = thiz.getSelection(); + if (!selection) { + return true; + } + var item = selection[0]; + if (!item) { + //console.error('canParent:have no item!!!',this); + return true; + } + if (this.command === 'Step/Move Left') { + return !item.getParent(); + } else { + return item.getParent(); + } + } + + function isItem() { + var selection = _selection(); + if (!selection) { + return true; + } + return false; + } + + function canMove() { + var selection = _selection(); + + if (!selection) { + return true; + } + + var item = selection[0]; + + if (this.command === 'Step/Move Up') { + return !item.canMove(null, -1); + } else { + return !item.canMove(null, 1); + } + + return false; + } + + result.push(thiz.createAction({ + label: 'Run', + command: 'Step/Run', + icon: 'fa-play', + tab: 'Home', + group: 'Step', + keycombo: ['r'], + shouldDisable: isItem + })); + + ////////////////////////////////////////////////////////////////// + // + // Step - Move + // + + result.push(thiz.createAction({ + label: 'MoveUp', + command: 'Step/Move Up', + icon: 'fa-arrow-up', + tab: 'Home', + group: 'Step', + keycombo: ['alt up'], + shouldDisable: canMove + })); + + result.push(thiz.createAction({ + label: 'MoveDown', + command: 'Step/Move Down', + icon: 'fa-arrow-down', + tab: 'Home', + group: 'Step', + keycombo: ['alt down'], + shouldDisable: canMove, + mixin: defaultMixin + })); + + result.push(thiz.createAction({ + label: 'MoveLeft', + command: 'Step/Move Left', + icon: 'fa-arrow-left', + tab: 'Home', + group: 'Step', + keycombo: ['alt left'], + shouldDisable: canMove, + mixin: defaultMixin + })); + + result.push(thiz.createAction({ + label: 'MoveRight', + command: 'Step/Move Right', + icon: 'fa-arrow-right', + tab: 'Home', + group: 'Step', + keycombo: ['alt right'], + shouldDisable: canParent, + mixin: defaultMixin + })); + + + //Step enable/disable + /* + addAction('On', 'Step/Enable', 'fa-toggle-off', ['alt d'], 'Home', 'Step', 'item|view', null, null, + { + addPermission:true, + onCreate: function (action) { + action.setVisibility(types.ACTION_VISIBILITY.RIBBON, { + widgetClass: declare.classFactory('_Checked', [ToggleButton,_ActionValueWidgetMixin], null, {} ,null), + widgetArgs: { + icon1: 'fa-toggle-on', + icon2: 'fa-toggle-off', + delegate: thiz, + checked:true, + iconClass:'fa-toggle-on' + } + }); + } + },null, function(){ + return thiz.getSelection().length==0; + }); + */ + + result.push(thiz.createAction({ + label: 'On', + command: 'Step/Enable', + icon: 'fa-toggle-off', + tab: 'Home', + group: 'Step', + keycombo: ['alt d'], + shouldDisable: isItem, + mixin: defaultMixin + })); + + + result.push(this.createAction({ + label: 'Edit', + command: 'Step/Edit', + icon: ACTION_ICON.EDIT, + keycombo: ['f4', 'enter', 'dblclick'], + tab: 'Home', + group: 'Step', + shouldDisable: isItem + })); + + /* + addAction('Properties', 'Step/Properties', 'fa-gears', ['alt enter'], 'Home', 'Step', 'item|view', null, null, + { + addPermission: true, + onCreate: function (action) { + action.handle=false; + action.setVisibility(types.ACTION_VISIBILITY.RIBBON, { + widgetClass: declare.classFactory('_Checked', [ToggleButton, _ActionValueWidgetMixin], null, {}, null), + widgetArgs: { + icon1: 'fa-toggle-on', + icon2: 'fa-toggle-off', + delegate: thiz, + checked: false, + iconClass: 'fa-toggle-off' + } + }); + } + }, null, function () { + return thiz.getSelection().length == 0; + }); + */ + result.push(thiz.createAction({ + label: 'Properties', + command: 'Step/Properties', + icon: 'fa-arrow-up', + tab: 'Home', + group: 'Step', + keycombo: ['alt enter'], + shouldDisable: isItem, + mixin: defaultMixin + })); + + this._emit('onAddActions', { + actions: result, + addAction: addAction, + permissions: permissions, + store: actionStore + }); + + //console.dir(_.pluck(result,'command')); + + + return result; + } + },mixin); + + grid = utils.addWidget(BlockGrid, gridArgs, null, parent, startup!==false ? true : false); + + return grid; + + + + } + + Module.createScope = createScope; + Module.createBlockGrid = createBlockGrid; + + return Module; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/views/BlockDesignEditor.js b/packages/xblox/ref-control-freak/xblox/views/BlockDesignEditor.js new file mode 100644 index 00000000..593cc2a1 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/BlockDesignEditor.js @@ -0,0 +1,185 @@ +/** @module xgrid/Base **/ +define([ + "xdojo/declare", + 'dojo/dom-class', + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/ThumbRenderer', + 'xide/views/_ActionMixin', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dijit/form/RadioButton', + 'xide/widgets/Ribbon', + 'xide/editor/Registry', + 'xaction/DefaultActions', + 'xaction/Action', + "xblox/widgets/BlockGridRowEditor", + 'dgrid/Editor', + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + + 'xide/factory', + + + 'dijit/Menu', + + 'xide/data/Reference', + + 'dijit/form/DropDownButton', + 'dijit/MenuItem', + "xide/views/CIViewMixin", + 'xide/layout/TabContainer', + "dojo/has!host-browser?xblox/views/BlockEditDialog", + 'xblox/views/BlockGrid', + 'xgrid/DnD', + 'xblox/views/BlocksGridDndSource', + 'xblox/widgets/DojoDndMixin', + 'xide/registry', + 'dojo/topic', + //'xide/tests/TestUtils', + //'./TestUtils', + 'module' +], function (declare, domClass,types, + utils, ListRenderer, TreeRenderer, ThumbRenderer, + _ActionMixin, + Grid, MultiRenderer, RadioButton, Ribbon, Registry, DefaultActions, Action, BlockGridRowEditor, + Editor,Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, factory,Menu,Reference,DropDownButton, + MenuItem,CIViewMixin,TabContainer, + + BlockEditDialog, + BlockGrid, + Dnd,BlocksGridDndSource,DojoDndMixin, + registry,topic,TestUtils,BlockUtils,module + ) { + /** + * + */ + + + + + /*** + * Block - Designer - Remarks + * + * + * VIEW : + * + * 1. Source - VIEW + * 2. Block - Grid ( Properties) + * 3. Console + * 4. + * + * + */ + + + /*** + * playground + */ + var ctx = window.sctx; + + function doGridTest(grid,scope){ + + + + grid.refresh().then(function(){ + grid.select([0]).then(function(e){ + grid.runAction('Step/Run'); + }); + }); + + } + + + + + if (ctx) { + + var blockManager = ctx.getBlockManager(); + var blockScope = BlockUtils.createScope(blockManager,[ + { + "_containsChildrenIds": [], + "id": "Shell-Block", + "description": "Runs a JSON-RPC-2.0 method on the server", + "name": "Run Server Method", + "method": "XShell::run", + "args": "ls", + "deferred": true, + "defaultServiceClass": "XShell", + "defaultServiceMethod": "run", + "declaredClass": "xblox.model.server.RunServerMethod", + "enabled": true, + "shareTitle": "", + "canDelete": true, + "order": 0, + "type": "added", + "group": "click" + } + ]); + + + var mainView = ctx.mainView; + + if (mainView) { + + var parent = TestUtils.createTab(null,null,module.id); + + var grid, + store = blockScope.blockStore, + gridArgs = { + ctx:ctx, + blockScope: blockScope, + blockGroup: 'click', + attachDirect:true, + collection: store.filter({ + group: "click" + }) + }; + + grid = utils.addWidget(BlockGrid,gridArgs,null,parent,true); + + ctx.getWindowManager().registerView(grid,true); + + + var blocks = blockScope.allBlocks(); + + + + + setTimeout(function () { + mainView.resize(); + grid.resize(); + }, 1000); + + + function test() { + doGridTest(grid,blockScope); + return; + + } + + function test2() { + return; + } + + setTimeout(function () { + test(); + }, 1000); + + + setTimeout(function () { + test2(); + }, 2000); + + } + } + + return Grid; + +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/views/BlockEditDialog.js b/packages/xblox/ref-control-freak/xblox/views/BlockEditDialog.js new file mode 100644 index 00000000..8bafbc29 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/BlockEditDialog.js @@ -0,0 +1,75 @@ +define([ + "dcl/dcl", + 'xide/views/_CIDialog', + 'xide/utils', + 'xblox/views/BlockEditView' +], function (dcl, _CIDialog, utils, BlockEditView) { + return dcl(_CIDialog, { + bodyCSS: { + 'height': 'auto', + 'min-height': '400px' + }, + declaredClass: "xblock.views.BlockEditDialog", + onSave: function (ci, value) { + if (ci.dst && this.item[ci.dst]) { + this.item[ci.dst] = value; + } + }, + __initWithCIS: function (cis) { + this.cisView = utils.addWidget(BlockEditView, { + delegate: this, + options: { + groupOrder: { + 'General': 0, + 'Advanced': 1, + 'Description': 2 + } + }, + cis: cis + }, this, this.containerNode, true); + }, + __onOk: function () { + var cis = this.cisView.getCIS(); + var options = utils.toOptions(cis); + //now convert back to block fields + for (var i = 0; i < options.length; i++) { + var option = options[i]; + var field = option.dst; + if (field != null && this.item[field] != null) { + if (option.active != null && option.active === false && option.changed === false) { + continue; + } + if (this.item[option.dst] != option.value || + this.item[option.dst] !== option.value) { + if (this.item.onChangeField) { + this.item.onChangeField(option.dst, option.value); + } + this.item[option.dst] = option.value; + } + } + } + if (this.delegate && this.delegate.onOk) { + this.delegate.onOk(this, this.item); + } + this.hide(); + return true; + }, + initWithBlock: function (item) { + var cis = item.getFields(); + if (cis) { + this.initWithCIS(cis); + } + }, + startup: function () { + if (this._started) { + return; + } + this.inherited(arguments); + if (this.item) { + this.initWithBlock(this.item); + } + this.addActionButtons(); + this.inherited(arguments); + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/views/BlockEditView.js b/packages/xblox/ref-control-freak/xblox/views/BlockEditView.js new file mode 100644 index 00000000..753fc55d --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/BlockEditView.js @@ -0,0 +1,13 @@ +define([ + 'dcl/dcl', + 'xide/views/CIView' +],function (dcl,CIView){ + return dcl([CIView],{ + declaredClass:"xblox.views.BlockEditView", + onSave:function(ci,value){ + if(this.delegate && this.delegate.onSave){ + this.delegate.onSave(ci,value); + } + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/views/BlockGrid.js b/packages/xblox/ref-control-freak/xblox/views/BlockGrid.js new file mode 100644 index 00000000..ffe11c89 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/BlockGrid.js @@ -0,0 +1,934 @@ +/** @module xblox/views/BlockGrid **/ +define([ + 'dojo/_base/declare', + 'xide/types', + 'xide/utils', + 'xide/factory', + 'xblox/views/Grid', + 'xide/layout/_Accordion', + 'xide/views/CIViewMixin', + 'xaction/DefaultActions', + 'dojo/dom-class', + 'dojo/Deferred', + 'xide/views/History', + 'xide/widgets/IconPicker' // not loaded yet +], function (declare, types, utils, factory, Grid, _Accordion, CIViewMixin, DefaultActions, domClass, Deferred, History, IconPicker) { + + var ACTION = types.ACTION; + var debugMark = false; + + function clearMarkTimer(element) { + if (element.hightlightTimer) { + clearTimeout(element.hightlightTimer); + element.hightlightTimer = null; + } + } + + /** + * Standard block grid class. + * @class module:xblox/views/BlockGrid + * @augments module:xide/mixins/EventedMixin + */ + var IMPLEMENTATION = { + highlightDelay: 500, + currentCIView: null, + targetTop: null, + lastSelectedTopTabTitle: 'General', + showHeader: false, + toolbarInitiallyHidden: true, + contextMenuArgs: { + actionFilter: { + quick: true + } + }, + menuOrder: { + 'File': 110, + 'Edit': 100, + 'View': 90, + 'Block': 50, + 'Settings': 20, + 'Navigation': 10, + 'Step': 115, + 'New': 4, + 'Window': 1 + }, + permissions: [ + //ACTION.TOOLBAR + //ACTION.EDIT, + //ACTION.RENAME, + ACTION.RELOAD, + ACTION.DELETE, + ACTION.CLIPBOARD, + ACTION.LAYOUT, + ACTION.COLUMNS, + ACTION.SELECTION, + ACTION.DOWNLOAD, + //ACTION.PREVIEW, + ACTION.SAVE, + ACTION.SEARCH, + 'Step/Run', + 'Step/Move Up', + 'Step/Move Down', + ACTION.TOOLBAR, + 'Step/Properties', + 'Step/Edit', + 'Step/Back', + 'Step/Pause', + ACTION.HEADER, + ACTION.CONTEXT_MENU + ], + tabOrder: { + 'Home': 100, + 'View': 50, + 'Settings': 20 + }, + tabSettings: { + 'Step': { + width: '190px' + }, + 'Show': { + width: '130px' + }, + 'Settings': { + width: '100%' + } + }, + groupOrder: { + 'Clipboard': 110, + 'File': 100, + 'Step': 80, + 'Open': 70, + 'Organize': 60, + 'Insert': 10, + 'Select': 0 + }, + options: utils.clone(types.DEFAULT_GRID_OPTIONS), + columns: [ + { + renderExpando: true, + label: "Name", + field: "name", + sortable: false, + formatter: function (value, block) { + var blockText = ''; + if (block.toText) { + + blockText = block.toText(); + + return '' + blockText + ''; + } + return value; + } + } + ], + getTypeMap: function () { + return {}; + }, + getBlock: function (id) { + return this.blockScope.getBlockById(id); + }, + formatOrder: function (value, block) { + + if (block) { + var parent = block.getParent(); + var _index = block.index(); + if (parent) { + var parents = block.numberOfParents(); + var _out = ' '; + for (var i = 0; i < parents; i++) { + _out += ' ' + } + _out += ''; + + for (var i = 0; i < parents; i++) { + _out += '-' + } + _out += ' '; + _index = _out + _index; + } else { + return ' ' + _index; + } + return _index; + } + + return ''; + }, + focus: function (element) { + + if (!element) { + return; + } + return this.inherited(arguments); + }, + postMixInProperties: function () { + + var _has = false; + + var self = this; + _.each(this.columns, function (col) { + if (col.field == 'order') { + _has = true; + } + }); + + if (!_has && this.blockGroup !== 'basicVariables') { + this.columns.unshift({ + label: "Line", + field: "order", + sortable: false, + hidden: false, + formatter: function (val, b) { + return self.formatOrder(val, b, self); + } + }); + } + this.inherited(arguments); + + }, /* + resize:function(){ + console.log('-r'); + return this.inherited(arguments); + }, + */ + /** + * Override render row to add additional CSS class + * for indicating a block's enabled state + * @param object + * @returns {*} + */ + renderRow: function (object) { + + if (!this.collection) { + console.warn('BlockGrid::renderRow : have no collection! Abort'); + return; + } + + var res = this.inherited(arguments); + + if (object.enabled == false) { + domClass.add(res, 'disabledBlock'); + } + var _row = $(res), + scope = object.getScope(); + var scopeId = scope ? scope.id : null; + + //restore status css class + if (object.getStatusClass && scopeId) { + var clsInfo = object.getStatusClass(scopeId); + var element = $(res); + //the block is marked with a class, restore it + if (clsInfo && !element[0].hightlightTimer) { + this.mark($(res), clsInfo.className, object, clsInfo.delay); + } + } + + //add inline editors + if (object.hasInlineEdits) { + if (_row.__inline) { + return; + } + _row.__inline = true; + var _links = _row.find('.editable'); + _.each(_links, function (link) { + link.object = object; + + var $link = $(link), + _field = $link.attr('data-prop'), + _type = $link.attr('display-type') || 'text', + options = object.getFieldOptions ? object.getFieldOptions(_field) : null, + _editOptions = { + type: _type, + placement: $link.attr('pos') || 'right', + mode: $link.attr('display-mode') || 'popup', + title: $link.prop('data-title'), + inputclass: 'input-transparent', + success: function (data, value) { + var prop = $(this).attr('data-prop'); + this.object.set(prop, value); + } + }; + if (options) { + _editOptions.source = options; + } + $link.editable(_editOptions); + $link.attr("tabIndex", "-1"); + }); + } + return res; + }, + getCurrentParent: function () { + var history = this.getHistory(); + var now = history.getNow(); + if (now) { + return this.getBlock(now); + } else { + return null; + } + }, + /** + * + * @param item + * @param action + */ + addItem: function (item, action) { + + if (!item) { + return false; + } + + action = action || {}; + + var target = this.getSelection()[0], + proto = item.proto || action.proto, + ctorArgs = item.ctrArgs || action.ctrArgs, + where = null, + newBlock = null; + + + if (!target) { + target = this.getCurrentParent(); + } + + //cache problem: + if (!target) { + var _item = this.getSelection()[0]; + if (_item) { + target = _item; + } + } + + + if (ctorArgs) { + if (ctorArgs.id) { + ctorArgs.id = utils.createUUID(); + } + if (ctorArgs.parent) { + ctorArgs.parent = null; + } + if (ctorArgs.parentId) { + ctorArgs.parentId = null; + } + if (ctorArgs.items) { + ctorArgs.items = []; + } + } + + if (target && proto && ctorArgs) { + + if (target.owner && target.dstField) { + where = target.dstField; + target = target.owner; + } + + ctorArgs['parentId'] = target.id; + ctorArgs['group'] = null; + ctorArgs['parent'] = target;//should be obselete + + newBlock = target.add(proto, ctorArgs, where); + newBlock.parent = target; + + } else if (!target && proto && ctorArgs) { + + if (ctorArgs.group === "No Group" && this.newRootItemGroup) { + ctorArgs.group = this.newRootItemGroup; + } + if (!ctorArgs.group && this.newRootItemGroup) { + ctorArgs.group = this.newRootItemGroup; + } + if (!ctorArgs.group && this.blockGroup) { + ctorArgs.group = this.blockGroup; + } + + if (ctorArgs.group && this.newRootItemGroup && ctorArgs.group !== this.newRootItemGroup) { + ctorArgs.group = this.newRootItemGroup; + } + + newBlock = factory.createBlock(proto, ctorArgs);//root block + } + + if (newBlock && newBlock.postCreate) { + newBlock.postCreate(); + } + + if (!newBlock) { + console.error('didnt create block'); + return; + } + + + var store = this.getStore(target) || this.collection, + storeItem = store.getSync(newBlock[store.idProperty]); + + if (!storeItem) { + console.error('new block not in store'); + store.putSync(newBlock); + } + + try { + newBlock.scope._emit(types.EVENTS.ON_ITEM_ADDED, { + item: newBlock, + owner: newBlock.scope + }); + } catch (e) { + logError(e); + } + this._itemChanged('added', newBlock, store); + + return storeItem; + + }, + _itemChanged: function (type, item, store) { + + store = store || this.getStore(item); + + var thiz = this; + + function _refreshParent(item, silent) { + + var parent = item.getParent(); + if (parent) { + var args = { + target: parent + }; + if (silent) { + this._muteSelectionEvents = true; + } + store.emit('update', args); + if (silent) { + this._muteSelectionEvents = false; + } + } else { + thiz.refresh(); + } + } + + function refresh() { + thiz.refreshRoot(); + //thiz.refreshCurrent(); + thiz.refresh(); + thiz.refreshCurrent(); + } + + function select(item, reason) { + thiz.select(item, null, true, { + focus: true, + delay: 1, + append: false, + expand: true + }, reason); + } + + + switch (type) { + + case 'added': { + refresh(); + select(item); + break; + } + case 'moved': { + break; + } + case 'deleted': { + var parent = item.getParent(); + var _prev = item.getPreviousBlock() || item.getNextBlock() || parent; + if (parent) { + var _container = parent.getContainer(); + if (_container) { + _.each(_container, function (child) { + if (child.id == item.id) { + _container.remove(child); + } + }); + } + } + this.refresh(); + if (_prev) { + select(_prev); + } + break; + } + } + }, + printRootOrder: function (items) { + + var items = items || this.collection.storage.fullData; + var str = ''; + _.each(items, function (item, i) { + str += 'id: ' + item.id + ' | ' + i + ' \n'; + }); + console.log('order: \n' + str, JSON.stringify(this.collection.storage.index)); + + }, + _refresh:function(){ + console.log('refresh'); + //var res = this.inherited(arguments); + this.refreshRoot(); + //eturn res; + }, + /** + * + * @param selection + */ + refreshAll: function (selection) { + + if (!selection) { + this._preserveSelection(); + } + + this.refreshRoot(); + this.refresh(); + + if (!selection) { + this._restoreSelection(); + } + var thiz = this; + + if (selection) { + setTimeout(function () { + thiz.select(selection, null, true, { + focus: true + }); + }, 30); + } + }, + getStore: function (item) { + if (item) { + if (_.isArray()) { + item = item[0]; + } + if (item._store) { + return item._store; + } + } + return null; + }, + /** + * Special refresh for changes at root level + */ + refreshRoot: function () { + return this.set('collection', this.collection.filter(this.getRootFilter())); + }, + refresCurrent: function () { + return this.set('collection', this.collection.filter(this.getRootFilter())); + }, + /** + * Standard filter for root level + * @returns {{group: (null|string|*|string|className)}} + */ + getRootFilter: function () { + return { + group: this.blockGroup + }; + }, + _onCIChanged: function (ci, block, oldValue, newValue, field) { + }, + clearPropertyPanel: function () { + + var props = this.getPropertyStruct(); + + if (props.currentCIView) { + props.currentCIView.empty(); + props.currentCIView = null; + } + + if (props.targetTop) { + utils.destroy(props.targetTop, true); + props.targetTop = null; + } + + props._lastItem = null; + + this.setPropertyStruct(props); + }, + getState: function (state) { + state = this.inherited(arguments) || {}; + var right = this.getRightPanel(); + state.properties = right.isExpanded(); + return state; + }, + onCIChanged: function (ci, block, oldValue, newValue, field, cis) { + if (oldValue === newValue) { + return; + } + var _col = this.collection; + block = this.collection.getSync(block.id); + block.set(field, newValue); + this.refreshActions(); + block[field] = newValue; + _col.emit('update', { + target: block, + type: 'update' + }); + block._store.emit('update', { + target: block + }); + this._itemChanged('changed', block, _col); + block.onChangeField(field, newValue, cis, this); + var cell = this.cell(block.id, '1'); + if (cell && cell.column) { + this.refreshCell(cell).then(function (e) { + }); + } + if (field === 'enabled') { + this.refresh(); + } + this._itemChanged('changed', block, _col); + }, + onShowProperties: function (item) { + var _console = this._console; + if (_console && item) { + var value = item.send || item.method; + var editor = _console.getConsoleEditor(); + editor.set('value', value); + this._lastConsoleItem = item; + } + }, + showProperties: function (item, force) { + var dfd = new Deferred(), + block = item || this.getSelection()[0], + thiz = this, + rightSplitPosition = thiz.getPanelSplitPosition(types.DOCKER.DOCK.RIGHT); + + if (!block || rightSplitPosition == 1 || block.canEdit()===false) { + return; + } + var right = this.getRightPanel('Properties', null, 'DefaultTab', {}); + right.closeable(false); + var props = this.getPropertyStruct(); + if (block == props._lastItem && force !== true) { + return; + } + + this.clearPropertyPanel(); + props = this.getPropertyStruct(); + props._lastItem = item; + var _title = block.name || block.title; + function propertyTabShown(tab, show) { + if (!tab) { + return; + } + var title = tab.title; + + if (show) { + props.lastSelectedTopTabTitle = title; + + } else if (!show && props.lastSelectedTopTabTitle === title) { + props.lastSelectedTopTabTitle = null; + } + } + + var tabContainer = props.targetTop; + if (!tabContainer) { + tabContainer = utils.addWidget(_Accordion, {}, null, right.containerNode, true); + + $(tabContainer.domNode).addClass('CIView Accordion'); + props.targetTop = tabContainer; + thiz.addHandle('show', tabContainer._on('show', function (evt) { + propertyTabShown(evt.view, true); + })); + + thiz.addHandle('hide', tabContainer._on('hide', function (evt) { + propertyTabShown(evt.view, false); + })); + } + + _.each(tabContainer.getChildren(), function (tab) { + tabContainer.removeChild(tab); + }); + + if (props.currentCIView) { + props.currentCIView.empty(); + } + + if (!block.getFields) { + console.log('have no fields', block); + return; + } + + var cis = block.getFields(); + for (var i = 0; i < cis.length; i++) { + cis[i].vertical = true; + } + + var ciView = new CIViewMixin({ + typeMap: thiz.getTypeMap(), + tabContainerClass: _Accordion, + tabContainer: props.targetTop, + delegate: this, + viewStyle: 'padding:0px;', + autoSelectLast: true, + item: block, + source: this.callee, + + options: { + groupOrder: { + 'General': 1, + 'Advanced': 2, + 'Script': 3, + 'Arguments': 4, + 'Description': 5, + 'Share': 6 + + } + }, + cis: cis + }); + ciView.initWithCIS(cis); + props.currentCIView = ciView; + if (block.onFieldsRendered) { + block.onFieldsRendered(block, cis); + } + thiz.addHandle('valueChanged', ciView._on('valueChanged', function (evt) { + setTimeout(function () { + thiz.onCIChanged && thiz.onCIChanged(evt.ci, block, evt.oldValue, evt.newValue, evt.ci.dst, cis); + }, 10); + })); + var tabToOpen = tabContainer.getTab(props.lastSelectedTopTabTitle); + if (tabToOpen) { + tabContainer.selectChild(props.lastSelectedTopTabTitle, true); + } else { + props.lastSelectedTopTabTitle = null; + } + this.setPropertyStruct(props); + this.onShowProperties(block); + return dfd; + + }, + onSelectionChanged: function (evt, force) { + var selection = evt.selection; + var item = selection[0]; + var thiz = this, + ctx = this.ctx, + actionStore = thiz.getActionStore(), + contextMenu = this.getContextMenu(), + toolbar = this.getToolbar(), + mainToolbar = ctx.getWindowManager().getToolbar(), + itemActions = item && item.getActions ? item.getActions() : null, + _debugTree = false, + enableAction = this.getAction('Step/Enable'); + + if (item) { + enableAction.value = item.enabled; + } + var globals = actionStore.query({ + global: true + }); + + if (globals && !itemActions) { + itemActions = []; + } + + _.each(globals, function (globalAction) { + var globalItemActions = globalAction.getActions ? globalAction.getActions(thiz.permissions, thiz) : null; + if (globalItemActions && globalItemActions.length) { + itemActions = itemActions.concat(globalItemActions); + } + }); + contextMenu && contextMenu.removeCustomActions(); + toolbar && toolbar.removeCustomActions(); + mainToolbar && mainToolbar.removeCustomActions(); + if (!itemActions || _.isEmpty(itemActions)) { + return; + } + var newStoredActions = this.addActions(itemActions); + actionStore._emit('onActionsAdded', newStoredActions); + }, + startup: function () { + if (this._started) { + return; + } + this.inherited(arguments); + this._history = new History(); + if (!this.propertyStruct) { + this.propertyStruct = { + currentCIView: null, + targetTop: null, + _lastItem: null + }; + } + this._dontSubscribeToHighligt = true; + + $(this.domNode).addClass('blockGrid'); + var clipboardData, pastedData; + /* + $(this.domNode).on('paste',function(e){ + e.stopPropagation(); + e.preventDefault(); + console.error('paste',e); + // Get pasted data via clipboard API + clipboardData = e.clipboardData || window.clipboardData; + pastedData = clipboardData.getData('Text'); + + // Do whatever with pasteddata + alert(pastedData); + }); + */ + var thiz = this, + permissions = this.permissions || []; + + if (permissions) { + var _defaultActions = DefaultActions.getDefaultActions(permissions, this); + _defaultActions = _defaultActions.concat(this.getBlockActions(permissions)); + this.addActions(_defaultActions); + } + + if (DefaultActions.hasAction(permissions, 'Step/Properties')) { + this._on('selectionChanged', function (evt) { + var selection = evt.selection, + item = selection[0]; + + clearTimeout(thiz._propsTimer); + thiz._propsTimer = null; + if (item) { + thiz._propsTimer = setTimeout(function () { + var props = thiz.getPropertyStruct(); + thiz.showProperties(item); + }, 500); + } else if (!item && evt.why == 'clear') { + //clear property panel when empty + thiz.clearPropertyPanel(); + } + evt.view = thiz; + item && thiz.publish('ON_SELECTED_BLOCK',evt); + }); + } + + this.addHandle('update', this.collection.on('update', function (evt) { + var item = evt.target, + type = evt.type; + + if (type === 'update' && evt.property === 'value') { + var node = thiz.toNode(evt); + if (node) { + thiz.mark(node, 'successBlock', item); + } + } + })); + + //console.error('- subscribe ' + this.collection.id + ' group ' + this.blockGroup); + this.addHandle('added', this.collection._on('added', function (e) { + if (thiz._refreshTimer) { + return; + } + + thiz._refreshTimer = setTimeout(function () { + thiz.refresh(); + delete thiz._refreshTimer; + thiz._refreshTimer = null; + e.target && thiz.select(e.target, null, true, { + focus: true, + append: false, + expand: true, + delay: 10 + }); + }, 10); + })); + + this.addHandle('update', this.collection._on('update', function (evt) { + var item = evt.target, + node = thiz.toNode(evt), + type = evt.type; + if (type === 'update' && evt.property === 'value') { + thiz.mark(node, 'successBlock', item); + } + })); + + + this.subscribe(types.EVENTS.ON_RUN_BLOCK, function (evt) { + thiz.mark(thiz.toNode(evt), 'activeBlock', evt.target, evt.timeout); + }); + this.subscribe(types.EVENTS.ON_RUN_BLOCK_FAILED, function (evt) { + thiz.mark(thiz.toNode(evt), 'failedBlock', evt.target, evt.timeout); + }); + this.subscribe(types.EVENTS.ON_RUN_BLOCK_SUCCESS, function (evt) { + thiz.mark(thiz.toNode(evt), 'successBlock', evt.target, evt.timeout); + }); + + this._on('selectionChanged', this.onSelectionChanged, this); + this.publish('ON_BLOCK_GRID',{ + view:this + }); + + this.setRenderer(this.selectedRenderer, false); + + }, + /** + * Override removeRow to prevent marking leaks + * @param rowElement {HTMLElement} + * @param preserveDom {boolean} + * @returns {*|{dir, lang}} + */ + removeRow: function (rowElement, preserveDom) { + rowElement = rowElement.element || rowElement; + if (rowElement && rowElement.hightlightTimer && preserveDom === false) { + clearMarkTimer(rowElement); + } + return this.inherited(arguments); + }, + mark: function (element, cssClass, block, timeout) { + if (!element) { + return; + } + if (!this.isRendered(block)) { + return; + } + block = block || {}; + if (this._highlight === false) { + return; + } + if (block.__ignoreChangeMark === true) { + return; + } + var thiz = this; + var debugMark = false; + timeout = timeout == false ? false : (thiz.highlightDelay || 500); + //already + if (element[0]._currentHighlightClass === cssClass) { + return; + } + + delete element[0]._currentHighlightClass; + element[0]._currentHighlightClass = null; + element[0]._currentHighlightClass = cssClass; + var scopeId = block.getScope().id; + //debugMark && console.log('mark ' + cssClass + ' delay ' + timeout + ' scope id');; + element.removeClass('failedBlock successBlock activeBlock'); + element.addClass(cssClass); + block.setStatusClass(scopeId, { + className: cssClass, + delay: timeout + }); + clearMarkTimer(element[0]); + if (timeout !== false && timeout > 0) { + element[0].hightlightTimer = setTimeout(function () { + element.removeClass(cssClass); + element.removeClass('failedBlock successBlock activeBlock'); + block.setStatusClass(scopeId, null); + delete element[0]._currentHighlightClass; + element[0]._currentHighlightClass = null; + }, timeout); + } + }, + toNode: function (evt) { + var item = evt.target || evt; + if (item) { + var row = this.row(item); + if (row) { + var element = row.element; + if (element) { + return $(element); + } + } + } + return null; + } + }; + + var Module = declare("xblox.views.BlockGrid", Grid,IMPLEMENTATION); + + Module.IMPLEMENTATION = IMPLEMENTATION; + + return Module; + +}); diff --git a/packages/xblox/ref-control-freak/xblox/views/BlockGridPalette.js b/packages/xblox/ref-control-freak/xblox/views/BlockGridPalette.js new file mode 100644 index 00000000..a4e80999 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/BlockGridPalette.js @@ -0,0 +1,631 @@ +define([ + "dcl/dcl", + "xdojo/declare", + 'xide/types', + 'xide/utils', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'dgrid/Editor', + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + 'xblox/views/BlockGrid', + 'xblox/BlockActions', + 'xide/views/_LayoutMixin', + "xide/widgets/_Widget", + 'module', + 'xide/factory', + 'xblox/views/ThumbRenderer', + "xblox/views/DnD", + 'dojo/dom-construct', + 'xgrid/Selection', + 'xgrid/KeyboardNavigation', + 'xaction/ActionStore', + 'xlang/i18', + 'xide/$', + 'xide/_base/_Widget', + 'xdojo/has', + 'dojo/has!debug?xide/tests/TestUtils' +], function (dcl, declare, types, + utils, ListRenderer, TreeRenderer, Grid, MultiRenderer, Editor, Defaults, Layout, Focus, + OnDemandGrid, EventedMixin, BlockGrid, + BlockActions, _LayoutMixin, _Widget, module, factory, ThumbRenderer, BlocksDnD, domConstruct, + Selection, KeyboardNavigation, ActionStore, i18, $, _BWidget, has, TestUtils) { + + var ThumbClass = declare('xblox.ThumbRenderer2', [ThumbRenderer], { + thumbSize: "400", + resizeThumb: true, + __type: 'thumb', + deactivateRenderer: function () { + $(this.domNode.parentNode).removeClass('metro'); + $(this.domNode).css('padding', ''); + this.isThumbGrid = false; + + }, + activateRenderer: function () { + $(this.contentNode).css('padding', '8px'); + this.isThumbGrid = true; + this.refresh(); + }, + /** + * Override renderRow + * @param obj + * @returns {*} + */ + renderRow: function (obj) { + var div = domConstruct.create('div', { + className: "tile widget form-control", + style: 'margin-left:8px;margin-top:8px;margin-right:8px;width:150px;height:initial;max-width:200px;float:left;padding:8px' + }), + label = obj.title, + imageClass = obj.icon || 'fa fa-folder fa-2x'; + + var iconStyle = 'float:left; margin-left:3px;margin-top:3px;margin-right:3px;text-shadow: 2px 2px 5px rgba(0,0,0,0.3);font-size: inherit;opacity: 0.4'; + var folderContent = ''; + div.innerHTML = + '
' + + folderContent + + '' + label + '' + + '
'; + return div; + } + }); + + var GroupRenderer = declare('xblox.GroupRenderer', TreeRenderer, { + itemClass: ThumbClass, + groupClass: TreeRenderer, + renderRow: function (obj) { + return (!obj.group ? this.itemClass : this.groupClass).prototype.renderRow.apply(this, arguments); + } + }); + var Implementation = {}, + renderers = [GroupRenderer, ThumbClass, TreeRenderer], + multiRenderer = declare.classFactory('xgrid/MultiRendererGroups', {}, renderers, MultiRenderer.Implementation); + + //console.clear(); + + var SharedDndGridSource = BlocksDnD.BlockGridSource; + var GridClass = Grid.createGridClass('xblox.views.BlockGridPalette', Implementation, { + SELECTION: { + CLASS: Selection + }, + KEYBOARD_SELECTION: true, + CONTEXT_MENU: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + WIDGET: { + CLASS: _Widget + }, + KEYBOARD_NAVIGATION: { + CLASS: KeyboardNavigation + }, + Dnd: { + CLASS: BlocksDnD + } + }, + { + RENDERER: multiRenderer + }, + { + renderers: renderers, + selectedRenderer: GroupRenderer + } + ); + + + var HelpWidget = dcl(_BWidget, { + templateString: '
', + startup: function () { + } + }); + var GridCSourcelass = declare('ActionGrid', GridClass, { + dndParams: { + allowNested: false, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return false;//source !== this; // Don't self-accept. + }, + isSource: true + }, + formatColumn: function (field, value, obj) { + var renderer = this.selectedRenderer ? this.selectedRenderer.prototype : this; + if (renderer.formatColumn_) { + var result = renderer.formatColumn.apply(arguments); + if (result) { + return result; + } + } + if (obj.renderColumn_) { + var rendered = obj.renderColumn.apply(this, arguments); + if (rendered) { + return rendered; + } + } + switch (field) { + case "title": { + value = obj.group ? ('' + value + '
') : value; + } + } + return value; + }, + _columns: {}, + postMixInProperties: function () { + var state = this.state; + if (state) { + if (state._columns) { + this._columns = state._columns; + } + } + this.columns = this.getColumns(); + var newBlockActions = this.getAddActions(); + var levelName = ''; + var result = []; + var BLOCK_INSERT_ROOT_COMMAND = ''; + var defaultMixin = {addPermission: true}; + var rootAction = BLOCK_INSERT_ROOT_COMMAND; + var thiz = this; + + function addItems(commandPrefix, items) { + for (var i = 0; i < items.length; i++) { + var item = items[i]; + levelName = item.name; + var path = commandPrefix ? commandPrefix + '/' + levelName : levelName; + var isContainer = !_.isEmpty(item.items); + result.push(thiz.createAction({ + label: levelName, + command: path, + icon: item.iconClass, + tab: 'Home', + mixin: utils.mixin({ + item: item, + parent: isContainer ? "root" : commandPrefix, + quick: true, + title: i18.localize(levelName), + group: isContainer, + icon: item.iconClass, + toDropObject: function (item, targetItem, params) { + var blockArgs = item.item; + var ctrArgs = blockArgs.ctrArgs; + ctrArgs.id = null; + ctrArgs.items = null; + ctrArgs.scope = params.targetGrid.blockScope; + var proto = blockArgs.proto; + var parent = params.center === true && targetItem ? targetItem : null; + if (parent) { + var block = factory.createBlock(proto, ctrArgs);//root block + return parent.add(block, null, null); + + } else { + ctrArgs.group = params.targetGrid.blockGroup; + } + return factory.createBlock(proto, ctrArgs);//root block + } + }, defaultMixin) + + })); + + if (isContainer) { + addItems(path, item.items); + } + } + } + + addItems(rootAction, newBlockActions); + var actionStore = new ActionStore({ + parentProperty: 'parent', + parentField: 'parent', + idProperty: 'command', + mayHaveChildren: function (parent) { + if (parent._mayHaveChildren === false) { + return false; + } + return true; + } + }); + actionStore.setData(result); + var all = actionStore.query(null); + _.each(all, function (item) { + item._store = actionStore; + }); + var $node = $(this.domNode); + $node.addClass('xFileGrid metro'); + this.collection = actionStore.filter({ + parent: "root" + }); + return this.inherited(arguments); + }, + getColumns: function (formatters) { + var self = this; + this.columns = [ + { + renderExpando: true, + label: 'Name', + field: 'title', + sortable: true, + formatter: function (value, obj) { + return self.formatColumn('title', value, obj); + }, + hidden: false + } + ]; + return this.columns; + }, + getAddActions: function (item) { + var thiz = this; + item = item || {}; + var items = factory.getAllBlocks(this.blockScope, this, item, this.blockGroup, false); + //tell everyone + thiz.publish(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items, + view: this, + owner: this, + item: item, + group: this.blockGroup, + scope: this.blockScope + }); + + thiz._emit(types.EVENTS.ON_BUILD_BLOCK_INFO_LIST_END, { + items: items, + view: this, + owner: this + }); + return items; + + + }, + startup: function () { + this._showHeader(false); + + this._on('selectionChanged', function (evt) { + var selection = evt.selection, + item = selection[0]; + var view = this.lastSource; + if (item && view && item.item.proto && item.item.proto.prototype) { + view.clearPropertyPanel(); + var help = view.getHelp(item.item.proto.prototype); + if (help) { + var props = view.getPropertyStruct(); + var target = view.__right; + var widget = utils.addWidget(HelpWidget, {}, null, target.containerNode, true); + $(widget.domNode).append(help); + props.targetTop = widget; + } + } + }); + var res = this.inherited(arguments); + _.each(this.getRows(), function (row) { + this.expand(row); + }, this); + $(this.domNode).addClass('blockPalette'); + return res; + } + }); + + var ctx = window.sctx, + root; + + function createContainer(ctx, title) { + var leftContainer = ctx.mainView.leftLayoutContainer; + return leftContainer.createTab(title, 'fa-cube', true, null, { + parentContainer: leftContainer, + open: true, + icon: 'fa-folder' + }); + } + + function handler(e, ctx) { + function check() { + if (ctx.blockPalette && ctx.blockPalette.sources.length === 0) { + utils.destroy(ctx.blockPalette); + utils.destroy(ctx.blockPalette._parent); + ctx.blockPalette = null; + } + } + + function wireView(view) { + if (ctx.blockPalette.sources.indexOf(view) === -1) { + ctx.blockPalette.sources.push(view); + view._on('destroy', function () { + ctx.blockPalette && ctx.blockPalette.sources.remove(view); + check(); + }) + } + } + + if (ctx.blockPalette) { + ctx.blockPalette.lastSource = e.view; + wireView(e.view); + return; + } + + var target = createContainer(ctx, 'Block-Palette'); + var grid2 = utils.addWidget(GridCSourcelass, { + ctx: ctx, + attachDirect: true, + open: true, + sources: [] + }, null, target, true); + target.add(grid2); + ctx.blockPalette = grid2; + grid2.lastSource = e.view; + wireView(e.view); + } + + function init(ctx) { + function inner(e) { + handler(e, ctx); + } + ctx.subscribe('ON_SELECTED_BLOCK', inner); + ctx.subscribe('ON_BLOCK_GRID', inner); + + } + var test = false; + if (test && has('debug')) { + function createScope() { + var data = { + "blocks": [ + { + "_containsChildrenIds": [ + "items" + ], + "group": "click", + "id": "root", + "items": [ + "sub0", + "sub1" + ], + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 1", + "method": "console.log('asd',this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "icon": 'fa-bell text-fatal', + "_scenario": "update" + }, + + { + "group": "click4", + "id": "root4", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 4", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + }, + { + "group": "click", + "id": "root2", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 2", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0 + + }, + + { + "group": "click", + "id": "root3", + "description": "Runs an expression.
\n\nBehaviour\n\n
\n\n    //to abort execution (child blocks), return something negative as -1 or false.\n    return false;\n\n
", + "name": "Root - 3", + "method": "console.log(this);", + "args": "", + "deferred": false, + "declaredClass": "xblox.model.code.RunScript", + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + 'icon': 'fa-play' + + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub0", + "name": "On Event", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "parentId": "root", + "id": "sub1", + "name": "On Event2", + "event": "", + "reference": "", + "declaredClass": "xblox.model.events.OnEvent", + "_didRegisterSubscribers": false, + "enabled": true, + "serializeMe": true, + "shareTitle": "", + "description": "No Description", + "canDelete": true, + "renderBlockIcon": true, + "order": 0, + "additionalProperties": true, + "_scenario": "update" + }, + { + "_containsChildrenIds": [], + "group": "click", + "id": "6a12d54a-f5af-aaf8-0abb-bff866211fc4", + "declaredClass": "xblox.model.logic.SwitchBlock", + "name": "Switch", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "order": 0, + "type": "added" + }, + { + "_containsChildrenIds": [], + "group": "click", + "id": "8e1c9d2a-d2fe-356f-d484-82e27c793dc9", + "declaredClass": "xblox.model.variables.VariableSwitch", + "name": "Switch on Variable", + "icon": "", + "variable": "PowerState", + "outlet": 0, + "enabled": true, + "shareTitle": "", + "description": "No Description", + "order": 0, + "type": "added" + } + ], + "variables": [] + }; + return blockManager.toScope(data); + } + if (ctx) { + var blockManager = ctx.getBlockManager(); + if(!blockManager){ + return; + } + + function createGridClass() { + var renderers = [TreeRenderer, ThumbRenderer]; + + var multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + return Grid.createGridClass('driverTreeView', { + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + SPLIT: { + CLASS: _LayoutMixin + }, + Dnd: { + CLASS: BlocksDnD + }, + Base: { + CLASS: declare('Base', null, BlockGrid.IMPLEMENTATION) + }, + BLOCK_ACTIONS: { + CLASS: BlockActions + }, + WIDGET: { + CLASS: _Widget + } + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + EDITOR: Editor, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + } + + var blockScope = createScope('docs'); + var mainView = sctx.mainView; + if (mainView) { + if (window['dHandle']) { + window['dHandle'].remove(); + } + if (window['dHandle2']) { + window['dHandle2'].remove(); + } + window['dHandle'] = ctx.subscribe('ON_SELECTED_BLOCK', handler); + window['dHandle2'] = ctx.subscribe('ON_BLOCK_GRID', handler); + + var parent = TestUtils.createTab('BlockGrid-Test-Source', null, module.id); + var thiz = this, + grid, grid2; + var store = blockScope.blockStore; + var _gridClass = createGridClass(); + var dndParams = { + allowNested: true, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return true;//source !== this; // Don't self-accept. + }, + isSource: true + }; + var gridArgs = { + ctx: ctx, + /** + * @type {module:xblox/views/SharedDndGridSource} + */ + dndConstructor: SharedDndGridSource, // use extension defined above + blockScope: blockScope, + blockGroup: 'click', + dndParams: dndParams, + name: "Grid - 1", + collection: store.filter({ + group: "click" + }) + }; + var gridArgs2 = { + ctx: ctx, + attachDirect: true, + open: true + + }; + grid = utils.addWidget(_gridClass, gridArgs, null, parent, true); + + } + } + } + GridCSourcelass.init = init; + return GridCSourcelass; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/views/BlocksFileEditor.js b/packages/xblox/ref-control-freak/xblox/views/BlocksFileEditor.js new file mode 100644 index 00000000..8716f149 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/BlocksFileEditor.js @@ -0,0 +1,762 @@ +define([ + 'dcl/dcl', + 'dojo/_base/declare', + 'xide/factory', + 'xide/utils', + 'xblox/model/variables/Variable', + 'dojo/Deferred', + 'xide/types', + 'xide/views/_LayoutMixin', + 'xblox/views/BlockGrid', + 'xaction/ActionProvider', + 'xide/layout/Container', + 'xdocker/Docker2', + "xide/views/_CIPanelDialog", + "xide/lodash" +], function (dcl, declare, factory, utils, Variable, Deferred, types, _LayoutMixin, BlockGrid, ActionProvider, Container, Docker, _CIPanelDialog, _) { + var LayoutClass = dcl(_LayoutMixin.dcl, { + rootPanel: null, + _lastTab: null, + getDocker: function () { + if (!this._docker) { + this._docker = Docker.createDefault(this.containerNode); + this.add(this._docker); + } + return this._docker; + }, + getRootContainer: function (args) { + if (this.rootPanel) { + return this.rootPanel; + } + this.reparent = true; + var docker = this.getDocker(); + var DOCKER = types.DOCKER; + var defaultTabArgs = { + icon: false, + closeable: true, + moveable: true, + tabOrientation: DOCKER.TAB.TOP, + location: DOCKER.DOCK.STACKED + + }; + this.rootPanel = docker.addTab(null, utils.mixin(defaultTabArgs, args)); + return this.rootPanel; + }, + createLayout: function () { + if (!this.rootPanel) { + this.rootPanel = this.getRootContainer({ + title: 'test' + }); + } + return this.rootPanel; + }, + startup: function () { + + }, + getPropertyStruct: function () { + return this.propertyStruct; + }, + setPropertyStruct: function (struct) { + this.propertyStruct = struct; + }, + getGroupContainer: function () { + return this.rootPanel; + }, + createGroupView: function (groupContainer, group, scope, closable, iconClass, selected, args) { + var DOCKER = types.DOCKER; + var defaultTabArgs = { + icon: iconClass, + closeable: true, + moveable: true, + title: group, + target: this._lastTab || this.rootPanel, + tabOrientation: DOCKER.TAB.TOP, + location: DOCKER.DOCK.STACKED + }; + var tab = this.getDocker().addTab(null, utils.mixin(defaultTabArgs, args)); + this._lastTab = tab; + this.tabs.push(tab); + return tab; + } + }); + + var GridClass = declare('BlockGrid', BlockGrid, { + /** + * Step/Move Down & Step/Move Up action + * @param dir + */ + move: function (dir) { + var items = this.getSelection(); + if (!items || !items.length) { + console.log('cant move, no selection or parentId', items); + return; + } + var thiz = this; + _.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; + } + }); + + var Module = dcl([Container, LayoutClass, 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, + registerGrids: true, + visibileTab: null, + setVisibleTab: function (tab) { + this.visibileTab = tab; + }, + getVisibleTab: function () { + return this.visibileTab; + }, + constructor: function (options, container) { + utils.mixin(this, options); + }, + onGridAction: function (evt) { + var action = evt.action ? evt.action : evt, + command = action.command, + ACTION = types.ACTION; + + switch (command) { + case ACTION.SAVE: { + return this.save(); + } + } + }, + clearGroupViews: function (all) { + _.each(this.tabs, function (tab) { + tab.destroy(); + }); + delete this.tabs; + this.destroyWidgets(); + }, + getContainerLabel: function (group) { + var title = '' + group; + if (utils.isNativeEvent(group)) { + title = title.replace('on', ''); + } + + if (group.indexOf(types.EVENTS.ON_DRIVER_VARIABLE_CHANGED) !== -1) { + var deviceManager = this.ctx.getDeviceManager(); + var driverManager = this.ctx.getDriverManager(); + var parts = group.split('__'); + var blockUrl = parts[1]; + var device = deviceManager.getDeviceByUrl(blockUrl); + var deviceTitle = device ? deviceManager.getMetaValue(device, types.DEVICE_PROPERTY.CF_DEVICE_TITLE) : 'Invalid Device'; + var variable = driverManager.getBlock(group); + title = '(' + deviceTitle + '/' + (variable ? variable.name : 'Unknown Variable') + ')'; + return title; + } + if(title==='basicVariables'){ + title = 'Action Variables'; + } + return title; + }, + addNewBlockGroup: function (group) { + if (!group) { + return; + } + var blockScope = this.blockScope; + var title = this.getContainerLabel(group); + var contentPane = this.createGroupView(null, title, blockScope, true, 'fa-bell'); + var newGroup = this.newGroupPrefix + group; + var gridViewConstructurArgs = { + newRootItemGroup: newGroup + }; + var view = this.createGroupedBlockView(contentPane, newGroup, blockScope, gridViewConstructurArgs); + contentPane.grid = view; + this.activeGrid = view; + contentPane.select(); + }, + createGroupedBlockView: function (container, group, scope, extra, gridBaseClass) { + var thiz = this; + gridBaseClass = gridBaseClass || this.gridClass; + var store = scope.blockStore; + if (this._lastTab && !this.__right) { + this.__right = this.getRightPanel(null, null, 'DefaultFixed', { + target: this._lastTab + }); + } + var gridArgs = { + __right: this.__right, + ctx: this.ctx, + blockScope: scope, + blockGroup: group, + attachDirect: true, + resizeToParent: true, + collection: store.filter({ + group: group + }), + _parent: container, + getPropertyStruct: this.getPropertyStruct, + setPropertyStruct: this.setPropertyStruct + }; + + extra && utils.mixin(gridArgs, extra); + var view = utils.addWidget(gridBaseClass, gridArgs, null, container, false); + + if (!view.__editorActions) { + view.__editorActions = true; + this.addGridActions(view, view); + } + container.grid = view; + view._on('selectionChanged', function (evt) { + thiz._emit('selectionChanged', evt); + }); + view._on('onAfterAction', function (e) { + thiz.onGridAction(e); + }); + container.on(types.DOCKER.EVENT.VISIBILITY_CHANGED, function (visible) { + if (visible) { + thiz.setVisibleTab(container); + view.onShow(); + var right = thiz.getRightPanel(); + var splitter = right.getSplitter(); + if (!splitter.isCollapsed()) { + view.showProperties(view.getSelection()[0]); + } + if (view.__last) { + view._restoreSelection(); + view.focus(view.row(view.__last.focused)); + } + } else { + view.__last = view._preserveSelection(); + } + if (visible && !this.grid._started) { + } + }); + this.registerGrids && this.ctx.getWindowManager().registerView(view, false); + if (!container._widgets) { + container._widgets = []; + } + container.add(view); + return view; + }, + getDeviceVariablesAsEventOptions: function (startIntend) { + var options = []; + var _item = function (label, value, intend, selected, displayValue) { + var string = "" + label + ""; + var pre = ""; + if (intend > 0) { + for (var i = 0; i < intend; i++) { + pre += " "; + pre += " "; + pre += " "; + } + } + 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 ? "" + label + "" : label; + var pre = ""; + if (intend > 0) { + for (var i = 0; i < intend; i++) { + pre += " "; + pre += " "; + pre += " "; + } + } + 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('onchange', 'change', 1), + _item('input', 'input', 1), + _item('load', 'load', 1) + ]); + + var inVariableStr = ""; + + var thiz = this; + var actionDialog = new _CIPanelDialog({ + title: 'Create a new block group', + style: 'width:500px;min-height:200px;', + size: types.DIALOG_SIZE.SIZE_NORMAL, + resizeable: true, + onOk: function (changedCIS) { + //command picker + if (changedCIS && changedCIS[0] && changedCIS[0].dst === 'variable') { + var url = changedCIS[0].newValue; + var parts = utils.parse_url(url);//strip scheme + parts = utils.urlArgs(parts.host);//go on with query string + //var value = types.EVENTS.ON_DRIVER_VARIABLE_CHANGED+ '__' + parts.device.value +'__'+parts.driver.value + '__'+ parts.block.value; + var value = types.EVENTS.ON_DRIVER_VARIABLE_CHANGED + '__' + url; + thiz.addNewBlockGroup(value); + return; + } + if (changedCIS && changedCIS[0]) { + thiz.addNewBlockGroup(changedCIS[0].newValue); + } + }, + cis: [ + utils.createCI('Event', types.ECIType.ENUMERATION, '', + { + group: 'Event', + delegate: null, + options: options, + value: 'HTML', + title: 'Select an event                        ', + widget: { + search: true, + "class": "xide.form.FilterSelect" + } + } + ), + utils.createCI('value', 'xcf.widgets.CommandPicker', inVariableStr, { + group: 'Event', + title: ' ', + dst: 'variable', + pickerType: 'variable', + value: ' Select Driver Variable ', + block: { + scope: { + ctx: thiz.ctx + } + }, + widget: { + //store:this.scope.blockStore, + labelField: 'name', + valueField: 'id', + value: 'Select Driver Variable', + query: [ + { + group: 'basicVariables' + }, + { + group: 'processVariables' + } + ] + + } + }) + ] + }); + + this.add(actionDialog); + 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 (_.isArray(data)) {// a flat list of blocks + + } else if (_.isObject(data)) { + scopeId = data.scopeId || scopeId; + blockInData = data.blocks || []; + } + 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 + }; + } + blockManager.onBlocksReady(blockScope); + if (allBlocks) { + return this.initWithScope(blockScope); + } + }, + destroyWidgets: function () { + if (this.blockManager && this.blockScope) { + this.blockManager.removeScope(this.blockScope.id); + } + this.blockManager = null; + }, + destroy: function () { + this.destroyWidgets(); + delete this.tabs; + delete this.propertyStruct; + delete this.visualEditor; + delete this.widget; + delete this.params; + delete this.pageEditor; + delete this.editorContext; + delete this.activeGrid; + + this.item = null; + this._parent = null; + this.ctx = null; + this.docker = null; + this.__right = null; + this.blockScope = null; + this.delegate = null; + + + }, + 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; + }, + 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) { + }, + renderGroup: function (group, blockScope, gridBaseClass) { + blockScope = blockScope || this.blockScope; + var 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(null, title, blockScope, !isVariableView, isVariableView ? ' fa-info-circle' : 'fa-bell', !isVariableView); + var gridViewConstructurArgs = {}; + if (group === 'Variables') { + gridViewConstructurArgs.newRootItemFunction = function () { + new Variable({ + title: 'No-Title-Yet', + type: 13, + value: 'No Value', + enumType: 'VariableType', + save: false, + initialize: '', + group: 'Variables', + id: utils.createUUID(), + scope: blockScope + }); + + }; + + 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.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; + !this.activeGrid && (this.activeGrid = view); + return view; + }, + renderGroups: function (_array, blockScope) { + this._isCreating = true; + this.activeGrid = null; + this.tabs = []; + this._lastTab = null; + this.setPropertyStruct({ + currentCIView: null, + targetTop: null, + _lastItem: null, + id: utils.createUUID() + }); + 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); + } + } + + if (this.tabs && this.tabs[0]) { + this.tabs[0].silent = true; + this.tabs[0].select(); + this.tabs[0].silent = false; + } + }, + onSave: function (groupedBlockView) { + this.save(); + }, + save: function () { + if (this.blockScope) { + 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; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/views/DeleteDialog.js b/packages/xblox/ref-control-freak/xblox/views/DeleteDialog.js new file mode 100644 index 00000000..355198ca --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/DeleteDialog.js @@ -0,0 +1,70 @@ +/** @module xfile/views/FileOperationDialog **/ +define([ + "dcl/dcl", + 'xide/types', + "xide/views/_Dialog" +], function (dcl, types, _Dialog) { + var Module = dcl(_Dialog, { + title: '', + type: types.DIALOG_TYPE.INFO, + size: types.DIALOG_SIZE.SIZE_SMALL, + bodyCSS: {}, + failedText: ' Failed!', + successText: ': Success!', + showSpinner: true, + spinner: ' ', + notificationMessage: null, + doOk: function (dfd) { + this.onBeforeOk && this.onBeforeOk(); + var msg = this.showMessage(), + thiz = this; + dfd.then(function (result) { + thiz._onSuccess(result); + }, function (err) { + thiz._onError(); + }); + }, + _onSuccess: function (title, suffix, message) { + title = title || this.title; + message = message || this.notificationMessage; + message && message.update({ + message: title + this.successText + (suffix ? '
' + suffix : ''), + type: 'info', + actions: false, + duration: 1500 + }); + this.onSuccess && this.onSuccess(); + }, + _onError: function (title, suffix, message) { + title = title || this.title; + message = message || this.notificationMessage; + message && message.update({ + message: title + this.failedText + (suffix ? '
' + suffix : ''), + type: 'error', + actions: false, + duration: 15000 + }); + this.onError && this.onError(suffix); + }, + onOk: function () { + var msg = this.showMessage(), + thiz = this; + this.doOk(this.getOkDfd()); + }, + showMessage: function (title) { + if (this.notificationMessage) { + return this.notificationMessage; + } + title = title || this.title; + var msg = this.ctx.getNotificationManager().postMessage({ + message: title + (this.showSpinner ? this.spinner : ''), + type: 'info', + showCloseButton: true, + duration: 4500 + }); + this.notificationMessage = msg; + return msg; + } + }); + return Module; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/views/DnD.js b/packages/xblox/ref-control-freak/xblox/views/DnD.js new file mode 100644 index 00000000..d96baa90 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/DnD.js @@ -0,0 +1,325 @@ +define([ + 'dojo/_base/declare', + 'dojo/when', + 'xgrid/DnD' +], function (declare, when, DnD) { + + var Source = DnD.GridSource; + /** + * @class module:xblox/views/BlockGridDnDSource + * @extends module:xgrid/DnD/GridDnDSource + */ + var BlockGridDnDSource = declare(DnD.GridSource, { + declaredClass: 'xblox/views/BlockGridDnDSource', + _lastTargetId: null, + _lastTargetCrossCounter: 0, + /** + * Assigns a class to the current target anchor based on "before" status + * @param before {boolean} insert before, if true, after otherwise + * @param center {boolean} + * @param canDrop {boolean} + * @private + */ + _markTargetAnchor: function (before, center, canDrop) { + if (this.current == this.targetAnchor && this.before === before && this.center === center) { + return; + } + if (this.targetAnchor) { + this._removeItemClass(this.targetAnchor, this.before ? "Before" : "After"); + this._removeItemClass(this.targetAnchor, "Disallow"); + this._removeItemClass(this.targetAnchor, "Center"); + } + //track crossing count for trees, open it if crossed + /* + if(!this.current){ + this._lastTargetId=null; + }else if(this.current.id!==this._lastTargetId){ + this._lastTargetCrossCounter=0; + }else if(this.current.id==this._lastTargetId && center){ + this._lastTargetCrossCounter++; + } + */ + + this.targetAnchor = this.current; + this.targetBox = null; + this.before = before; + this.center = center; + this._before = before; + this._center = center; + if (this.targetAnchor) { + if (center) { + this._removeItemClass(this.targetAnchor, "Before"); + this._removeItemClass(this.targetAnchor, "After"); + this._addItemClass(this.targetAnchor, 'Center'); + }!center && this._addItemClass(this.targetAnchor, this.before ? "Before" : "After"); + center && canDrop === false && this._addItemClass(this.targetAnchor, "Disallow"); + + /* + if(this._lastTargetCrossCounter>1 && this.grid.expand){ + console.log('open'); + } + this._lastTargetId = this.targetAnchor.id; + */ + + } + }, + /** + * @param nodes {HTMLElement[]} + * @param copy {boolean} + * @param targetItem {module:xide/data/Model} + */ + onDropInternal: function (nodes, copy, targetItem) { + var grid = this.grid, + store = grid.collection, + targetSource = this, + anchor = targetSource._targetAnchor, + targetRow, + nodeRow; + + if (anchor) { // (falsy if drop occurred in empty space after rows) + targetRow = this._center ? anchor : this.before ? anchor.previousSibling : anchor.nextSibling; + } + // Don't bother continuing if the drop is really not moving anything. + // (Don't need to worry about edge first/last cases since dropping + // directly on self doesn't fire onDrop, but we do have to worry about + // dropping last node into empty space beyond rendered rows.) + nodeRow = grid.row(nodes[0]); + if (!copy && (targetRow === nodes[0] || (!targetItem && nodeRow && grid.down(nodeRow).element === nodes[0]))) { + return; + } + var rows = grid.__dndNodesToModel(nodes); + var _target = grid._sourceToModel(targetSource, grid); + var DND_PARAMS = { + before: this._before, + center: this._center, + targetGrid: grid + }; + + if (this._center) { + grid.__onDrop(rows, _target, grid, DND_PARAMS, copy); + } + nodes.forEach(function (node) { + when(targetSource.getObject(node), function (object) { + var id = store.getIdentity(object); + // For copy DnD operations, copy object, if supported by store; + // otherwise settle for put anyway. + // (put will relocate an existing item with the same id, i.e. move). + grid._trackError(function () { + return store[copy && store.copy ? 'copy' : 'put'](object, { + beforeId: targetItem ? store.getIdentity(targetItem) : null + }).then(function () { + + // Self-drops won't cause the dgrid-select handler to re-fire, + // so update the cached node manually + if (targetSource._selectedNodes[id]) { + targetSource._selectedNodes[id] = grid.row(id).element; + }!this.center && grid.__onDrop(rows, _target, grid, DND_PARAMS, copy); + }); + }); + }); + }); + }, + /** + * @param sourceSource {module:dojo/dnd/Source} + * @param nodes {HTMLElement[]} + * @param copy {boolean} + * @param targetItem {module:dojo/dnd/Target} + */ + onDropExternal: function (sourceSource, nodes, copy, targetItem) { + // Note: this default implementation expects that two grids do not + // share the same store. There may be more ideal implementations in the + // case of two grids using the same store (perhaps differentiated by + // query), dragging to each other. + var grid = this.grid, + store = this.grid.collection, + sourceGrid = sourceSource.grid, + targetSource = this; + + function done(grid, object) { + grid.refresh(); + return grid.select(object, null, true, { + focus: true, + append: false, + expand: true, + delay: 2 + }, 'mouse'); + } + + nodes.forEach(function (node, i) { + when(sourceSource.getObject(node), function (object) { + var DND_PARAMS = { + before: targetSource._before, + center: targetSource._center, + targetGrid: grid + }; + if (object.toDropObject) { + object = object.toDropObject(object, DND_PARAMS.center ? sourceGrid._sourceToModel(targetSource) : targetItem, DND_PARAMS, copy); + if (DND_PARAMS.center) { + return done(grid, object); + } else { + grid.refreshRoot(); + setTimeout(()=>{ + done(grid, object); + },100); + } + } + // Copy object, if supported by store; otherwise settle for put + // (put will relocate an existing item with the same id). + // Note that we use store.copy if available even for non-copy dnd: + // since this coming from another dnd source, always behave as if + // it is a new store item if possible, rather than replacing existing. + grid._trackError(function () { + return store[store.copy ? 'copy' : 'put'](object, { + beforeId: targetItem ? store.getIdentity(targetItem) : null + }).then(function () { + if (!copy) { + if (sourceGrid) { + // Remove original in the case of inter-grid move. + // (Also ensure dnd source is cleaned up properly) + var id = sourceGrid.collection.getIdentity(object); + !i && sourceSource.selectNone(); // Deselect all, one time + sourceSource.delItem(node.id); + done(grid, object); + return sourceGrid.collection.remove(id); + } else { + sourceSource.deleteSelectedNodes(); + } + } + }); + }); + }); + }); + } + }); + + /** + * @class module:xblox/views/SharedDndGridSource + * @extends module:xblox/views/BlockGridDnDSource + * @extends module:xblox/views/DojoDndMixin + */ + var SharedDndGridSource = declare([BlockGridDnDSource], { + autoSync: true, + skipForm: true, + selfAccept: false, + onDndDrop: function (source, nodes, copy, target) { + if (this.targetAnchor) { + this._removeItemClass(this.targetAnchor, "Hover"); + } + if (this == target) { + // this one is for us => move nodes! + this.onDrop(source, nodes, copy, target); + this.grid.refresh(); + } + this.onDndCancel(); + } + }); + + + var dndParams = { + allowNested: true, // also pick up indirect children w/ dojoDndItem class + checkAcceptance: function (source, nodes) { + return true; //source !== this; // Don't self-accept. + }, + isSource: true + }; + /** + * @class module:xblox/views/DnD + * @extends module:xgrid/DnD/ + */ + var Module = declare("xblox.widgets.DojoDndMixin", DnD, { + dndParams: dndParams, + dndConstructor: SharedDndGridSource, // use extension defined above + dropEvent: "/dnd/drop", + dragEvent: "/dnd/start", + overEvent: "/dnd/source/over", + /** + * Final + * @param sources {module:xblox/model/Block[]} + * @param target {module:xblox/model/Block} + * @param sourceGrid {module:xgrid/Base| module:dgrid/List} + * @param params {object} + * @param params.SAME_STORE {object} + * @param copy {boolean} + * @private + */ + __onDrop: function (sources, target, sourceGrid, params, copy) { + var into = params.center === true; + _.each(sources, function (source) { + if (source.toDropObject) { + source = source.toDropObject(source, target, params, copy); + } + //happens when dragged on nothing + if (source == target) { + return; + } + if (into) { + if (target.canAdd(source) !== false) { + target.scope.moveTo(source, target, params.before, into); + } + } else { + var sourceParent = source.getParent(); + if (sourceParent) { + var targetParent = target.getParent(); + if (!targetParent) { + //move to root - end + source.group = this.blockGroup; + sourceParent.removeBlock(source, false); + source._store.putSync(source); + } else { + + //we dropped at the end within the same tree + if (targetParent == source.getParent()) { + target.scope.moveTo(source, target, params.before, into); + } + } + } + } + source.refresh(); + }, this); + + + this.set('collection', this.collection.filter({ + group: this.blockGroup + })); + + this.select(sources[0], null, true, { + focus: true, + append: false, + expand: true, + delay: 2 + }, 'mouse'); + } + /*, + startup: function () { + if (this._started) { + return; + } + + this.dropEvent && this.subscribe(this.dropEvent, function (source, nodes, copy, target) { + return; + if (source.grid == this) { + var rows = this.__dndNodesToModel(nodes); + target = target || {}; + var _target = this._sourceToModel(target, target.grid); + + if (!_target || _.isEmpty(rows)) { + return; + } + var DND_PARAMS = { + SAME_STORE: rows[0]._store == _target._store, + before: target._before, + center: target._center + }; + if (rows && _target) { + this.__onDrop(rows, _target, source.grid, DND_PARAMS, copy); + } + } + }); + return this.inherited(arguments); + }*/ + }); + + Module.BlockGridSource = BlockGridDnDSource; + + return Module; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/views/Grid.js b/packages/xblox/ref-control-freak/xblox/views/Grid.js new file mode 100644 index 00000000..d86c78e7 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/Grid.js @@ -0,0 +1,98 @@ +/** @module xblox/views/Grid **/ +define([ + "xdojo/declare", + 'xide/types', + 'xgrid/ListRenderer', + 'xgrid/TreeRenderer', + 'xblox/views/ThumbRenderer', + 'xgrid/Grid', + 'xgrid/MultiRenderer', + 'xide/views/_LayoutMixin', + 'xgrid/Defaults', + 'xgrid/Layout', + 'xgrid/Focus', + 'xgrid/KeyboardNavigation', + 'dgrid/OnDemandGrid', + 'xide/mixins/EventedMixin', + 'xide/utils', + 'xblox/BlockActions', + 'xgrid/Search', + "xide/widgets/_Widget", + "xblox/views/DnD" + +], function (declare, types, + ListRenderer, TreeRenderer, ThumbRenderer, + Grid, MultiRenderer, _LayoutMixin, + Defaults, Layout, Focus, KeyboardNavigation, + OnDemandGrid, EventedMixin, utils, BlockActions, Search, _Widget,DnD) { + + var renderers = [TreeRenderer, ThumbRenderer], + multiRenderer = declare.classFactory('multiRenderer', {}, renderers, MultiRenderer.Implementation); + + + /** + * Block grid base class. + * @class module:xblox/views/Grid + */ + var GridClass = Grid.createGridClass('xblox.views.Grid', + { + options: utils.clone(types.DEFAULT_GRID_OPTIONS) + }, + //features + { + + SELECTION: true, + KEYBOARD_SELECTION: true, + PAGINATION: false, + ACTIONS: types.GRID_FEATURES.ACTIONS, + CONTEXT_MENU: types.GRID_FEATURES.CONTEXT_MENU, + TOOLBAR: types.GRID_FEATURES.TOOLBAR, + CLIPBOARD: types.GRID_FEATURES.CLIPBOARD, + COLUMN_RESIZER: types.GRID_FEATURES.COLUMN_RESIZER, + SPLIT: { + CLASS: _LayoutMixin + }, + BLOCK_ACTIONS: { + CLASS: BlockActions + }, + DND:{ + CLASS:DnD + }, + KEYBOARD_NAVIGATION: { + CLASS: KeyboardNavigation + }, + + SEARCH: { + CLASS: Search + }, + WIDGET: { + CLASS: _Widget + } + + }, + { + //base flip + RENDERER: multiRenderer + + }, + { + //args + renderers: renderers, + selectedRenderer: TreeRenderer + }, + { + GRID: OnDemandGrid, + //EDITOR: Editor, + LAYOUT: Layout, + DEFAULTS: Defaults, + RENDERER: ListRenderer, + EVENTED: EventedMixin, + FOCUS: Focus + } + ); + + //static exports + GridClass.DEFAULT_RENDERERS = renderers; + GridClass.DEFAULT_MULTI_RENDERER = multiRenderer; + return GridClass; +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/views/ThumbRenderer.js b/packages/xblox/ref-control-freak/xblox/views/ThumbRenderer.js new file mode 100644 index 00000000..373b4841 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/views/ThumbRenderer.js @@ -0,0 +1,139 @@ +/** @module xblox/views/ThumbRenderer **/ +define([ + "xdojo/declare", + 'xide/utils', + 'dojo/dom-construct', + 'xgrid/ThumbRenderer', + "xide/$" +], function (declare, utils, domConstruct, ThumbRenderer, $) { + /** + * The list renderer does nothing since the xgrid/Base is already inherited from + * dgrid/OnDemandList and its rendering as list already. + * + * @class module:xfile/ThumbRenderer + * @extends module:xgrid/Renderer + */ + return declare('xblox.views.ThumbRenderer', [ThumbRenderer], { + thumbSize: "400", + resizeThumb: true, + deactivateRenderer: function () { + $(this.domNode.parentNode).removeClass('metro'); + $(this.domNode).css('padding', ''); + this.isThumbGrid = false; + + }, + activateRenderer: function () { + $(this.domNode.parentNode).addClass('metro'); + $(this.contentNode).css('padding', '8px'); + this.isThumbGrid = true; + this.refresh(); + }, + /** + * Override renderRow + * @param obj + * @returns {*} + */ + renderRow: function (obj) { + if (obj.renderRow) { + var _res = obj.renderRow.apply(this, [obj]); + if (_res) { + return _res; + } + } + var thiz = this, + div = domConstruct.create('div', { + className: "tile widget" + }), + icon = obj.icon, + no_access = obj.read === false && obj.write === false, + isBack = obj.name == '..', + directory = true, + useCSS = false, + label = '', + imageClass = obj.icon ? (obj.icon + ' fa-5x' ) : 'fa fa-folder fa-5x', + isImage = false; + + + this._doubleWidthThumbs = true; + var iconStyle = 'text-shadow: 2px 2px 5px rgba(0,0,0,0.3);font-size: 72px;opacity: 0.7'; + var contentClass = 'icon'; + if (directory) { + + if (isBack) { + imageClass = 'fa fa-level-up fa-5x itemFolder'; + useCSS = true; + } else if (!no_access) { + imageClass = 'fa fa-folder fa-5x itemFolder'; + useCSS = true; + } else { + imageClass = 'fa fa-lock fa-5x itemFolder'; + useCSS = true; + } + + } else { + imageClass = 'itemIcon'; + if (no_access) { + imageClass = 'fa fa-lock fa-5x itemFolder'; + useCSS = true; + } else { + + + imageClass = 'fa fa-5x ' + 'fa-play'; + useCSS = true; + } + + } + + label = obj.name; + + imageClass = obj.icon ? (obj.icon + ' fa-5x' ) : 'fa fa-folder fa-5x'; + + var folderContent = ''; + + var _image = false; + if (_image) { + + var url = this.getImageUrl(obj); + if (url) { + obj.icon = url; + } else { + obj.icon = thiz.config.REPO_URL + '/' + obj.path; + } + + imageClass = ''; + contentClass = 'image'; + folderContent = '
' + + '' + + '
'; + + useCSS = true; + isImage = true; + + } + var label2 = label + '\n' + obj.modified; + var html = '
' + + folderContent + + '
' + + + '
' + + '' + + label + + '' + + '
'; + if (isImage || this._doubleWidthThumbs) { + $(div).addClass('double'); + } + + if (useCSS) { + div.innerHTML = html; + return div; + } + if (directory) { + div.innerHTML = html; + } else { + div.innerHTML = ' 
' + obj.name + '
'; + } + return div; + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/widgets/BlockEditor.js b/packages/xblox/ref-control-freak/xblox/widgets/BlockEditor.js new file mode 100644 index 00000000..506638f7 --- /dev/null +++ b/packages/xblox/ref-control-freak/xblox/widgets/BlockEditor.js @@ -0,0 +1,66 @@ +define([ + "dojo/_base/declare", + "dojo/_base/html", + 'dojo/dom-class', + 'dojo/dom-construct', + "dojo/on", + 'xide/types', + 'xide/factory', + 'dijit/_Widget', + 'xide/widgets/TemplatedWidgetBase', + 'xide/mixins/EventedMixin', + 'xide/mixins/ReloadMixin' +], function (declare, html, domClass, domConstruct, on, types, factory, _Widget, TemplatedWidgetBase, EventedMixin, ReloadMixin) { + return declare("xide.widgets.ActionToolbarButton", [_Widget, TemplatedWidgetBase, EventedMixin, ReloadMixin], { + _didRenderBlock: false, + containerNode: null, + templateString: "
" + + "
no set
" + + "
", + set: function (what, value) { + this.inherited(arguments); + //console.log(' setting ' + what + ' to ' + value); + }, + get: function (what) { + return this.inherited(arguments); + }, + renderBlock: function (block) { + var text = 'render block : ' + block.declaredClass; + + /*console.dir(block);*/ + if (!this.containerNode) { + return; + } + dojo.empty(this.containerNode); + + var blockText = ''; + if (block.toText) { + blockText = block.toText(); + var textItem = domConstruct.create('span', { + innerHTML: blockText + }); + this.containerNode.appendChild(textItem); + } + console.log(text + ' to ' + blockText); + + }, + onReloaded: function () { + this._didRenderBlock = false; + if (this.containerNode) { + dojo.empty(this.containerNode); + } + this.renderBlock(this.object); + }, + startup: function () { + this.inherited(arguments); + if (!this._didRenderBlock) { + if (this.object) { + this.renderBlock(this.object); + } + } + this._didRenderBlock = true; + this.initReload(); + + } + }); +}); \ No newline at end of file diff --git a/packages/xblox/ref-control-freak/xblox/xblox.js b/packages/xblox/ref-control-freak/xblox/xblox.js new file mode 100644 index 00000000..e69de29b