@iebh/tera-fy 1.0.16 → 1.0.18

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/hints.md ADDED
@@ -0,0 +1,26 @@
1
+ TERA-fy Hints
2
+ =============
3
+ Hints are a single string attached to file uploads to provide some context as to where it fits within the overall process. They are usually past-tense single-words indicating the output from each stage.
4
+
5
+ When attached to files they indicate that the generated file is an output from a stage. e.g. if a file is noted with the hint 'deduped' it indicates it is the product of a de-duplication stage.
6
+
7
+ They are used in multiple places:
8
+
9
+ * Calls to most project library handling functions - `selectProjectLibrary()`, `setProjectLibrary()`
10
+ * Within the UI to indicate what library is used during what process
11
+
12
+
13
+ Hint Reference
14
+ --------------
15
+ The following is a non-definitive list of hints that can be associated with files. In all cases the hint should be a lower-case single, past-tense word. Upper-case indicates a variable.
16
+
17
+ This list should also be mirrored in the main tera-tools.com/api/tools.json meta-list.
18
+
19
+
20
+ | Hint | Description |
21
+ |--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------|
22
+ | `search` | The product of a search design stage |
23
+ | `deduped` | One or more files, that are the product of a deduplication stage |
24
+ | `screened-X` | One or more files from the screening stage, generally there will be one file per reviewer where `X` indicates the reviewer number starting with the number 1 |
25
+ | `disputed` | Generally a single file which is the product of a resolved dispute stage which merges in multiple `screened-X` files |
26
+ | `snowballed` | A file which has been through the snowballing stage |
@@ -17,16 +17,19 @@ export default class TeraFy {
17
17
  *
18
18
  * @type {Object}
19
19
  * @property {Boolean} devMode Operate in devMode - i.e. force outer refresh when encountering an existing TeraFy instance
20
- * @property {'detect'|'parent'|'child'} How to communicate with TERA. 'parent' assumes that the parent of the current document is TERA, 'child' spawns an iFrame and uses TERA there, 'detect' tries parent and fallsback to 'child'
20
+ * @property {'detect'|'parent'|'child'|'popup'} mode How to communicate with TERA. 'parent' assumes that the parent of the current document is TERA, 'child' spawns an iFrame and uses TERA there, 'detect' tries parent and switches to `modeFallback` if communication fails
21
+ * @property {String} modeFallback Method to use when all method detection fails
21
22
  * @property {Number} modeTimeout How long entities have in 'detect' mode to identify themselves
22
23
  * @property {String} siteUrl The TERA URL to connect to
23
24
  * @property {String} restrictOrigin URL to restrict communications to
24
25
  * @property {Array<String>} List of sandbox allowables for the embedded if in embed mode
26
+ * @property {Number} handshakeInterval Interval in milliseconds when sanning for a handshake
25
27
  */
26
28
  settings = {
27
29
  devMode: true,
28
30
  mode: 'detect',
29
31
  modeTimeout: 300,
32
+ modeFallback: 'child', // ENUM: 'child' (use iframes), 'popup' (use popup windows)
30
33
  siteUrl: 'https://tera-tools.com/embed',
31
34
  restrictOrigin: '*', // FIXME: Need to restrict this to TERA site
32
35
  frameSandbox: [
@@ -39,8 +42,9 @@ export default class TeraFy {
39
42
  'allow-presentation',
40
43
  'allow-same-origin',
41
44
  'allow-scripts',
42
- 'allow-top-navigation'
45
+ 'allow-top-navigation',
43
46
  ],
47
+ handshakeInterval: 1000,
44
48
  };
45
49
 
46
50
 
@@ -56,12 +60,14 @@ export default class TeraFy {
56
60
  *
57
61
  * @type {Object}
58
62
  * @property {DOMElement} el The main tera-fy div wrapper
59
- * @property {DOMElement} iframe The internal iFrame element
63
+ * @property {DOMElement} iframe The internal iFrame element (if `settings.mode == 'child'`)
64
+ * @property {Window} popup The popup window context (if `settings.mode == 'popup'`)
60
65
  * @property {DOMElement} stylesheet The corresponding stylesheet
61
66
  */
62
67
  dom = {
63
68
  el: null,
64
69
  iframe: null,
70
+ popup: null,
65
71
  stylesheet: null,
66
72
  };
67
73
 
@@ -87,13 +93,13 @@ export default class TeraFy {
87
93
  // For bindProjectState() - See individual plugins
88
94
 
89
95
  // Project files
90
- 'getProjectFiles',
96
+ 'selectProjectFile', 'getProjectFiles',
91
97
 
92
98
  // Project Libraries
93
- 'getProjectLibrary', 'setProjectLibrary',
99
+ 'selectProjectLibrary', 'parseProjectLibrary', 'setProjectLibrary',
94
100
 
95
101
  // UI
96
- 'uiAlert',
102
+ 'uiAlert', 'uiSplat', 'uiWindow',
97
103
  ];
98
104
 
99
105
 
@@ -149,6 +155,8 @@ export default class TeraFy {
149
155
  window.parent.postMessage(payload, this.settings.restrictOrigin);
150
156
  } else if (this.settings.mode == 'child') {
151
157
  this.dom.iframe.contentWindow.postMessage(payload, this.settings.restrictOrigin);
158
+ } else if (this.settings.mode == 'popup') {
159
+ this.dom.popup.postMessage(payload, this.settings.restrictOrigin);
152
160
  } else if (this.settings.mode == 'detect') {
153
161
  throw new Error('Call init() or detectMode() before trying to send data to determine the mode');
154
162
  } else {
@@ -305,7 +313,8 @@ export default class TeraFy {
305
313
  ]))
306
314
  .then(()=> this.rpc('setServerMode', // Tell server what mode its in
307
315
  this.settings.mode == 'child' ? 'embedded'
308
- : this.settings.mode == 'parent' ? 'window'
316
+ : this.settings.mode == 'parent' ? 'frame'
317
+ : this.settings.mode == 'popup' ? 'popup'
309
318
  : (()=> { throw(`Unknown server mode "${this.settings.mode}"`) })()
310
319
  ))
311
320
  .then(()=> Promise.all( // Init all plugins (with this outer module as the context)
@@ -327,7 +336,7 @@ export default class TeraFy {
327
336
  if (this.settings.mode != 'detect') { // Dev has specified a forced mode to use
328
337
  return this.settings.mode;
329
338
  } else if (window.self === window.parent) { // This frame is already at the top
330
- return 'child';
339
+ return this.settings.modeFallback;
331
340
  } else { // No idea - try messaging
332
341
  return Promise.resolve()
333
342
  .then(()=> this.settings.mode = 'parent') // Switch to parent mode...
@@ -339,7 +348,7 @@ export default class TeraFy {
339
348
  .then(()=> resolve())
340
349
  }))
341
350
  .then(()=> 'parent')
342
- .catch(()=> 'child')
351
+ .catch(()=> this.settings.modeFallback)
343
352
  }
344
353
  }
345
354
 
@@ -373,9 +382,34 @@ export default class TeraFy {
373
382
  this.dom.el.append(this.dom.iframe);
374
383
  break;
375
384
  case 'parent':
376
- this.debug('Using TERA site stack parent');
385
+ this.debug('Using TERA window parent');
377
386
  resolve();
378
387
  break;
388
+ case 'popup':
389
+ this.debug('Injecting TERA site as a popup window');
390
+ this.dom.popup = window.open(this.settings.siteUrl, '_blank', 'popup=1, location=0, menubar=0, status=0, scrollbars=0, width=500, height=600');
391
+
392
+ // Loop until the window context returns a handshake
393
+ (()=> {
394
+ let handshakeCount = 0;
395
+ let handshakeTimer;
396
+
397
+ const tryHandshake = ()=> {
398
+ this.debug('Trying handshake', ++handshakeCount);
399
+
400
+ clearTimeout(handshakeTimer);
401
+ handshakeTimer = setTimeout(tryHandshake, this.settings.handshakeInterval);
402
+
403
+ this.rpc('handshake')
404
+ .then(()=> {
405
+ clearTimeout(handshakeTimer);
406
+ this.debug('Popup window accepted handshake');
407
+ resolve();
408
+ });
409
+ };
410
+ tryHandshake();
411
+ })();
412
+ break;
379
413
  default:
380
414
  throw new Error(`Unsupported mode "${this.settings.mode}" when calling injectComms()`);
381
415
  }
@@ -434,6 +468,7 @@ export default class TeraFy {
434
468
  document.head.appendChild(this.dom.stylesheet);
435
469
  break;
436
470
  case 'parent':
471
+ case 'popup':
437
472
  break;
438
473
  default:
439
474
  throw new Error(`Unsupported mode "${this.settings.mode}" when injectStylesheet()`);
@@ -624,7 +659,11 @@ export default class TeraFy {
624
659
  * This is an pre-requisite step for requireProject()
625
660
  *
626
661
  * @function requireUser
627
- * @returns {Promise} A promise which will resolve if the there is a user and they are logged in
662
+ *
663
+ * @param {Object} [options] Additional options to mutate behaviour
664
+ * @param {Boolean} [options.forceRetry=false] Forcabily try to refresh the user state
665
+ *
666
+ * @returns {Promise<User>} The current logged in user or null if none
628
667
  */
629
668
 
630
669
 
@@ -798,6 +837,35 @@ export default class TeraFy {
798
837
  * @property {String} mime The associated mime type for the file
799
838
  */
800
839
 
840
+ /**
841
+ * Data structure for a file filter
842
+ * @name FileFilters
843
+ *
844
+ * @property {Boolean} [library=false] Restrict to library files only
845
+ * @property {String} [filename] CSV of @momsfriendlydevco/match expressions to filter the filename by (filenames are the basename sans extension)
846
+ * @property {String} [basename] CSV of @momsfriendlydevco/match expressions to filter the basename by
847
+ * @property {String} [ext] CSV of @momsfriendlydevco/match expressions to filter the file extension by
848
+ */
849
+
850
+
851
+ /**
852
+ * Prompt the user to select a library to operate on
853
+ *
854
+ * @function selectProjectFile
855
+ * @param {Object} [options] Additional options to mutate behaviour
856
+ * @param {String} [options.title="Select a file"] The title of the dialog to display
857
+ * @param {String|Array<String>} [options.hint] Hints to identify the file to select in array order of preference
858
+ * @param {FileFilters} [options.filters] Optional file filters
859
+ * @param {Boolean} [options.allowUpload=true] Allow uploading new files
860
+ * @param {Boolean} [options.allowRefresh=true] Allow the user to manually refresh the file list
861
+ * @param {Boolean} [options.allowDownloadZip=true] Allow the user to download a Zip of all files
862
+ * @param {Boolean} [options.allowCancel=true] Allow cancelling the operation. Will throw `'CANCEL'` as the promise rejection if acationed
863
+ * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
864
+ * @param {FileFilters} [options.filter] Optional file filters
865
+ *
866
+ * @returns {Promise<ProjectFile>} The eventually selected file
867
+ */
868
+
801
869
 
802
870
  /**
803
871
  * Fetch the files associated with a given project
@@ -807,25 +875,60 @@ export default class TeraFy {
807
875
  * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
808
876
  * @param {Boolean} [options.meta=true] Pull meta information for each file entity
809
877
  *
810
- * @returns {Promise<ProjectFile>} A collection of project files for the given project
878
+ * @returns {Promise<Array<ProjectFile>>} A collection of project files for the given project
879
+ */
880
+
881
+
882
+ /**
883
+ * Prompt the user to select a library to operate on and return a array of references in a given format
884
+ *
885
+ * @function selectProjectLibrary
886
+ * @param {Object} [options] Additional options to mutate behaviour
887
+ * @param {String} [options.title="Select a citation library"] The title of the dialog to display
888
+ * @param {String|Array<String>} [options.hint] Hints to identify the library to select in array order of preference. Generally corresponds to the previous stage - e.g. 'deduped', 'review1', 'review2', 'dedisputed'
889
+ * @param {Boolean} [options.allowUpload=true] Allow uploading new files
890
+ * @param {Boolean} [options.allowRefresh=true] Allow the user to manually refresh the file list
891
+ * @param {Boolean} [options.allowDownloadZip=true] Allow the user to download a Zip of all files
892
+ * @param {Boolean} [options.allowCancel=true] Allow cancelling the operation. Will throw `'CANCEL'` as the promise rejection if acationed
893
+ * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
894
+ * @param {FileFilters} [options.filters] Optional file filters, defaults to citation library selection only
895
+ * @param {*} [options.*] Additional options - see `parseProjectLibrary()`
896
+ *
897
+ * @returns {Promise<Array<Ref>>} A collection of references from the selected file
811
898
  */
812
899
 
813
900
 
814
901
  /**
815
- * Fetch the active projects citation library
902
+ * Convert a project file into a library of citations
816
903
  *
817
- * @function getProjectLibrary
818
- * @param {String} [path] Optional file path to use, if omitted the contents of `options` are used to guess at a suitable file
904
+ * @function parseProjectLibrary
905
+ * @param {String} path File path to read, if omitted the contents of `options` are used to guess at a suitable file
819
906
  *
820
907
  * @param {Object} [options] Additional options to mutate behaviour
821
908
  * @param {String} [options.format='json'] Format for the file. ENUM: 'pojo' (return a parsed JS collection), 'blob' (raw JS Blob object), 'file' (named JS File object)
822
909
  * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
823
- * @param {Boolean} [options.multiple=false] Allow selection of multiple libraries
824
910
  * @param {Function} [options.filter] Optional async file filter, called each time as `(File:ProjectFile)`
825
911
  * @param {Function} [options.find] Optional async final stage file filter to reduce all candidates down to one subject file
826
- * @param {String|Array<String>} [options.hint] Hints to identify the library to select in array order of preference. Generally corresponds to the previous stage - e.g. 'deduped', 'review1', 'review2', 'dedisputed'
827
912
  *
828
- * @returns {Promise<Array<ProjectFile>>} Collection of references for the selected library matching the given hint + filter, this could be a zero length array
913
+ * @returns {Promise<Array<Ref>>|Promise<*>} A collection of references (default bevahiour) or a whatever format was requested
914
+ */
915
+
916
+
917
+ /**
918
+ * Save back a citation library from some input
919
+ *
920
+ * @function setProjectLibrary
921
+ * @param {String} [path] File path to save back to
922
+ * @param {Array<RefLibRef>} Collection of references for the selected library
923
+ *
924
+ * @param {Object} [options] Additional options to mutate behaviour
925
+ * @param {String} [options.format='json'] Input format used. ENUM: 'pojo' (return a parsed JS collection), 'blob' (raw JS Blob object), 'file' (named JS File object)
926
+ * @param {Boolean} [options.autoRequire=true] Run `requireProject()` automatically before continuing
927
+ * @param {String} [options.hint] Hint to store against the library. Generally corresponds to the current operation being performed - e.g. 'deduped'
928
+ * @param {Boolean} [options.overwrite=true] Allow existing file upsert
929
+ * @param {Object} [options.meta] Optional meta data to merge into the file data
930
+ *
931
+ * @returns {Promise} A promise which resolves when the save operation has completed
829
932
  */
830
933
 
831
934
 
@@ -856,5 +959,33 @@ export default class TeraFy {
856
959
  * @returns {Promise} A promise which resolves when the alert has been dismissed
857
960
  */
858
961
 
962
+
963
+ /**
964
+ * Display HTML content full-screen within TERA
965
+ * This function is ideally called within a requestFocus() wrapper
966
+ *
967
+ * @function uiSplat
968
+ * @param {DOMElement|String|false} content Either a prepared DOM element or string to compile, set to falsy to remove existing content
969
+ *
970
+ * @param {Object} [options] Additional options to mutate behaviour
971
+ * @param {Boolean|String} [options.logo=false] Add a logo to the output, if boolean true the Tera-tools logo is used otherwise specify a path or URL
972
+ */
973
+
974
+
975
+ /**
976
+ * Open a popup window containing a new site
977
+ *
978
+ * @function uiWindow
979
+ * @param {String} url The URL to open
980
+ *
981
+ * @param {Object} [options] Additional options to mutate behaviour
982
+ * @param {Number} [options.width=500] The desired width of the window
983
+ * @param {Number} [options.height=600] The desired height of the window
984
+ * @param {Boolean} [options.center=true] Attempt to center the window on the screen
985
+ * @param {Object} [options.permissions] Additional permissions to set on opening, defaults to a suitable set of permission for popups (see code)
986
+ *
987
+ * @returns {WindowProxy} The opened window object (if `noopener` is not set in permissions)
988
+ */
989
+
859
990
  // }}}
860
991
  }