@peter.naydenov/cuts 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Changelog.md +7 -0
- package/LICENSE +21 -0
- package/Migration.guide.md +1 -0
- package/README.md +94 -0
- package/cypress.config.js +11 -0
- package/dist/cuts.cjs +1 -0
- package/dist/cuts.esm.mjs +1 -0
- package/dist/cuts.umd.js +1 -0
- package/index.html +13 -0
- package/main.js +26 -0
- package/package.json +55 -0
- package/rollup.config.js +43 -0
- package/src/findInstructions.js +75 -0
- package/src/main.js +124 -0
- package/src/methods/hide.js +53 -0
- package/src/methods/index.js +13 -0
- package/src/methods/listShortcuts.js +21 -0
- package/src/methods/setScenes.js +36 -0
- package/src/methods/show.js +105 -0
- package/src/setInstruction.js +17 -0
- package/test/01-general.cy.js +243 -0
- package/test/blue.vue +13 -0
- package/test/dummy.vue +34 -0
- package/test/gray.vue +12 -0
- package/test/red.vue +11 -0
- package/test-mocha/01-findPosition.js +109 -0
- package/vite.config.js +7 -0
package/Changelog.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Peter Naydenov
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Migration Guides
|
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Cuts (@peter.naydenov/cuts)
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
In filmmaking, a change of scene is typically referred to as a "**cut**". Cuts are an essential part of film editing and are used to create a cohesive narrative by connecting different scenes together.
|
|
9
|
+
|
|
10
|
+
Let's define SPA application as a set of scenes (pages/visual states) and each scene discribes a visual elements on the page and possible user interaction ( mouse, keyboard, etc.). **Cuts** controls the flow among scenes in SPA application.
|
|
11
|
+
|
|
12
|
+
When all interactions can be described in the **Scene** data models, means that the components (react,vue, svelte or any other framework) can be used as simple template engines - to render only html.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
npm install @peter.naydenov/cuts
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import cuts from '@peter.naydenov/cuts'
|
|
29
|
+
|
|
30
|
+
const script = cuts ();
|
|
31
|
+
|
|
32
|
+
script.setScenes () // provide list of Scenes to the app
|
|
33
|
+
script.setDependencies () // add object to the "dependencies" object. This object will be passed to the Scene "show" method
|
|
34
|
+
// script is ready to use
|
|
35
|
+
script.show ({ scene : 'sceneName'}) // change the current Scene
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Screen Cuts Methods Overview
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
setScenes : 'Provide list of scenes to the app'
|
|
47
|
+
, show : 'Change the current scene'
|
|
48
|
+
, hide : 'Hide the current scene'
|
|
49
|
+
, listScenes : 'List of loaded Scene names'
|
|
50
|
+
, listShortcuts : 'List shortcuts per Scene. Provide the name of the Scene'
|
|
51
|
+
, setDependencies : 'Add object to the "dependencies" object. This object will be passed to the Scene "show" method'
|
|
52
|
+
, getDependencies : 'Returns the "dependencies" object'
|
|
53
|
+
, enablePlugin : 'Enable a shortcut plugin. Available after version 1.1.0'
|
|
54
|
+
, disablePlugin : 'Disable a shortcut plugin. Available after version 1.1.0'
|
|
55
|
+
, emit : 'Emit an event. Available after version 2.0.0'
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
## Scene
|
|
61
|
+
|
|
62
|
+
Scene is a data model that describes how to render the scene and how the user will interact with it ( mouse, keyboard, etc.):
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
{
|
|
66
|
+
show // method. Returns a promise
|
|
67
|
+
, hide // method. Returns a promise
|
|
68
|
+
, parents // list of parent Scene names.
|
|
69
|
+
// ... shortcuts for all user interactions with the screen ( mouse, keyboard, etc.)
|
|
70
|
+
// Shortcuts are implemented with @peter.naydenov/shortcuts
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
Scenes can be `visible`(show) and `unvisible`(hide), also some scenes can be a child of another scene. Every scene can be treated as a visual state of the application. All provided to script `Scenes` are available on demand by calling them with a simple instruction `show({scene:sceneName})`. What should be visible and what should be unvisible is managed by the library.
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
## Links
|
|
82
|
+
- [History of changes](https://github.com/PeterNaydenov/cuts/blob/main/Changelog.md)
|
|
83
|
+
- [Learn how shortcuts are implemented](https://github.com/PeterNaydenov/shortcuts)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
## Credits
|
|
89
|
+
'@peter.naydenov/cuts' was created and supported by Peter Naydenov.
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
'@peter.naydenov/cuts' is released under the [MIT License](http://opensource.org/licenses/MIT).
|
package/dist/cuts.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";var e=require("@peter.naydenov/shortcuts"),n=require("ask-for-promise"),r=require("@peter.naydenov/log");function*t(e,n,r,t){let s=null;if(null===e)return yield*t.map((e=>[e,"show"])),void(yield[r,"show"]);if(!t||0===t.length)return n?(yield[e,"hide"],n.reverse(),yield*n.map((e=>[e,"hide"])),void(yield[r,"show"])):(yield[e,"hide"],void(yield[r,"show"]));if(t.includes(e)){for(let n=t.indexOf(e)+1;n<t.length;n++)yield[t[n],"show"];yield[r,"show"]}else{if(!n)return yield[e,"hide"],void(yield*t.map((e=>[e,"show"])));if(t.every(((e,r)=>n[r]===t[r]&&(s=r,!0))),null==s)return yield[e,"hide"],n.reverse(),yield*n.map((e=>[e,"hide"])),yield*t.map((e=>[e,"show"])),void(yield[r,"show"]);yield[e,"hide"];for(let e=n.length;e>s+1;e--)yield[n[e-1],"hide"];for(let e=s+1;e<t.length;e++)yield[t[e],"show"];yield[r,"show"]}}function s(e){return function(n,...r){const{askForPromise:t,deps:s}=e;return function(){const e=t();return n({task:e,dependencies:s()},...r),e.promise}}}var o={hide:function(e,n){return function(r=1){const{askForPromise:t,shortcutMngr:s,setInstruction:o}=e,{currentScene:i,currentParents:c,scenes:u}=n,l=t(),d=[],a=c.length>0;let h;return s.pause(),d.push(o(u[i].hide)),a||(n.currentScene=null),a&&"*"===r&&[...c].reverse().forEach((e=>{h=n.currentParents.pop(),n.currentScene=h,d.push(o(u[e].hide))})),a&&!["*",1].includes(r)&&c.slice("-"+(r-1)).reverse().map((e=>{h=n.currentParents.pop(),n.currentScene=h,d.push(o(u[e].hide))})),(r=t.sequence(d)).onComplete((()=>{s.changeContext(n.currentScene),l.done()})),l.promise}},listShortcuts:function(e,n){return function(e){const{scenes:r,sceneNames:t}=n;if(!t.has(e))return null;const{show:s,hide:o,parents:i,...c}=r[e];return Object.keys(c)}},setScenes:function(e,n){return function(r){const{shortcutMngr:t}=e;r.forEach((({name:e,scene:r})=>{r.parents||(r.parents=[]),n.scenes[e]=r,n.sceneNames.add(e);const{show:s,hide:o,parents:i,...c}=r,u={};u[e]=c,t.load(u)}))}},show:function(e,n){return function({scene:r,options:t={ssr:!1}},...s){const{shortcutMngr:o,askForPromise:i,log:c,findInstructions:u,setInstruction:l}=e,d=i(),{opened:a,scenes:h,sceneNames:p}=n;if(!p.has(r)&&c)return c({message:`Scene ${r} is not available.`,level:1,type:"error"}),d.done(),d.promise;if(!a&&t.ssr)return n.opened=!0,n.currentPage=r,o.changeContext(r),d.done(),d.promise;const{show:f,parents:m}=h[r];if("*"===m[0])return f().then((()=>d.done())),n.currentParents.push(n.currentPage),n.currentPage=r,d.promise;if(!(m.forEach((e=>p.has(e)))||!0))return c&&c({message:`Some of '${r}' parent scenes are not set.`,level:1,type:"error"}),d.done(),d.promise;function g([e,r]){if("show"===r)n.currentScene&&n.currentParents.push(n.currentScene),n.currentScene=e;else{let e=n.currentParents.pop();n.currentScene=e}return h[e][r]}n.currentParents||(n.currentParents=[]);const y=u(n.currentScene,n.currentParents,r,m),P=[];for(let e of y)[e].map(g).map((e=>P.push(l(e),...s)));return i.sequence(P).onComplete((()=>{n.opened=!0,o.changeContext(r),d.done()})),d.promise}}};module.exports=function(i={logLevel:0}){const c=e.shortcuts(),u=i.logLevel||0,l=r({level:u}),d={currentScene:null,currentParents:null,sceneNames:new Set,scenes:{},opened:!1},a={},h={},p={shortcutMngr:c,API:a,inAPI:h,findInstructions:t,askForPromise:n,setInstruction:s({askForPromise:n,deps:c.getDependencies}),log:l};return Object.entries(o).forEach((([e,n])=>{e.startsWith("_")?h[e]=n(p,d):a[e]=n(p,d)})),a.setDependencies=e=>c.setDependencies(e),a.getDependencies=()=>c.getDependencies(),a.setNote=n=>e.shortcuts.setNote(n),a.listScenes=()=>[...d.sceneNames],a.enablePlugin=(e,n)=>c.enablePlugin(e,n),a.disablePlugin=e=>c.disablePlugin(e),a.emit=(e,...n)=>c.emit(e,...n),a};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{shortcuts as e}from"@peter.naydenov/shortcuts";import n from"ask-for-promise";import r from"@peter.naydenov/log";function*t(e,n,r,t){let s=null;if(null===e)return yield*t.map((e=>[e,"show"])),void(yield[r,"show"]);if(!t||0===t.length)return n?(yield[e,"hide"],n.reverse(),yield*n.map((e=>[e,"hide"])),void(yield[r,"show"])):(yield[e,"hide"],void(yield[r,"show"]));if(t.includes(e)){for(let n=t.indexOf(e)+1;n<t.length;n++)yield[t[n],"show"];yield[r,"show"]}else{if(!n)return yield[e,"hide"],void(yield*t.map((e=>[e,"show"])));if(t.every(((e,r)=>n[r]===t[r]&&(s=r,!0))),null==s)return yield[e,"hide"],n.reverse(),yield*n.map((e=>[e,"hide"])),yield*t.map((e=>[e,"show"])),void(yield[r,"show"]);yield[e,"hide"];for(let e=n.length;e>s+1;e--)yield[n[e-1],"hide"];for(let e=s+1;e<t.length;e++)yield[t[e],"show"];yield[r,"show"]}}function s(e){return function(n,...r){const{askForPromise:t,deps:s}=e;return function(){const e=t();return n({task:e,dependencies:s()},...r),e.promise}}}var o={hide:function(e,n){return function(r=1){const{askForPromise:t,shortcutMngr:s,setInstruction:o}=e,{currentScene:i,currentParents:c,scenes:u}=n,l=t(),d=[],a=c.length>0;let h;return s.pause(),d.push(o(u[i].hide)),a||(n.currentScene=null),a&&"*"===r&&[...c].reverse().forEach((e=>{h=n.currentParents.pop(),n.currentScene=h,d.push(o(u[e].hide))})),a&&!["*",1].includes(r)&&c.slice("-"+(r-1)).reverse().map((e=>{h=n.currentParents.pop(),n.currentScene=h,d.push(o(u[e].hide))})),(r=t.sequence(d)).onComplete((()=>{s.changeContext(n.currentScene),l.done()})),l.promise}},listShortcuts:function(e,n){return function(e){const{scenes:r,sceneNames:t}=n;if(!t.has(e))return null;const{show:s,hide:o,parents:i,...c}=r[e];return Object.keys(c)}},setScenes:function(e,n){return function(r){const{shortcutMngr:t}=e;r.forEach((({name:e,scene:r})=>{r.parents||(r.parents=[]),n.scenes[e]=r,n.sceneNames.add(e);const{show:s,hide:o,parents:i,...c}=r,u={};u[e]=c,t.load(u)}))}},show:function(e,n){return function({scene:r,options:t={ssr:!1}},...s){const{shortcutMngr:o,askForPromise:i,log:c,findInstructions:u,setInstruction:l}=e,d=i(),{opened:a,scenes:h,sceneNames:p}=n;if(!p.has(r)&&c)return c({message:`Scene ${r} is not available.`,level:1,type:"error"}),d.done(),d.promise;if(!a&&t.ssr)return n.opened=!0,n.currentPage=r,o.changeContext(r),d.done(),d.promise;const{show:f,parents:m}=h[r];if("*"===m[0])return f().then((()=>d.done())),n.currentParents.push(n.currentPage),n.currentPage=r,d.promise;if(!(m.forEach((e=>p.has(e)))||!0))return c&&c({message:`Some of '${r}' parent scenes are not set.`,level:1,type:"error"}),d.done(),d.promise;function g([e,r]){if("show"===r)n.currentScene&&n.currentParents.push(n.currentScene),n.currentScene=e;else{let e=n.currentParents.pop();n.currentScene=e}return h[e][r]}n.currentParents||(n.currentParents=[]);const y=u(n.currentScene,n.currentParents,r,m),P=[];for(let e of y)[e].map(g).map((e=>P.push(l(e),...s)));return i.sequence(P).onComplete((()=>{n.opened=!0,o.changeContext(r),d.done()})),d.promise}}};function i(i={logLevel:0}){const c=e(),u=i.logLevel||0,l=r({level:u}),d={currentScene:null,currentParents:null,sceneNames:new Set,scenes:{},opened:!1},a={},h={},p={shortcutMngr:c,API:a,inAPI:h,findInstructions:t,askForPromise:n,setInstruction:s({askForPromise:n,deps:c.getDependencies}),log:l};return Object.entries(o).forEach((([e,n])=>{e.startsWith("_")?h[e]=n(p,d):a[e]=n(p,d)})),a.setDependencies=e=>c.setDependencies(e),a.getDependencies=()=>c.getDependencies(),a.setNote=n=>e.setNote(n),a.listScenes=()=>[...d.sceneNames],a.enablePlugin=(e,n)=>c.enablePlugin(e,n),a.disablePlugin=e=>c.disablePlugin(e),a.emit=(e,...n)=>c.emit(e,...n),a}export{i as default};
|
package/dist/cuts.umd.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).cuts=t()}(this,(function(){"use strict";var e={_normalizeWithPlugins:function(e,t){return function(e){const n=t.shortcuts;Object.keys(n).forEach((t=>{Object.entries(n[t]).forEach((([r,o])=>{const s=e(r);s!==r&&(delete n[t][r],n[t][s]=o)}))}))}},_readShortcutWithPlugins:function(e,t){return function(n){const{inAPI:r}=e,o=n.split(":")[0],s=r._systemAction(o,"none");let c=n;return-1!==s&&(c=t.plugins[s].shortcutName(n)),c}},_systemAction:function(e,t){return function(e,n,r=null){return t.plugins.findIndex((t=>t.getPrefix()===e&&(t[n]&&t[n](r),!0)))}},changeContext:function(e,t){const{shortcuts:n,currentContext:r}=t,{ev:o}=e;return function(e=!1){const s=r.name;if(!e)return o.reset(),void(r.name=null);s!==e&&(n[e]?(n[s]&&o.reset(),r.name=e,t.plugins.forEach((t=>t.contextChange(e))),Object.entries(n[e]).forEach((([e,t])=>{t.forEach((t=>o.on(e,t)))})),o.on("*",((...e)=>{t.exposeShortcut&&t.exposeShortcut(...e)}))):o.emit("@shortcuts-error",`Context '${e}' does not exist`))}},listShortcuts:function(e,t){const n=t.shortcuts;return function(e=null){if(null!=e){let t=n[e];return null==t?null:Object.entries(t).map((([e,t])=>e))}return Object.keys(n).map((e=>{let t={};return t.context=e,t.shortcuts=Object.entries(n[e]).map((([e,t])=>e)),t}))}},load:function(e,t){const{shortcuts:n,plugins:r}=t,{API:{changeContext:o,getContext:s}}=e;return function(e){const t=s(),c=r.map((e=>e.getPrefix().toUpperCase()));let i=!1;Object.entries(e).forEach((([e,o])=>{e===t&&(i=!0),n[e]={},Object.entries(o).forEach((([t,o])=>{let s=t,i=t.toUpperCase().trim(),u=c.map(((e,t)=>i.startsWith(e)?t:null)).filter((e=>null!==e));if(u.length){let e=u[0];s=r[e].shortcutName(t)}o instanceof Function&&(o=[o]),n[e][s]=o}))})),i&&(o(),o(t))}},unload:function(e,t){const{currentContext:n,shortcuts:r}=t,{ev:o}=e;return function(e){n.name!==e?r[e]?delete r[e]:o.emit("shortcuts-error",`Context '${e}' does not exist`):o.emit("shortcuts-error",`Context '${e}' can't be removed during is current active context. Change the context first`)}}};function t(t={}){const n=new function(){let e={"*":[]},t={},n=[],r=!1,o="";return{on:function(t,n){e[t]||(e[t]=[]),e[t].push(n)},once:function(e,n){"*"!==e&&(t[e]||(t[e]=[]),t[e].push(n))},off:function(n,r){if(r)return e[n]&&(e[n]=e[n].filter((e=>e!==r))),t[n]&&(t[n]=t[n].filter((e=>e!==r))),e[n]&&0===e[n].length&&delete e[n],void(t[n]&&0===t[n].length&&delete e[n]);t[n]&&delete t[n],e[n]&&delete e[n]},reset:function(){e={"*":[]},t={},n=[]},emit:function(){const[s,...c]=arguments;function i(t){let r=!1;"*"!==t&&(n.includes(t)||(e[t].every((e=>{const t=e(...c);return"string"!=typeof t||"STOP"!==t.toUpperCase()||(r=!0,!1)})),r||e["*"].forEach((e=>e(s,...c)))))}if(r&&(console.log(`${o} Event "${s}" was triggered.`),c.length>0&&(console.log("Arguments:"),console.log(...c),console.log("^----"))),"*"!==s){if(t[s]){if(n.includes(s))return;t[s].forEach((e=>e(...c))),delete t[s]}e[s]&&i(s)}else Object.keys(e).forEach((e=>i(e)))},stop:function(r){if("*"!==r)n.push(r);else{const r=Object.keys(e),o=Object.keys(t);n=[...o,...r]}},start:function(e){n="*"!==e?n.filter((t=>e!=t)):[]},debug:function(e,t){r=!!e,t&&"string"==typeof t&&(o=t)}}},r={},o={},s={currentContext:{name:null,note:null},shortcuts:{},plugins:[],exposeShortcut:!(!t.onShortcut||"function"!=typeof t.onShortcut)&&t.onShortcut},c={ev:n,inAPI:r,API:o,extra:{}};return o.enablePlugin=(e,t={})=>{const n=e.name;if(-1===r._systemAction(n,"none")){let n;n=e(c,s,t),s.plugins.push(n)}},o.disablePlugin=e=>{const t=r._systemAction(e,"destroy");-1!==t&&(s.plugins=s.plugins.filter(((e,n)=>n!==t)))},o.mutePlugin=e=>r._systemAction(e,"mute"),o.unmutePlugin=e=>r._systemAction(e,"unmute"),o.getContext=()=>s.currentContext.name,o.getNote=()=>s.currentContext.note,o.setNote=(e=null)=>{"string"!=typeof e&&null!=e||(s.currentContext.note=e)},o.pause=(e="*")=>{let t=r._readShortcutWithPlugins(e);n.stop(t)},o.resume=(e="*")=>{const t=r._readShortcutWithPlugins(e);n.start(t)},o.emit=(e,...t)=>n.emit(r._readShortcutWithPlugins(e),...t),o.listContexts=()=>Object.keys(s.shortcuts),o.setDependencies=e=>c.extra={...c.extra,...e},o.getDependencies=()=>c.extra,Object.entries(e).forEach((([e,t])=>{e.startsWith("_")?r[e]=t(c,s):o[e]=t(c,s)})),o}function n(e){let t,n=!1;return e?(t=function(e){let t=e.map((e=>r())),n=t.map((e=>e.promise));return t.promises=n,t.onComplete=o(Promise.all(n)),t}(e),n=!0):t=r(),t.timeout=function(e,t){let n;return n=e?Promise.all(t.promises):t.promise,function(e,r){let s,c=new Promise(((t,o)=>{s=setTimeout((()=>{t(r),Promise.resolve(n)}),e)}));return n.then((()=>clearTimeout(s))),t.onComplete=o(Promise.race([n,c])),t}}(n,t),t}function r(){let e,t;const n=new Promise(((n,r)=>{e=n,t=r}));return{promise:n,done:e,cancel:t,onComplete:o(n)}}function o(e){return function(t){e.then((e=>t(e)))}}function s({message:e,level:t,type:n,logLevel:r}){if(0===r)return null;if(r<t)return null;const o=`[Debug]: ${e}`;switch(n){case"warn":console.warn(o);break;case"error":console.error(o);break;default:console.log(o)}return o}function c(e={},t=s){return function({message:n,type:r,level:o,...s}){const{level:c,type:i,defaultMessageLevel:u}=Object.assign({},{level:1e3,type:"log",defaultMessageLevel:1},e);return r||(r=i),o||(o=u),t({message:n,type:r,level:o,logLevel:c,...s})}}function*i(e,t,n,r){let o=null;if(null===e)return yield*r.map((e=>[e,"show"])),void(yield[n,"show"]);if(!r||0===r.length)return t?(yield[e,"hide"],t.reverse(),yield*t.map((e=>[e,"hide"])),void(yield[n,"show"])):(yield[e,"hide"],void(yield[n,"show"]));if(r.includes(e)){for(let t=r.indexOf(e)+1;t<r.length;t++)yield[r[t],"show"];yield[n,"show"]}else{if(!t)return yield[e,"hide"],void(yield*r.map((e=>[e,"show"])));if(r.every(((e,n)=>t[n]===r[n]&&(o=n,!0))),null==o)return yield[e,"hide"],t.reverse(),yield*t.map((e=>[e,"hide"])),yield*r.map((e=>[e,"show"])),void(yield[n,"show"]);yield[e,"hide"];for(let e=t.length;e>o+1;e--)yield[t[e-1],"hide"];for(let e=o+1;e<r.length;e++)yield[r[e],"show"];yield[n,"show"]}}function u(e){return function(t,...n){const{askForPromise:r,deps:o}=e;return function(){const e=r();return t({task:e,dependencies:o()},...n),e.promise}}}n.sequence=function(e,...t){const r=n(),o=[],s=function*(e){for(const t of e)yield t}(e);return function e(t,...n){t.done?r.done(o):t.value(...n).then((t=>{o.push(t),e(s.next(),...n,t)}))}(s.next(),...t),r},n.all=function(e,...t){const r=n(),o=[],s=e.map(((e,n)=>"function"==typeof e?e(...t).then((e=>o[n]=e)):e.then((e=>o[n]=e))));return Promise.all(s).then((()=>r.done(o))),r};var l={hide:function(e,t){return function(n=1){const{askForPromise:r,shortcutMngr:o,setInstruction:s}=e,{currentScene:c,currentParents:i,scenes:u}=t,l=r(),a=[],f=i.length>0;let h;return o.pause(),a.push(s(u[c].hide)),f||(t.currentScene=null),f&&"*"===n&&[...i].reverse().forEach((e=>{h=t.currentParents.pop(),t.currentScene=h,a.push(s(u[e].hide))})),f&&!["*",1].includes(n)&&i.slice("-"+(n-1)).reverse().map((e=>{h=t.currentParents.pop(),t.currentScene=h,a.push(s(u[e].hide))})),(n=r.sequence(a)).onComplete((()=>{o.changeContext(t.currentScene),l.done()})),l.promise}},listShortcuts:function(e,t){return function(e){const{scenes:n,sceneNames:r}=t;if(!r.has(e))return null;const{show:o,hide:s,parents:c,...i}=n[e];return Object.keys(i)}},setScenes:function(e,t){return function(n){const{shortcutMngr:r}=e;n.forEach((({name:e,scene:n})=>{n.parents||(n.parents=[]),t.scenes[e]=n,t.sceneNames.add(e);const{show:o,hide:s,parents:c,...i}=n,u={};u[e]=i,r.load(u)}))}},show:function(e,t){return function({scene:n,options:r={ssr:!1}},...o){const{shortcutMngr:s,askForPromise:c,log:i,findInstructions:u,setInstruction:l}=e,a=c(),{opened:f,scenes:h,sceneNames:d}=t;if(!d.has(n)&&i)return i({message:`Scene ${n} is not available.`,level:1,type:"error"}),a.done(),a.promise;if(!f&&r.ssr)return t.opened=!0,t.currentPage=n,s.changeContext(n),a.done(),a.promise;const{show:p,parents:m}=h[n];if("*"===m[0])return p().then((()=>a.done())),t.currentParents.push(t.currentPage),t.currentPage=n,a.promise;if(!(m.forEach((e=>d.has(e)))||!0))return i&&i({message:`Some of '${n}' parent scenes are not set.`,level:1,type:"error"}),a.done(),a.promise;function g([e,n]){if("show"===n)t.currentScene&&t.currentParents.push(t.currentScene),t.currentScene=e;else{let e=t.currentParents.pop();t.currentScene=e}return h[e][n]}t.currentParents||(t.currentParents=[]);const y=u(t.currentScene,t.currentParents,n,m),P=[];for(let e of y)[e].map(g).map((e=>P.push(l(e),...o)));return c.sequence(P).onComplete((()=>{t.opened=!0,s.changeContext(n),a.done()})),a.promise}}};return function(e={logLevel:0}){const r=t(),o=c({level:e.logLevel||0}),s={currentScene:null,currentParents:null,sceneNames:new Set,scenes:{},opened:!1},a={},f={},h={shortcutMngr:r,API:a,inAPI:f,findInstructions:i,askForPromise:n,setInstruction:u({askForPromise:n,deps:r.getDependencies}),log:o};return Object.entries(l).forEach((([e,t])=>{e.startsWith("_")?f[e]=t(h,s):a[e]=t(h,s)})),a.setDependencies=e=>r.setDependencies(e),a.getDependencies=()=>r.getDependencies(),a.setNote=e=>t.setNote(e),a.listScenes=()=>[...s.sceneNames],a.enablePlugin=(e,t)=>r.enablePlugin(e,t),a.disablePlugin=e=>r.disablePlugin(e),a.emit=(e,...t)=>r.emit(e,...t),a}}));
|
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
|
+
<link rel="stylesheet" href="public/style.css" />
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
|
+
<title>No demo yet!</title>
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<div id="app"></div>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/main.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import './style.css'
|
|
2
|
+
import javascriptLogo from './javascript.svg'
|
|
3
|
+
import viteLogo from '/vite.svg'
|
|
4
|
+
// import { setupCounter } from './counter.js'
|
|
5
|
+
|
|
6
|
+
import top from './src/main.js'
|
|
7
|
+
|
|
8
|
+
document.querySelector('#app').innerHTML = `
|
|
9
|
+
<div>
|
|
10
|
+
<a href="https://vitejs.dev" target="_blank">
|
|
11
|
+
<img src="${viteLogo}" class="logo" alt="Vite logo" />
|
|
12
|
+
</a>
|
|
13
|
+
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript" target="_blank">
|
|
14
|
+
<img src="${javascriptLogo}" class="logo vanilla" alt="JavaScript logo" />
|
|
15
|
+
</a>
|
|
16
|
+
<h1>Hello Vite!</h1>
|
|
17
|
+
<div class="card">
|
|
18
|
+
<button id="counter" data-click="count" data-quick-click type="button">0</button>
|
|
19
|
+
</div>
|
|
20
|
+
<p class="read-the-docs">
|
|
21
|
+
Click on the Vite logo to learn more
|
|
22
|
+
</p>
|
|
23
|
+
</div>
|
|
24
|
+
`
|
|
25
|
+
|
|
26
|
+
top ()
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@peter.naydenov/cuts",
|
|
3
|
+
"description": "SPA scene manager",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Peter Naydenov",
|
|
7
|
+
"main": "./dist/cuts.umd.js",
|
|
8
|
+
"type": "module",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/cuts.esm.mjs",
|
|
12
|
+
"require": "./dist/cuts.cjs",
|
|
13
|
+
"default": "./dist/cuts.umd.js"
|
|
14
|
+
},
|
|
15
|
+
"./package.json": "./package.json",
|
|
16
|
+
"./dist/*": "./dist/*",
|
|
17
|
+
"./src/*": "./src/*"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite",
|
|
21
|
+
"build": "rollup -c",
|
|
22
|
+
"preview": "vite preview",
|
|
23
|
+
"test": "mocha test-mocha/*.js",
|
|
24
|
+
"cypress": "cypress open --component --browser chrome"
|
|
25
|
+
},
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/PeterNaydenov/screen-cuts.git"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@peter.naydenov/visual-controller-for-vue3": "^2.1.0",
|
|
32
|
+
"@rollup/plugin-commonjs": "^25.0.7",
|
|
33
|
+
"@rollup/plugin-node-resolve": "^15.2.3",
|
|
34
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
35
|
+
"@vitejs/plugin-vue": "^5.0.4",
|
|
36
|
+
"c8": "^9.1.0",
|
|
37
|
+
"chai": "^5.1.0",
|
|
38
|
+
"cypress": "^13.8.1",
|
|
39
|
+
"mocha": "^10.4.0",
|
|
40
|
+
"rollup": "^4.17.2",
|
|
41
|
+
"vite": "^5.2.11",
|
|
42
|
+
"vue": "^3.4.26"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@peter.naydenov/log": "^1.1.0",
|
|
46
|
+
"@peter.naydenov/shortcuts": "^3.1.1",
|
|
47
|
+
"ask-for-promise": "^2.0.3"
|
|
48
|
+
},
|
|
49
|
+
"keywords": [
|
|
50
|
+
"script",
|
|
51
|
+
"visual",
|
|
52
|
+
"state",
|
|
53
|
+
"manager"
|
|
54
|
+
]
|
|
55
|
+
}
|
package/rollup.config.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import resolve from '@rollup/plugin-node-resolve'
|
|
2
|
+
import commonjs from '@rollup/plugin-commonjs'
|
|
3
|
+
import terser from '@rollup/plugin-terser';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
export default [
|
|
7
|
+
// browser-friendly UMD build
|
|
8
|
+
{
|
|
9
|
+
input: 'src/main.js',
|
|
10
|
+
output: {
|
|
11
|
+
name: 'cuts',
|
|
12
|
+
file: 'dist/cuts.umd.js',
|
|
13
|
+
format: 'umd'
|
|
14
|
+
},
|
|
15
|
+
plugins: [
|
|
16
|
+
resolve(), // so Rollup can find `ms`
|
|
17
|
+
commonjs() // so Rollup can convert `ms` to an ES module
|
|
18
|
+
, terser()
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
|
|
22
|
+
// CommonJS (for Node) and ES module (for bundlers) build.
|
|
23
|
+
// (We could have three entries in the configuration array
|
|
24
|
+
// instead of two, but it's quicker to generate multiple
|
|
25
|
+
// builds from a single configuration where possible, using
|
|
26
|
+
// an array for the `output` option, where we can specify
|
|
27
|
+
// `file` and `format` for each target)
|
|
28
|
+
{
|
|
29
|
+
input: 'src/main.js',
|
|
30
|
+
external: [
|
|
31
|
+
'ask-for-promise',
|
|
32
|
+
'@peter.naydenov/log',
|
|
33
|
+
'@peter.naydenov/shortcuts'
|
|
34
|
+
],
|
|
35
|
+
output: [
|
|
36
|
+
{ file: 'dist/cuts.cjs' , format: 'cjs' },
|
|
37
|
+
{ file: 'dist/cuts.esm.mjs', format: 'es' }
|
|
38
|
+
],
|
|
39
|
+
plugins: [ terser() ]
|
|
40
|
+
}
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
function* findInstructions ( currentName, currentParents, targetName, targetParents ) {
|
|
5
|
+
let backToName, backIndex=null;
|
|
6
|
+
|
|
7
|
+
if ( currentName === null ) { // When the scene manager is just started
|
|
8
|
+
yield* targetParents.map ( name => [ name, 'show' ])
|
|
9
|
+
yield [ targetName, 'show' ]
|
|
10
|
+
return
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
if ( !targetParents || targetParents.length === 0 ) { // Need to close all current parents, then open the target
|
|
14
|
+
if ( !currentParents ) {
|
|
15
|
+
yield [ currentName, 'hide' ]
|
|
16
|
+
yield [ targetName, 'show' ]
|
|
17
|
+
return
|
|
18
|
+
}
|
|
19
|
+
yield [ currentName, 'hide' ]
|
|
20
|
+
currentParents.reverse ()
|
|
21
|
+
yield* currentParents.map ( name => [ name, 'hide' ])
|
|
22
|
+
yield [ targetName, 'show' ]
|
|
23
|
+
return
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if ( targetParents.includes ( currentName ) ) { // Target is a subpage of the current page. No closing needed.
|
|
27
|
+
let id = targetParents.indexOf ( currentName );
|
|
28
|
+
for ( let i = id+1; i < targetParents.length; i++ ) {
|
|
29
|
+
yield [ targetParents[i], 'show' ]
|
|
30
|
+
}
|
|
31
|
+
yield [ targetName, 'show' ]
|
|
32
|
+
return
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if ( !currentParents ) {
|
|
36
|
+
yield [ currentName, 'hide' ]
|
|
37
|
+
yield* targetParents.map ( name => [ name, 'show' ])
|
|
38
|
+
return
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
targetParents.every ( (name,i) => {
|
|
42
|
+
if ( currentParents[i] === targetParents[i] ) {
|
|
43
|
+
backToName = name
|
|
44
|
+
backIndex = i
|
|
45
|
+
return true
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
return false
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
if ( backIndex == null ) { // different branches - close all current parents, then open the target branch
|
|
53
|
+
yield [ currentName, 'hide' ]
|
|
54
|
+
currentParents.reverse ()
|
|
55
|
+
yield* currentParents.map ( name => [ name, 'hide' ])
|
|
56
|
+
yield* targetParents.map ( name => [ name, 'show' ])
|
|
57
|
+
yield [ targetName, 'show' ]
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
yield [ currentName, 'hide' ]
|
|
62
|
+
for ( let i = currentParents.length; i > backIndex+1; i-- ) {
|
|
63
|
+
yield [ currentParents[i-1], 'hide' ]
|
|
64
|
+
}
|
|
65
|
+
for ( let i = backIndex+1; i < targetParents.length; i++ ) {
|
|
66
|
+
yield [ targetParents[i], 'show' ]
|
|
67
|
+
}
|
|
68
|
+
yield [ targetName, 'show' ]
|
|
69
|
+
} // findInstructions func.
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
export default findInstructions
|
|
74
|
+
|
|
75
|
+
|
package/src/main.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
/**
|
|
3
|
+
* Cuts
|
|
4
|
+
* ===============
|
|
5
|
+
*
|
|
6
|
+
* SPA Scene manager.
|
|
7
|
+
*
|
|
8
|
+
* History notes:
|
|
9
|
+
* - Started as @peter.naydenov/screen-writer on gitHub for the first time on 2023-11-24;
|
|
10
|
+
* - Version 2.0.0 of screen-writer. Works with shortcuts@3.0.1. Published on March 6th, 2024;
|
|
11
|
+
* - Rename to @peter.naydenov/cuts
|
|
12
|
+
* Changes in documentation and some methods were renamed accordingly.
|
|
13
|
+
* Version 1.0.0.
|
|
14
|
+
* Published on May 8th, 2024;
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
import { shortcuts } from '@peter.naydenov/shortcuts' // Docs : https://github.com/PeterNaydenov/shortcuts
|
|
20
|
+
import askForPromise from 'ask-for-promise' // Docs : https://github.com/PeterNaydenov/ask-for-promise
|
|
21
|
+
import createLog from '@peter.naydenov/log' // Docs : https://github.com/PeterNaydenov/log
|
|
22
|
+
|
|
23
|
+
import findInstructions from './findInstructions.js'
|
|
24
|
+
import setInstruction from './setInstruction.js'
|
|
25
|
+
import methods from './methods/index.js'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
function main ( cfg= {logLevel:0} ) {
|
|
32
|
+
const
|
|
33
|
+
shortcutMngr = shortcuts ()
|
|
34
|
+
, logLevel = cfg.logLevel || 0
|
|
35
|
+
, log = createLog ({ level:logLevel })
|
|
36
|
+
, state = {
|
|
37
|
+
currentScene : null // Current scene name;
|
|
38
|
+
, currentParents : null // Current scene parents if any;
|
|
39
|
+
, sceneNames : new Set () // Set with all loaded scenes;
|
|
40
|
+
, scenes : {} // Collection of all scenes;
|
|
41
|
+
, opened : false // Flag to indicate if the scene manager is opened
|
|
42
|
+
}
|
|
43
|
+
, API = {}
|
|
44
|
+
, inAPI = {}
|
|
45
|
+
, dependencies = {
|
|
46
|
+
shortcutMngr
|
|
47
|
+
, API
|
|
48
|
+
, inAPI
|
|
49
|
+
, findInstructions
|
|
50
|
+
, askForPromise
|
|
51
|
+
, setInstruction : setInstruction ({ askForPromise, deps: shortcutMngr.getDependencies })
|
|
52
|
+
, log
|
|
53
|
+
}
|
|
54
|
+
;
|
|
55
|
+
|
|
56
|
+
Object.entries ( methods ).forEach ( ([name, fn]) => {
|
|
57
|
+
if ( name.startsWith ( '_' ) ) inAPI[name] = fn ( dependencies, state )
|
|
58
|
+
else API[name] = fn ( dependencies, state )
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* @function setDependencies
|
|
63
|
+
* @description Set dependencies for the Scenes
|
|
64
|
+
* @param {*} deps - dependencies objects
|
|
65
|
+
* @returns void
|
|
66
|
+
*/
|
|
67
|
+
API.setDependencies = deps => shortcutMngr.setDependencies ( deps )
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @function getDependencies
|
|
71
|
+
* @description Get dependencies for the Scenes
|
|
72
|
+
* @returns {*} - dependencies objects
|
|
73
|
+
*/
|
|
74
|
+
API.getDependencies = () => shortcutMngr.getDependencies ()
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @function setNote
|
|
78
|
+
* @param {string} note - note, provided to action functions
|
|
79
|
+
* @returns void
|
|
80
|
+
*/
|
|
81
|
+
API.setNote = note => shortcuts.setNote ( note )
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @function listScenes
|
|
85
|
+
* @description List all loaded Scene names
|
|
86
|
+
* @returns {Array.<string>} - list of scene names
|
|
87
|
+
*/
|
|
88
|
+
API.listScenes = () => [ ...state.sceneNames ]
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @function enablePlugin
|
|
92
|
+
* @description Enable a shortcut plugin
|
|
93
|
+
* @param {function} plugin - plugin library
|
|
94
|
+
* @param {*} options - plugin options
|
|
95
|
+
* @returns void
|
|
96
|
+
*/
|
|
97
|
+
API.enablePlugin = (plugin,options) => shortcutMngr.enablePlugin ( plugin, options )
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @function disablePlugin
|
|
101
|
+
* @description Disable a shortcut plugin
|
|
102
|
+
* @param {string} pluginName - plugin name
|
|
103
|
+
* @returns void
|
|
104
|
+
*/
|
|
105
|
+
API.disablePlugin = ( pluginName ) => shortcutMngr.disablePlugin ( pluginName )
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @function emit
|
|
110
|
+
* @description Emit an event
|
|
111
|
+
* @param {string} event - event name
|
|
112
|
+
* @param {*} data - event data
|
|
113
|
+
* @returns void
|
|
114
|
+
*/
|
|
115
|
+
API.emit = ( event, ...args ) => shortcutMngr.emit ( event, ...args )
|
|
116
|
+
|
|
117
|
+
return API
|
|
118
|
+
} // main func.
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
export default main
|
|
123
|
+
|
|
124
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
function hide ( dependencies, state ) {
|
|
6
|
+
/**
|
|
7
|
+
* @function hide
|
|
8
|
+
* @description Hide the current scene and returns to the parent one
|
|
9
|
+
* @param {number|string} endSteps - number of steps to end or '*' to end all scenes to the root
|
|
10
|
+
* @returns {Promise} - promise that resolves when the process 'end' is completed
|
|
11
|
+
*/
|
|
12
|
+
return function hide ( endSteps=1 ) {
|
|
13
|
+
const
|
|
14
|
+
{ askForPromise, shortcutMngr, setInstruction } = dependencies
|
|
15
|
+
, { currentScene, currentParents, scenes } = state
|
|
16
|
+
, hideTask = askForPromise ()
|
|
17
|
+
, instructions = []
|
|
18
|
+
, hasParents = currentParents.length > 0
|
|
19
|
+
;
|
|
20
|
+
let el;
|
|
21
|
+
|
|
22
|
+
shortcutMngr.pause () // Stop all shortcuts
|
|
23
|
+
instructions.push ( setInstruction ( scenes[currentScene].hide )) // Set instruction to hide current scene
|
|
24
|
+
|
|
25
|
+
if ( !hasParents ) state.currentScene = null
|
|
26
|
+
|
|
27
|
+
if ( hasParents && endSteps === '*' ) {
|
|
28
|
+
[...currentParents].reverse().forEach ( name => {
|
|
29
|
+
el = state.currentParents.pop ();
|
|
30
|
+
state.currentScene = el
|
|
31
|
+
instructions.push ( setInstruction ( scenes[name].hide))
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
if ( hasParents && ![ '*', 1].includes(endSteps) ) {
|
|
35
|
+
currentParents.slice ( `-${endSteps-1}` ).reverse().map ( name => {
|
|
36
|
+
el = state.currentParents.pop ();
|
|
37
|
+
state.currentScene = el
|
|
38
|
+
instructions.push ( setInstruction (scenes[name].hide))
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
endSteps = askForPromise.sequence ( instructions )
|
|
42
|
+
endSteps.onComplete ( () => {
|
|
43
|
+
shortcutMngr.changeContext ( state.currentScene ) // If state.currentScene is null, then all shortcuts are switched off and currentContext is null
|
|
44
|
+
hideTask.done ()
|
|
45
|
+
})
|
|
46
|
+
return hideTask.promise
|
|
47
|
+
}} // hide func.
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export default hide
|
|
52
|
+
|
|
53
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function listShortcuts ( dependencies, state ) {
|
|
4
|
+
/**
|
|
5
|
+
* @function listShortcuts
|
|
6
|
+
* @description List all shortcuts for Scene
|
|
7
|
+
* @param {string} sceneName - name of the scene to list shortcuts for.
|
|
8
|
+
* @returns {(Array.<string>|null)} - list of shortcuts for the scene or null if page not found
|
|
9
|
+
*/
|
|
10
|
+
return function listShortcuts ( sceneName ) {
|
|
11
|
+
const { scenes, sceneNames } = state;
|
|
12
|
+
if ( ! sceneNames.has ( sceneName ) ) return null
|
|
13
|
+
const { show, hide, parents, ...shortcuts } = scenes[sceneName];
|
|
14
|
+
return Object.keys ( shortcuts )
|
|
15
|
+
}} // listShortcuts func.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export default listShortcuts
|
|
20
|
+
|
|
21
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @type sceneDescription
|
|
5
|
+
* @param {string} name - name of the scene
|
|
6
|
+
* @param {object} scene - object should contain 'show' and 'hide' + shortcuts and action functions
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
function setScenes ( dependencies, state ) {
|
|
10
|
+
/**
|
|
11
|
+
* @function setScenes
|
|
12
|
+
* @param {Array} sceneDescription - list of objects with name and scene. Scene should be a Scene model.
|
|
13
|
+
* @returns { void }
|
|
14
|
+
*/
|
|
15
|
+
return function setScenes ( list ) {
|
|
16
|
+
const { shortcutMngr } = dependencies;
|
|
17
|
+
list.forEach ( ({name, scene }) => {
|
|
18
|
+
if ( !scene.parents ) scene.parents = []
|
|
19
|
+
state.scenes[name] = scene
|
|
20
|
+
state.sceneNames.add ( name )
|
|
21
|
+
|
|
22
|
+
const
|
|
23
|
+
{ show, hide, parents, ...shortcuts } = scene
|
|
24
|
+
, context = {}
|
|
25
|
+
;
|
|
26
|
+
|
|
27
|
+
context[name] = shortcuts
|
|
28
|
+
shortcutMngr.load ( context )
|
|
29
|
+
})
|
|
30
|
+
}} // setScenes func.
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
export default setScenes
|
|
35
|
+
|
|
36
|
+
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
function show ( dependencies, state ) {
|
|
6
|
+
/**
|
|
7
|
+
* @function show
|
|
8
|
+
* @description Switch to the requested scene
|
|
9
|
+
* @param {Object} request - request object
|
|
10
|
+
* @param {string} request.scene - requested scene name
|
|
11
|
+
* @param {Object} request.options - options object. Optional
|
|
12
|
+
* @param {boolean} request.options.ssr - flag to indicate if the scene is already rendered(ssr: server side rendering). Optional
|
|
13
|
+
* @param {Array} args - arguments to pass to the scene method. Optional
|
|
14
|
+
* @returns {Promise} - promise that resolves when the process to 'show' is completed
|
|
15
|
+
*/
|
|
16
|
+
return function show ({scene:requestedScene, options={ssr:false}}, ...args ) {
|
|
17
|
+
const
|
|
18
|
+
{ shortcutMngr, askForPromise, log, findInstructions, setInstruction } = dependencies
|
|
19
|
+
, showTask = askForPromise ()
|
|
20
|
+
, { opened, scenes, sceneNames } = state
|
|
21
|
+
;
|
|
22
|
+
|
|
23
|
+
if ( !sceneNames.has ( requestedScene ) ) {
|
|
24
|
+
if ( log ) {
|
|
25
|
+
log ({
|
|
26
|
+
message: `Scene ${requestedScene} is not available.`
|
|
27
|
+
, level : 1
|
|
28
|
+
, type : 'error'
|
|
29
|
+
})
|
|
30
|
+
showTask.done ()
|
|
31
|
+
return showTask.promise
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if ( !opened && options.ssr ) { // Check for Server side rendering on first scene load only
|
|
36
|
+
// Executes only once when the scene manager is started
|
|
37
|
+
state.opened = true
|
|
38
|
+
state.currentPage = requestedScene
|
|
39
|
+
shortcutMngr.changeContext ( requestedScene )
|
|
40
|
+
showTask.done ()
|
|
41
|
+
return showTask.promise
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const { show, parents } = scenes[requestedScene];
|
|
45
|
+
|
|
46
|
+
if ( parents[0] === '*' ) {
|
|
47
|
+
show ().then ( () => showTask.done () )
|
|
48
|
+
state.currentParents.push ( state.currentPage )
|
|
49
|
+
state.currentPage = requestedScene
|
|
50
|
+
return showTask.promise
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let checkParents = parents.forEach ( name => sceneNames.has ( name ) ) || true ;
|
|
54
|
+
if ( !checkParents ) {
|
|
55
|
+
if ( log ) {
|
|
56
|
+
log ({
|
|
57
|
+
message: `Some of '${requestedScene}' parent scenes are not set.`
|
|
58
|
+
, level : 1
|
|
59
|
+
, type : 'error'
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
showTask.done ()
|
|
63
|
+
return showTask.promise
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function getStep ([name, action]) { // Returns a scene function(hide or show) according to instructions
|
|
67
|
+
if ( action === 'show' ) {
|
|
68
|
+
if ( state.currentScene ) state.currentParents.push ( state.currentScene )
|
|
69
|
+
state.currentScene = name
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
let el = state.currentParents.pop ();
|
|
73
|
+
state.currentScene = el
|
|
74
|
+
}
|
|
75
|
+
return scenes[name][action]
|
|
76
|
+
} // getStep func.
|
|
77
|
+
|
|
78
|
+
if ( !state.currentParents ) state.currentParents = []
|
|
79
|
+
const
|
|
80
|
+
g = findInstructions ( state.currentScene, state.currentParents, requestedScene, parents )
|
|
81
|
+
, instructions = []
|
|
82
|
+
;
|
|
83
|
+
|
|
84
|
+
for ( let inst of g ) {
|
|
85
|
+
[inst]
|
|
86
|
+
.map ( getStep )
|
|
87
|
+
.map ( step => instructions.push ( setInstruction(step),...args ))
|
|
88
|
+
// Note: Methods 'show' and 'hide' are async, but we need to provide them a data,
|
|
89
|
+
// so we need to wrap them in a another function that returns a promise
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const goingTask = askForPromise.sequence ( instructions ); // Execute open steps(show and hide) in sequence
|
|
93
|
+
goingTask.onComplete ( () => {
|
|
94
|
+
state.opened = true
|
|
95
|
+
shortcutMngr.changeContext ( requestedScene )
|
|
96
|
+
showTask.done ()
|
|
97
|
+
})
|
|
98
|
+
return showTask.promise
|
|
99
|
+
}} // show func.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
export default show
|
|
104
|
+
|
|
105
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function setInstruction ( dependencies ) {
|
|
4
|
+
return function setInstruction ( fn, ...args ) {
|
|
5
|
+
const { askForPromise, deps } = dependencies;
|
|
6
|
+
return function applyInstruction () {
|
|
7
|
+
const task = askForPromise ();
|
|
8
|
+
fn ({ task, dependencies:deps() }, ...args )
|
|
9
|
+
return task.promise
|
|
10
|
+
} // applyInstruction func.
|
|
11
|
+
}} // setInstruction func.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export default setInstruction
|
|
16
|
+
|
|
17
|
+
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import VisualController from '@peter.naydenov/visual-controller-for-vue3' // Docs : https://github.com/PeterNaydenov/visual-controller-for-vue3
|
|
4
|
+
import { expect } from 'chai'
|
|
5
|
+
import cuts from '../src/main.js'
|
|
6
|
+
import { pluginClick } from '@peter.naydenov/shortcuts'
|
|
7
|
+
|
|
8
|
+
import Dummy from './dummy.vue'
|
|
9
|
+
import Red from './red.vue'
|
|
10
|
+
import Blue from './blue.vue'
|
|
11
|
+
import Gray from './gray.vue'
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
describe ( 'General', () => {
|
|
16
|
+
|
|
17
|
+
it ( 'Start a sreenWriter. ScreenWriter API', () => {
|
|
18
|
+
const script = cuts ();
|
|
19
|
+
expect ( script ).to.have.property ( 'show' )
|
|
20
|
+
expect ( script ).to.have.property ( 'hide' )
|
|
21
|
+
expect ( script ).to.have.property ( 'setScenes' )
|
|
22
|
+
expect ( script ).to.have.property ( 'setNote' )
|
|
23
|
+
expect ( script ).to.have.property ( 'listScenes' )
|
|
24
|
+
expect ( script ).to.have.property ( 'listShortcuts' )
|
|
25
|
+
expect ( script ).to.have.property ( 'setDependencies' )
|
|
26
|
+
expect ( script ).to.have.property ( 'getDependencies' )
|
|
27
|
+
}) // it start a director
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
it ( 'Load a top level page', done => {
|
|
32
|
+
let
|
|
33
|
+
render = false
|
|
34
|
+
, click = false
|
|
35
|
+
, html = new VisualController ()
|
|
36
|
+
;
|
|
37
|
+
const
|
|
38
|
+
script = cuts ()
|
|
39
|
+
, d = document.querySelector ('[data-cy-root]')
|
|
40
|
+
, testScene = {
|
|
41
|
+
show : ({ task, dependencies }) => {
|
|
42
|
+
const { html } = dependencies;
|
|
43
|
+
html.publish ( Red, {}, 'container' )
|
|
44
|
+
render = true
|
|
45
|
+
task.done ()
|
|
46
|
+
}
|
|
47
|
+
, hide : ({task, dependencies}) => {
|
|
48
|
+
const { html } = dependencies;
|
|
49
|
+
html.destroy ( 'container' )
|
|
50
|
+
task.done ()
|
|
51
|
+
}
|
|
52
|
+
, 'click: left-1' : ({dependencies,target}) => {
|
|
53
|
+
click = true
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
;
|
|
57
|
+
script.enablePlugin ( pluginClick )
|
|
58
|
+
d.id = 'root'
|
|
59
|
+
html.publish ( Dummy, {}, 'root' )
|
|
60
|
+
|
|
61
|
+
cy.wait ( 0 )
|
|
62
|
+
.then ( () => {
|
|
63
|
+
script.setDependencies ({ html })
|
|
64
|
+
script.setScenes ([
|
|
65
|
+
{ name:'test', scene:testScene }
|
|
66
|
+
])
|
|
67
|
+
script.show ({ scene : 'test' })
|
|
68
|
+
// cy.get ( '#target' ).click ()
|
|
69
|
+
})
|
|
70
|
+
.then ( () => {
|
|
71
|
+
expect ( render ).to.be.true
|
|
72
|
+
// expect ( click ).to.be.true
|
|
73
|
+
return cy.wait ( 0 )
|
|
74
|
+
})
|
|
75
|
+
.then ( () => {
|
|
76
|
+
return script.hide ()
|
|
77
|
+
})
|
|
78
|
+
.then ( () => {
|
|
79
|
+
const pageContent = document.getElementById('container').innerHTML
|
|
80
|
+
expect ( pageContent ).to.be.empty
|
|
81
|
+
done ()
|
|
82
|
+
})
|
|
83
|
+
}) // it load a top level page
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
it ( 'Load a deep level page', done => {
|
|
88
|
+
let
|
|
89
|
+
render = false
|
|
90
|
+
, click = false
|
|
91
|
+
, html = new VisualController ()
|
|
92
|
+
;
|
|
93
|
+
const
|
|
94
|
+
script = cuts ()
|
|
95
|
+
, d = document.querySelector ('[data-cy-root]' )
|
|
96
|
+
, testScene = {
|
|
97
|
+
show : ({ task, dependencies }) => {
|
|
98
|
+
const { html } = dependencies;
|
|
99
|
+
html.publish ( Red, {}, 'container' )
|
|
100
|
+
render = true
|
|
101
|
+
task.done ()
|
|
102
|
+
}
|
|
103
|
+
, hide : ({task, dependencies}) => {
|
|
104
|
+
const { html } = dependencies;
|
|
105
|
+
html.destroy ( 'container' )
|
|
106
|
+
task.done ()
|
|
107
|
+
}
|
|
108
|
+
, parents : [ 'blue' ]
|
|
109
|
+
, 'click: 1-left' : ({dependencies,target}) => { // Order in the event name after the prefix is not important
|
|
110
|
+
click = true
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
, bluePage = {
|
|
114
|
+
show : ({ task, dependencies }) => {
|
|
115
|
+
const { html } = dependencies;
|
|
116
|
+
html.publish ( Blue, {}, 'blue' )
|
|
117
|
+
task.done ()
|
|
118
|
+
}
|
|
119
|
+
, hide : ({task, dependencies}) => {
|
|
120
|
+
const { html } = dependencies;
|
|
121
|
+
html.destroy ( 'blue' )
|
|
122
|
+
task.done ()
|
|
123
|
+
}
|
|
124
|
+
, 'click: left-1' : ({dependencies,target}) => {}
|
|
125
|
+
}
|
|
126
|
+
;
|
|
127
|
+
script.enablePlugin ( pluginClick )
|
|
128
|
+
d.id = 'root'
|
|
129
|
+
html.publish ( Dummy, {}, 'root' )
|
|
130
|
+
|
|
131
|
+
cy.wait ( 0 )
|
|
132
|
+
.then ( () => {
|
|
133
|
+
script.setDependencies ({ html })
|
|
134
|
+
script.setScenes ([
|
|
135
|
+
{ name:'test' , scene : testScene }
|
|
136
|
+
, { name: 'blue', scene : bluePage }
|
|
137
|
+
])
|
|
138
|
+
script.show ({ scene: 'test' })
|
|
139
|
+
cy.get ( '#target' ).click ()
|
|
140
|
+
})
|
|
141
|
+
.then ( () => {
|
|
142
|
+
expect ( render ).to.be.true
|
|
143
|
+
expect ( click ).to.be.true
|
|
144
|
+
return cy.wait ( 0 )
|
|
145
|
+
})
|
|
146
|
+
.then ( () => {
|
|
147
|
+
return script.hide ( '*' )
|
|
148
|
+
})
|
|
149
|
+
.then ( () => {
|
|
150
|
+
const pageContent = document.getElementById('container').innerHTML
|
|
151
|
+
expect ( pageContent ).to.be.empty
|
|
152
|
+
done ()
|
|
153
|
+
})
|
|
154
|
+
}) // it load a deep level page
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
it ( 'Turn to other branch', done => {
|
|
159
|
+
let
|
|
160
|
+
render = false
|
|
161
|
+
, click = false
|
|
162
|
+
, html = new VisualController ()
|
|
163
|
+
;
|
|
164
|
+
const
|
|
165
|
+
script = cuts ()
|
|
166
|
+
, d = document.querySelector ('[data-cy-root]' )
|
|
167
|
+
, testScene = {
|
|
168
|
+
show : ({ task, dependencies }) => {
|
|
169
|
+
const { html } = dependencies;
|
|
170
|
+
html.publish ( Red, {}, 'subgray' ) // subgray is inside 'blue' component
|
|
171
|
+
render = true
|
|
172
|
+
task.done ()
|
|
173
|
+
}
|
|
174
|
+
, hide : ({task, dependencies}) => {
|
|
175
|
+
const { html } = dependencies;
|
|
176
|
+
html.destroy ( 'subgray' )
|
|
177
|
+
task.done ()
|
|
178
|
+
}
|
|
179
|
+
, parents : [ 'blue' ]
|
|
180
|
+
, 'click: left-1' : ({dependencies,target}) => {
|
|
181
|
+
click = true
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
, bluePage = {
|
|
185
|
+
show : ({ task, dependencies }) => {
|
|
186
|
+
const { html } = dependencies;
|
|
187
|
+
html.publish ( Blue, {}, 'blue' )
|
|
188
|
+
task.done ()
|
|
189
|
+
}
|
|
190
|
+
, hide : ({task, dependencies}) => {
|
|
191
|
+
const { html } = dependencies;
|
|
192
|
+
html.destroy ( 'blue' )
|
|
193
|
+
task.done ()
|
|
194
|
+
}
|
|
195
|
+
, 'click: left-1' : ({dependencies,target}) => {}
|
|
196
|
+
}
|
|
197
|
+
, grayPage = {
|
|
198
|
+
show : ({ task, dependencies }) => {
|
|
199
|
+
const { html } = dependencies;
|
|
200
|
+
html.publish ( Gray, {}, 'subgray' )
|
|
201
|
+
task.done ()
|
|
202
|
+
}
|
|
203
|
+
, hide : ({task, dependencies}) => {
|
|
204
|
+
const { html } = dependencies;
|
|
205
|
+
html.destroy ( 'subgray' )
|
|
206
|
+
task.done ()
|
|
207
|
+
}
|
|
208
|
+
, parents : [ 'blue' ]
|
|
209
|
+
, 'click: left-1' : ({dependencies,target}) => {}
|
|
210
|
+
}
|
|
211
|
+
;
|
|
212
|
+
script.enablePlugin ( pluginClick )
|
|
213
|
+
d.id = 'root'
|
|
214
|
+
html.publish ( Dummy, {}, 'root' )
|
|
215
|
+
|
|
216
|
+
cy.wait ( 0 )
|
|
217
|
+
.then ( () => {
|
|
218
|
+
script.setDependencies ({ html })
|
|
219
|
+
script.setScenes ([
|
|
220
|
+
{ name:'test', scene : testScene }
|
|
221
|
+
, { name: 'blue', scene : bluePage }
|
|
222
|
+
, { name: 'gray', scene : grayPage }
|
|
223
|
+
])
|
|
224
|
+
script.show ({ scene : 'test' })
|
|
225
|
+
cy.get ( '#target' ).click ()
|
|
226
|
+
})
|
|
227
|
+
.then ( () => {
|
|
228
|
+
expect ( render ).to.be.true
|
|
229
|
+
expect ( click ).to.be.true
|
|
230
|
+
return cy.wait ( 0 )
|
|
231
|
+
})
|
|
232
|
+
.then ( () => script.show ({ scene : 'gray' } ))
|
|
233
|
+
.then ( () => {
|
|
234
|
+
const
|
|
235
|
+
red = document.querySelectorAll ( '.red' )
|
|
236
|
+
, blue = document.querySelectorAll ( '.gray' )
|
|
237
|
+
;
|
|
238
|
+
expect ( blue.length ).to.be.equal ( 1 )
|
|
239
|
+
expect ( red.length ).to.be.equal ( 0 )
|
|
240
|
+
done ()
|
|
241
|
+
})
|
|
242
|
+
}) // it turn to other branch
|
|
243
|
+
}) // describe
|
package/test/blue.vue
ADDED
package/test/dummy.vue
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div>
|
|
3
|
+
<h1>Test</h1>
|
|
4
|
+
<p id="target">Test <a href="#">lorem ipsum</a></p>
|
|
5
|
+
<div id="container">
|
|
6
|
+
|
|
7
|
+
</div>
|
|
8
|
+
<div id="blue"></div>
|
|
9
|
+
<div id="gray"></div>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
|
|
13
|
+
<style>
|
|
14
|
+
:root {
|
|
15
|
+
--blue: #007bff;
|
|
16
|
+
--gray: #6c757d;
|
|
17
|
+
--link-color: rgb(204, 51, 10);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
body.dark {
|
|
21
|
+
--background-color: #000;
|
|
22
|
+
--link-color: rgb(249, 249, 249);
|
|
23
|
+
background-color: var(--background-color);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#target {
|
|
27
|
+
--blue: var(--link-color);
|
|
28
|
+
color: var(--blue);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
#target a {
|
|
32
|
+
color: var(--link-color);
|
|
33
|
+
}
|
|
34
|
+
</style>
|
package/test/gray.vue
ADDED
package/test/red.vue
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
import findInstructions from "../src/findInstructions.js"
|
|
4
|
+
import { expect } from 'chai'
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
describe ( 'Find a Position', () => {
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
it ( 'Switch top level pages', () => {
|
|
12
|
+
let result = []
|
|
13
|
+
let g = findInstructions ( 'old', undefined, 'new', undefined );
|
|
14
|
+
|
|
15
|
+
for ( const n of g ) { result.push ( n ) }
|
|
16
|
+
|
|
17
|
+
expect ( result.length ).to.be.equal ( 2 )
|
|
18
|
+
expect ( result[0][0] ).to.be.equal ( 'old' )
|
|
19
|
+
expect ( result[0][1] ).to.be.equal ( 'hide' )
|
|
20
|
+
|
|
21
|
+
expect ( result[1][0] ).to.be.equal ( 'new' )
|
|
22
|
+
expect ( result[1][1] ).to.be.equal ( 'show' )
|
|
23
|
+
}) // it switch top level pages
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
it ( 'Switch from child page to top level page', () => {
|
|
28
|
+
let result = [];
|
|
29
|
+
let g = findInstructions ( 'old', ['one','two'], 'new', undefined );
|
|
30
|
+
|
|
31
|
+
for ( const n of g ) { result.push ( n ) }
|
|
32
|
+
|
|
33
|
+
expect ( result[0]).to.be.deep.equal ( ['old', 'hide'] )
|
|
34
|
+
expect ( result[1]).to.be.deep.equal ( ['two', 'hide'] )
|
|
35
|
+
expect ( result[2]).to.be.deep.equal ( ['one', 'hide'] )
|
|
36
|
+
expect ( result[3]).to.be.deep.equal ( ['new', 'show'] )
|
|
37
|
+
}) // it switch from child page to top level page
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
it ( 'Switch to child page', () => {
|
|
42
|
+
let result = [];
|
|
43
|
+
let g = findInstructions ( 'old', ['bla'], 'new', ['old','two','three'] );
|
|
44
|
+
for ( const n of g ) { result.push ( n ) }
|
|
45
|
+
|
|
46
|
+
expect ( result ).to.have.lengthOf ( 3 )
|
|
47
|
+
expect ( result[0]).to.be.deep.equal ( ['two', 'show'] )
|
|
48
|
+
expect ( result[1]).to.be.deep.equal ( ['three', 'show'] )
|
|
49
|
+
expect ( result[2]).to.be.deep.equal ( ['new', 'show'] )
|
|
50
|
+
}) // it switch to child page
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
it ( 'From child page to other siblings page', () => {
|
|
55
|
+
let result = [];
|
|
56
|
+
let g = findInstructions ( 'old', ['two', 'three'], 'new', ['two','three'] );
|
|
57
|
+
for ( const n of g ) { result.push ( n ) }
|
|
58
|
+
|
|
59
|
+
expect ( result ).to.have.lengthOf ( 2 )
|
|
60
|
+
expect ( result[0]).to.be.deep.equal ( ['old', 'hide'] )
|
|
61
|
+
expect ( result[1]).to.be.deep.equal ( ['new', 'show'] )
|
|
62
|
+
}) // it from top level page to not own child page
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
it ( 'From top level page to not own child page', () => {
|
|
67
|
+
let result = [];
|
|
68
|
+
let g = findInstructions ( 'old', undefined, 'new', ['two','three'] );
|
|
69
|
+
for ( const n of g ) { result.push ( n ) }
|
|
70
|
+
|
|
71
|
+
expect ( result ).to.have.lengthOf ( 3 )
|
|
72
|
+
expect ( result[0]).to.be.deep.equal ( ['old', 'hide'] )
|
|
73
|
+
expect ( result[1]).to.be.deep.equal ( ['two', 'show'] )
|
|
74
|
+
expect ( result[2]).to.be.deep.equal ( ['three', 'show'] )
|
|
75
|
+
}) // it from top level page to not own child page
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
it ( 'Long chain of parents', () => {
|
|
80
|
+
let result = [];
|
|
81
|
+
let g = findInstructions ( 'old', [ 'one', 'branch' ], 'new', [ 'one','two','three'] );
|
|
82
|
+
for ( const n of g ) { result.push ( n ) }
|
|
83
|
+
|
|
84
|
+
expect ( result ).to.have.lengthOf ( 5 )
|
|
85
|
+
expect ( result[0]).to.be.deep.equal ( ['old', 'hide'] )
|
|
86
|
+
expect ( result[1]).to.be.deep.equal ( ['branch', 'hide'] )
|
|
87
|
+
expect ( result[2]).to.be.deep.equal ( ['two', 'show'] )
|
|
88
|
+
expect ( result[3]).to.be.deep.equal ( ['three', 'show'] )
|
|
89
|
+
expect ( result[4]).to.be.deep.equal ( ['new', 'show'] )
|
|
90
|
+
}) // it long chain of parents
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
it ( 'Different branches', () => {
|
|
95
|
+
let result = [];
|
|
96
|
+
let g = findInstructions ( 'old', [ 'something', 'branch' ], 'new', [ 'one','two','three'] );
|
|
97
|
+
for ( const n of g ) { result.push ( n ) }
|
|
98
|
+
|
|
99
|
+
expect ( result ).to.have.lengthOf ( 7 )
|
|
100
|
+
expect ( result[0]).to.be.deep.equal ( ['old', 'hide'] )
|
|
101
|
+
expect ( result[1]).to.be.deep.equal ( ['branch', 'hide'] )
|
|
102
|
+
expect ( result[2]).to.be.deep.equal ( ['something', 'hide'] )
|
|
103
|
+
expect ( result[3]).to.be.deep.equal ( ['one', 'show'] )
|
|
104
|
+
expect ( result[4]).to.be.deep.equal ( ['two', 'show'] )
|
|
105
|
+
expect ( result[5]).to.be.deep.equal ( ['three', 'show'] )
|
|
106
|
+
expect ( result[6]).to.be.deep.equal ( ['new', 'show'] )
|
|
107
|
+
}) // it long chain of parents
|
|
108
|
+
|
|
109
|
+
}) // describe
|