From d097f699671ec692bd573e71b7561e3c6eb6b278 Mon Sep 17 00:00:00 2001 From: tangollama Date: Wed, 12 Apr 2017 22:29:23 -0400 Subject: [PATCH 1/5] ignore electron-out --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2c5750e3d8..abdbff375c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ # compiled output /dist /tmp +/electron-out # dependencies /node_modules @@ -32,4 +33,4 @@ newrelic_agent.log newrelic.js /async-disk-cache -c9-couch.js \ No newline at end of file +c9-couch.js From c0ff406f35b144ff61eb10521cf5da4457b077bb Mon Sep 17 00:00:00 2001 From: tangollama Date: Fri, 14 Apr 2017 01:06:34 -0400 Subject: [PATCH 2/5] Checking in the text replacement feature Now in generally ES6-accepted notation. --- app/admin/textreplace/controller.js | 27 ++++ app/admin/textreplace/route.js | 46 +++++++ app/admin/textreplace/template.hbs | 35 +++++ app/appointments/edit/template.hbs | 2 +- app/components/expand-text.js | 153 +++++++++++++++++++++ app/imaging/edit/template.hbs | 2 +- app/incident/edit/template.hbs | 2 +- app/incident/note/edit/template.hbs | 2 +- app/inventory/adjust/template.hbs | 2 +- app/invoices/payment/template.hbs | 2 +- app/labs/edit/template.hbs | 2 +- app/locales/en/translations.js | 14 ++ app/medication/edit/template.hbs | 2 +- app/medication/return/template.hbs | 2 +- app/mixins/navigation.js | 6 + app/models/text-expansion.js | 17 +++ app/patients/notes/template.hbs | 2 +- app/patients/operation-report/template.hbs | 4 +- app/patients/operative-plan/template.hbs | 6 +- app/procedures/edit/template.hbs | 2 +- app/router.js | 1 + app/styles/components/_form_styles.scss | 10 ++ app/templates/components/custom-form.hbs | 2 +- app/templates/components/expand-text.hbs | 2 + app/templates/inventory-basic.hbs | 2 +- app/utils/text-expansion.js | 58 ++++++++ app/visits/edit/template.hbs | 2 +- tests/unit/utils/text-expansion-test.js | 18 +++ 28 files changed, 406 insertions(+), 19 deletions(-) create mode 100644 app/admin/textreplace/controller.js create mode 100644 app/admin/textreplace/route.js create mode 100644 app/admin/textreplace/template.hbs create mode 100644 app/components/expand-text.js create mode 100644 app/models/text-expansion.js create mode 100644 app/templates/components/expand-text.hbs create mode 100644 app/utils/text-expansion.js create mode 100644 tests/unit/utils/text-expansion-test.js diff --git a/app/admin/textreplace/controller.js b/app/admin/textreplace/controller.js new file mode 100644 index 0000000000..2dcb704b88 --- /dev/null +++ b/app/admin/textreplace/controller.js @@ -0,0 +1,27 @@ +import Ember from 'ember'; +import EmberValidations from 'ember-validations'; + +export default Ember.Controller.extend(EmberValidations, { + hideCancelButton: true, + updateCapability: 'update_config', + + createExpansion: function() { + let newExpansion = this.get('store').createRecord('text-expansion'); + this.set('newExpansion', newExpansion); + }.on('init'), + + actions: { + cancelExpansion() { + this.createExpansion(); + } + }, + + validations: { + 'newExpansion.from': { + presence: true + }, + 'newExpansion.to': { + presence: true + } + } +}); diff --git a/app/admin/textreplace/route.js b/app/admin/textreplace/route.js new file mode 100644 index 0000000000..4132671d7b --- /dev/null +++ b/app/admin/textreplace/route.js @@ -0,0 +1,46 @@ +import AbstractIndexRoute from 'hospitalrun/routes/abstract-index-route'; +import { translationMacro as t } from 'ember-i18n'; + +export default AbstractIndexRoute.extend({ + pageTitle: t('admin.text_replacements.page_title'), + hideNewButton: true, + + model() { + let store = this.get('store'); + return store.findAll('text-expansion').then((result) => { + return result.filter((model) => { + let isNew = model.get('isNew'); + console.log(`${model.get('from')} ${isNew}`); + return !isNew; + }); + }); + }, + + setupController(controller, model) { + this._super(controller, model); + controller.createExpansion(); + }, + + actions: { + addExpansion(newExpansion) { + newExpansion.save() + .then(() => { + this.refresh(); + }) + .catch(() => { + this.refresh(); + }); + }, + + deleteExpansion(expansion) { + expansion.deleteRecord(); + expansion.save() + .then(() => { + this.refresh(); + }) + .catch(() => { + this.refresh(); + }); + } + } +}); diff --git a/app/admin/textreplace/template.hbs b/app/admin/textreplace/template.hbs new file mode 100644 index 0000000000..d6915c0846 --- /dev/null +++ b/app/admin/textreplace/template.hbs @@ -0,0 +1,35 @@ +
+

{{t 'admin.text_replacements.existing_repl'}}

+

{{t 'admin.text_replacements.repl_desc'}}

+ + + + + + +{{#each model as |expansion|}} + + + + + +{{/each}} + +
{{t 'labels.from'}}{{t 'labels.to'}} +
#{{expansion.from}}{{expansion.to}} + +
+
+
+

{{t 'admin.text_replacements.create_new'}}

+ {{#em-form model=newExpansion action="addExpansion" formLayout="horizontal" showErrorsOnFocusIn="true" submitButton=false}} + {{em-input property="from" label=(t 'labels.from') placeholder=(t 'admin.text_replacements.to_replace')}} + {{em-input property="to" label=(t 'labels.from') placeholder=(t 'admin.text_replacements.replace_with')}} + {{/em-form}} +
+ +
+
diff --git a/app/appointments/edit/template.hbs b/app/appointments/edit/template.hbs index fdb84b30ce..5d75235ce9 100644 --- a/app/appointments/edit/template.hbs +++ b/app/appointments/edit/template.hbs @@ -55,6 +55,6 @@ }} {{/if}} - {{em-text label=(t 'models.appointment.labels.notes') property="notes" rows=3 }} + {{expand-text label=(t 'models.appointment.labels.notes') property="notes" rows=3 }} {{/em-form}} {{/edit-panel}} diff --git a/app/components/expand-text.js b/app/components/expand-text.js new file mode 100644 index 0000000000..38f8a47ac7 --- /dev/null +++ b/app/components/expand-text.js @@ -0,0 +1,153 @@ +import Ember from 'ember'; +import textExpansion from '../utils/text-expansion'; + +export default Ember.Component.extend({ + i18n: Ember.inject.service(), + store: Ember.inject.service(), + + userText: '', + + didInsertElement() { + try { + let feedbackDiv = document.createElement('div'); + feedbackDiv.style.position = 'absolute'; + let textarea = this.$()[0].getElementsByTagName('textarea')[0]; + this.set('textarea', textarea); + let textPos = textarea.getBoundingClientRect(); + let fbStyle = feedbackDiv.style; + fbStyle.top = `${textPos.bottom}px`; + fbStyle.left = `${textPos.left}px`; + fbStyle.width = `${textarea.offsetWidth}px`; + fbStyle.backgroundColor = 'lightyellow'; + fbStyle.borderStyle = 'solid'; + fbStyle.borderWidth = '1px'; + fbStyle.borderRadius = '3px'; + fbStyle.paddingLeft = '5px'; + fbStyle.visibility = 'hidden'; + + this.set('feedbackDiv', feedbackDiv); + this.get('feedbackText'); + this.get('activeExpansionSite'); + + this.get('store') + .findAll('text-expansion') + .then((expansions) => { + return expansions.reduce((prev, curr) => { + // console.log(`curr ${JSON.stringify(prev)}`); + prev[curr.get('from')] = curr.get('to'); + return prev; + }, {}); + }) + .then((expansions) => { + this.set('expansions', expansions); + }); + + } catch(e) { + // console.log(`didInsert {e}`); + } + }, + + keyUp(k) { + let textArea = k.target; + let text = textArea.value; + this.set('userText', text); + this.set('cursorLocation', textArea.selectionStart); + }, + + keyDown(k) { + if (k.keyCode === 13) { + let possibleSwaps = this.get('possibleSwaps'); + if (possibleSwaps && possibleSwaps.length === 1) { + let swapTo = possibleSwaps[0].to; + let activeSite = this.get('activeExpansionSite'); + let sliceLength = activeSite.match.length; + let currentText = k.target.value; + let modifiedText = currentText.slice(0, activeSite.index) + swapTo + currentText.slice(activeSite.index + sliceLength); + k.target.value = modifiedText; + + k.preventDefault(); + k.returnValue = false; + k.cancelBubble = true; + return false; + } + } + }, + + // Find an expandable word that has the cursor within it + activeExpansionSite: Ember.computed('userText', 'cursorLocation', function() { + + let userText = this.get('userText'); + let textarea = this.get('textarea'); + if (!textarea) { + return null; + } + let cursorLoc = textarea.selectionStart; + let subjects = textExpansion.findExpansionSubjects(userText); + let sites = textExpansion.findExpansionSites(userText, subjects); + + return sites.find((s) => { + let endIndex = s.index + s.match.length; + + return cursorLoc >= s.index && cursorLoc <= endIndex; + }); + }), + + // If an expansion site is active, which possible swaps could occur there? + possibleSwaps: Ember.computed('activeExpansionSite', 'expansions', function() { + let activeSite = this.get('activeExpansionSite'); + + if (activeSite) { + let expansions = this.get('expansions'); + return Object.keys(expansions) + .filter((ex) => { + return ex.startsWith(activeSite.term); + }) + .sort() + .map((from) => { + return { + from, + to: expansions[from] + }; + }); + } + }), + + expansionText: Ember.computed('possibleSwaps', 'activeExpansionSite', 'userText', function() { + let result = ''; + + let i18n = this.get('i18n'); + let possibleSwaps = this.get('possibleSwaps'); + if (possibleSwaps) { + let activeSite = this.get('activeExpansionSite'); + + if (possibleSwaps.length === 1) { + let swapTo = possibleSwaps[0].to; + result = i18n.t('admin.text_replacements.perform_expand', { from: activeSite.term, to: swapTo }); + } else if (possibleSwaps.length > 1) { + let possible = possibleSwaps + .map((swap) => { + return swap.from; + }).join(', '); + result = i18n.t('admin.text_replacements.possible_expansions', { possible }); + } else { + result = i18n.t('admin.text_replacements.no_matches', { term: activeSite.term }); + } + } + + return result; + }), + + expansionDivStyle: Ember.computed('expansionText', function() { + let expansionText = this.get('expansionText'); + let visiblility = expansionText ? 'visible' : 'hidden'; + let textArea = this.get('textarea'); + + let styleString = `visibility: ${visiblility};`; + + if (textArea) { + let textPos = textArea.getBoundingClientRect(); + styleString += ` top: ${textPos.bottom}px; left: ${textPos.left}px; width: ${textArea.offsetWidth}px;`; + } + return new Ember.Handlebars.SafeString(styleString); + }) +}); diff --git a/app/imaging/edit/template.hbs b/app/imaging/edit/template.hbs index 7c834a46ac..9ea9616da6 100644 --- a/app/imaging/edit/template.hbs +++ b/app/imaging/edit/template.hbs @@ -50,6 +50,6 @@ }} {{em-input property="result" label=(t 'labels.result') class="result-input"}} {{/if}} - {{em-text property="notes" label=(t 'labels.notes') rows=3 }} + {{expand-text property="notes" label=(t 'labels.notes') rows=3 }} {{/em-form}} {{/edit-panel}} diff --git a/app/incident/edit/template.hbs b/app/incident/edit/template.hbs index b12d967574..76c36fcfc0 100644 --- a/app/incident/edit/template.hbs +++ b/app/incident/edit/template.hbs @@ -75,7 +75,7 @@ {{/if}}
- {{em-text label=(t 'incident.labels.description') property="description" class="required col-sm-12 incident-description" rows=3}} + {{expand-text label=(t 'incident.labels.description') property="description" class="required col-sm-12 incident-description" rows=3}}
{{#if canManageIncident}} diff --git a/app/incident/note/edit/template.hbs b/app/incident/note/edit/template.hbs index da57fb1782..e87d80bd54 100644 --- a/app/incident/note/edit/template.hbs +++ b/app/incident/note/edit/template.hbs @@ -4,7 +4,7 @@ {{date-picker property="dateRecorded" label=(t 'incident.labels.dateRecorded') class="col-sm-6" format="l h:mm A" showTime=true }}
- {{em-text property="description" label=(t 'incident.labels.note') class="col-sm-12 note-description"}} + {{expand-text property="description" label=(t 'incident.labels.note') class="col-sm-12 note-description"}}
{{/em-form}} {{/modal-dialog}} diff --git a/app/inventory/adjust/template.hbs b/app/inventory/adjust/template.hbs index e6e8bcd0e8..3401e17c44 100644 --- a/app/inventory/adjust/template.hbs +++ b/app/inventory/adjust/template.hbs @@ -23,7 +23,7 @@ }} {{number-input property="adjustmentQuantity" label=(t 'labels.quantity') class="col-sm-3 required"}} - {{em-text label=(t 'inventory.labels.reason') property="reason" rows=3}} + {{expand-text label=(t 'inventory.labels.reason') property="reason" rows=3}}
{{date-picker property="dateCompleted" label=(t 'inventory.labels.adjustmentDate') class="col-sm-4 required"}} {{select-or-typeahead property="expenseAccount" label=(t 'inventory.labels.expense') list=expenseAccountList selection=model.expenseAccount class="col-sm-8"}} diff --git a/app/invoices/payment/template.hbs b/app/invoices/payment/template.hbs index daab9b632e..856782b2bb 100644 --- a/app/invoices/payment/template.hbs +++ b/app/invoices/payment/template.hbs @@ -18,6 +18,6 @@ {{number-input property="amount" label=(t 'labels.amount') class="required payment-amount"}} {{date-picker property="datePaid" label=(t 'labels.datePaid') maxDate="now" class="required"}} {{select-or-typeahead property="expenseAccount" label=(t 'labels.creditTo') list=expenseAccountList selection=model.expenseAccount }} - {{em-text property="notes" label=(t 'labels.notes')}} + {{expand-text property="notes" label=(t 'labels.notes')}} {{/em-form}} {{/modal-dialog}} diff --git a/app/labs/edit/template.hbs b/app/labs/edit/template.hbs index 847cef98f9..a6ff38d1b1 100644 --- a/app/labs/edit/template.hbs +++ b/app/labs/edit/template.hbs @@ -42,7 +42,7 @@ {{#if canComplete}} {{em-input property="result" label=(t 'labels.result') class="test-result-input"}} {{/if}} - {{em-text property="notes" label=(t 'labels.notes') rows=3 }} + {{expand-text property="notes" label=(t 'labels.notes') rows=3 }} {{custom-form-manager model=model formType="lab"}} {{/em-form}} {{/edit-panel}} diff --git a/app/locales/en/translations.js b/app/locales/en/translations.js index 46905c5209..ba07a26d35 100644 --- a/app/locales/en/translations.js +++ b/app/locales/en/translations.js @@ -208,6 +208,17 @@ export default { messages: { roleSaved: 'The {{roleName}} role has been saved.' }, titles: { roleSaved: 'Role Saved' } }, + text_replacements: { + create_new: 'Create a new text replacement', + existing_repl: 'Existing replacements', + repl_desc: 'When entering text, these shortcuts allow you to replace a short sequence of characters with a longer phrase.', + page_title: 'Text Replacements', + to_replace: 'Text to replace', + replace_with: 'Replace with', + perform_expand: "Press Enter to replace #{{from}} with '{{to}}'", + possible_expansions: 'Possible replacements: {{possible}}', + no_matches: "No replacements match '{{term}}'" + }, userRoles: 'User Roles', users: 'Users', visitForms: { @@ -793,6 +804,7 @@ export default { fileLoadSuccessful: 'File To Load Successful', fileName: 'File Name', fileToLoad: 'File Load', + from: 'From', fulfill: 'Fulfill', fulfillRequest: 'Fulfill Request', fulfillRequestNow: 'Fulfill Request Now', @@ -841,6 +853,7 @@ export default { startTime: 'Start Time', status: 'Status', takenBy: 'Taken By', + to: 'To', total: 'Total', type: 'Type', userCanAddNewValue: 'User Can Add New Values', @@ -1098,6 +1111,7 @@ export default { "today'sAppointments": "Today's Appointments", userRoles: 'User Roles', users: 'Users', + text_replacements: 'Text Replacements', workflow: 'Workflow' } }, diff --git a/app/medication/edit/template.hbs b/app/medication/edit/template.hbs index ffac6d87b9..466c97abb8 100644 --- a/app/medication/edit/template.hbs +++ b/app/medication/edit/template.hbs @@ -45,7 +45,7 @@ {{static-text label=(t 'medication.labels.refills') class="col-xs-3" value=model.refills }}
{{else}} - {{em-text property="prescription" label=(t 'labels.prescription')rows="3" class=prescriptionClass }} + {{expand-text property="prescription" label=(t 'labels.prescription')rows="3" class=prescriptionClass }}
{{date-picker property="prescriptionDate" label=(t 'labels.prescriptionDate') class="col-sm-4"}}
diff --git a/app/medication/return/template.hbs b/app/medication/return/template.hbs index 7e70c351eb..f83c6eb1e8 100644 --- a/app/medication/return/template.hbs +++ b/app/medication/return/template.hbs @@ -22,7 +22,7 @@ {{select-or-typeahead property="deliveryLocation" label=(t 'medication.labels.returnLocation') list=warehouseList selection=model.location className="col-xs-4"}} {{select-or-typeahead property="deliveryAisle" label=(t 'medication.labels.returnAisle') list=aisleLocationList selection=model.aisleLocation className="col-xs-4"}} - {{em-text property="reason" label=(t 'medication.labels.returnReason') rows="3"}} + {{expand-text property="reason" label=(t 'medication.labels.returnReason') rows="3"}}
{{date-picker property="dateCompleted" label=(t 'medication.labels.adjustmentDate') class="col-sm-4"}} {{select-or-typeahead property="expenseAccount" label=(t 'medication.labels.creditToAccount') list=expenseAccountList selection=model.expenseAccount className="col-sm-8"}} diff --git a/app/mixins/navigation.js b/app/mixins/navigation.js index 1a9588ca02..64241a2647 100644 --- a/app/mixins/navigation.js +++ b/app/mixins/navigation.js @@ -336,6 +336,12 @@ export default Ember.Mixin.create({ route: 'admin.lookup', capability: 'update_config' }, + { + title: 'Text Replacements', + iconClass: 'octicon-plus', + route: 'admin.textreplace', + capability: 'update_config' + }, { title: 'Print Header', iconClass: 'octicon-chevron-right', diff --git a/app/models/text-expansion.js b/app/models/text-expansion.js new file mode 100644 index 0000000000..7120175047 --- /dev/null +++ b/app/models/text-expansion.js @@ -0,0 +1,17 @@ +import DS from 'ember-data'; +import EmberValidations from 'ember-validations'; +import { Model } from 'ember-pouch'; + +export default Model.extend(EmberValidations, { + from: DS.attr('string'), + to: DS.attr('string'), + + validations: { + from: { + presence: true + }, + to: { + presence: true + } + } +}); diff --git a/app/patients/notes/template.hbs b/app/patients/notes/template.hbs index dedc3f382e..7275a7fa51 100644 --- a/app/patients/notes/template.hbs +++ b/app/patients/notes/template.hbs @@ -6,7 +6,7 @@ updateButtonAction=updateButtonAction updateButtonText=updateButtonText }} {{#em-form model=model submitButton=false }} - {{em-text label=(t 'labels.note') property="content" rows=3 class="test-note-content required form-input-group"}} + {{expand-text label=(t 'labels.note') property="content" rows=3 class="test-note-content required form-input-group"}} {{em-select class="required" label=(t 'labels.visit') property="visit" content=patientVisitsForSelect optionValuePath="selectObject" optionLabelPath="selectObject.visitDescription" diff --git a/app/patients/operation-report/template.hbs b/app/patients/operation-report/template.hbs index e51c5b5de3..33facf27b8 100644 --- a/app/patients/operation-report/template.hbs +++ b/app/patients/operation-report/template.hbs @@ -15,7 +15,7 @@ secondaryDiagnosisLabel=(t 'operationReport.labels.preOpSecondaryDiagnosis') }}
- {{em-text property="operationDescription" label=(t 'operationReport.labels.operationDescription') class="operation-description"}} + {{expand-text property="operationDescription" label=(t 'operationReport.labels.operationDescription') class="operation-description"}}
{{date-picker property="surgeryDate" label=(t 'operationReport.labels.surgeryDate') class="form-input-group col-sm-4 surgery-date"}} {{select-or-typeahead className="col-sm-3" property="surgeon" @@ -31,7 +31,7 @@ {{em-input property="caseComplexity" label=(t 'operationReport.labels.caseComplexity') class="col-sm-2 case-complexity"}}
{{operative-procedures model=model procedureList=procedureList}} - {{em-text property="additionalNotes" label=(t 'operationReport.labels.additionalNotes')}} + {{expand-text property="additionalNotes" label=(t 'operationReport.labels.additionalNotes')}} {{upcoming-appointments patient=model.patient}} {{custom-form-manager model=model formType="operativePlan"}} {{/em-form}} diff --git a/app/patients/operative-plan/template.hbs b/app/patients/operative-plan/template.hbs index 915321a5fc..7895667269 100644 --- a/app/patients/operative-plan/template.hbs +++ b/app/patients/operative-plan/template.hbs @@ -1,7 +1,7 @@ {{#edit-panel editPanelProps=editPanelProps}} {{#em-form model=model submitButton=false }} {{patient-summary patient=model.patient disablePatientLink=true diagnosisContainer=model}} - {{em-text property="operationDescription" label=(t 'operativePlan.labels.operationDescription') class="operation-description"}} + {{expand-text property="operationDescription" label=(t 'operativePlan.labels.operationDescription') class="operation-description"}} {{operative-procedures model=model procedureList=procedureList}}
{{select-or-typeahead className="col-sm-4" property="surgeon" @@ -18,8 +18,8 @@ }} {{em-input property="caseComplexity" label=(t 'operativePlan.labels.caseComplexity') class="col-sm-2 case-complexity"}}
- {{em-text property="admissionInstructions" label=(t 'operativePlan.labels.admissionInstructions') class="admission-instructions"}} - {{em-text property="additionalNotes" label=(t 'operativePlan.labels.additionalNotes') class="additional-notes"}} + {{expand-text property="admissionInstructions" label=(t 'operativePlan.labels.admissionInstructions') class="admission-instructions"}} + {{expand-text property="additionalNotes" label=(t 'operativePlan.labels.additionalNotes') class="additional-notes"}} {{upcoming-appointments patient=model.patient}} {{custom-form-manager model=model formType="operativePlan"}} {{/em-form}} diff --git a/app/procedures/edit/template.hbs b/app/procedures/edit/template.hbs index 160f56a54c..23d75c2d73 100644 --- a/app/procedures/edit/template.hbs +++ b/app/procedures/edit/template.hbs @@ -39,7 +39,7 @@ selection=model.anesthesiaTypes }} - {{em-text label="Notes" property="notes" rows=3 class="procedure-notes"}} + {{expand-text label="Notes" property="notes" rows=3 class="procedure-notes"}} {{partial 'item-charges'}}
diff --git a/app/router.js b/app/router.js index ee8f66abbc..4e33fbdfd8 100755 --- a/app/router.js +++ b/app/router.js @@ -20,6 +20,7 @@ Router.map(function() { this.route('edit', { path: '/edit/:inc-category_id' }); }); this.route('lookup', { path: '/' }); + this.route('textreplace'); this.route('users', { resetNamespace: true }, function() { diff --git a/app/styles/components/_form_styles.scss b/app/styles/components/_form_styles.scss index c0e2f2763c..e8b71627de 100644 --- a/app/styles/components/_form_styles.scss +++ b/app/styles/components/_form_styles.scss @@ -39,3 +39,13 @@ .container { label { font-weight: 300; } } + +.text-expansion-container { + position: absolute; + border-width: 1px; + border-style: solid; + border-radius: 3px; + background-color: #ffffe0; + padding-right: 5px; + padding-left: 5px; +} diff --git a/app/templates/components/custom-form.hbs b/app/templates/components/custom-form.hbs index 30ec76f13f..4cab19df25 100644 --- a/app/templates/components/custom-form.hbs +++ b/app/templates/components/custom-form.hbs @@ -21,7 +21,7 @@
{{/if}} {{#if (eq field.type 'textarea')}} - {{em-text label=field.label property=(concat propertyPrefix field.property) class=field.displayClassNames}} + {{expand-text label=field.label property=(concat propertyPrefix field.property) class=field.displayClassNames}} {{/if}} {{#if (eq field.type 'text')}} {{em-input label=field.label property=(concat propertyPrefix field.property) class=field.displayClassNames}} diff --git a/app/templates/components/expand-text.hbs b/app/templates/components/expand-text.hbs new file mode 100644 index 0000000000..9cc23a4a12 --- /dev/null +++ b/app/templates/components/expand-text.hbs @@ -0,0 +1,2 @@ +{{em-text label=label property=property rows=rows}} +
{{expansionText}}
diff --git a/app/templates/inventory-basic.hbs b/app/templates/inventory-basic.hbs index 2736ba17f3..c48255eb93 100644 --- a/app/templates/inventory-basic.hbs +++ b/app/templates/inventory-basic.hbs @@ -15,7 +15,7 @@ {{/unless}}
-{{em-text label=(t 'labels.description') property="description" rows=1 }} +{{expand-text label=(t 'labels.description') property="description" rows=1 }}
{{em-select label=(t 'labels.type') property="inventoryType" content=inventoryTypes diff --git a/app/utils/text-expansion.js b/app/utils/text-expansion.js new file mode 100644 index 0000000000..485fc81e90 --- /dev/null +++ b/app/utils/text-expansion.js @@ -0,0 +1,58 @@ +export default { + // Find words with expansion prefix + // '#abc abc #cd' -> ['#abc', '#cd'] + findExpansionSubjects(text) { + let search = /(^|\s+)(#\S+)/g; + let match = true; + + let subjects = []; + while (match != null) { + + match = search.exec(text); + if (match && match.length > 2) { + subjects.push(match[2]); + } + } + + return subjects.filter(onlyUnique); + }, + + // Find all detected expandable sites by index + // 'abc #abc cd', ['#abc'] -> [{ index: 4, match: '#abc', term: 'abc'}] + findExpansionSites(text, subjects) { + + return subjects + .map(findAllIndices(text)) + .reduce((a, b) => { // flatmap + return a.concat(b); + }, []); + } +}; + +function findAllIndices(text) { + return function(value) { + let result = []; + let keepMatching = true; + let matchPoint = 0; + while (keepMatching) { + matchPoint = text.indexOf(value, matchPoint); + if (matchPoint > -1) { + result.push({ + index: matchPoint, + match: value, + term: value.slice(1) + }); + } else { + keepMatching = false; + } + + matchPoint += 1; + } + + return result; + }; +} + +function onlyUnique(value, index, self) { + return self.indexOf(value) === index; +} diff --git a/app/visits/edit/template.hbs b/app/visits/edit/template.hbs index 474f1b8512..493137ca87 100644 --- a/app/visits/edit/template.hbs +++ b/app/visits/edit/template.hbs @@ -88,7 +88,7 @@ selection=model.examiner }}
- {{em-text property="reasonForVisit" label=(t 'visits.labels.reasonForVisit') rows=3 }} + {{expand-text property="reasonForVisit" label=(t 'visits.labels.reasonForVisit') rows=3 }} {{#if canAddBillingDiagnosis}}
{{select-or-typeahead className="col-sm-5" property="primaryBillingDiagnosis" diff --git a/tests/unit/utils/text-expansion-test.js b/tests/unit/utils/text-expansion-test.js new file mode 100644 index 0000000000..05d8789012 --- /dev/null +++ b/tests/unit/utils/text-expansion-test.js @@ -0,0 +1,18 @@ +import sut from 'hospitalrun/utils/text-expansion'; +import { moduleFor, test } from 'ember-qunit'; + +moduleFor('util:text-expansion', 'Unit | Utils | text-expansion'); + +test('findExpansionSubjects', function(assert) { + assert.deepEqual(sut.findExpansionSubjects(''), []); + assert.deepEqual(sut.findExpansionSubjects('#abc'), ['#abc']); + assert.deepEqual(sut.findExpansionSubjects('#abc #def'), ['#abc', '#def']); +}); + +test('findExpansionSites', function(assert) { + assert.deepEqual(sut.findExpansionSites('abc', []), []); + assert.deepEqual(sut.findExpansionSites('#abc', ['#abc']), [{ index: 0, match: '#abc', term: 'abc' }]); + assert.deepEqual( + sut.findExpansionSites('#abc #abc', ['#abc']), [{ index: 0, match: '#abc', term: 'abc' }, { index: 5, match: '#abc', term: 'abc' }] + ); +}); From a3c0302a49ef73e9b3eb56f0dc8a6b78df4e6e84 Mon Sep 17 00:00:00 2001 From: tangollama Date: Fri, 14 Apr 2017 22:53:34 -0400 Subject: [PATCH 3/5] aslant reconciliation --- app/components/expand-text.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/components/expand-text.js b/app/components/expand-text.js index 38f8a47ac7..de812a2534 100644 --- a/app/components/expand-text.js +++ b/app/components/expand-text.js @@ -11,7 +11,8 @@ export default Ember.Component.extend({ try { let feedbackDiv = document.createElement('div'); feedbackDiv.style.position = 'absolute'; - let textarea = this.$()[0].getElementsByTagName('textarea')[0]; + // let textarea = this.$()[0].getElementsByTagName('textarea')[0]; + let [textarea] = this.$('textarea'); this.set('textarea', textarea); let textPos = textarea.getBoundingClientRect(); let fbStyle = feedbackDiv.style; From 3c4ffa00563968eaab1107e6a719a4388e21eeaa Mon Sep 17 00:00:00 2001 From: tangollama Date: Tue, 18 Apr 2017 14:43:45 -0400 Subject: [PATCH 4/5] adjustments to shortcakes --- app/admin/textreplace/template.hbs | 43 +++++++++++++++--------------- app/locales/en/translations.js | 9 ++++--- 2 files changed, 27 insertions(+), 25 deletions(-) diff --git a/app/admin/textreplace/template.hbs b/app/admin/textreplace/template.hbs index d6915c0846..3c24be7f77 100644 --- a/app/admin/textreplace/template.hbs +++ b/app/admin/textreplace/template.hbs @@ -1,24 +1,26 @@
-

{{t 'admin.text_replacements.existing_repl'}}

-

{{t 'admin.text_replacements.repl_desc'}}

- - - - - - -{{#each model as |expansion|}} - - - - - -{{/each}} - -
{{t 'labels.from'}}{{t 'labels.to'}} -
#{{expansion.from}}{{expansion.to}} - -
+
+

{{t 'admin.text_replacements.repl_desc'}}

+ + + + + + + {{#each model as |expansion|}} + + + + + + {{/each}} + +
{{t 'labels.from'}}{{t 'labels.to'}} +
#{{expansion.from}}{{expansion.to}} + +
+
+

{{t 'admin.text_replacements.create_new'}}

@@ -32,4 +34,3 @@
-
diff --git a/app/locales/en/translations.js b/app/locales/en/translations.js index ba07a26d35..a6678f123c 100644 --- a/app/locales/en/translations.js +++ b/app/locales/en/translations.js @@ -209,10 +209,10 @@ export default { titles: { roleSaved: 'Role Saved' } }, text_replacements: { - create_new: 'Create a new text replacement', - existing_repl: 'Existing replacements', + create_new: 'Create a new shortcode', + existing_repl: 'Existing Shortcodes', repl_desc: 'When entering text, these shortcuts allow you to replace a short sequence of characters with a longer phrase.', - page_title: 'Text Replacements', + page_title: 'Shortcodes', to_replace: 'Text to replace', replace_with: 'Replace with', perform_expand: "Press Enter to replace #{{from}} with '{{to}}'", @@ -1107,11 +1107,12 @@ export default { requests: 'Requests', returnMedication: 'Return Medication', scheduleSurgery: 'Schedule Surgery', + textReplacements: 'Shortcodes', theaterSchedule: 'Theater Schedule', "today'sAppointments": "Today's Appointments", userRoles: 'User Roles', users: 'Users', - text_replacements: 'Text Replacements', + text_replacements: 'Shortcodes', workflow: 'Workflow' } }, From ce721aad7ad3ea1ad45e1324138024c7c705bd83 Mon Sep 17 00:00:00 2001 From: tangollama Date: Wed, 19 Apr 2017 10:16:18 -0400 Subject: [PATCH 5/5] Fixing the underscores in the language translation --- app/admin/textreplace/route.js | 2 +- app/admin/textreplace/template.hbs | 8 ++++---- app/components/expand-text.js | 6 +++--- app/locales/en/translations.js | 21 ++++++++++----------- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/app/admin/textreplace/route.js b/app/admin/textreplace/route.js index 4132671d7b..2705799ea4 100644 --- a/app/admin/textreplace/route.js +++ b/app/admin/textreplace/route.js @@ -2,7 +2,7 @@ import AbstractIndexRoute from 'hospitalrun/routes/abstract-index-route'; import { translationMacro as t } from 'ember-i18n'; export default AbstractIndexRoute.extend({ - pageTitle: t('admin.text_replacements.page_title'), + pageTitle: t('admin.textReplacements.pageTitle'), hideNewButton: true, model() { diff --git a/app/admin/textreplace/template.hbs b/app/admin/textreplace/template.hbs index 3c24be7f77..782f8ba633 100644 --- a/app/admin/textreplace/template.hbs +++ b/app/admin/textreplace/template.hbs @@ -1,6 +1,6 @@
-

{{t 'admin.text_replacements.repl_desc'}}

+

{{t 'admin.textReplacements.replDesc'}}

@@ -23,10 +23,10 @@
-

{{t 'admin.text_replacements.create_new'}}

+

{{t 'admin.textReplacements.createNew'}}

{{#em-form model=newExpansion action="addExpansion" formLayout="horizontal" showErrorsOnFocusIn="true" submitButton=false}} - {{em-input property="from" label=(t 'labels.from') placeholder=(t 'admin.text_replacements.to_replace')}} - {{em-input property="to" label=(t 'labels.from') placeholder=(t 'admin.text_replacements.replace_with')}} + {{em-input property="from" label=(t 'labels.from') placeholder=(t 'admin.textReplacements.toReplace')}} + {{em-input property="to" label=(t 'labels.to') placeholder=(t 'admin.textReplacements.replaceWith')}} {{/em-form}}
{{t 'labels.from'}}