From cb875b19137c343ac4f4811d938ffc717ebd7c9e Mon Sep 17 00:00:00 2001 From: Bruno Alves de Faria Date: Tue, 12 Jul 2016 15:37:53 -0300 Subject: [PATCH] LT-253: Creating dynamic form components --- LesionTracker/.meteor/packages | 2 + LesionTracker/.meteor/versions | 1 + OHIFViewer/client/components/viewer/viewer.js | 13 +- .../components/radioOption/radioOption.styl | 2 +- .../matchedProtocols/matchedProtocols.js | 2 + .../hangingprotocols/client/protocolEngine.js | 6 +- ...tionalFinding.js => additionalFindings.js} | 4 +- .../additionalFindings.html | 7 + .../additionalFindings/additionalFindings.js | 6 +- .../additionalFindings.styl | 11 +- .../toolbarSection/toolbarSection.js | 2 + .../toolbarSectionButton.js | 2 + .../client/components/viewer/viewer.js | 7 +- Packages/lesiontracker/lib/activateLesion.js | 2 + Packages/lesiontracker/package.js | 2 +- .../ohif-core/components/base/component.js | 49 ++++++ Packages/ohif-core/components/base/index.js | 26 +++ Packages/ohif-core/components/base/mixin.js | 126 ++++++++++++++ .../components/base/mixins/component.js | 16 ++ .../ohif-core/components/base/mixins/form.js | 50 ++++++ .../components/base/mixins/formItem.js | 154 ++++++++++++++++++ .../ohif-core/components/base/mixins/group.js | 50 ++++++ .../components/base/mixins/groupRadio.js | 30 ++++ .../ohif-core/components/base/mixins/input.js | 18 ++ .../components/base/mixins/schemaData.js | 46 ++++++ .../components/base/mixins/select.js | 18 ++ .../components/base/mixins/select2.js | 18 ++ .../ohif-core/components/base/template.js | 106 ++++++++++++ .../components/base/templates/custom.html | 3 + .../components/base/templates/div.html | 9 + .../components/base/templates/form.html | 16 ++ .../components/base/templates/input.html | 11 ++ .../components/base/templates/select.html | 16 ++ .../components/base/wrappers/label.html | 11 ++ .../components/base/wrappers/title.html | 11 ++ .../components/bootstrap/form/form.html | 8 + .../components/bootstrap/form/group.html | 8 + .../ohif-core/components/bootstrap/index.js | 7 + .../bootstrap/input/groupRadio.html | 20 +++ .../components/bootstrap/input/radio.html | 14 ++ .../components/bootstrap/input/select.html | 13 ++ .../components/bootstrap/input/text.html | 13 ++ Packages/ohif-core/components/index.js | 2 + Packages/ohif-core/helpers/blaze.js | 26 +++ Packages/ohif-core/helpers/data.js | 59 +++++++ Packages/ohif-core/helpers/index.js | 4 + .../lib => ohif-core}/helpers/logical.js | 12 ++ Packages/ohif-core/helpers/string.js | 16 ++ Packages/ohif-core/lib/blaze.js | 42 +++++ Packages/ohif-core/lib/index.js | 1 + Packages/ohif-core/main.js | 6 + Packages/ohif-core/ohif.js | 11 ++ Packages/ohif-core/package.js | 15 ++ .../ui/draggable/draggable.js | 0 Packages/ohif-core/ui/index.js | 2 + .../ui/resizable/resizable.js | 0 .../ui/resizable/resizable.styl | 0 .../components/selectInput/selectInput.html | 4 +- .../client/helpers/equals.js | 10 -- Packages/reactive-form-controls/package.js | 69 ++++---- .../viewer/cineDialog/cineDialog.js | 2 + .../viewer/gridLayout/gridLayout.js | 2 + .../imageViewerViewport.js | 8 +- .../loadingIndicator/loadingIndicator.js | 4 +- .../viewer/playClipButton/playClipButton.js | 2 + .../components/viewer/toolbar/toolbar.js | 2 + Packages/viewerbase/lib/WLPresets.js | 2 + .../viewerbase/lib/displayReferenceLines.js | 6 +- Packages/viewerbase/lib/enableHotkeys.js | 6 +- Packages/viewerbase/lib/helpers/blaze.js | 38 ----- Packages/viewerbase/lib/toolManager.js | 2 + Packages/viewerbase/package.js | 6 - 72 files changed, 1169 insertions(+), 126 deletions(-) rename Packages/lesiontracker/both/schema/{additionalFinding.js => additionalFindings.js} (95%) create mode 100644 Packages/ohif-core/components/base/component.js create mode 100644 Packages/ohif-core/components/base/index.js create mode 100644 Packages/ohif-core/components/base/mixin.js create mode 100644 Packages/ohif-core/components/base/mixins/component.js create mode 100644 Packages/ohif-core/components/base/mixins/form.js create mode 100644 Packages/ohif-core/components/base/mixins/formItem.js create mode 100644 Packages/ohif-core/components/base/mixins/group.js create mode 100644 Packages/ohif-core/components/base/mixins/groupRadio.js create mode 100644 Packages/ohif-core/components/base/mixins/input.js create mode 100644 Packages/ohif-core/components/base/mixins/schemaData.js create mode 100644 Packages/ohif-core/components/base/mixins/select.js create mode 100644 Packages/ohif-core/components/base/mixins/select2.js create mode 100644 Packages/ohif-core/components/base/template.js create mode 100644 Packages/ohif-core/components/base/templates/custom.html create mode 100644 Packages/ohif-core/components/base/templates/div.html create mode 100644 Packages/ohif-core/components/base/templates/form.html create mode 100644 Packages/ohif-core/components/base/templates/input.html create mode 100644 Packages/ohif-core/components/base/templates/select.html create mode 100644 Packages/ohif-core/components/base/wrappers/label.html create mode 100644 Packages/ohif-core/components/base/wrappers/title.html create mode 100644 Packages/ohif-core/components/bootstrap/form/form.html create mode 100644 Packages/ohif-core/components/bootstrap/form/group.html create mode 100644 Packages/ohif-core/components/bootstrap/index.js create mode 100644 Packages/ohif-core/components/bootstrap/input/groupRadio.html create mode 100644 Packages/ohif-core/components/bootstrap/input/radio.html create mode 100644 Packages/ohif-core/components/bootstrap/input/select.html create mode 100644 Packages/ohif-core/components/bootstrap/input/text.html create mode 100644 Packages/ohif-core/components/index.js create mode 100644 Packages/ohif-core/helpers/blaze.js create mode 100644 Packages/ohif-core/helpers/data.js create mode 100644 Packages/ohif-core/helpers/index.js rename Packages/{viewerbase/lib => ohif-core}/helpers/logical.js (84%) create mode 100644 Packages/ohif-core/helpers/string.js create mode 100644 Packages/ohif-core/lib/blaze.js create mode 100644 Packages/ohif-core/lib/index.js create mode 100644 Packages/ohif-core/main.js create mode 100644 Packages/ohif-core/ohif.js create mode 100644 Packages/ohif-core/package.js rename Packages/{viewerbase/lib => ohif-core}/ui/draggable/draggable.js (100%) create mode 100644 Packages/ohif-core/ui/index.js rename Packages/{viewerbase/lib => ohif-core}/ui/resizable/resizable.js (100%) rename Packages/{viewerbase/lib => ohif-core}/ui/resizable/resizable.styl (100%) delete mode 100644 Packages/reactive-form-controls/client/helpers/equals.js delete mode 100644 Packages/viewerbase/lib/helpers/blaze.js diff --git a/LesionTracker/.meteor/packages b/LesionTracker/.meteor/packages index e9d5f691a..b351149bd 100755 --- a/LesionTracker/.meteor/packages +++ b/LesionTracker/.meteor/packages @@ -15,6 +15,8 @@ tracker # Meteor's client-side reactive programming library es5-shim # ECMAScript 5 compatibility for older browsers. ecmascript # Enable ECMAScript2015+ syntax in app code +ohif:core + insecure # Allow all DB writes from clients (for prototyping) cornerstone worklist diff --git a/LesionTracker/.meteor/versions b/LesionTracker/.meteor/versions index 3045839db..45543dd44 100644 --- a/LesionTracker/.meteor/versions +++ b/LesionTracker/.meteor/versions @@ -90,6 +90,7 @@ natestrauser:select2@4.0.2 npm-bcrypt@0.8.6_3 npm-mongo@1.4.45 observe-sequence@1.0.12 +ohif:core@0.0.1 ordered-dict@1.0.8 orthanc-remote@0.0.1 peppelg:bootstrap-3-modal@1.0.4 diff --git a/OHIFViewer/client/components/viewer/viewer.js b/OHIFViewer/client/components/viewer/viewer.js index 0d908e237..fdcd2baee 100644 --- a/OHIFViewer/client/components/viewer/viewer.js +++ b/OHIFViewer/client/components/viewer/viewer.js @@ -1,3 +1,6 @@ +import { OHIF } from 'meteor/ohif:core'; +OHIF.viewer = OHIF.viewer || {}; + Template.viewer.onCreated(function() { // Attach the Window resize listener $(window).on('resize', handleResize); @@ -7,10 +10,6 @@ Template.viewer.onCreated(function() { log.info('viewer onCreated'); - OHIF = window.OHIF || { - viewer: {} - }; - OHIF.viewer.loadIndicatorDelay = 500; OHIF.viewer.defaultTool = 'wwwc'; OHIF.viewer.refLinesEnabled = true; @@ -57,7 +56,7 @@ Template.viewer.onCreated(function() { } var contentId = this.data.contentId; - + if (ViewerData[contentId].loadedSeriesData) { log.info('Reloading previous loadedSeriesData'); @@ -66,7 +65,7 @@ Template.viewer.onCreated(function() { } else { log.info('Setting default ViewerData'); OHIF.viewer.loadedSeriesData = {}; - + ViewerData[contentId].loadedSeriesData = OHIF.viewer.loadedSeriesData; // Update the viewer data object @@ -80,7 +79,7 @@ Template.viewer.onCreated(function() { // Update the ViewerStudies collection with the loaded studies ViewerStudies.remove({}); - + this.data.studies.forEach(function(study) { study.selected = true; ViewerStudies.insert(study); diff --git a/Packages/design/components/radioOption/radioOption.styl b/Packages/design/components/radioOption/radioOption.styl index 65b92154c..f827e8984 100644 --- a/Packages/design/components/radioOption/radioOption.styl +++ b/Packages/design/components/radioOption/radioOption.styl @@ -6,7 +6,7 @@ label.radio-option height: 0 width: 0 span - padding-left: 20px + padding-left: 23px position: relative &:before background: white diff --git a/Packages/hangingprotocols/client/components/matchedProtocols/matchedProtocols.js b/Packages/hangingprotocols/client/components/matchedProtocols/matchedProtocols.js index 9be083fe3..00ab87993 100644 --- a/Packages/hangingprotocols/client/components/matchedProtocols/matchedProtocols.js +++ b/Packages/hangingprotocols/client/components/matchedProtocols/matchedProtocols.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + Template.matchedProtocols.onRendered(function() { $('#matchedProtocols button').tooltip(OHIF.viewer.tooltipConfig); }); diff --git a/Packages/hangingprotocols/client/protocolEngine.js b/Packages/hangingprotocols/client/protocolEngine.js index 2336fccd6..e510c0c1f 100644 --- a/Packages/hangingprotocols/client/protocolEngine.js +++ b/Packages/hangingprotocols/client/protocolEngine.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + // Define a global variable that will be used to refer to the Protocol Engine // It must be populated by HP.setEngine when the Viewer is initialized and a ProtocolEngine // is instantiated on top of the LayoutManager. If the global ProtocolEngine variable remains @@ -214,7 +216,7 @@ HP.ProtocolEngine = class ProtocolEngine { score: matchedDetails.score, protocol: protocol }); - } + } }); if (!matched.length) { @@ -300,7 +302,7 @@ HP.ProtocolEngine = class ProtocolEngine { if (!protocol.protocolMatchingRules) { return; } - + var studies = WorklistStudies.find({ patientId: study.patientId, studyDate: { diff --git a/Packages/lesiontracker/both/schema/additionalFinding.js b/Packages/lesiontracker/both/schema/additionalFindings.js similarity index 95% rename from Packages/lesiontracker/both/schema/additionalFinding.js rename to Packages/lesiontracker/both/schema/additionalFindings.js index 3175b4d63..34e261d26 100644 --- a/Packages/lesiontracker/both/schema/additionalFinding.js +++ b/Packages/lesiontracker/both/schema/additionalFindings.js @@ -4,10 +4,10 @@ SimpleSchema.extendOptions({ allowedSelect2Values: Match.Optional(Array), }); -export const AdditionalFinding = new SimpleSchema({ +export const schema = new SimpleSchema({ measurableDisease: { type: String, - label: 'Measurable Disease', + label: 'Measurable disease', allowedValues: ['Present', 'Absent'], defaultValue: 'Absent', optional: true diff --git a/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.html b/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.html index 052a1b958..b30d2a762 100644 --- a/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.html +++ b/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.html @@ -7,6 +7,13 @@ {{>radioOptionGroup (stateDataWithKey "measurableDisease")}} + + +
Supplementary Measurements diff --git a/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.js b/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.js index 1d7d44020..1adc9a7e6 100644 --- a/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.js +++ b/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.js @@ -1,14 +1,12 @@ import { Template } from 'meteor/templating'; import { ReactiveDict } from 'meteor/reactive-dict'; - -// TODO: Check why I can't use absolute paths for this? Keeps saying module not found -import { AdditionalFinding } from '../../../both/schema/additionalFinding'; +import { schema as AdditionalFindingsSchema } from 'meteor/lesiontracker/both/schema/additionalFindings'; Template.additionalFindings.onCreated(function additionalFindingsOnCreated() { console.log('additionalFindingsOnCreated'); const instance = Template.instance(); - instance.currentSchema = AdditionalFinding; + instance.currentSchema = AdditionalFindingsSchema; instance.state = new ReactiveDict(); if (!instance.data.currentTimepointId) { diff --git a/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.styl b/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.styl index bab23263f..d2c0b3954 100644 --- a/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.styl +++ b/Packages/lesiontracker/client/components/additionalFindings/additionalFindings.styl @@ -25,21 +25,22 @@ .header height: 33px line-height: 33px - font-weight: 400 + font-weight: bold font-size: 14px - padding: 0 10px + padding: 1px 13px 0 color: $textPrimaryColor - background-color: $uiGrayDark + background-color: $uiGray .form-group - padding: 10px + padding: 7px 11px color: $textPrimaryColor &:not(:last-child) border-bottom: $uiBorderThickness solid $uiBorderColorDark h5 - font-weight: 300 + font-weight: bold + margin-bottom: 8px .control-label font-size: 14px diff --git a/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.js b/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.js index 32190286f..3b4d0bc21 100644 --- a/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.js +++ b/Packages/lesiontracker/client/components/toolbarSection/toolbarSection.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + Template.toolbarSection.helpers({ // Returns true if the view shall be split in two viewports splitView() { diff --git a/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.js b/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.js index 483dddb9a..169390638 100644 --- a/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.js +++ b/Packages/lesiontracker/client/components/toolbarSectionButton/toolbarSectionButton.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + Template.toolbarSectionButton.helpers({ activeClass() { const instance = Template.instance(); diff --git a/Packages/lesiontracker/client/components/viewer/viewer.js b/Packages/lesiontracker/client/components/viewer/viewer.js index f6c030748..0b2c53e64 100644 --- a/Packages/lesiontracker/client/components/viewer/viewer.js +++ b/Packages/lesiontracker/client/components/viewer/viewer.js @@ -1,5 +1,8 @@ +import { OHIF } from 'meteor/ohif:core'; import { TimepointApi } from 'meteor/lesiontracker/lib/api/timepoint'; +OHIF.viewer = OHIF.viewer || {}; + Session.setDefault('activeViewport', false); Session.setDefault('leftSidebar', null); Session.setDefault('rightSidebar', null); @@ -22,10 +25,6 @@ Template.viewer.onCreated(() => { var contentId = instance.data.contentId; - OHIF = OHIF || window.OHIF || { - viewer: {} - }; - OHIF.viewer.loadIndicatorDelay = 3000; OHIF.viewer.defaultTool = 'wwwc'; OHIF.viewer.refLinesEnabled = true; diff --git a/Packages/lesiontracker/lib/activateLesion.js b/Packages/lesiontracker/lib/activateLesion.js index ae9e00399..f50d0df2b 100644 --- a/Packages/lesiontracker/lib/activateLesion.js +++ b/Packages/lesiontracker/lib/activateLesion.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + /** * Activates a set of lesions when lesion table row is clicked * diff --git a/Packages/lesiontracker/package.js b/Packages/lesiontracker/package.js index 9bce6156f..d16f693c9 100644 --- a/Packages/lesiontracker/package.js +++ b/Packages/lesiontracker/package.js @@ -35,7 +35,7 @@ Package.onUse(function(api) { api.addFiles('log.js', [ 'client', 'server' ]); // Schema - api.addFiles('both/schema/additionalFinding.js', [ 'client', 'server' ]); + api.addFiles('both/schema/additionalFindings.js', [ 'client', 'server' ]); // Client-side collections api.addFiles('client/collections/LesionLocations.js', 'client'); diff --git a/Packages/ohif-core/components/base/component.js b/Packages/ohif-core/components/base/component.js new file mode 100644 index 000000000..7a81769d9 --- /dev/null +++ b/Packages/ohif-core/components/base/component.js @@ -0,0 +1,49 @@ +import { OHIF } from 'meteor/ohif:core'; + +/* + * Base component to template instances of all dynamic components + */ +class Component { + + // Set up the component + constructor(templateInstance) { + // Store the component in the current view + templateInstance.view._component = this; + + // Create an object to register section's content + templateInstance.sections = {}; + + // Store the template instance in the component + this.templateInstance = templateInstance; + + // Store the component's registered sub-components + this.registeredItems = new Set(); + } + + // Self register the component in its first parent component + registerSelf() { + const parent = OHIF.blaze.getParentComponent(this.templateInstance.view); + if (parent) { + // Store this component's parent in a property + this.parent = parent; + + // Add this component in its parent's registered items list + parent.registeredItems.add(this); + } + } + + // Self unregister the component in its first parent component + unregisterSelf() { + const parent = OHIF.blaze.getParentComponent(this.templateInstance.view); + if (parent) { + // Remove the parent property from this component + delete this.parent; + + // Remove this component from its parent's registered items list + parent.registeredItems.delete(this); + } + } + +} + +OHIF.Component = Component; diff --git a/Packages/ohif-core/components/base/index.js b/Packages/ohif-core/components/base/index.js new file mode 100644 index 000000000..78f354fdc --- /dev/null +++ b/Packages/ohif-core/components/base/index.js @@ -0,0 +1,26 @@ +// Core files +import './component.js'; +import './mixin.js'; +import './template.js'; + +// Mixins +import './mixins/component.js'; +import './mixins/form.js'; +import './mixins/formItem.js'; +import './mixins/group.js'; +import './mixins/groupRadio.js'; +import './mixins/input.js'; +import './mixins/schemaData.js'; +import './mixins/select.js'; +import './mixins/select2.js'; + +// Templates +import './templates/custom.html'; +import './templates/div.html'; +import './templates/form.html'; +import './templates/input.html'; +import './templates/select.html'; + +// wrappers +import './wrappers/label.html'; +import './wrappers/title.html'; diff --git a/Packages/ohif-core/components/base/mixin.js b/Packages/ohif-core/components/base/mixin.js new file mode 100644 index 000000000..eee820828 --- /dev/null +++ b/Packages/ohif-core/components/base/mixin.js @@ -0,0 +1,126 @@ +import { OHIF } from 'meteor/ohif:core'; +import { _ } from 'meteor/underscore'; + +// Create an object to store all the application mixins +OHIF.mixins = {}; + +// Class to manage new mixins and its dependencies +class Mixin { + + // Create the mixin instance + constructor({ dependencies, composition }) { + // Store the mixin dependencies + this.dependencies = dependencies || ''; + + // Store the mixin composition + this.composition = composition; + } + + // Initialize the mixin applying all its composition functions + init(template, data, applied, behaviors) { + const dependenciesArray = this.dependencies.split(' '); + _.each(dependenciesArray, dependency => { + // Go to next dependency if the current dependency string is blank + if (!dependency) { + return; + } + + // Get the dependent mixin to be initizalized + const mixin = Mixin.getMixin(dependency); + + // Initizalize the mixin dependencies recursively + mixin.init(template, data, applied, behaviors); + }); + + // Apply the mixin's composition behaviors to the template + this.apply(template, data, applied, behaviors); + } + + // Add the mixin's composition behaviors to the template + apply(template, data, applied, behaviors) { + // Ignore if the mixin was already applied to the template + if (_.contains(applied, this)) { + return; + } + + // Store the mixin's composition + const composition = this.composition; + + // Iterate over each behavior + _.each(behaviors, behavior => { + // Execute something only after all the mixins are done + let functionName = behavior; + if (functionName === 'onMixins') { + functionName = 'onRendered'; + } + + if (behavior === 'onData' && composition[behavior]) { + // If it's just data manipulation, call it immediately + composition[behavior](data); + } else if (composition[behavior]) { + // Register the behavior in the template + template[functionName](composition[behavior]); + } + }); + + // Set the current mixin's state as applied + applied.push(this); + } + + // Initialize all data manipulation mixins + static initData(data) { + // Split the mixins by space + const mixinsArray = data.mixins.split(' '); + _.each(mixinsArray, mixinName => { + // Ignore blank strings + if (!mixinName) { + return; + } + + // Get the current mixin + const mixin = Mixin.getMixin(mixinName); + + // Initialize the data manipulation composition + mixin.init(null, data, [], ['onData']); + }); + } + + // Initialize all the template's mixins + static initAll(template, data) { + // Split the mixins by space + const mixinsArray = data.mixins.split(' '); + _.each(mixinsArray, mixinName => { + // Ignore blank strings + if (!mixinName) { + return; + } + + // Get the current mixin + const mixin = Mixin.getMixin(mixinName); + + // Initialize blaze default compositions + mixin.init(template, data, [], ['onCreated', 'onRendered', 'onDestroyed', 'events', 'helpers']); + + // Execute some behaviors after all mixins are applied + mixin.init(template, data, [], ['onMixins']); + }); + } + + // Get a mixin by name + static getMixin(mixinName) { + // Get the mixin from mixins object + const mixin = OHIF.mixins[mixinName]; + + // Throw an error if the mixin does not exists + if (!mixin) { + throw new Error(`Mixin ${mixinName} not found.`); + } + + // Return the found mixin + return mixin; + } + +} + +// Store the Mixin class inside the shared OHIF object +OHIF.Mixin = Mixin; diff --git a/Packages/ohif-core/components/base/mixins/component.js b/Packages/ohif-core/components/base/mixins/component.js new file mode 100644 index 000000000..4b5b48f9b --- /dev/null +++ b/Packages/ohif-core/components/base/mixins/component.js @@ -0,0 +1,16 @@ +import { OHIF } from 'meteor/ohif:core'; +import { Template } from 'meteor/templating'; + +/* + * component: create the base structure to aplly specific component mixins + */ +OHIF.mixins.component = new OHIF.Mixin({ + composition: { + onCreated() { + const instance = Template.instance(); + + // Declare a property that will be shared among all dependent mixins + instance.component = new OHIF.Component(this); + } + } +}); diff --git a/Packages/ohif-core/components/base/mixins/form.js b/Packages/ohif-core/components/base/mixins/form.js new file mode 100644 index 000000000..f03bcb7f7 --- /dev/null +++ b/Packages/ohif-core/components/base/mixins/form.js @@ -0,0 +1,50 @@ +import { OHIF } from 'meteor/ohif:core'; +import { Template } from 'meteor/templating'; + +/* + * form: controls a form and its registered inputs + */ +OHIF.mixins.form = new OHIF.Mixin({ + dependencies: 'group', + composition: { + onCreated() { + const instance = Template.instance(); + const component = instance.component; + + // Define the form's data schema + const schema = instance.data.schema; + component.schema = schema && schema.newContext(); + + // Check if the form data is valid in its schema + component.validate = () => { + // Assume validation result as true + let result = true; + + // Return true if there's no data schema defined + if (!component.schema) { + return result; + } + + // Iterate over each registered form item and validate it + component.registeredItems.forEach(child => { + const key = child.templateInstance.data.key; + + // Change result to false if any form item is invalid + if (key && !child.validate()) { + result = false; + } + }); + + // Return the validation result + return result; + }; + }, + onRendered() { + const instance = Template.instance(); + const component = instance.component; + + // Set the component main and style elements + component.$style = component.$element = instance.$('form').first(); + } + } +}); diff --git a/Packages/ohif-core/components/base/mixins/formItem.js b/Packages/ohif-core/components/base/mixins/formItem.js new file mode 100644 index 000000000..95955701d --- /dev/null +++ b/Packages/ohif-core/components/base/mixins/formItem.js @@ -0,0 +1,154 @@ +import { OHIF } from 'meteor/ohif:core'; +import { Template } from 'meteor/templating'; +import { _ } from 'meteor/underscore'; +import { $ } from 'meteor/jquery'; + +/* + * formItem: create a generic controller for form items + * It may be used to manage all components that belong to forms + */ +OHIF.mixins.formItem = new OHIF.Mixin({ + dependencies: 'component', + composition: { + + onCreated() { + const instance = Template.instance(); + const component = instance.component; + + // Register the component in the parent component + component.registerSelf(); + + // Declare the component elements that will be manipulated + component.$element = $(); + component.$style = $(); + component.$wrapper = $(); + + // Get or set the component's value using jQuery's val method + component.value = value => { + const isGet = _.isUndefined(value); + if (isGet) { + return component.$element.val(); + } + + component.$element.val(value).trigger('change'); + }; + + // Disable or enable the component + component.disable = isDisable => { + component.$element.prop('disabled', !!isDisable); + }; + + // Set or unset component's readonly property + component.readonly = isReadonly => { + component.$element.prop('readonly', !!isReadonly); + }; + + // Show or hide the component + component.show = isShow => { + const method = isShow ? 'show' : 'hide'; + component.$wrapper[method](); + }; + + // Add or remove a state from the component + component.state = (state, flag) => { + component.$wrapper.toggleClass(`state-${state}`, !!flag); + }; + + // Set the component in error state and display the error message + component.error = errorMessage => { + // Set the component error state + component.state('error', errorMessage); + + // Set or remove the error message + if (errorMessage) { + component.$element.trigger('errorin'); + component.$wrapper.attr('data-error', errorMessage); + } else { + component.$element.trigger('errorout'); + component.$wrapper.removeAttr('data-error', errorMessage); + } + }; + + // Check if the component value is valid in its form's schema + component.validate = () => { + // Get the component's form + const form = component.parent; + + // Get the form's data schema + const schema = form && form.schema; + + // Get the current component's key + const key = instance.data.key; + + // Return true if validation is not needed + if (!key || !schema || !component.$wrapper.is(':visible')) { + return true; + } + + // Create the data document for validation + const document = { + [key]: component.value() + }; + + // Check if the document validation failed + if (!schema.validateOne(document, key)) { + // Set the component in error state and display the message + component.error(schema.keyErrorMessage(key)); + + // Return false for validation + return false; + } + + // Remove the component error state and message + component.error(false); + + // Return true for validation + return true; + }; + + }, + + onRendered() { + const instance = Template.instance(); + const component = instance.component; + + // Set the element to be controlled + component.$element = instance.$(':input:first'); + + // Set the element to be styled + component.$style = component.$element; + + // Set the most outer wrapper element + component.$wrapper = instance.wrapper.$('*').first(); + }, + + onDestroyed() { + const instance = Template.instance(); + + // Register the component in the parent component + instance.component.unregisterSelf(); + }, + + onMixins() { + const instance = Template.instance(); + const component = instance.component; + + // Set the component in jQuery data after all mixins are rendered + component.$element.data('component', component); + }, + + events: { + + // TODO: [design] remove log, show error box/hint over the wrapper + errorin(event, instance) { + console.log('ERROR when validating component', instance.component); + }, + + // TODO: [design] hide error box/hint + errorout(event, instance) { + } + + } + + } +}); diff --git a/Packages/ohif-core/components/base/mixins/group.js b/Packages/ohif-core/components/base/mixins/group.js new file mode 100644 index 000000000..b27e67399 --- /dev/null +++ b/Packages/ohif-core/components/base/mixins/group.js @@ -0,0 +1,50 @@ +import { OHIF } from 'meteor/ohif:core'; +import { Template } from 'meteor/templating'; +import { _ } from 'meteor/underscore'; + +/* + * group: controls a group and its registered items + */ +OHIF.mixins.group = new OHIF.Mixin({ + dependencies: 'formItem', + composition: { + onCreated() { + const instance = Template.instance(); + const component = instance.component; + + // Get or set the child components values + component.value = value => { + const isGet = _.isUndefined(value); + if (isGet) { + const result = {}; + component.registeredItems.forEach(child => { + const key = child.templateInstance.data.key; + if (key) { + result[key] = child.value(); + } + }); + return result; + } + + const groupValue = typeof value === 'object' ? value : {}; + component.registeredItems.forEach(child => { + const key = child.templateInstance.data.key; + const childValue = _.isUndefined(groupValue[key]) ? null : groupValue[key]; + child.value(childValue); + }); + component.$element.trigger('change'); + }; + + // Disable or enable the component + component.disable = isDisable => { + component.registeredItems.forEach(child => child.disable(isDisable)); + }; + + // Set or unset component's readonly property + component.readonly = isReadonly => { + component.registeredItems.forEach(child => child.readonly(isReadonly)); + }; + + } + } +}); diff --git a/Packages/ohif-core/components/base/mixins/groupRadio.js b/Packages/ohif-core/components/base/mixins/groupRadio.js new file mode 100644 index 000000000..b760b3800 --- /dev/null +++ b/Packages/ohif-core/components/base/mixins/groupRadio.js @@ -0,0 +1,30 @@ +import { OHIF } from 'meteor/ohif:core'; +import { Template } from 'meteor/templating'; +import { _ } from 'meteor/underscore'; + +/* + * groupRadio: controls all the radio inputs inside the group + */ +OHIF.mixins.groupRadio = new OHIF.Mixin({ + dependencies: 'group', + composition: { + + onCreated() { + const instance = Template.instance(); + const component = instance.component; + + // Get the selected radio's value or select a radio based on value + component.value = value => { + const isGet = _.isUndefined(value); + const $elements = $(); + component.registeredItems.forEach(child => $elements.add(child.$element)); + if (isGet) { + return $elements.filter(':checked').val(); + } + + $elements.filter(`[value='${value}']`).prop('checked', true).trigger('change'); + }; + } + + } +}); diff --git a/Packages/ohif-core/components/base/mixins/input.js b/Packages/ohif-core/components/base/mixins/input.js new file mode 100644 index 000000000..5775c9d6c --- /dev/null +++ b/Packages/ohif-core/components/base/mixins/input.js @@ -0,0 +1,18 @@ +import { OHIF } from 'meteor/ohif:core'; +import { Template } from 'meteor/templating'; + +/* + * input: controls a basic input + */ +OHIF.mixins.input = new OHIF.Mixin({ + dependencies: 'formItem', + composition: { + onRendered() { + const instance = Template.instance(); + const component = instance.component; + + // Set the element to be controlled + component.$element = instance.$('input:first'); + } + } +}); diff --git a/Packages/ohif-core/components/base/mixins/schemaData.js b/Packages/ohif-core/components/base/mixins/schemaData.js new file mode 100644 index 000000000..1ac30d301 --- /dev/null +++ b/Packages/ohif-core/components/base/mixins/schemaData.js @@ -0,0 +1,46 @@ +import { OHIF } from 'meteor/ohif:core'; +import { Blaze } from 'meteor/blaze'; +import { Template } from 'meteor/templating'; +import { _ } from 'meteor/underscore'; + +/* + * schemaData: change the component data based on its form's schema data + */ +OHIF.mixins.schemaData = new OHIF.Mixin({ + dependencies: 'formItem', + composition: { + + onData() { + const data = Template.currentData(); + + const parent = OHIF.blaze.getParentComponent(Blaze.currentView); + + // Get the parent component schema + const schema = parent && parent.schema; + + // Get the current component's key + const key = data.key; + + // Stop here if there's no key or schema defined + if (!key || !schema) { + return; + } + + // Get the current schema data using component's key + const currentSchema = schema._schema[key]; + + // Stop here if there's no schema data for current key + if (!currentSchema) { + return; + } + + // Use schema's label if it was not defined + if (!data.label) { + data.label = new ReactiveVar(currentSchema.label); + } + + // TODO: [design] convert allowedValues to items and find a way to get key/value pairs + } + + } +}); diff --git a/Packages/ohif-core/components/base/mixins/select.js b/Packages/ohif-core/components/base/mixins/select.js new file mode 100644 index 000000000..17fe6d95f --- /dev/null +++ b/Packages/ohif-core/components/base/mixins/select.js @@ -0,0 +1,18 @@ +import { OHIF } from 'meteor/ohif:core'; +import { Template } from 'meteor/templating'; + +/* + * input: controls a basic select + */ +OHIF.mixins.select = new OHIF.Mixin({ + dependencies: 'formItem', + composition: { + onRendered() { + const instance = Template.instance(); + const component = instance.component; + + // Set the element to be controlled + component.$element = instance.$('select:first'); + } + } +}); diff --git a/Packages/ohif-core/components/base/mixins/select2.js b/Packages/ohif-core/components/base/mixins/select2.js new file mode 100644 index 000000000..298f6ae50 --- /dev/null +++ b/Packages/ohif-core/components/base/mixins/select2.js @@ -0,0 +1,18 @@ +import { OHIF } from 'meteor/ohif:core'; +import { Template } from 'meteor/templating'; + +/* + * input: controls a select2 component + */ +OHIF.mixins.select2 = new OHIF.Mixin({ + dependencies: 'select', + composition: { + onRendered() { + const instance = Template.instance(); + const component = instance.component; + + // Set the element to be controlled + component.$element = instance.$('select:first'); + } + } +}); diff --git a/Packages/ohif-core/components/base/template.js b/Packages/ohif-core/components/base/template.js new file mode 100644 index 000000000..08ce69e4b --- /dev/null +++ b/Packages/ohif-core/components/base/template.js @@ -0,0 +1,106 @@ +import { OHIF } from 'meteor/ohif:core'; +import { _ } from 'meteor/underscore'; + +// Create a new custom template for the base component +Template.baseComponent = new Template('baseComponent', () => {}); + +// Inject some custom behaviors in the view's construction function +Template.baseComponent.constructView = function(contentFunc, elseFunc) { + // Get the data passed to the template + const data = Template.currentData(); + + // Check the base template. If it's not informed set as the custom base + data.base || (data.base = 'baseCustom'); + + // Get the base template object + const baseTemplate = Template[data.base]; + + // Throw an error if the base template does not exists + if (!baseTemplate) { + throw new Error(`Template ${data.base} not found.`); + } + + // Declare the template object and name it as base name + 'Component' + const template = OHIF.blaze.cloneTemplate(baseTemplate, data.base + 'Component'); + + // Extract the render function from the base template + template.renderFunction = baseTemplate.renderFunction; + + // Check for the mixins. If it's not informed set the lowest level mixin + const mixins = data.mixins || 'component'; + + // Init the data manipulation mixins + OHIF.Mixin.initData(data); + + // Create and fill a list of wrappers that will enclose the component + const wrappers = []; + if (data.wrappers) { + const wrappersList = data.wrappers.split(' '); + _.each(wrappersList, wrapper => wrapper && wrappers.push(wrapper)); + } + + // Declare a variable to store the wrapper instances that will be rendered + const wrapperInstances = []; + + // Declare the content function to render the component + let contentFunction = () => { + // Create the most inner content function + const innerContentFunction = () => { + // Assign properties to all wrappers after template's creation + template.onCreated(() => { + const instance = Template.instance(); + + // Assign the template most outer wrapper + instance.wrapper = wrapperInstances[0] || instance; + + // Iterate over all wrappers and assign the component to them + wrapperInstances.forEach(wrappeInstance => { + wrappeInstance.component = instance.component; + }); + }); + + // Return the view instance + return template.constructView(contentFunc, elseFunc); + }; + + // Apply the mixins to the component + OHIF.Mixin.initAll(template, data); + + // Return the recursive function for wrappers + return Blaze.With(data, innerContentFunction); + }; + + let wrapper; + while (wrapper = wrappers.shift()) { + // Get the wrapper template + const wrapperTemplate = Template[wrapper]; + + // Throw an error if the wrapper template does not exists + if (!wrapperTemplate) { + throw new Error(`Template ${data.base} not found.`); + } + + // Clone the wrapper template to avoid assigning duplicated handlers + const currentTemplate = OHIF.blaze.cloneTemplate(wrapperTemplate); + + // Store the child content function to render it inside the wrapper + const childContentFunction = contentFunction; + + // Create a function that will enable the recursion for wrappers + const enclosingContentFunction = () => { + // Add the current wrapper's instance to the wrapper instances list + currentTemplate.onCreated(() => wrapperInstances.push(Template.instance())); + + // Return the wrapper view instance with its child as content + return currentTemplate.constructView(childContentFunction, elseFunc); + }; + + // Replace the content function enclosing it recursively + contentFunction = () => { + return Blaze.With(data, enclosingContentFunction); + }; + + } + + return contentFunction(contentFunc, elseFunc); +}; diff --git a/Packages/ohif-core/components/base/templates/custom.html b/Packages/ohif-core/components/base/templates/custom.html new file mode 100644 index 000000000..302917fd3 --- /dev/null +++ b/Packages/ohif-core/components/base/templates/custom.html @@ -0,0 +1,3 @@ + diff --git a/Packages/ohif-core/components/base/templates/div.html b/Packages/ohif-core/components/base/templates/div.html new file mode 100644 index 000000000..a39733ce3 --- /dev/null +++ b/Packages/ohif-core/components/base/templates/div.html @@ -0,0 +1,9 @@ + diff --git a/Packages/ohif-core/components/base/templates/form.html b/Packages/ohif-core/components/base/templates/form.html new file mode 100644 index 000000000..b198b345b --- /dev/null +++ b/Packages/ohif-core/components/base/templates/form.html @@ -0,0 +1,16 @@ + diff --git a/Packages/ohif-core/components/base/templates/input.html b/Packages/ohif-core/components/base/templates/input.html new file mode 100644 index 000000000..e51c0c04c --- /dev/null +++ b/Packages/ohif-core/components/base/templates/input.html @@ -0,0 +1,11 @@ + diff --git a/Packages/ohif-core/components/base/templates/select.html b/Packages/ohif-core/components/base/templates/select.html new file mode 100644 index 000000000..d2c32a08f --- /dev/null +++ b/Packages/ohif-core/components/base/templates/select.html @@ -0,0 +1,16 @@ + diff --git a/Packages/ohif-core/components/base/wrappers/label.html b/Packages/ohif-core/components/base/wrappers/label.html new file mode 100644 index 000000000..a97d660ba --- /dev/null +++ b/Packages/ohif-core/components/base/wrappers/label.html @@ -0,0 +1,11 @@ + diff --git a/Packages/ohif-core/components/base/wrappers/title.html b/Packages/ohif-core/components/base/wrappers/title.html new file mode 100644 index 000000000..f461812f3 --- /dev/null +++ b/Packages/ohif-core/components/base/wrappers/title.html @@ -0,0 +1,11 @@ + diff --git a/Packages/ohif-core/components/bootstrap/form/form.html b/Packages/ohif-core/components/bootstrap/form/form.html new file mode 100644 index 000000000..347da8e95 --- /dev/null +++ b/Packages/ohif-core/components/bootstrap/form/form.html @@ -0,0 +1,8 @@ + diff --git a/Packages/ohif-core/components/bootstrap/form/group.html b/Packages/ohif-core/components/bootstrap/form/group.html new file mode 100644 index 000000000..a02064ca4 --- /dev/null +++ b/Packages/ohif-core/components/bootstrap/form/group.html @@ -0,0 +1,8 @@ + diff --git a/Packages/ohif-core/components/bootstrap/index.js b/Packages/ohif-core/components/bootstrap/index.js new file mode 100644 index 000000000..6dd7c5552 --- /dev/null +++ b/Packages/ohif-core/components/bootstrap/index.js @@ -0,0 +1,7 @@ +import './form/form.html'; +import './form/group.html'; + +import './input/groupRadio.html'; +import './input/radio.html'; +import './input/select.html'; +import './input/text.html'; diff --git a/Packages/ohif-core/components/bootstrap/input/groupRadio.html b/Packages/ohif-core/components/bootstrap/input/groupRadio.html new file mode 100644 index 000000000..ebf56577b --- /dev/null +++ b/Packages/ohif-core/components/bootstrap/input/groupRadio.html @@ -0,0 +1,20 @@ + diff --git a/Packages/ohif-core/components/bootstrap/input/radio.html b/Packages/ohif-core/components/bootstrap/input/radio.html new file mode 100644 index 000000000..4febb6e6f --- /dev/null +++ b/Packages/ohif-core/components/bootstrap/input/radio.html @@ -0,0 +1,14 @@ + diff --git a/Packages/ohif-core/components/bootstrap/input/select.html b/Packages/ohif-core/components/bootstrap/input/select.html new file mode 100644 index 000000000..a74b4dfd5 --- /dev/null +++ b/Packages/ohif-core/components/bootstrap/input/select.html @@ -0,0 +1,13 @@ + diff --git a/Packages/ohif-core/components/bootstrap/input/text.html b/Packages/ohif-core/components/bootstrap/input/text.html new file mode 100644 index 000000000..c2c824fe8 --- /dev/null +++ b/Packages/ohif-core/components/bootstrap/input/text.html @@ -0,0 +1,13 @@ + diff --git a/Packages/ohif-core/components/index.js b/Packages/ohif-core/components/index.js new file mode 100644 index 000000000..3a50cb813 --- /dev/null +++ b/Packages/ohif-core/components/index.js @@ -0,0 +1,2 @@ +import './base'; +import './bootstrap'; diff --git a/Packages/ohif-core/helpers/blaze.js b/Packages/ohif-core/helpers/blaze.js new file mode 100644 index 000000000..0bb880839 --- /dev/null +++ b/Packages/ohif-core/helpers/blaze.js @@ -0,0 +1,26 @@ +import { Template } from 'meteor/templating'; +import { Session } from 'meteor/session'; +import { ReactiveVar } from 'meteor/reactive-var'; + +/** + * Global Blaze UI helpers to work with Blaze + */ + +// Return the current template instance +Template.registerHelper('instance', () => { + return Template.instance(); +}); + +// Return the session value for the given key +Template.registerHelper('session', key => { + return Session.get(key); +}); + +// Return the value for given parameter regardless if it's reactive or not +Template.registerHelper('reactive', parameter => { + if (parameter instanceof ReactiveVar) { + return parameter.get(); + } + + return parameter; +}); diff --git a/Packages/ohif-core/helpers/data.js b/Packages/ohif-core/helpers/data.js new file mode 100644 index 000000000..ebaf54d8d --- /dev/null +++ b/Packages/ohif-core/helpers/data.js @@ -0,0 +1,59 @@ +import { _ } from 'meteor/underscore'; +import { Template } from 'meteor/templating'; + +/** + * Global Blaze UI helpers to manipulate data + */ + +// Base extend function to be used by extend and clone helpers +const extend = (...argsArray) => { + // Create the resulting object + const result = argsArray[0] || {}; + + // Extract the Spacebars kw hash + const kwHash = _.last(argsArray).hash; + + // Extract the given objects + const objects = _.initial(argsArray); + + // Iterate over the given objects + _.each(objects, current => { + // Stop here if the current argument is not an object + if (typeof current !== 'object') { + return; + } + + // Extend the resulting object with the current argument object + _.extend(result, current); + }); + + // Extend the resulting object with the Spacebars kw hash + _.extend(result, kwHash); + + // Return the resulting object + return result; +}; + +// Extend the first argument object it with the other argument objects +Template.registerHelper('extend', (...argsArray) => { + return extend(...argsArray); +}); + +// Create a new object and extends it with the argument objects +Template.registerHelper('clone', (...argsArray) => { + const newArgs = argsArray.slice(); + newArgs.unshift({}); + return extend(...newArgs); +}); + +// Return the first thrut value in the given arguments +Template.registerHelper('choose', (...argsArray) => { + // Iterate over the given objects + for (let i = 0; i < argsArray.length; i++) { + // Check if the current value is truth + if (!!argsArray[i]) { + // Return the current value + return argsArray[i]; + } + } +}); diff --git a/Packages/ohif-core/helpers/index.js b/Packages/ohif-core/helpers/index.js new file mode 100644 index 000000000..cbd6db156 --- /dev/null +++ b/Packages/ohif-core/helpers/index.js @@ -0,0 +1,4 @@ +import './blaze'; +import './data'; +import './logical'; +import './string'; diff --git a/Packages/viewerbase/lib/helpers/logical.js b/Packages/ohif-core/helpers/logical.js similarity index 84% rename from Packages/viewerbase/lib/helpers/logical.js rename to Packages/ohif-core/helpers/logical.js index 408ee00e2..b828a45d8 100644 --- a/Packages/viewerbase/lib/helpers/logical.js +++ b/Packages/ohif-core/helpers/logical.js @@ -1,3 +1,6 @@ +import { _ } from 'meteor/underscore'; +import { Template } from 'meteor/templating'; + /** * Global Blaze UI helpers to work with logical operations */ @@ -63,6 +66,15 @@ Template.registerHelper('choose', (...values) => { return result; }); +// Return the second parameter if the first is true or the third if it's false +Template.registerHelper('valueIf', (condition, valueIfTrue, valueIfFalse) => { + if (condition) { + return valueIfTrue; + } + + return valueIfFalse; +}); + // Check if the value is different from undefined Template.registerHelper('isDefined', value => { return typeof value !== 'undefined'; diff --git a/Packages/ohif-core/helpers/string.js b/Packages/ohif-core/helpers/string.js new file mode 100644 index 000000000..88cf9af74 --- /dev/null +++ b/Packages/ohif-core/helpers/string.js @@ -0,0 +1,16 @@ +import { _ } from 'meteor/underscore'; +import { Template } from 'meteor/templating'; + +/** + * Global Blaze UI helpers to work with Strings + */ + +// Concatenate the give strings +Template.registerHelper('concat', (...args) => { + const values = _.initial(args, 1); + let result = ''; + _.each(values, value => { + result += value || ''; + }); + return result; +}); diff --git a/Packages/ohif-core/lib/blaze.js b/Packages/ohif-core/lib/blaze.js new file mode 100644 index 000000000..beeaf083f --- /dev/null +++ b/Packages/ohif-core/lib/blaze.js @@ -0,0 +1,42 @@ +import { OHIF } from 'meteor/ohif:core'; + +OHIF.blaze = {}; + +// Clone a template and return the clone +OHIF.blaze.cloneTemplate = (template, newName) => { + if (!template){ + return; + } + + const name = newName || template.viewName; + const clone = new Template(name, template.renderFunction); + clone.inheritsEventsFrom(template); + clone.inheritsHelpersFrom(template); + clone.inheritsHooksFrom(template); + return clone; +}; + +// Navigate upwards the component and get the parent with the given view name +OHIF.blaze.getParentView = (view, parentViewName) => { + let currentView = view; + while (currentView) { + if (currentView.name === parentViewName) { + break; + } + + currentView = currentView.originalParentView || currentView.parentView; + } + + return currentView; +}; + +// Search for the parent component of the given view +OHIF.blaze.getParentComponent = (view) => { + let currentView = view; + while (currentView) { + currentView = currentView.originalParentView || currentView.parentView; + if (currentView && currentView._component) { + return currentView._component; + } + } +}; diff --git a/Packages/ohif-core/lib/index.js b/Packages/ohif-core/lib/index.js new file mode 100644 index 000000000..f2d66b2ef --- /dev/null +++ b/Packages/ohif-core/lib/index.js @@ -0,0 +1 @@ +import './blaze.js'; diff --git a/Packages/ohif-core/main.js b/Packages/ohif-core/main.js new file mode 100644 index 000000000..2606966f6 --- /dev/null +++ b/Packages/ohif-core/main.js @@ -0,0 +1,6 @@ +export { OHIF } from './ohif.js'; + +import './lib'; +import './components'; +import './helpers'; +import './ui'; diff --git a/Packages/ohif-core/ohif.js b/Packages/ohif-core/ohif.js new file mode 100644 index 000000000..5af84d941 --- /dev/null +++ b/Packages/ohif-core/ohif.js @@ -0,0 +1,11 @@ +/* + * Defines the base OHIF object + */ + +const OHIF = {}; + +if (Meteor.isDevelopment) { + window.OHIF = OHIF; +} + +export { OHIF }; diff --git a/Packages/ohif-core/package.js b/Packages/ohif-core/package.js new file mode 100644 index 000000000..8185f1d0a --- /dev/null +++ b/Packages/ohif-core/package.js @@ -0,0 +1,15 @@ +Package.describe({ + name: 'ohif:core', + summary: 'OHIF core components, helpers and UI functions', + version: '0.0.1' +}); + +Package.onUse(function(api) { + api.versionsFrom('1.3.4.1'); + + api.use('ecmascript'); + api.use('standard-app-packages'); + api.use('jquery'); + + api.mainModule('main.js', 'client'); +}); diff --git a/Packages/viewerbase/lib/ui/draggable/draggable.js b/Packages/ohif-core/ui/draggable/draggable.js similarity index 100% rename from Packages/viewerbase/lib/ui/draggable/draggable.js rename to Packages/ohif-core/ui/draggable/draggable.js diff --git a/Packages/ohif-core/ui/index.js b/Packages/ohif-core/ui/index.js new file mode 100644 index 000000000..ad5af1950 --- /dev/null +++ b/Packages/ohif-core/ui/index.js @@ -0,0 +1,2 @@ +import './draggable/draggable.js'; +import './resizable/resizable.js'; diff --git a/Packages/viewerbase/lib/ui/resizable/resizable.js b/Packages/ohif-core/ui/resizable/resizable.js similarity index 100% rename from Packages/viewerbase/lib/ui/resizable/resizable.js rename to Packages/ohif-core/ui/resizable/resizable.js diff --git a/Packages/viewerbase/lib/ui/resizable/resizable.styl b/Packages/ohif-core/ui/resizable/resizable.styl similarity index 100% rename from Packages/viewerbase/lib/ui/resizable/resizable.styl rename to Packages/ohif-core/ui/resizable/resizable.styl diff --git a/Packages/reactive-form-controls/client/components/selectInput/selectInput.html b/Packages/reactive-form-controls/client/components/selectInput/selectInput.html index 028921639..224182ae6 100644 --- a/Packages/reactive-form-controls/client/components/selectInput/selectInput.html +++ b/Packages/reactive-form-controls/client/components/selectInput/selectInput.html @@ -11,7 +11,7 @@ class="form-control js-form-update"> {{#each option in allowedValues}} {{/each}} @@ -20,4 +20,4 @@
{{ /with }}
- \ No newline at end of file + diff --git a/Packages/reactive-form-controls/client/helpers/equals.js b/Packages/reactive-form-controls/client/helpers/equals.js deleted file mode 100644 index 74f199cc5..000000000 --- a/Packages/reactive-form-controls/client/helpers/equals.js +++ /dev/null @@ -1,10 +0,0 @@ -import { Template } from 'meteor/templating'; - -/** - * Compares two variables are equal in value - * - * @returns {boolean} - */ -Template.registerHelper('equals', function(a, b) { - return a === b; -}); \ No newline at end of file diff --git a/Packages/reactive-form-controls/package.js b/Packages/reactive-form-controls/package.js index dd237a383..1dd8ef8df 100755 --- a/Packages/reactive-form-controls/package.js +++ b/Packages/reactive-form-controls/package.js @@ -1,35 +1,34 @@ -Package.describe({ - name: 'reactive-form-controls', - summary: 'A set of basic form controls that store their state in a ReactiveDict', - version: '0.0.1' -}); - -Package.onUse(function(api) { - api.versionsFrom('1.3.4.1'); - - api.use('standard-app-packages'); - api.use('ecmascript'); - api.use('jquery'); - api.use('stylus'); - api.use('reactive-dict'); - api.use('templating'); - api.use('natestrauser:select2@4.0.1', 'client'); - - api.addFiles('client/helpers/getSchema.js', ['client']); - api.addFiles('client/helpers/isInvalidKey.js', ['client']); - api.addFiles('client/helpers/stateDataWithKey.js', ['client']); - api.addFiles('client/helpers/equals.js', ['client']); - - api.addFiles('client/components/helpBlock/helpBlock.html', ['client']); - api.addFiles('client/components/helpBlock/helpBlock.js', ['client']); - - api.addFiles('client/components/radioOptionGroup/radioOptionGroup.html', ['client']); - api.addFiles('client/components/radioOptionGroup/radioOptionGroup.js', ['client']); - - api.addFiles('client/components/selectInput/selectInput.html', ['client']); - api.addFiles('client/components/selectInput/selectInput.js', ['client']); - - api.addFiles('client/components/select2Input/select2Input.html', ['client']); - api.addFiles('client/components/select2Input/select2Input.js', ['client']); - -}); \ No newline at end of file +Package.describe({ + name: 'reactive-form-controls', + summary: 'A set of basic form controls that store their state in a ReactiveDict', + version: '0.0.1' +}); + +Package.onUse(function(api) { + api.versionsFrom('1.3.4.1'); + + api.use('standard-app-packages'); + api.use('ecmascript'); + api.use('jquery'); + api.use('stylus'); + api.use('reactive-dict'); + api.use('templating'); + api.use('natestrauser:select2@4.0.1', 'client'); + + api.addFiles('client/helpers/getSchema.js', ['client']); + api.addFiles('client/helpers/isInvalidKey.js', ['client']); + api.addFiles('client/helpers/stateDataWithKey.js', ['client']); + + api.addFiles('client/components/helpBlock/helpBlock.html', ['client']); + api.addFiles('client/components/helpBlock/helpBlock.js', ['client']); + + api.addFiles('client/components/radioOptionGroup/radioOptionGroup.html', ['client']); + api.addFiles('client/components/radioOptionGroup/radioOptionGroup.js', ['client']); + + api.addFiles('client/components/selectInput/selectInput.html', ['client']); + api.addFiles('client/components/selectInput/selectInput.js', ['client']); + + api.addFiles('client/components/select2Input/select2Input.html', ['client']); + api.addFiles('client/components/select2Input/select2Input.js', ['client']); + +}); diff --git a/Packages/viewerbase/client/components/viewer/cineDialog/cineDialog.js b/Packages/viewerbase/client/components/viewer/cineDialog/cineDialog.js index 171e20936..f556c18c0 100644 --- a/Packages/viewerbase/client/components/viewer/cineDialog/cineDialog.js +++ b/Packages/viewerbase/client/components/viewer/cineDialog/cineDialog.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + toggleCinePlay = function() { var element = getActiveViewportElement(); var playClipToolData = cornerstoneTools.getToolState(element, 'playClip'); diff --git a/Packages/viewerbase/client/components/viewer/gridLayout/gridLayout.js b/Packages/viewerbase/client/components/viewer/gridLayout/gridLayout.js index 076e45d72..d98f62b82 100644 --- a/Packages/viewerbase/client/components/viewer/gridLayout/gridLayout.js +++ b/Packages/viewerbase/client/components/viewer/gridLayout/gridLayout.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + Template.gridLayout.helpers({ height: function() { var rows = this.rows || 1; diff --git a/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js b/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js index f9879697c..49ad2ca39 100644 --- a/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js +++ b/Packages/viewerbase/client/components/viewer/imageViewerViewport/imageViewerViewport.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + var allCornerstoneEvents = 'CornerstoneToolsMouseDown CornerstoneToolsMouseDownActivate ' + 'CornerstoneToolsMouseClick CornerstoneToolsMouseDrag CornerstoneToolsMouseUp ' + 'CornerstoneToolsMouseWheel CornerstoneToolsTap CornerstoneToolsTouchPress ' + @@ -115,7 +117,7 @@ function loadSeriesIntoViewport(data, templateData) { templateData.imageId = imageId; // Save the current image ID inside the ViewportLoading object. - // + // // The ViewportLoading object relates the viewport elements with whichever // image is currently being loaded into them. This is useful so that we can // place progress (download %) for each image inside the proper viewports. @@ -144,7 +146,7 @@ function loadSeriesIntoViewport(data, templateData) { } // Update the enabled element with the image and viewport data - // This is not usually necessary, but we need them stored in case + // This is not usually necessary, but we need them stored in case // a sopClassUid-specific viewport setting is present. enabledElement.image = image; enabledElement.viewport = cornerstone.getDefaultViewport(enabledElement.canvas, image); @@ -564,4 +566,4 @@ Template.imageViewerViewport.events({ var viewportIndex = $('.imageViewerViewport').index(e.currentTarget); layoutManager.toggleEnlargement(viewportIndex); } -}); \ No newline at end of file +}); diff --git a/Packages/viewerbase/client/components/viewer/loadingIndicator/loadingIndicator.js b/Packages/viewerbase/client/components/viewer/loadingIndicator/loadingIndicator.js index b45330d50..a677806f3 100644 --- a/Packages/viewerbase/client/components/viewer/loadingIndicator/loadingIndicator.js +++ b/Packages/viewerbase/client/components/viewer/loadingIndicator/loadingIndicator.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + Meteor.startup(function() { cornerstoneTools.loadHandlerManager.setStartLoadHandler(startLoadingHandler); cornerstoneTools.loadHandlerManager.setEndLoadHandler(doneLoadingHandler); @@ -67,4 +69,4 @@ Template.loadingIndicator.helpers({ return percentComplete + '%'; } } -}); \ No newline at end of file +}); diff --git a/Packages/viewerbase/client/components/viewer/playClipButton/playClipButton.js b/Packages/viewerbase/client/components/viewer/playClipButton/playClipButton.js index 2a50a14db..493d5e00d 100644 --- a/Packages/viewerbase/client/components/viewer/playClipButton/playClipButton.js +++ b/Packages/viewerbase/client/components/viewer/playClipButton/playClipButton.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + toggleCinePlay = function(element) { var viewports = $('.imageViewerViewport'); diff --git a/Packages/viewerbase/client/components/viewer/toolbar/toolbar.js b/Packages/viewerbase/client/components/viewer/toolbar/toolbar.js index 38a48bf63..35c663f39 100644 --- a/Packages/viewerbase/client/components/viewer/toolbar/toolbar.js +++ b/Packages/viewerbase/client/components/viewer/toolbar/toolbar.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + function getDefaultButtonData() { var buttonData = []; diff --git a/Packages/viewerbase/lib/WLPresets.js b/Packages/viewerbase/lib/WLPresets.js index 0230ea60e..951f39c5a 100644 --- a/Packages/viewerbase/lib/WLPresets.js +++ b/Packages/viewerbase/lib/WLPresets.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + applyWLPreset = function(presetName, element) { log.info("Applying WL Preset: " + presetName); var viewport = cornerstone.getViewport(element); diff --git a/Packages/viewerbase/lib/displayReferenceLines.js b/Packages/viewerbase/lib/displayReferenceLines.js index 7c5fe8a93..7c9c80a2c 100644 --- a/Packages/viewerbase/lib/displayReferenceLines.js +++ b/Packages/viewerbase/lib/displayReferenceLines.js @@ -1,6 +1,8 @@ +import { OHIF } from 'meteor/ohif:core'; + /** * This function disables reference lines for a specific viewport element. - * It also enables reference lines for all other viewports with the + * It also enables reference lines for all other viewports with the * class .imageViewerViewport. * * @param element {node} DOM Node representing the viewport element @@ -36,4 +38,4 @@ displayReferenceLines = function(element) { cornerstoneTools.referenceLines.tool.enable(element, OHIF.viewer.updateImageSynchronizer); }); -}; \ No newline at end of file +}; diff --git a/Packages/viewerbase/lib/enableHotkeys.js b/Packages/viewerbase/lib/enableHotkeys.js index b9c1295c2..00abd4b37 100644 --- a/Packages/viewerbase/lib/enableHotkeys.js +++ b/Packages/viewerbase/lib/enableHotkeys.js @@ -1,4 +1,4 @@ -OHIF = window.OHIF || {}; +import { OHIF } from 'meteor/ohif:core'; Meteor.startup(function() { @@ -31,7 +31,7 @@ Meteor.startup(function() { WLPresetBone: ['NUMPAD4', '4'], WLPresetBrain: ['NUMPAD5', '5'] }; - + // For now OHIF.viewer.hotkeys = OHIF.viewer.defaultHotkeys; @@ -262,7 +262,7 @@ function bindHotkey(hotkey, task) { } else { fn = hotkeyFunctions[task]; - // If the function doesn't exist in the + // If the function doesn't exist in the // hotkey function list, try the viewer-specific function list if (!fn && OHIF.viewer && OHIF.viewer.functionList) { fn = OHIF.viewer.functionList[task]; diff --git a/Packages/viewerbase/lib/helpers/blaze.js b/Packages/viewerbase/lib/helpers/blaze.js deleted file mode 100644 index a81fdcc92..000000000 --- a/Packages/viewerbase/lib/helpers/blaze.js +++ /dev/null @@ -1,38 +0,0 @@ -// Return the current template instance -Template.registerHelper('instance', () => { - return Template.instance(); -}); - -// Return the session value for the given key -Template.registerHelper('session', key => { - return Session.get(key); -}); - -// Create a new object and extends it with the argument objects -Template.registerHelper('extend', (...argsArray) => { - // Create the resulting object - const result = {}; - - // Extract the Spacebars kw hash - const kwHash = _.last(argsArray).hash; - - // Extract the given objects - const objects = _.initial(argsArray); - - // Iterate over the given objects - _.each(objects, current => { - // Stop here if the current argument is not an object - if (typeof current !== 'object') { - return; - } - - // Extend the resulting object with the current argument object - _.extend(result, current); - }); - - // Extend the resulting object with the Spacebars kw hash - _.extend(result, kwHash); - - // Return the resulting object - return result; -}); diff --git a/Packages/viewerbase/lib/toolManager.js b/Packages/viewerbase/lib/toolManager.js index 04a83dbdf..12d9f9ae8 100644 --- a/Packages/viewerbase/lib/toolManager.js +++ b/Packages/viewerbase/lib/toolManager.js @@ -1,3 +1,5 @@ +import { OHIF } from 'meteor/ohif:core'; + var activeTool = 'wwwc'; var defaultTool = 'wwwc'; diff --git a/Packages/viewerbase/package.js b/Packages/viewerbase/package.js index a9289d73b..536a58286 100644 --- a/Packages/viewerbase/package.js +++ b/Packages/viewerbase/package.js @@ -117,9 +117,6 @@ Package.onUse(function(api) { api.addFiles('lib/toolManager.js', 'client'); api.addFiles('lib/enablePrefetchOnElement.js', 'client'); api.addFiles('lib/displayReferenceLines.js', 'client'); - api.addFiles('lib/ui/draggable/draggable.js', 'client'); - api.addFiles('lib/ui/resizable/resizable.js', 'client'); - api.addFiles('lib/ui/resizable/resizable.styl', 'client'); api.addFiles('lib/toggleDialog.js', 'client'); api.addFiles('lib/setActiveViewport.js', 'client'); api.addFiles('lib/switchToImageByIndex.js', 'client'); @@ -168,7 +165,6 @@ Package.onUse(function(api) { api.export('LayoutManager', 'client'); // Global objects - api.export('OHIF', 'client'); api.export('ClientId', 'client'); // Collections @@ -177,8 +173,6 @@ Package.onUse(function(api) { // UI Helpers api.addFiles([ 'lib/helpers/formatDA.js', - 'lib/helpers/blaze.js', - 'lib/helpers/logical.js', 'lib/helpers/formatJSDate.js', 'lib/helpers/jsDateFromNow.js', 'lib/helpers/formatNumberPrecision.js',