From a002f32239e66a6ecb3f2fb23eb780803ebf0a4f Mon Sep 17 00:00:00 2001 From: cesine Date: Mon, 20 Jul 2020 15:48:30 -0400 Subject: [PATCH 1/2] decaffeinate app and test --- Gruntfile.coffee | 668 ------- Gruntfile.js | 808 ++++++++ .../collections/application-settings.coffee | 13 - app/scripts/collections/collections.coffee | 23 - app/scripts/collections/corpora.coffee | 56 - .../collections/elicitation-methods.coffee | 16 - app/scripts/collections/files.coffee | 84 - app/scripts/collections/forms.coffee | 235 --- app/scripts/collections/keyboards.coffee | 32 - .../collections/language-models.coffee | 17 - app/scripts/collections/languages.coffee | 18 - .../collections/morphological-parsers.coffee | 17 - app/scripts/collections/morphologies.coffee | 15 - .../old-application-settings.coffee | 38 - app/scripts/collections/orthographies.coffee | 18 - app/scripts/collections/pages.coffee | 33 - app/scripts/collections/phonologies.coffee | 15 - app/scripts/collections/resources.coffee | 299 --- app/scripts/collections/searches.coffee | 17 - app/scripts/collections/servers.coffee | 18 - app/scripts/collections/sources.coffee | 15 - app/scripts/collections/speakers.coffee | 15 - app/scripts/collections/subcorpora.coffee | 19 - .../collections/syntactic-categories.coffee | 15 - app/scripts/collections/tags.coffee | 17 - app/scripts/collections/users.coffee | 24 - app/scripts/main.coffee | 78 - .../models/application-settings.coffee | 794 -------- app/scripts/models/base-relational.coffee | 18 - app/scripts/models/base.coffee | 20 - app/scripts/models/collection.coffee | 117 -- app/scripts/models/corpus.coffee | 254 --- app/scripts/models/database.coffee | 18 - app/scripts/models/elicitation-method.coffee | 37 - app/scripts/models/file.coffee | 289 --- app/scripts/models/form.coffee | 968 ---------- app/scripts/models/fst-based.coffee | 129 -- .../models/keyboard-preference-set.coffee | 74 - app/scripts/models/keyboard.coffee | 50 - app/scripts/models/language-model.coffee | 173 -- app/scripts/models/language.coffee | 32 - .../models/morphological-parser.coffee | 245 --- app/scripts/models/morphology.coffee | 122 -- .../models/old-application-settings.coffee | 202 -- app/scripts/models/orthography.coffee | 40 - app/scripts/models/page.coffee | 53 - app/scripts/models/parser-task-set.coffee | 101 - app/scripts/models/phonology.coffee | 200 -- app/scripts/models/resource.coffee | 376 ---- app/scripts/models/search.coffee | 236 --- app/scripts/models/server.coffee | 22 - app/scripts/models/source.coffee | 482 ----- app/scripts/models/speaker.coffee | 44 - app/scripts/models/subcorpus.coffee | 84 - app/scripts/models/syntactic-category.coffee | 43 - app/scripts/models/tag.coffee | 37 - app/scripts/models/user-old.coffee | 171 -- app/scripts/models/user.coffee | 13 - app/scripts/routes/router.coffee | 39 - app/scripts/utils/bibtex.coffee | 129 -- app/scripts/utils/cors.coffee | 159 -- app/scripts/utils/globals.coffee | 216 --- app/scripts/utils/help.coffee | 6 - app/scripts/utils/indexeddb-utils.coffee | 213 --- app/scripts/utils/keyboard-shortcuts.coffee | 95 - app/scripts/utils/paginator.coffee | 137 -- app/scripts/utils/tooltips.coffee | 1268 ------------- app/scripts/utils/utils.coffee | 522 ------ app/scripts/views/active-server.coffee | 111 -- app/scripts/views/add-user.coffee | 157 -- app/scripts/views/alert-dialog.coffee | 297 --- app/scripts/views/app.coffee | 1528 --------------- .../views/application-settings-edit.coffee | 16 - .../views/application-settings-view.coffee | 15 - app/scripts/views/application-settings.coffee | 392 ---- app/scripts/views/apply-control.coffee | 191 -- app/scripts/views/apply-down-control.coffee | 176 -- app/scripts/views/apply-up-control.coffee | 11 - ...-of-objects-with-name-field-display.coffee | 19 - ...of-objects-with-title-field-display.coffee | 19 - ...rray-of-related-files-field-display.coffee | 120 -- ...-of-related-resources-field-display.coffee | 94 - ...elated-resources-representation-set.coffee | 29 - ...array-of-related-tags-field-display.coffee | 57 - ...rray-of-related-users-field-display.coffee | 44 - app/scripts/views/base.coffee | 585 ------ app/scripts/views/boolean-icon-display.coffee | 16 - .../views/boolean-icon-representation.coffee | 18 - app/scripts/views/boolean-select-field.coffee | 32 - .../views/browse-corpus-control.coffee | 115 -- .../browse-search-results-control.coffee | 113 -- app/scripts/views/bytes-field-display.coffee | 29 - .../views/character-names-control.coffee | 102 - .../views/collection-add-widget.coffee | 108 -- .../views/collection-contents-input.coffee | 21 - .../views/collection-contents-view.coffee | 216 --- app/scripts/views/collection-controls.coffee | 21 - app/scripts/views/collection.coffee | 276 --- app/scripts/views/collections.coffee | 56 - app/scripts/views/comment-input.coffee | 25 - .../views/comment-representation.coffee | 15 - .../views/comments-field-display.coffee | 21 - app/scripts/views/comments-field.coffee | 33 - app/scripts/views/comments-input-set.coffee | 53 - .../views/comments-representation-set.coffee | 37 - app/scripts/views/compile-control.coffee | 150 -- app/scripts/views/control.coffee | 186 -- app/scripts/views/controls.coffee | 112 -- app/scripts/views/corpora.coffee | 218 --- app/scripts/views/corpus.coffee | 539 ------ app/scripts/views/count-corpus-control.coffee | 116 -- .../views/count-search-results-control.coffee | 110 -- app/scripts/views/create-corpus.coffee | 212 --- app/scripts/views/csv-import-header.coffee | 128 -- app/scripts/views/csv-import-row.coffee | 1011 ---------- app/scripts/views/csv-import.coffee | 1639 ----------------- app/scripts/views/date-field-display.coffee | 24 - app/scripts/views/date-field.coffee | 21 - app/scripts/views/date-input.coffee | 39 - app/scripts/views/dialog-base.coffee | 166 -- .../display-collection-files-control.coffee | 79 - .../display-collection-forms-control.coffee | 114 -- app/scripts/views/edit-corpus.coffee | 131 -- .../elicitation-method-add-widget.coffee | 41 - ...method-select-field-with-add-button.coffee | 25 - app/scripts/views/elicitation-method.coffee | 36 - app/scripts/views/elicitation-methods.coffee | 25 - .../views/enterer-field-display.coffee | 8 - app/scripts/views/event-based-keyboard.coffee | 118 -- .../views/exporter-collection-csv.coffee | 283 --- app/scripts/views/exporter-dialog.coffee | 171 -- app/scripts/views/exporter-ids.coffee | 109 -- app/scripts/views/exporter-json.coffee | 88 - app/scripts/views/exporter-latex.coffee | 579 ------ ...imple-interlinear-glosses-wordpress.coffee | 151 -- app/scripts/views/exporter-xml.coffee | 232 --- app/scripts/views/exporter.coffee | 214 --- app/scripts/views/field-display.coffee | 168 -- app/scripts/views/field.coffee | 438 ----- app/scripts/views/file-add-widget.coffee | 422 ----- app/scripts/views/file-as-row.coffee | 53 - .../views/file-data-upload-field.coffee | 33 - .../views/file-data-upload-input.coffee | 201 -- app/scripts/views/file-data.coffee | 418 ----- app/scripts/views/file-field-display.coffee | 26 - .../views/file-select-via-search-field.coffee | 12 - .../views/file-select-via-search-input.coffee | 54 - .../views/file-with-parent-file-link.coffee | 42 - app/scripts/views/file.coffee | 216 --- .../files-select-via-search-field.coffee | 11 - .../files-select-via-search-input.coffee | 51 - app/scripts/views/files.coffee | 54 - app/scripts/views/filter-expression.coffee | 898 --------- app/scripts/views/form-add-widget.coffee | 262 --- app/scripts/views/form-add-widget_bk.coffee | 647 ------- app/scripts/views/form-add.coffee | 337 ---- app/scripts/views/form-base.coffee | 977 ---------- app/scripts/views/form-handler-base.coffee | 87 - .../views/form-previous-version.coffee | 46 - app/scripts/views/form-settings.coffee | 94 - app/scripts/views/form.coffee | 136 -- app/scripts/views/forms-search.coffee | 20 - app/scripts/views/forms.coffee | 72 - .../views/generate-and-compile-control.coffee | 204 -- app/scripts/views/generate-control.coffee | 202 -- .../views/get-probabilities-control.coffee | 227 --- .../grammaticality-value-field-display.coffee | 28 - ...grammaticality-value-representation.coffee | 15 - app/scripts/views/help-dialog.coffee | 371 ---- app/scripts/views/home.coffee | 186 -- .../html-snippet-display-circular.coffee | 16 - app/scripts/views/html-snippet-display.coffee | 26 - ...tml-snippet-representation-circular.coffee | 45 - .../views/html-snippet-representation.coffee | 45 - app/scripts/views/importer-dialog.coffee | 1074 ----------- app/scripts/views/input-set.coffee | 224 --- app/scripts/views/input-textarea-input.coffee | 31 - .../views/input-validation-controls.coffee | 41 - app/scripts/views/input-validation.coffee | 119 -- app/scripts/views/input.coffee | 127 -- app/scripts/views/json-field-display.coffee | 15 - app/scripts/views/json-representation.coffee | 11 - .../judgement-value-field-display.coffee | 28 - app/scripts/views/keyboard-add-widget.coffee | 42 - app/scripts/views/keyboard-as-row.coffee | 14 - .../views/keyboard-field-display.coffee | 44 - app/scripts/views/keyboard-field.coffee | 27 - app/scripts/views/keyboard-input.coffee | 656 ------- .../keyboard-preference-set-add-widget.coffee | 46 - .../views/keyboard-preference-set.coffee | 101 - .../keyboard-select-via-search-field.coffee | 10 - .../keyboard-select-via-search-input.coffee | 26 - app/scripts/views/keyboard.coffee | 62 - app/scripts/views/keyboards.coffee | 29 - app/scripts/views/label.coffee | 40 - .../views/language-model-add-widget.coffee | 143 -- .../views/language-model-as-row.coffee | 30 - .../views/language-model-controls.coffee | 53 - ...guage-model-select-via-search-field.coffee | 10 - ...guage-model-select-via-search-input.coffee | 33 - app/scripts/views/language-model.coffee | 116 -- app/scripts/views/language-models.coffee | 24 - app/scripts/views/language.coffee | 61 - app/scripts/views/languages.coffee | 62 - app/scripts/views/login-dialog.coffee | 267 --- app/scripts/views/mainmenu.coffee | 498 ----- .../modified-by-user-field-display.coffee | 36 - ...modified-by-user-representation-set.coffee | 35 - .../modified-by-user-representation.coffee | 15 - .../views/modifier-field-display.coffee | 9 - .../views/morpheme-break-field-display.coffee | 15 - app/scripts/views/morpheme-break-field.coffee | 675 ------- .../morpheme-break-representation.coffee | 14 - .../views/morpheme-gloss-field-display.coffee | 15 - app/scripts/views/morpheme-gloss-field.coffee | 181 -- .../morpheme-gloss-representation.coffee | 19 - .../morphological-parser-add-widget.coffee | 86 - .../views/morphological-parser-as-row.coffee | 31 - .../morphological-parser-controls.coffee | 29 - ...ical-parser-select-via-search-field.coffee | 10 - ...ical-parser-select-via-search-input.coffee | 30 - app/scripts/views/morphological-parser.coffee | 110 -- .../views/morphological-parsers.coffee | 24 - app/scripts/views/morphologies.coffee | 23 - .../views/morphology-add-widget.coffee | 87 - app/scripts/views/morphology-as-row.coffee | 29 - app/scripts/views/morphology-controls.coffee | 148 -- .../morphology-select-via-search-field.coffee | 10 - .../morphology-select-via-search-input.coffee | 28 - app/scripts/views/morphology.coffee | 95 - .../views/multi-element-tag-field.coffee | 53 - .../views/multi-element-tag-input.coffee | 166 -- app/scripts/views/multiselect-field.coffee | 41 - app/scripts/views/multiselect-input.coffee | 51 - ...narrow-phonetic-transcription-field.coffee | 38 - app/scripts/views/notification.coffee | 59 - app/scripts/views/notifier.coffee | 744 -------- .../object-with-name-field-display.coffee | 21 - ...jects-with-title-representation-set.coffee | 33 - ...old-application-settings-add-widget.coffee | 351 ---- .../old-application-settings-resource.coffee | 228 --- .../views/old-application-settings.coffee | 24 - app/scripts/views/orthographies.coffee | 32 - .../views/orthography-add-widget.coffee | 85 - .../views/orthography-field-display.coffee | 39 - ...graphy-select-field-with-add-button.coffee | 28 - app/scripts/views/orthography.coffee | 59 - app/scripts/views/page-add-widget.coffee | 69 - app/scripts/views/page.coffee | 36 - app/scripts/views/pages.coffee | 25 - .../views/pagination-item-table.coffee | 31 - app/scripts/views/pagination-menu-top.coffee | 273 --- app/scripts/views/parse-control.coffee | 179 -- .../views/parser-task-set-add-widget.coffee | 44 - app/scripts/views/parser-task-set.coffee | 137 -- app/scripts/views/password-field.coffee | 47 - app/scripts/views/password-input.coffee | 34 - app/scripts/views/person-field-display.coffee | 24 - app/scripts/views/person-select-field.coffee | 19 - ...honetic-transcription-field-display.coffee | 15 - .../views/phonetic-transcription-field.coffee | 38 - ...onetic-transcription-representation.coffee | 23 - app/scripts/views/phonologies.coffee | 23 - app/scripts/views/phonology-add-widget.coffee | 54 - app/scripts/views/phonology-as-row.coffee | 25 - app/scripts/views/phonology-controls.coffee | 44 - .../phonology-select-via-search-field.coffee | 11 - .../phonology-select-via-search-input.coffee | 27 - app/scripts/views/phonology.coffee | 71 - app/scripts/views/progress-widget.coffee | 61 - app/scripts/views/query-field-display.coffee | 16 - app/scripts/views/query-representation.coffee | 79 - app/scripts/views/register-dialog.coffee | 351 ---- .../views/related-model-display.coffee | 10 - .../views/related-model-representation.coffee | 76 - .../related-resource-field-display.coffee | 131 -- .../related-resource-representation.coffee | 111 -- .../views/related-tag-representation.coffee | 27 - .../views/related-user-field-display.coffee | 60 - ...tional-select-field-with-add-button.coffee | 77 - .../views/relational-select-field.coffee | 24 - app/scripts/views/representation-set.coffee | 101 - app/scripts/views/representation.coffee | 49 - .../views/required-select-field.coffee | 13 - app/scripts/views/resource-add-widget.coffee | 631 ------- app/scripts/views/resource-as-row.coffee | 226 --- .../views/resource-displayer-dialog.coffee | 233 --- .../resource-select-via-search-field.coffee | 28 - .../resource-select-via-search-input.coffee | 632 ------- app/scripts/views/resource.coffee | 1315 ------------- .../views/resources-displayer-dialog.coffee | 207 --- .../resources-select-via-search-field.coffee | 21 - .../resources-select-via-search-input.coffee | 278 --- app/scripts/views/resources.coffee | 1440 --------------- app/scripts/views/run-tests-control.coffee | 172 -- app/scripts/views/script-display.coffee | 24 - app/scripts/views/script-field.coffee | 24 - app/scripts/views/script-input.coffee | 20 - .../views/script-representation.coffee | 14 - app/scripts/views/search-add-widget.coffee | 54 - app/scripts/views/search-as-row.coffee | 14 - app/scripts/views/search-controls.coffee | 23 - app/scripts/views/search-field-display.coffee | 24 - app/scripts/views/search-field.coffee | 50 - app/scripts/views/search-input.coffee | 52 - .../search-select-via-search-field.coffee | 11 - .../search-select-via-search-input.coffee | 31 - app/scripts/views/search-widget.coffee | 838 --------- app/scripts/views/search.coffee | 54 - app/scripts/views/searches.coffee | 24 - app/scripts/views/select-field.coffee | 45 - .../views/select-input-with-add-button.coffee | 29 - app/scripts/views/select-input.coffee | 32 - .../views/select-textarea-button-input.coffee | 78 - .../views/select-textarea-input.coffee | 65 - .../views/selected-resource-wrapper.coffee | 41 - .../views/serve-compiled-control.coffee | 129 -- app/scripts/views/server.coffee | 254 --- app/scripts/views/servers.coffee | 196 -- app/scripts/views/settings.coffee | 330 ---- app/scripts/views/smart-query-preview.coffee | 125 -- app/scripts/views/source-add-widget.coffee | 211 --- app/scripts/views/source-as-row.coffee | 40 - app/scripts/views/source-field-display.coffee | 43 - app/scripts/views/source-select-field.coffee | 38 - .../source-select-via-search-field.coffee | 10 - .../source-select-via-search-input.coffee | 42 - app/scripts/views/source.coffee | 197 -- app/scripts/views/sources.coffee | 25 - app/scripts/views/speaker-add-widget.coffee | 58 - .../views/speaker-field-display.coffee | 70 - ...peaker-select-field-with-add-button.coffee | 26 - app/scripts/views/speaker.coffee | 45 - app/scripts/views/speakers.coffee | 25 - app/scripts/views/subcorpora.coffee | 28 - app/scripts/views/subcorpus-add-widget.coffee | 52 - app/scripts/views/subcorpus-as-row.coffee | 35 - app/scripts/views/subcorpus-controls.coffee | 20 - .../subcorpus-select-via-search-field.coffee | 10 - .../subcorpus-select-via-search-input.coffee | 32 - app/scripts/views/subcorpus.coffee | 81 - .../views/suggestion-receiver-field.coffee | 233 --- app/scripts/views/syntactic-categories.coffee | 26 - .../syntactic-category-add-widget.coffee | 54 - ...tegory-select-field-with-add-button.coffee | 21 - app/scripts/views/syntactic-category.coffee | 37 - app/scripts/views/tag-add-widget.coffee | 42 - app/scripts/views/tag-field-display.coffee | 28 - app/scripts/views/tag.coffee | 32 - app/scripts/views/tags.coffee | 26 - app/scripts/views/tasks-dialog.coffee | 364 ---- .../views/test-validation-control.coffee | 117 -- .../views/textarea-button-input.coffee | 64 - .../views/textarea-field-backup.coffee | 108 -- app/scripts/views/textarea-field.coffee | 12 - app/scripts/views/textarea-input.coffee | 36 - app/scripts/views/top-label.coffee | 16 - .../views/tranlsation-representation.coffee | 15 - .../views/transcription-base-field.coffee | 408 ---- .../transcription-grammaticality-field.coffee | 82 - .../views/translation-representation.coffee | 32 - .../views/translations-field-display.coffee | 21 - app/scripts/views/translations-field.coffee | 44 - .../views/translations-input-set.coffee | 77 - .../translations-representation-set.coffee | 34 - .../views/unicode-string-field-display.coffee | 35 - app/scripts/views/user-add-widget.coffee | 113 -- app/scripts/views/user-as-row.coffee | 25 - app/scripts/views/user-old-circular.coffee | 22 - app/scripts/views/user-old.coffee | 98 - .../user-select-field-with-add-button.coffee | 30 - app/scripts/views/user-select-field.coffee | 21 - app/scripts/views/user.coffee | 82 - .../users-select-via-search-field.coffee | 10 - .../users-select-via-search-input.coffee | 91 - app/scripts/views/users.coffee | 35 - .../views/utterance-judgement-field.coffee | 38 - app/scripts/views/value-representation.coffee | 10 - test/collections/forms.spec.coffee | 50 - test/collections/forms.spec.js | 69 + .../collections/pages.spec.js | 0 test/models/application-settings.spec.coffee | 321 ---- test/models/application-settings.spec.js | 344 ++++ test/models/base.spec.coffee | 59 - test/models/base.spec.js | 71 + .../pages.spec.coffee => models/file.spec.js} | 0 test/models/form.spec.coffee | 260 --- test/models/form.spec.js | 335 ++++ test/models/page.spec.coffee | 0 .../models/{file.spec.coffee => page.spec.js} | 0 test/routers/router.spec.coffee | 9 - test/routers/router.spec.js | 16 + test/spec/test.coffee | 8 - test/spec/test.js | 9 + test/specrunner.coffee | 82 - test/specrunner.js | 93 + test/utils/indexeddb-utils.spec.coffee | 414 ----- test/utils/indexeddb-utils.spec.js | 577 ++++++ test/utils/utils.spec.coffee | 563 ------ test/utils/utils.spec.js | 553 ++++++ test/views/app.spec.coffee | 174 -- test/views/app.spec.js | 190 ++ test/views/application-settings.spec.coffee | 239 --- test/views/application-settings.spec.js | 257 +++ test/views/base.spec.coffee | 168 -- test/views/base.spec.js | 184 ++ test/views/basepage.spec.coffee | 6 - test/views/basepage.spec.js | 11 + test/views/form-add.spec.coffee | 6 - test/views/form-add.spec.js | 11 + test/views/{form.spec.coffee => form.spec.js} | 422 +++-- test/views/forms-browse.spec.coffee | 6 - test/views/forms-browse.spec.js | 11 + test/views/login-dialog.spec.coffee | 241 --- test/views/login-dialog.spec.js | 261 +++ test/views/mainmenu.spec.coffee | 183 -- test/views/mainmenu.spec.js | 229 +++ test/views/page.spec.coffee | 6 - test/views/page.spec.js | 11 + test/views/pages.spec.coffee | 6 - test/views/pages.spec.js | 11 + 421 files changed, 4279 insertions(+), 54117 deletions(-) delete mode 100644 Gruntfile.coffee create mode 100644 Gruntfile.js delete mode 100644 app/scripts/collections/application-settings.coffee delete mode 100644 app/scripts/collections/collections.coffee delete mode 100644 app/scripts/collections/corpora.coffee delete mode 100644 app/scripts/collections/elicitation-methods.coffee delete mode 100644 app/scripts/collections/files.coffee delete mode 100644 app/scripts/collections/forms.coffee delete mode 100644 app/scripts/collections/keyboards.coffee delete mode 100644 app/scripts/collections/language-models.coffee delete mode 100644 app/scripts/collections/languages.coffee delete mode 100644 app/scripts/collections/morphological-parsers.coffee delete mode 100644 app/scripts/collections/morphologies.coffee delete mode 100644 app/scripts/collections/old-application-settings.coffee delete mode 100644 app/scripts/collections/orthographies.coffee delete mode 100644 app/scripts/collections/pages.coffee delete mode 100644 app/scripts/collections/phonologies.coffee delete mode 100644 app/scripts/collections/resources.coffee delete mode 100644 app/scripts/collections/searches.coffee delete mode 100644 app/scripts/collections/servers.coffee delete mode 100644 app/scripts/collections/sources.coffee delete mode 100644 app/scripts/collections/speakers.coffee delete mode 100644 app/scripts/collections/subcorpora.coffee delete mode 100644 app/scripts/collections/syntactic-categories.coffee delete mode 100644 app/scripts/collections/tags.coffee delete mode 100644 app/scripts/collections/users.coffee delete mode 100644 app/scripts/main.coffee delete mode 100644 app/scripts/models/application-settings.coffee delete mode 100644 app/scripts/models/base-relational.coffee delete mode 100644 app/scripts/models/base.coffee delete mode 100644 app/scripts/models/collection.coffee delete mode 100644 app/scripts/models/corpus.coffee delete mode 100644 app/scripts/models/database.coffee delete mode 100644 app/scripts/models/elicitation-method.coffee delete mode 100644 app/scripts/models/file.coffee delete mode 100644 app/scripts/models/form.coffee delete mode 100644 app/scripts/models/fst-based.coffee delete mode 100644 app/scripts/models/keyboard-preference-set.coffee delete mode 100644 app/scripts/models/keyboard.coffee delete mode 100644 app/scripts/models/language-model.coffee delete mode 100644 app/scripts/models/language.coffee delete mode 100644 app/scripts/models/morphological-parser.coffee delete mode 100644 app/scripts/models/morphology.coffee delete mode 100644 app/scripts/models/old-application-settings.coffee delete mode 100644 app/scripts/models/orthography.coffee delete mode 100644 app/scripts/models/page.coffee delete mode 100644 app/scripts/models/parser-task-set.coffee delete mode 100644 app/scripts/models/phonology.coffee delete mode 100644 app/scripts/models/resource.coffee delete mode 100644 app/scripts/models/search.coffee delete mode 100644 app/scripts/models/server.coffee delete mode 100644 app/scripts/models/source.coffee delete mode 100644 app/scripts/models/speaker.coffee delete mode 100644 app/scripts/models/subcorpus.coffee delete mode 100644 app/scripts/models/syntactic-category.coffee delete mode 100644 app/scripts/models/tag.coffee delete mode 100644 app/scripts/models/user-old.coffee delete mode 100644 app/scripts/models/user.coffee delete mode 100644 app/scripts/routes/router.coffee delete mode 100644 app/scripts/utils/bibtex.coffee delete mode 100644 app/scripts/utils/cors.coffee delete mode 100644 app/scripts/utils/globals.coffee delete mode 100644 app/scripts/utils/help.coffee delete mode 100644 app/scripts/utils/indexeddb-utils.coffee delete mode 100644 app/scripts/utils/keyboard-shortcuts.coffee delete mode 100644 app/scripts/utils/paginator.coffee delete mode 100644 app/scripts/utils/tooltips.coffee delete mode 100644 app/scripts/utils/utils.coffee delete mode 100644 app/scripts/views/active-server.coffee delete mode 100644 app/scripts/views/add-user.coffee delete mode 100644 app/scripts/views/alert-dialog.coffee delete mode 100644 app/scripts/views/app.coffee delete mode 100644 app/scripts/views/application-settings-edit.coffee delete mode 100644 app/scripts/views/application-settings-view.coffee delete mode 100644 app/scripts/views/application-settings.coffee delete mode 100644 app/scripts/views/apply-control.coffee delete mode 100644 app/scripts/views/apply-down-control.coffee delete mode 100644 app/scripts/views/apply-up-control.coffee delete mode 100644 app/scripts/views/array-of-objects-with-name-field-display.coffee delete mode 100644 app/scripts/views/array-of-objects-with-title-field-display.coffee delete mode 100644 app/scripts/views/array-of-related-files-field-display.coffee delete mode 100644 app/scripts/views/array-of-related-resources-field-display.coffee delete mode 100644 app/scripts/views/array-of-related-resources-representation-set.coffee delete mode 100644 app/scripts/views/array-of-related-tags-field-display.coffee delete mode 100644 app/scripts/views/array-of-related-users-field-display.coffee delete mode 100644 app/scripts/views/base.coffee delete mode 100644 app/scripts/views/boolean-icon-display.coffee delete mode 100644 app/scripts/views/boolean-icon-representation.coffee delete mode 100644 app/scripts/views/boolean-select-field.coffee delete mode 100644 app/scripts/views/browse-corpus-control.coffee delete mode 100644 app/scripts/views/browse-search-results-control.coffee delete mode 100644 app/scripts/views/bytes-field-display.coffee delete mode 100644 app/scripts/views/character-names-control.coffee delete mode 100644 app/scripts/views/collection-add-widget.coffee delete mode 100644 app/scripts/views/collection-contents-input.coffee delete mode 100644 app/scripts/views/collection-contents-view.coffee delete mode 100644 app/scripts/views/collection-controls.coffee delete mode 100644 app/scripts/views/collection.coffee delete mode 100644 app/scripts/views/collections.coffee delete mode 100644 app/scripts/views/comment-input.coffee delete mode 100644 app/scripts/views/comment-representation.coffee delete mode 100644 app/scripts/views/comments-field-display.coffee delete mode 100644 app/scripts/views/comments-field.coffee delete mode 100644 app/scripts/views/comments-input-set.coffee delete mode 100644 app/scripts/views/comments-representation-set.coffee delete mode 100644 app/scripts/views/compile-control.coffee delete mode 100644 app/scripts/views/control.coffee delete mode 100644 app/scripts/views/controls.coffee delete mode 100644 app/scripts/views/corpora.coffee delete mode 100644 app/scripts/views/corpus.coffee delete mode 100644 app/scripts/views/count-corpus-control.coffee delete mode 100644 app/scripts/views/count-search-results-control.coffee delete mode 100644 app/scripts/views/create-corpus.coffee delete mode 100644 app/scripts/views/csv-import-header.coffee delete mode 100644 app/scripts/views/csv-import-row.coffee delete mode 100644 app/scripts/views/csv-import.coffee delete mode 100644 app/scripts/views/date-field-display.coffee delete mode 100644 app/scripts/views/date-field.coffee delete mode 100644 app/scripts/views/date-input.coffee delete mode 100644 app/scripts/views/dialog-base.coffee delete mode 100644 app/scripts/views/display-collection-files-control.coffee delete mode 100644 app/scripts/views/display-collection-forms-control.coffee delete mode 100644 app/scripts/views/edit-corpus.coffee delete mode 100644 app/scripts/views/elicitation-method-add-widget.coffee delete mode 100644 app/scripts/views/elicitation-method-select-field-with-add-button.coffee delete mode 100644 app/scripts/views/elicitation-method.coffee delete mode 100644 app/scripts/views/elicitation-methods.coffee delete mode 100644 app/scripts/views/enterer-field-display.coffee delete mode 100644 app/scripts/views/event-based-keyboard.coffee delete mode 100644 app/scripts/views/exporter-collection-csv.coffee delete mode 100644 app/scripts/views/exporter-dialog.coffee delete mode 100644 app/scripts/views/exporter-ids.coffee delete mode 100644 app/scripts/views/exporter-json.coffee delete mode 100644 app/scripts/views/exporter-latex.coffee delete mode 100644 app/scripts/views/exporter-simple-interlinear-glosses-wordpress.coffee delete mode 100644 app/scripts/views/exporter-xml.coffee delete mode 100644 app/scripts/views/exporter.coffee delete mode 100644 app/scripts/views/field-display.coffee delete mode 100644 app/scripts/views/field.coffee delete mode 100644 app/scripts/views/file-add-widget.coffee delete mode 100644 app/scripts/views/file-as-row.coffee delete mode 100644 app/scripts/views/file-data-upload-field.coffee delete mode 100644 app/scripts/views/file-data-upload-input.coffee delete mode 100644 app/scripts/views/file-data.coffee delete mode 100644 app/scripts/views/file-field-display.coffee delete mode 100644 app/scripts/views/file-select-via-search-field.coffee delete mode 100644 app/scripts/views/file-select-via-search-input.coffee delete mode 100644 app/scripts/views/file-with-parent-file-link.coffee delete mode 100644 app/scripts/views/file.coffee delete mode 100644 app/scripts/views/files-select-via-search-field.coffee delete mode 100644 app/scripts/views/files-select-via-search-input.coffee delete mode 100644 app/scripts/views/files.coffee delete mode 100644 app/scripts/views/filter-expression.coffee delete mode 100644 app/scripts/views/form-add-widget.coffee delete mode 100644 app/scripts/views/form-add-widget_bk.coffee delete mode 100644 app/scripts/views/form-add.coffee delete mode 100644 app/scripts/views/form-base.coffee delete mode 100644 app/scripts/views/form-handler-base.coffee delete mode 100644 app/scripts/views/form-previous-version.coffee delete mode 100644 app/scripts/views/form-settings.coffee delete mode 100644 app/scripts/views/form.coffee delete mode 100644 app/scripts/views/forms-search.coffee delete mode 100644 app/scripts/views/forms.coffee delete mode 100644 app/scripts/views/generate-and-compile-control.coffee delete mode 100644 app/scripts/views/generate-control.coffee delete mode 100644 app/scripts/views/get-probabilities-control.coffee delete mode 100644 app/scripts/views/grammaticality-value-field-display.coffee delete mode 100644 app/scripts/views/grammaticality-value-representation.coffee delete mode 100644 app/scripts/views/help-dialog.coffee delete mode 100644 app/scripts/views/home.coffee delete mode 100644 app/scripts/views/html-snippet-display-circular.coffee delete mode 100644 app/scripts/views/html-snippet-display.coffee delete mode 100644 app/scripts/views/html-snippet-representation-circular.coffee delete mode 100644 app/scripts/views/html-snippet-representation.coffee delete mode 100644 app/scripts/views/importer-dialog.coffee delete mode 100644 app/scripts/views/input-set.coffee delete mode 100644 app/scripts/views/input-textarea-input.coffee delete mode 100644 app/scripts/views/input-validation-controls.coffee delete mode 100644 app/scripts/views/input-validation.coffee delete mode 100644 app/scripts/views/input.coffee delete mode 100644 app/scripts/views/json-field-display.coffee delete mode 100644 app/scripts/views/json-representation.coffee delete mode 100644 app/scripts/views/judgement-value-field-display.coffee delete mode 100644 app/scripts/views/keyboard-add-widget.coffee delete mode 100644 app/scripts/views/keyboard-as-row.coffee delete mode 100644 app/scripts/views/keyboard-field-display.coffee delete mode 100644 app/scripts/views/keyboard-field.coffee delete mode 100644 app/scripts/views/keyboard-input.coffee delete mode 100644 app/scripts/views/keyboard-preference-set-add-widget.coffee delete mode 100644 app/scripts/views/keyboard-preference-set.coffee delete mode 100644 app/scripts/views/keyboard-select-via-search-field.coffee delete mode 100644 app/scripts/views/keyboard-select-via-search-input.coffee delete mode 100644 app/scripts/views/keyboard.coffee delete mode 100644 app/scripts/views/keyboards.coffee delete mode 100644 app/scripts/views/label.coffee delete mode 100644 app/scripts/views/language-model-add-widget.coffee delete mode 100644 app/scripts/views/language-model-as-row.coffee delete mode 100644 app/scripts/views/language-model-controls.coffee delete mode 100644 app/scripts/views/language-model-select-via-search-field.coffee delete mode 100644 app/scripts/views/language-model-select-via-search-input.coffee delete mode 100644 app/scripts/views/language-model.coffee delete mode 100644 app/scripts/views/language-models.coffee delete mode 100644 app/scripts/views/language.coffee delete mode 100644 app/scripts/views/languages.coffee delete mode 100644 app/scripts/views/login-dialog.coffee delete mode 100644 app/scripts/views/mainmenu.coffee delete mode 100644 app/scripts/views/modified-by-user-field-display.coffee delete mode 100644 app/scripts/views/modified-by-user-representation-set.coffee delete mode 100644 app/scripts/views/modified-by-user-representation.coffee delete mode 100644 app/scripts/views/modifier-field-display.coffee delete mode 100644 app/scripts/views/morpheme-break-field-display.coffee delete mode 100644 app/scripts/views/morpheme-break-field.coffee delete mode 100644 app/scripts/views/morpheme-break-representation.coffee delete mode 100644 app/scripts/views/morpheme-gloss-field-display.coffee delete mode 100644 app/scripts/views/morpheme-gloss-field.coffee delete mode 100644 app/scripts/views/morpheme-gloss-representation.coffee delete mode 100644 app/scripts/views/morphological-parser-add-widget.coffee delete mode 100644 app/scripts/views/morphological-parser-as-row.coffee delete mode 100644 app/scripts/views/morphological-parser-controls.coffee delete mode 100644 app/scripts/views/morphological-parser-select-via-search-field.coffee delete mode 100644 app/scripts/views/morphological-parser-select-via-search-input.coffee delete mode 100644 app/scripts/views/morphological-parser.coffee delete mode 100644 app/scripts/views/morphological-parsers.coffee delete mode 100644 app/scripts/views/morphologies.coffee delete mode 100644 app/scripts/views/morphology-add-widget.coffee delete mode 100644 app/scripts/views/morphology-as-row.coffee delete mode 100644 app/scripts/views/morphology-controls.coffee delete mode 100644 app/scripts/views/morphology-select-via-search-field.coffee delete mode 100644 app/scripts/views/morphology-select-via-search-input.coffee delete mode 100644 app/scripts/views/morphology.coffee delete mode 100644 app/scripts/views/multi-element-tag-field.coffee delete mode 100644 app/scripts/views/multi-element-tag-input.coffee delete mode 100644 app/scripts/views/multiselect-field.coffee delete mode 100644 app/scripts/views/multiselect-input.coffee delete mode 100644 app/scripts/views/narrow-phonetic-transcription-field.coffee delete mode 100644 app/scripts/views/notification.coffee delete mode 100644 app/scripts/views/notifier.coffee delete mode 100644 app/scripts/views/object-with-name-field-display.coffee delete mode 100644 app/scripts/views/objects-with-title-representation-set.coffee delete mode 100644 app/scripts/views/old-application-settings-add-widget.coffee delete mode 100644 app/scripts/views/old-application-settings-resource.coffee delete mode 100644 app/scripts/views/old-application-settings.coffee delete mode 100644 app/scripts/views/orthographies.coffee delete mode 100644 app/scripts/views/orthography-add-widget.coffee delete mode 100644 app/scripts/views/orthography-field-display.coffee delete mode 100644 app/scripts/views/orthography-select-field-with-add-button.coffee delete mode 100644 app/scripts/views/orthography.coffee delete mode 100644 app/scripts/views/page-add-widget.coffee delete mode 100644 app/scripts/views/page.coffee delete mode 100644 app/scripts/views/pages.coffee delete mode 100644 app/scripts/views/pagination-item-table.coffee delete mode 100644 app/scripts/views/pagination-menu-top.coffee delete mode 100644 app/scripts/views/parse-control.coffee delete mode 100644 app/scripts/views/parser-task-set-add-widget.coffee delete mode 100644 app/scripts/views/parser-task-set.coffee delete mode 100644 app/scripts/views/password-field.coffee delete mode 100644 app/scripts/views/password-input.coffee delete mode 100644 app/scripts/views/person-field-display.coffee delete mode 100644 app/scripts/views/person-select-field.coffee delete mode 100644 app/scripts/views/phonetic-transcription-field-display.coffee delete mode 100644 app/scripts/views/phonetic-transcription-field.coffee delete mode 100644 app/scripts/views/phonetic-transcription-representation.coffee delete mode 100644 app/scripts/views/phonologies.coffee delete mode 100644 app/scripts/views/phonology-add-widget.coffee delete mode 100644 app/scripts/views/phonology-as-row.coffee delete mode 100644 app/scripts/views/phonology-controls.coffee delete mode 100644 app/scripts/views/phonology-select-via-search-field.coffee delete mode 100644 app/scripts/views/phonology-select-via-search-input.coffee delete mode 100644 app/scripts/views/phonology.coffee delete mode 100644 app/scripts/views/progress-widget.coffee delete mode 100644 app/scripts/views/query-field-display.coffee delete mode 100644 app/scripts/views/query-representation.coffee delete mode 100644 app/scripts/views/register-dialog.coffee delete mode 100644 app/scripts/views/related-model-display.coffee delete mode 100644 app/scripts/views/related-model-representation.coffee delete mode 100644 app/scripts/views/related-resource-field-display.coffee delete mode 100644 app/scripts/views/related-resource-representation.coffee delete mode 100644 app/scripts/views/related-tag-representation.coffee delete mode 100644 app/scripts/views/related-user-field-display.coffee delete mode 100644 app/scripts/views/relational-select-field-with-add-button.coffee delete mode 100644 app/scripts/views/relational-select-field.coffee delete mode 100644 app/scripts/views/representation-set.coffee delete mode 100644 app/scripts/views/representation.coffee delete mode 100644 app/scripts/views/required-select-field.coffee delete mode 100644 app/scripts/views/resource-add-widget.coffee delete mode 100644 app/scripts/views/resource-as-row.coffee delete mode 100644 app/scripts/views/resource-displayer-dialog.coffee delete mode 100644 app/scripts/views/resource-select-via-search-field.coffee delete mode 100644 app/scripts/views/resource-select-via-search-input.coffee delete mode 100644 app/scripts/views/resource.coffee delete mode 100644 app/scripts/views/resources-displayer-dialog.coffee delete mode 100644 app/scripts/views/resources-select-via-search-field.coffee delete mode 100644 app/scripts/views/resources-select-via-search-input.coffee delete mode 100644 app/scripts/views/resources.coffee delete mode 100644 app/scripts/views/run-tests-control.coffee delete mode 100644 app/scripts/views/script-display.coffee delete mode 100644 app/scripts/views/script-field.coffee delete mode 100644 app/scripts/views/script-input.coffee delete mode 100644 app/scripts/views/script-representation.coffee delete mode 100644 app/scripts/views/search-add-widget.coffee delete mode 100644 app/scripts/views/search-as-row.coffee delete mode 100644 app/scripts/views/search-controls.coffee delete mode 100644 app/scripts/views/search-field-display.coffee delete mode 100644 app/scripts/views/search-field.coffee delete mode 100644 app/scripts/views/search-input.coffee delete mode 100644 app/scripts/views/search-select-via-search-field.coffee delete mode 100644 app/scripts/views/search-select-via-search-input.coffee delete mode 100644 app/scripts/views/search-widget.coffee delete mode 100644 app/scripts/views/search.coffee delete mode 100644 app/scripts/views/searches.coffee delete mode 100644 app/scripts/views/select-field.coffee delete mode 100644 app/scripts/views/select-input-with-add-button.coffee delete mode 100644 app/scripts/views/select-input.coffee delete mode 100644 app/scripts/views/select-textarea-button-input.coffee delete mode 100644 app/scripts/views/select-textarea-input.coffee delete mode 100644 app/scripts/views/selected-resource-wrapper.coffee delete mode 100644 app/scripts/views/serve-compiled-control.coffee delete mode 100644 app/scripts/views/server.coffee delete mode 100644 app/scripts/views/servers.coffee delete mode 100644 app/scripts/views/settings.coffee delete mode 100644 app/scripts/views/smart-query-preview.coffee delete mode 100644 app/scripts/views/source-add-widget.coffee delete mode 100644 app/scripts/views/source-as-row.coffee delete mode 100644 app/scripts/views/source-field-display.coffee delete mode 100644 app/scripts/views/source-select-field.coffee delete mode 100644 app/scripts/views/source-select-via-search-field.coffee delete mode 100644 app/scripts/views/source-select-via-search-input.coffee delete mode 100644 app/scripts/views/source.coffee delete mode 100644 app/scripts/views/sources.coffee delete mode 100644 app/scripts/views/speaker-add-widget.coffee delete mode 100644 app/scripts/views/speaker-field-display.coffee delete mode 100644 app/scripts/views/speaker-select-field-with-add-button.coffee delete mode 100644 app/scripts/views/speaker.coffee delete mode 100644 app/scripts/views/speakers.coffee delete mode 100644 app/scripts/views/subcorpora.coffee delete mode 100644 app/scripts/views/subcorpus-add-widget.coffee delete mode 100644 app/scripts/views/subcorpus-as-row.coffee delete mode 100644 app/scripts/views/subcorpus-controls.coffee delete mode 100644 app/scripts/views/subcorpus-select-via-search-field.coffee delete mode 100644 app/scripts/views/subcorpus-select-via-search-input.coffee delete mode 100644 app/scripts/views/subcorpus.coffee delete mode 100644 app/scripts/views/suggestion-receiver-field.coffee delete mode 100644 app/scripts/views/syntactic-categories.coffee delete mode 100644 app/scripts/views/syntactic-category-add-widget.coffee delete mode 100644 app/scripts/views/syntactic-category-select-field-with-add-button.coffee delete mode 100644 app/scripts/views/syntactic-category.coffee delete mode 100644 app/scripts/views/tag-add-widget.coffee delete mode 100644 app/scripts/views/tag-field-display.coffee delete mode 100644 app/scripts/views/tag.coffee delete mode 100644 app/scripts/views/tags.coffee delete mode 100644 app/scripts/views/tasks-dialog.coffee delete mode 100644 app/scripts/views/test-validation-control.coffee delete mode 100644 app/scripts/views/textarea-button-input.coffee delete mode 100644 app/scripts/views/textarea-field-backup.coffee delete mode 100644 app/scripts/views/textarea-field.coffee delete mode 100644 app/scripts/views/textarea-input.coffee delete mode 100644 app/scripts/views/top-label.coffee delete mode 100644 app/scripts/views/tranlsation-representation.coffee delete mode 100644 app/scripts/views/transcription-base-field.coffee delete mode 100644 app/scripts/views/transcription-grammaticality-field.coffee delete mode 100644 app/scripts/views/translation-representation.coffee delete mode 100644 app/scripts/views/translations-field-display.coffee delete mode 100644 app/scripts/views/translations-field.coffee delete mode 100644 app/scripts/views/translations-input-set.coffee delete mode 100644 app/scripts/views/translations-representation-set.coffee delete mode 100644 app/scripts/views/unicode-string-field-display.coffee delete mode 100644 app/scripts/views/user-add-widget.coffee delete mode 100644 app/scripts/views/user-as-row.coffee delete mode 100644 app/scripts/views/user-old-circular.coffee delete mode 100644 app/scripts/views/user-old.coffee delete mode 100644 app/scripts/views/user-select-field-with-add-button.coffee delete mode 100644 app/scripts/views/user-select-field.coffee delete mode 100644 app/scripts/views/user.coffee delete mode 100644 app/scripts/views/users-select-via-search-field.coffee delete mode 100644 app/scripts/views/users-select-via-search-input.coffee delete mode 100644 app/scripts/views/users.coffee delete mode 100644 app/scripts/views/utterance-judgement-field.coffee delete mode 100644 app/scripts/views/value-representation.coffee delete mode 100644 test/collections/forms.spec.coffee create mode 100644 test/collections/forms.spec.js rename app/scripts/views/forms-browse.coffee => test/collections/pages.spec.js (100%) delete mode 100644 test/models/application-settings.spec.coffee create mode 100644 test/models/application-settings.spec.js delete mode 100644 test/models/base.spec.coffee create mode 100644 test/models/base.spec.js rename test/{collections/pages.spec.coffee => models/file.spec.js} (100%) delete mode 100644 test/models/form.spec.coffee create mode 100644 test/models/form.spec.js delete mode 100644 test/models/page.spec.coffee rename test/models/{file.spec.coffee => page.spec.js} (100%) delete mode 100644 test/routers/router.spec.coffee create mode 100644 test/routers/router.spec.js delete mode 100644 test/spec/test.coffee create mode 100644 test/spec/test.js delete mode 100644 test/specrunner.coffee create mode 100644 test/specrunner.js delete mode 100644 test/utils/indexeddb-utils.spec.coffee create mode 100644 test/utils/indexeddb-utils.spec.js delete mode 100644 test/utils/utils.spec.coffee create mode 100644 test/utils/utils.spec.js delete mode 100644 test/views/app.spec.coffee create mode 100644 test/views/app.spec.js delete mode 100644 test/views/application-settings.spec.coffee create mode 100644 test/views/application-settings.spec.js delete mode 100644 test/views/base.spec.coffee create mode 100644 test/views/base.spec.js delete mode 100644 test/views/basepage.spec.coffee create mode 100644 test/views/basepage.spec.js delete mode 100644 test/views/form-add.spec.coffee create mode 100644 test/views/form-add.spec.js rename test/views/{form.spec.coffee => form.spec.js} (74%) delete mode 100644 test/views/forms-browse.spec.coffee create mode 100644 test/views/forms-browse.spec.js delete mode 100644 test/views/login-dialog.spec.coffee create mode 100644 test/views/login-dialog.spec.js delete mode 100644 test/views/mainmenu.spec.coffee create mode 100644 test/views/mainmenu.spec.js delete mode 100644 test/views/page.spec.coffee create mode 100644 test/views/page.spec.js delete mode 100644 test/views/pages.spec.coffee create mode 100644 test/views/pages.spec.js diff --git a/Gruntfile.coffee b/Gruntfile.coffee deleted file mode 100644 index 55fd3f6..0000000 --- a/Gruntfile.coffee +++ /dev/null @@ -1,668 +0,0 @@ -'use strict' -LIVERELOAD_PORT = 35729 -SERVER_PORT = 9000 -lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT}) -mountFolder = (connect, dir) -> - connect.static(require('path').resolve(dir)) - -# # Globbing -# for performance reasons we're only matching one level down: -# 'test/spec/{,*/}*.js' -# use this if you want to match all subfolders: -# 'test/spec/**/*.js' -# templateFramework: 'lodash' - -module.exports = (grunt) -> - - # show elapsed time at the end - require('time-grunt') grunt - - # load all grunt tasks - require('load-grunt-tasks') grunt - - # configurable paths - yeomanConfig = app: 'app', dist: 'dist' - - grunt.initConfig - - markdown: - all: - files: [ - expand: true, - flatten: true, - src: '<%= yeoman.app %>/help/src/*.md' - dest: '<%= yeoman.app %>/help/html/' - ext: '.html' - ] - options: - template: '<%= yeoman.app %>/help/src/template.jst' - - yeoman: yeomanConfig - - watch: - options: - nospawn: true - livereload: true - help: - files: ['<%= yeoman.app %>/help/src/*.md'] - tasks: ['markdown:all'] - coffee: - files: ['<%= yeoman.app %>/scripts/{,*/}*.coffee'] - tasks: ['copy:coffee', 'coffee:serve'] - coffeeTest: - #files: ['test/spec/{,*/}*.coffee'] - files: ['test/**/*.coffee'] - tasks: ['coffee:test'] - compass: - files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'] - tasks: ['compass'] - livereload: - options: - livereload: grunt.option('livereloadport') || LIVERELOAD_PORT - files: [ - '<%= yeoman.app %>/*.html' - '{.tmp,<%= yeoman.app %>}/styles/{,*/}*.css' - '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js' - '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}' - '<%= yeoman.app %>/scripts/templates/*.{ejs,mustache,hbs}' - 'test/spec/**/*.js' - ] - jst: - files: ['<%= yeoman.app %>/scripts/templates/*.ejs'] - tasks: ['jst'] - eco: - files: ['<%= yeoman.app %>/scripts/templates/{,*/}*.eco'] - tasks: ['eco'] - test: - files: ['<%= yeoman.app %>/scripts/{,*/}*.js', 'test/spec/**/*.js'] - tasks: ['test:true'] - - docco: - src: ['.doctmp/*.coffee'] - #src: ['<%= yeoman.app %>/scripts/**/*.coffee'] - options: - output: 'docs/' - - connect: - options: - port: grunt.option('port') || SERVER_PORT - # change this to '0.0.0.0' to access the server from outside - #hostname: 'localhost' - hostname: '0.0.0.0' - livereload: - options: - middleware: (connect) -> - [ - lrSnippet, - mountFolder(connect, '.tmp') - mountFolder(connect, yeomanConfig.app) - ] - test: - options: - port: 9001 - middleware: (connect) -> - [ - lrSnippet - mountFolder(connect, '.tmp') - mountFolder(connect, 'test') - mountFolder(connect, yeomanConfig.app) - ] - dist: - options: - middleware: (connect) -> - [mountFolder(connect, yeomanConfig.dist)] - - open: - server: - path: 'http://localhost:<%= connect.options.port %>' - test: - path: 'http://localhost:<%= connect.test.options.port %>' - #app: 'firefox' - - clean: - dist: ['.tmp', '<%= yeoman.dist %>/*'] - postdist: ['<%= yeoman.dist %>/bower_components'] - server: '.tmp' - doctmp: '.doctmp' - docs: 'docs' - - jshint: - options: - jshintrc: '.jshintrc' - reporter: require('jshint-stylish') - all: [ - 'Gruntfile.js' - '<%= yeoman.app %>/scripts/{,*/}*.js' - '!<%= yeoman.app %>/scripts/vendor/*' - '!<%= yeoman.app %>/scripts/jquery-extensions/*' - 'test/spec/{,*/}*.js' - ] - - mocha: - all: - options: - log: true - run: false # default: true - reporter: 'Spec' - testtimeout: 90000 - urls: ['http://localhost:<%= connect.test.options.port %>/index.html'] - - coffee: - serve: - options: - sourceMap: true - files: [ - # rather than compiling multiple files here you should - # require them into your main .coffee file - expand: true - cwd: '.tmp/scripts' - src: '**/*.coffee' - dest: '.tmp/scripts' - ext: '.js' - ] - dist: - options: - sourceMap: false - files: [ - # rather than compiling multiple files here you should - # require them into your main .coffee file - expand: true - cwd: '.tmp/scripts' - src: '**/*.coffee' - dest: '.tmp/scripts' - ext: '.js' - ] - test: - files: [ - expand: true - #cwd: 'test/spec' # original - cwd: 'test' - #src: '{,*/}*.coffee' # original - src: '**/*.coffee' - dest: '.tmp/spec' # original - #dest: '.tmp' - ext: '.js' - ] - - # see http://brianflove.com/2014/04/18/web-development-automation-gruntfile-using-coffeescript/ - # see https://www.npmjs.org/package/grunt-coffeelint - # see http://www.coffeelint.org/ - coffeelint: - app: - src: '<%= yeoman.app %>/scripts/**/*.coffee' - options: - no_tabs: - level: 'error' - indentation: - level: 'ignore' # Unfortunately, coffeelint and requirejs's define callbacks don't play well together - no_trailing_whitespace: - level: 'error' - no_trailing_semicolons: - level: 'error' - no_plusplus: - level: 'warn' - no_implicit_parens: - level: 'ignore' # change to 'warn' to warn about this - max_line_length: - level: 'ignore' - - compass: - options: - sassDir: '<%= yeoman.app %>/styles', - cssDir: '.tmp/styles', - imagesDir: '<%= yeoman.app %>/images', - javascriptsDir: '<%= yeoman.app %>/scripts', - fontsDir: '<%= yeoman.app %>/styles/fonts', - importPath: '<%= yeoman.app %>/bower_components', - relativeAssets: true - dist: {}, - server: - options: - debugInfo: true - - requirejs: - dist: - # Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js - options: - baseUrl: '.tmp/scripts' - optimize: 'none' - preserveLicenseComments: false - useStrict: true - name: 'main' - out: '<%= yeoman.dist %>/scripts/main.js' - generateSourceMaps: false - #mainConfigFile: '.tmp/scripts/main.js' - # TODO: Figure out how to make sourcemaps work with grunt-usemin - # https://github.com/yeoman/grunt-usemin/issues/30 - #generateSourceMaps: true - # required to support SourceMaps - # http://requirejs.org/docs/errors.html#sourcemapcomments - shim_: - jquery: - exports: '$' - lodash: - exports: '_' - backbone: - exports: 'Backbone' - deps: ['lodash', 'jquery'] - jqueryui: ['jquery'] - backboneindexeddb: ['backbone'] - multiselect: ['jquery', 'jqueryui'] - jqueryelastic: ['jquery'] - perfectscrollbar: ['jquery'] - superfish: ['jquery'] - superclick: ['jquery'] - supersubs: ['jquery'] - backbonerelational: ['backbone'] - backbonelocalstorage: ['backbone'] - - paths_: - jquery: '../../<%= yeoman.app %>/bower_components/jquery/dist/jquery' - backbone: '../../<%= yeoman.app %>/bower_components/backbone/backbone' - lodash: '../../<%= yeoman.app %>/bower_components/lodash/dist/lodash' - underscore: '../../<%= yeoman.app %>/bower_components/lodash/dist/lodash.underscore' - backboneindexeddb: - '../../<%= yeoman.app %>/bower_components/indexeddb-backbonejs-adapter/backbone-indexeddb' - bootstrap: '../../<%= yeoman.app %>/bower_components/sass-bootstrap/dist/js/bootstrap' - text: '../../<%= yeoman.app %>/bower_components/requirejs-text/text' - jqueryui: '../../<%= yeoman.app %>/bower_components/jqueryui/jquery-ui' - superfish: '../../<%= yeoman.app%>/bower_components/superfish/dist/js/superfish' - superclick: '../../<%= yeoman.app%>/bower_components/superclick/dist/js/superclick' - #superfish: '../../<%= yeoman.app%>/scripts/jquery-extensions/superfish/dist/js/superfish' - #superfish: '../../<%= yeoman.app %>/bower_components/superfish/dist/js/superfish' - igt: '../../<%= yeoman.app%>/scripts/jquery-extensions/igt' - jqueryuicolors: '../../<%= yeoman.app%>/scripts/jquery-extensions/jqueryui-colors' - sfjquimatch: '../../<%= yeoman.app%>/scripts/jquery-extensions/superfish-jqueryui-match' - supersubs: '../../<%= yeoman.app%>/bower_components/superfish/dist/js/supersubs' - #supersubs: '../../<%= yeoman.app%>/scripts/jquery-extensions/superfish/dist/js/supersubs' - #supersubs: '../../<%= yeoman.app %>/bower_components/superfish/dist/js/supersubs' - multiselect: '../../<%= yeoman.app %>/bower_components/multiselect/js/jquery.multi-select' - jqueryelastic: '../../<%= yeoman.app %>/bower_components/jakobmattsson-jquery-elastic/jquery.elastic.source' - spin: '../../<%= yeoman.app %>/bower_components/spin.js/spin' - jqueryspin: '../../<%= yeoman.app %>/bower_components/spin.js/jquery.spin' - perfectscrollbar: '../../<%= yeoman.app %>/bower_components/perfect-scrollbar/src/perfect-scrollbar' - backbonerelational: '../../<%= yeoman.app %>/bower_components/backbone-relational/backbone-relational' - backbonelocalstorage: '../../<%= yeoman.app %>/bower_components/backbone.localStorage/backbone.localStorage' - - useminPrepare: - html: '<%= yeoman.app %>/index.html' - options: - dest: '<%= yeoman.dist %>' - # Next 5 lines from http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 - # "Next, the flow configuration tells usemin to skip the concat step for - # css files. This is because cssmin does a concatenation itself, and - # cssmin needs to know the origin of the css files in order to do the - # relative-path correction for referenced resources." - flow_: - steps: - js: ['concat', 'uglify'] - css: ['cssmin'] - post: {} - - usemin: - html: ['<%= yeoman.dist %>/{,*/}*.html'] - css: ['<%= yeoman.dist %>/styles/{,*/}*.css'] - options: - dirs: ['<%= yeoman.dist %>'] - - imagemin: - dist: - files: [ - expand: true - cwd: '<%= yeoman.app %>/images' - src: '{,*/}*.{png,jpg,jpeg}' - dest: '<%= yeoman.dist %>/images' - ] - - # A config for cssmin is unnecessary since that created by useminPrepare works - # fine. Hence the name change to `cssmin_' here. - cssmin_: - dist: - files: - '<%= yeoman.dist %>/styles/main.css': [ - '.tmp/styles/{,*/}*.css' - '<%= yeoman.app %>/styles/{,*/}*.css' - '<%= yeoman.app %>/bower_components/jqueryui/themes/eggplant/jquery-ui.css' - ] - options: - root: '<% yeoman.app %>' # This is where `bower_components/` is to be found - - htmlmin: - dist: - options: {} - ### - removeCommentsFromCDATA: true, - # https://github.com/yeoman/grunt-usemin/issues/44 - #collapseWhitespace: true, - collapseBooleanAttributes: true, - removeAttributeQuotes: true, - removeRedundantAttributes: true, - useShortDoctype: true, - removeEmptyAttributes: true, - removeOptionalTags: true - ### - files: [ - expand: true, - cwd: '<%= yeoman.app %>', - src: '*.html', - dest: '<%= yeoman.dist %>' - ] - - copy: - coffee: - files: [ - expand: true - dot: true - cwd: '<%= yeoman.app %>/scripts' - dest: '.tmp/scripts' - src: '**/*.coffee' - ] - packagejson: - src: 'package.json' - dest: '.tmp/' - unicodedatajson: - src: 'UnicodeData.json' - dest: '.tmp/' - serversjson: - src: 'servers.json' - dest: '.tmp/' - unicodedatajsondist: - src: 'UnicodeData.json' - dest: 'dist/' - serversjsondist: - src: 'servers.json' - dest: 'dist/' - dist: - files: [ - expand: true - dot: true - cwd: '<%= yeoman.app %>' - dest: '<%= yeoman.dist %>' - src_: [ - 'UnicodeData.json' - 'bower_components/jqueryui/**/*.*' # added, cf. http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 - ] - src: [ - '*.{ico,txt}' - '.htaccess' - 'favicon.png' - 'help/html/help.html' - './../package.json' - './../.tmp/UnicodeData.json' - './../.tmp/servers.json' - 'images/{,*/}*.{webp,gif,png,jpg}' # added png so that my jQuery images in /images would copy over, cf. imagemin headache - 'styles/fonts/{,*/}*.*' - 'bower_components/sass-bootstrap/fonts/*.*' - 'bower_components/jqueryui/**/*.*' # added, cf. http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 - ] - ] - disttmp: - files: [ - expand: true - dot: true - cwd: '<%= yeoman.app %>' - dest: '.tmp' - src: [ - 'bower_components/{,**/}*.*' - 'scripts/jquery-extensions/{,**/}*.*' - ] - ] - # Copy jQueryUI images to dist/styles/images/ - distJQueryUIImages: - files: [ - expand: true - dot: true - cwd: '<%= yeoman.app %>' - dest: '<%= yeoman.dist %>/styles/images' - flatten: true # CRUCIAL!!! - src: [ - 'bower_components/jqueryui/**/*.{png,jpg,jpeg,gif,webp,svg,eot,ttf,woff}' # added, cf. http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 - ] - ] - distrequirejs: # from https://github.com/yeoman/grunt-usemin/issues/192 - expand: true, - cwd: '<%= yeoman.app %>/bower_components/requirejs/', - dest: '<%= yeoman.dist %>/scripts/vendor/', - src: ['require.js'] - docco: - files: [ - expand: true - cwd: '<%= yeoman.app %>/scripts/' # src/modules/' - src: ['**/*.coffee'] - dest: '.doctmp/' # dev/js/' - rename: (dest, src) -> - return dest + src.replace(/\//g, '.') - , - expand: true - cwd: 'test/' - src: ['!bower_components/*.coffee', '**/*.coffee'] - dest: '.doctmp/' - rename: (dest, src) -> - return dest + 'test.' + src.replace(/\//g, '.') - ] - requirejs: - src: '<%= yeoman.app %>/bower_components/requirejs/require.js' - dest: '<%= yeoman.dist %>/bower_components/requirejs/require.js' - - uglify: # altered for debugging purposes. - dist_: - files: - '<%= yeoman.dist %>/scripts/main.js': ['<%= yeoman.dist %>/scripts/main.js'] - requirejs: - files: - '<%= yeoman.dist %>/scripts/vendor/require.js': ['<%= yeoman.dist %>/scripts/vendor/require.js'] - - bower: - all: - rjsConfig: '<%= yeoman.app %>/scripts/main.js' - - jst: - options: - amd: true - compile: - files: - '.tmp/scripts/templates.js': ['<%= yeoman.app %>/scripts/templates/*.ejs'] - - eco: - options: - amd: true - files: - expand: true - cwd: '<%= yeoman.app %>/scripts/templates' - src: ['*.eco', 'fields/*.eco'] - dest: '.tmp/scripts/templates' - ext: '.js' - - exec: - setContinuousDeploymentVersion: - cmd: -> - return 'bash scripts/set_ci_version.sh' - - rev: - dist: - files: - src: [ - '<%= yeoman.dist %>/scripts/{,*/!(require)}*.js' # Add exception not to change name of copied file during "rev" task (added only !(require) exception), cf. https://github.com/yeoman/grunt-usemin/issues/192 - '<%= yeoman.dist %>/styles/{,*/}*.css', - '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}', - '/styles/fonts/{,*/}*.*', - 'bower_components/sass-bootstrap/fonts/*.*' - 'bower_components/**/*.{png,jpg,jpeg,gif,webp,svg,eot,ttf,woff}' # added, cf. http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 - ] - - # Replace script tag in index.html, to call require.js from new path using grunt-regex-replace plugin: - # See https://github.com/yeoman/grunt-usemin/issues/192 - 'regex-replace': - dist: - src: ['<%= yeoman.dist %>/index.html'], - actions: [ - name: 'requirejs-newpath', - search: '', - replace: (match) -> - regex = /scripts\/.*main/ - result = regex.exec(match) - '' - flags: 'g' - ] - - grunt.registerTask 'createDefaultTemplate', -> - grunt.file.write '.tmp/scripts/templates.js', 'this.JST = this.JST || {}' - - grunt.registerTask 'server', (target) -> - grunt.log.warn 'The `server` task has been deprecated. Use `grunt serve` to start a server.' - grunt.task.run ['serve' + (target ? ':' + target : '')] - - grunt.registerTask 'serve', (target) -> - if target is 'dist' - return grunt.task.run ['build', 'open:server', 'connect:dist:keepalive'] - - if target is 'test' - return grunt.task.run [ - 'markdown:all' - 'clean:server' - 'copy:coffee' - 'copy:packagejson' - 'copy:unicodedatajson' - 'copy:serversjson' - 'coffee:serve' - 'coffee:test' - #'createDefaultTemplate' - #'jst' - 'eco' - #'compass:server' - 'connect:test' - 'open:test' - 'watch' - ] - - grunt.task.run [ - 'markdown:all' - 'clean:server' - 'copy:coffee' - 'copy:packagejson' - 'copy:unicodedatajson' - 'copy:serversjson' - 'coffee:serve' - #'createDefaultTemplate' - #'jst' - 'eco' - #'compass:server' - 'connect:livereload' - #'open:server' - 'watch' - ] - - grunt.registerTask 'coffeeDist', ['copy:coffee', 'coffee:dist'] - - grunt.registerTask 'test', (isConnected) -> - isConnected = Boolean(isConnected) - testTasks = [ - 'clean:server' - 'coffee' - #'createDefaultTemplate' - #'jst' - 'eco' - #'compass' - 'connect:test' - 'mocha' - ] - if not isConnected - grunt.task.run testTasks - else - # already connected so not going to connect again, remove the connect:test task - testTasks.splice testTasks.indexOf('connect:test'), 1 - grunt.task.run testTasks - - grunt.registerTask 'build', [ - 'markdown:all' - 'clean:dist' # remove everything in dist/ and .tmp/ - 'copy:coffee' # copy all .coffee files in app/scripts/ to .tmp/scripts/ - 'copy:packagejson' - 'copy:unicodedatajson' - 'copy:serversjson' - 'coffee:dist' # convert all .coffee files in .tmp/scripts to .js in situ - - # eco: convert all .eco files in app/scripts/templates/ to .js files in - # .tmp/scripts/templates/ - 'eco' - - # permits execution of shell scripts - 'exec:setContinuousDeploymentVersion' - - #'compass:dist' # commented out because not currently using compass - - # useminPrepare: read the `build` comments in index.html and dynamically - # generate concat, uglify, and or cssmin `generated` tasks. - 'useminPrepare' - - # copy:disttmp: my copy task: copies bower_components and jquery-extensions - # to .tmp. This seems to be necessary: SHOW FAILM MSG: ... Error: ENOENT, - # no such file or directory '.tmp/bower_components/lodash/dist/lodash.js' - 'copy:disttmp' - - # copy:distJQueryUIImages: my other copy task: copies images in - # bower_components/jqueryui/ to dist/. - 'copy:distJQueryUIImages' - - # copy:distrequirejs: copy require.js into dist/scripts/vendor/, see - # https://github.com/yeoman/grunt-usemin/issues/192 # - 'copy:distrequirejs' - - # requirejs: creates a single dist/scripts/main.js file containing all of - # my JavaScript - 'requirejs' - - # I have commented-out imagemin because it adds a random prefix to my - # images which screws up my application logic and I don't know how to stop - # that from happening... The images are already quite small, ... - # 'imagemin' - - 'htmlmin' - 'concat' # task configured by `useminPrepare` above. - - 'cssmin' # Concatenate all CSS files into one, configured by useminPrepare - - #'uglify:dist' - #'copy:dist' - #'copy:requirejs' - #'uglify:requirejs' - 'uglify' # JavaScript minification - 'uglify:requirejs' # minify dist/scripts/vendor/require.js - - # rev: Use the rev task together with yeoman/grunt-usemin for cache busting - # of static files in your app. This allows them to be cached forever by the - # browser. See https://github.com/cbas/grunt-rev - 'rev' - - # usemin': two subtasks are configured by useminPrepare: usemin:html and - # usemin:css tasks; the first renames the references to `main.js`, - # `modernizr.js` and `main.css` # # - 'usemin' - - # regex-replace:dist: change `src="bower_components/requirejs/require.js"` - # to `src="scripts/vendor/require.js">` in dist/index.html # - 'regex-replace:dist' # change `src="bower_components/requirejs/require.js"` to `src="scripts/vendor/require.js">` in dist/index.html - - # I don't know why dist/bower_components/ is created (...). In any case, - # I'm just cleaning it up hackily like so. - # 'clean:postdist' - - # copy everything that is supposed ot be in the dist, not sure why there are other copy tasks like disttmp and distJQueryUIImages and distrequirejs - 'copy:dist' - - 'copy:unicodedatajsondist' - 'copy:serversjsondist' - ] - - grunt.registerTask 'default', ['jshint', 'test', 'build'] - - grunt.registerTask 'lint', 'coffeelint' - - grunt.registerTask 'docs', ['clean:docs', 'clean:doctmp', 'copy:docco', 'docco', 'clean:doctmp'] - - grunt.registerTask 'deploy', ['jshint', 'build', 'exec:setContinuousDeploymentVersion'] - - grunt.registerTask 'copydist', 'copy:dist' - grunt.registerTask 'cleandist', 'clean:dist' - grunt.registerTask 'imagemin', 'clean:dist' - diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..40c72de --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,808 @@ +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md + */ +'use strict'; +const LIVERELOAD_PORT = 35729; +const SERVER_PORT = 9000; +const lrSnippet = require('connect-livereload')({port: LIVERELOAD_PORT}); +const mountFolder = (connect, dir) => connect.static(require('path').resolve(dir)); + +// # Globbing +// for performance reasons we're only matching one level down: +// 'test/spec/{,*/}*.js' +// use this if you want to match all subfolders: +// 'test/spec/**/*.js' +// templateFramework: 'lodash' + +module.exports = function(grunt) { + + // show elapsed time at the end + require('time-grunt')(grunt); + + // load all grunt tasks + require('load-grunt-tasks')(grunt); + + // configurable paths + const yeomanConfig = {app: 'app', dist: 'dist'}; + + grunt.initConfig({ + + markdown: { + all: { + files: [{ + expand: true, + flatten: true, + src: '<%= yeoman.app %>/help/src/*.md', + dest: '<%= yeoman.app %>/help/html/', + ext: '.html' + } + ], + options: { + template: '<%= yeoman.app %>/help/src/template.jst' + } + } + }, + + yeoman: yeomanConfig, + + watch: { + options: { + nospawn: true, + livereload: true + }, + help: { + files: ['<%= yeoman.app %>/help/src/*.md'], + tasks: ['markdown:all'] + }, + coffee: { + files: ['<%= yeoman.app %>/scripts/{,*/}*.coffee'], + tasks: ['copy:coffee', 'coffee:serve'] + }, + coffeeTest: { + //files: ['test/spec/{,*/}*.coffee'] + files: ['test/**/*.coffee'], + tasks: ['coffee:test'] + }, + compass: { + files: ['<%= yeoman.app %>/styles/{,*/}*.{scss,sass}'], + tasks: ['compass'] + }, + livereload: { + options: { + livereload: grunt.option('livereloadport') || LIVERELOAD_PORT + }, + files: [ + '<%= yeoman.app %>/*.html', + '{.tmp,<%= yeoman.app %>}/styles/{,*/}*.css', + '{.tmp,<%= yeoman.app %>}/scripts/{,*/}*.js', + '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}', + '<%= yeoman.app %>/scripts/templates/*.{ejs,mustache,hbs}', + 'test/spec/**/*.js' + ] + }, + jst: { + files: ['<%= yeoman.app %>/scripts/templates/*.ejs'], + tasks: ['jst'] + }, + eco: { + files: ['<%= yeoman.app %>/scripts/templates/{,*/}*.eco'], + tasks: ['eco'] + }, + test: { + files: ['<%= yeoman.app %>/scripts/{,*/}*.js', 'test/spec/**/*.js'], + tasks: ['test:true'] + } + }, + + docco: { + src: ['.doctmp/*.coffee'], + //src: ['<%= yeoman.app %>/scripts/**/*.coffee'] + options: { + output: 'docs/' + } + }, + + connect: { + options: { + port: grunt.option('port') || SERVER_PORT, + // change this to '0.0.0.0' to access the server from outside + //hostname: 'localhost' + hostname: '0.0.0.0' + }, + livereload: { + options: { + middleware(connect) { + return [ + lrSnippet, + mountFolder(connect, '.tmp'), + mountFolder(connect, yeomanConfig.app) + ]; + } + } + }, + test: { + options: { + port: 9001, + middleware(connect) { + return [ + lrSnippet, + mountFolder(connect, '.tmp'), + mountFolder(connect, 'test'), + mountFolder(connect, yeomanConfig.app) + ]; + } + } + }, + dist: { + options: { + middleware(connect) { + return [mountFolder(connect, yeomanConfig.dist)]; + } + } + } + }, + + open: { + server: { + path: 'http://localhost:<%= connect.options.port %>' + }, + test: { + path: 'http://localhost:<%= connect.test.options.port %>' + } + }, + //app: 'firefox' + + clean: { + dist: ['.tmp', '<%= yeoman.dist %>/*'], + postdist: ['<%= yeoman.dist %>/bower_components'], + server: '.tmp', + doctmp: '.doctmp', + docs: 'docs' + }, + + jshint: { + options: { + jshintrc: '.jshintrc', + reporter: require('jshint-stylish') + }, + all: [ + 'Gruntfile.js', + '<%= yeoman.app %>/scripts/{,*/}*.js', + '!<%= yeoman.app %>/scripts/vendor/*', + '!<%= yeoman.app %>/scripts/jquery-extensions/*', + 'test/spec/{,*/}*.js' + ] + }, + + mocha: { + all: { + options: { + log: true, + run: false, // default: true + reporter: 'Spec', + testtimeout: 90000, + urls: ['http://localhost:<%= connect.test.options.port %>/index.html'] + } + } + }, + + coffee: { + serve: { + options: { + sourceMap: true + }, + files: [{ + // rather than compiling multiple files here you should + // require them into your main .coffee file + expand: true, + cwd: '.tmp/scripts', + src: '**/*.coffee', + dest: '.tmp/scripts', + ext: '.js' + } + ] + }, + dist: { + options: { + sourceMap: false + }, + files: [{ + // rather than compiling multiple files here you should + // require them into your main .coffee file + expand: true, + cwd: '.tmp/scripts', + src: '**/*.coffee', + dest: '.tmp/scripts', + ext: '.js' + } + ] + }, + test: { + files: [{ + expand: true, + //cwd: 'test/spec' # original + cwd: 'test', + //src: '{,*/}*.coffee' # original + src: '**/*.coffee', + dest: '.tmp/spec', // original + //dest: '.tmp' + ext: '.js' + } + ] + } + }, + + // see http://brianflove.com/2014/04/18/web-development-automation-gruntfile-using-coffeescript/ + // see https://www.npmjs.org/package/grunt-coffeelint + // see http://www.coffeelint.org/ + coffeelint: { + app: { + src: '<%= yeoman.app %>/scripts/**/*.coffee' + }, + options: { + no_tabs: { + level: 'error' + }, + indentation: { + level: 'ignore' + }, // Unfortunately, coffeelint and requirejs's define callbacks don't play well together + no_trailing_whitespace: { + level: 'error' + }, + no_trailing_semicolons: { + level: 'error' + }, + no_plusplus: { + level: 'warn' + }, + no_implicit_parens: { + level: 'ignore' + }, // change to 'warn' to warn about this + max_line_length: { + level: 'ignore' + } + } + }, + + compass: { + options: { + sassDir: '<%= yeoman.app %>/styles', + cssDir: '.tmp/styles', + imagesDir: '<%= yeoman.app %>/images', + javascriptsDir: '<%= yeoman.app %>/scripts', + fontsDir: '<%= yeoman.app %>/styles/fonts', + importPath: '<%= yeoman.app %>/bower_components', + relativeAssets: true + }, + dist: {}, + server: { + options: { + debugInfo: true + } + } + }, + + requirejs: { + dist: { + // Options: https://github.com/jrburke/r.js/blob/master/build/example.build.js + options: { + baseUrl: '.tmp/scripts', + optimize: 'none', + preserveLicenseComments: false, + useStrict: true, + name: 'main', + out: '<%= yeoman.dist %>/scripts/main.js', + generateSourceMaps: false, + //mainConfigFile: '.tmp/scripts/main.js' + // TODO: Figure out how to make sourcemaps work with grunt-usemin + // https://github.com/yeoman/grunt-usemin/issues/30 + //generateSourceMaps: true + // required to support SourceMaps + // http://requirejs.org/docs/errors.html#sourcemapcomments + shim_: { + jquery: { + exports: '$' + }, + lodash: { + exports: '_' + }, + backbone: { + exports: 'Backbone', + deps: ['lodash', 'jquery'] + }, + jqueryui: ['jquery'], + backboneindexeddb: ['backbone'], + multiselect: ['jquery', 'jqueryui'], + jqueryelastic: ['jquery'], + perfectscrollbar: ['jquery'], + superfish: ['jquery'], + superclick: ['jquery'], + supersubs: ['jquery'], + backbonerelational: ['backbone'], + backbonelocalstorage: ['backbone'] + }, + + paths_: { + jquery: '../../<%= yeoman.app %>/bower_components/jquery/dist/jquery', + backbone: '../../<%= yeoman.app %>/bower_components/backbone/backbone', + lodash: '../../<%= yeoman.app %>/bower_components/lodash/dist/lodash', + underscore: '../../<%= yeoman.app %>/bower_components/lodash/dist/lodash.underscore', + backboneindexeddb: + '../../<%= yeoman.app %>/bower_components/indexeddb-backbonejs-adapter/backbone-indexeddb', + bootstrap: '../../<%= yeoman.app %>/bower_components/sass-bootstrap/dist/js/bootstrap', + text: '../../<%= yeoman.app %>/bower_components/requirejs-text/text', + jqueryui: '../../<%= yeoman.app %>/bower_components/jqueryui/jquery-ui', + superfish: '../../<%= yeoman.app%>/bower_components/superfish/dist/js/superfish', + superclick: '../../<%= yeoman.app%>/bower_components/superclick/dist/js/superclick', + //superfish: '../../<%= yeoman.app%>/scripts/jquery-extensions/superfish/dist/js/superfish' + //superfish: '../../<%= yeoman.app %>/bower_components/superfish/dist/js/superfish' + igt: '../../<%= yeoman.app%>/scripts/jquery-extensions/igt', + jqueryuicolors: '../../<%= yeoman.app%>/scripts/jquery-extensions/jqueryui-colors', + sfjquimatch: '../../<%= yeoman.app%>/scripts/jquery-extensions/superfish-jqueryui-match', + supersubs: '../../<%= yeoman.app%>/bower_components/superfish/dist/js/supersubs', + //supersubs: '../../<%= yeoman.app%>/scripts/jquery-extensions/superfish/dist/js/supersubs' + //supersubs: '../../<%= yeoman.app %>/bower_components/superfish/dist/js/supersubs' + multiselect: '../../<%= yeoman.app %>/bower_components/multiselect/js/jquery.multi-select', + jqueryelastic: '../../<%= yeoman.app %>/bower_components/jakobmattsson-jquery-elastic/jquery.elastic.source', + spin: '../../<%= yeoman.app %>/bower_components/spin.js/spin', + jqueryspin: '../../<%= yeoman.app %>/bower_components/spin.js/jquery.spin', + perfectscrollbar: '../../<%= yeoman.app %>/bower_components/perfect-scrollbar/src/perfect-scrollbar', + backbonerelational: '../../<%= yeoman.app %>/bower_components/backbone-relational/backbone-relational', + backbonelocalstorage: '../../<%= yeoman.app %>/bower_components/backbone.localStorage/backbone.localStorage' + } + } + } + }, + + useminPrepare: { + html: '<%= yeoman.app %>/index.html', + options: { + dest: '<%= yeoman.dist %>', + // Next 5 lines from http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 + // "Next, the flow configuration tells usemin to skip the concat step for + // css files. This is because cssmin does a concatenation itself, and + // cssmin needs to know the origin of the css files in order to do the + // relative-path correction for referenced resources." + flow_: { + steps: { + js: ['concat', 'uglify'], + css: ['cssmin'] + }, + post: {} + } + } + }, + + usemin: { + html: ['<%= yeoman.dist %>/{,*/}*.html'], + css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], + options: { + dirs: ['<%= yeoman.dist %>'] + } + }, + + imagemin: { + dist: { + files: [{ + expand: true, + cwd: '<%= yeoman.app %>/images', + src: '{,*/}*.{png,jpg,jpeg}', + dest: '<%= yeoman.dist %>/images' + } + ] + } + }, + + // A config for cssmin is unnecessary since that created by useminPrepare works + // fine. Hence the name change to `cssmin_' here. + cssmin_: { + dist: { + files: { + '<%= yeoman.dist %>/styles/main.css': [ + '.tmp/styles/{,*/}*.css', + '<%= yeoman.app %>/styles/{,*/}*.css', + '<%= yeoman.app %>/bower_components/jqueryui/themes/eggplant/jquery-ui.css' + ] + }, + options: { + root: '<% yeoman.app %>' + } + } + }, // This is where `bower_components/` is to be found + + htmlmin: { + dist: { + options: {}, + /* + removeCommentsFromCDATA: true, + * https://github.com/yeoman/grunt-usemin/issues/44 + *collapseWhitespace: true, + collapseBooleanAttributes: true, + removeAttributeQuotes: true, + removeRedundantAttributes: true, + useShortDoctype: true, + removeEmptyAttributes: true, + removeOptionalTags: true + */ + files: [{ + expand: true, + cwd: '<%= yeoman.app %>', + src: '*.html', + dest: '<%= yeoman.dist %>' + } + ] + } + }, + + copy: { + coffee: { + files: [{ + expand: true, + dot: true, + cwd: '<%= yeoman.app %>/scripts', + dest: '.tmp/scripts', + src: '**/*.coffee' + } + ] + }, + packagejson: { + src: 'package.json', + dest: '.tmp/' + }, + unicodedatajson: { + src: 'UnicodeData.json', + dest: '.tmp/' + }, + serversjson: { + src: 'servers.json', + dest: '.tmp/' + }, + unicodedatajsondist: { + src: 'UnicodeData.json', + dest: 'dist/' + }, + serversjsondist: { + src: 'servers.json', + dest: 'dist/' + }, + dist: { + files: [{ + expand: true, + dot: true, + cwd: '<%= yeoman.app %>', + dest: '<%= yeoman.dist %>', + src_: [ + 'UnicodeData.json', + 'bower_components/jqueryui/**/*.*' // added, cf. http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 + ], + src: [ + '*.{ico,txt}', + '.htaccess', + 'favicon.png', + 'help/html/help.html', + './../package.json', + './../.tmp/UnicodeData.json', + './../.tmp/servers.json', + 'images/{,*/}*.{webp,gif,png,jpg}', // added png so that my jQuery images in /images would copy over, cf. imagemin headache + 'styles/fonts/{,*/}*.*', + 'bower_components/sass-bootstrap/fonts/*.*', + 'bower_components/jqueryui/**/*.*' // added, cf. http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 + ] + } + ] + }, + disttmp: { + files: [{ + expand: true, + dot: true, + cwd: '<%= yeoman.app %>', + dest: '.tmp', + src: [ + 'bower_components/{,**/}*.*', + 'scripts/jquery-extensions/{,**/}*.*' + ] + } + ] + }, + // Copy jQueryUI images to dist/styles/images/ + distJQueryUIImages: { + files: [{ + expand: true, + dot: true, + cwd: '<%= yeoman.app %>', + dest: '<%= yeoman.dist %>/styles/images', + flatten: true, // CRUCIAL!!! + src: [ + 'bower_components/jqueryui/**/*.{png,jpg,jpeg,gif,webp,svg,eot,ttf,woff}' // added, cf. http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 + ] + } + ] + }, + distrequirejs: { // from https://github.com/yeoman/grunt-usemin/issues/192 + expand: true, + cwd: '<%= yeoman.app %>/bower_components/requirejs/', + dest: '<%= yeoman.dist %>/scripts/vendor/', + src: ['require.js'] + }, + docco: { + files: [{ + expand: true, + cwd: '<%= yeoman.app %>/scripts/', // src/modules/' + src: ['**/*.coffee'], + dest: '.doctmp/', // dev/js/' + rename(dest, src) { + return dest + src.replace(/\//g, '.'); + } + } + , { + expand: true, + cwd: 'test/', + src: ['!bower_components/*.coffee', '**/*.coffee'], + dest: '.doctmp/', + rename(dest, src) { + return dest + 'test.' + src.replace(/\//g, '.'); + } + } + ] + }, + requirejs: { + src: '<%= yeoman.app %>/bower_components/requirejs/require.js', + dest: '<%= yeoman.dist %>/bower_components/requirejs/require.js' + } + }, + + uglify: { // altered for debugging purposes. + dist_: { + files: { + '<%= yeoman.dist %>/scripts/main.js': ['<%= yeoman.dist %>/scripts/main.js'] + } + }, + requirejs: { + files: { + '<%= yeoman.dist %>/scripts/vendor/require.js': ['<%= yeoman.dist %>/scripts/vendor/require.js'] + } + } + }, + + bower: { + all: { + rjsConfig: '<%= yeoman.app %>/scripts/main.js' + } + }, + + jst: { + options: { + amd: true + }, + compile: { + files: { + '.tmp/scripts/templates.js': ['<%= yeoman.app %>/scripts/templates/*.ejs'] + } + } + }, + + eco: { + options: { + amd: true + }, + files: { + expand: true, + cwd: '<%= yeoman.app %>/scripts/templates', + src: ['*.eco', 'fields/*.eco'], + dest: '.tmp/scripts/templates', + ext: '.js' + } + }, + + exec: { + setContinuousDeploymentVersion: { + cmd() { + return 'bash scripts/set_ci_version.sh'; + } + } + }, + + rev: { + dist: { + files: { + src: [ + '<%= yeoman.dist %>/scripts/{,*/!(require)}*.js', // Add exception not to change name of copied file during "rev" task (added only !(require) exception), cf. https://github.com/yeoman/grunt-usemin/issues/192 + '<%= yeoman.dist %>/styles/{,*/}*.css', + '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp}', + '/styles/fonts/{,*/}*.*', + 'bower_components/sass-bootstrap/fonts/*.*', + 'bower_components/**/*.{png,jpg,jpeg,gif,webp,svg,eot,ttf,woff}' // added, cf. http://stackoverflow.com/questions/20509145/managing-images-in-bower-packages-using-grunt?lq=1 + ] + } + } + }, + + // Replace script tag in index.html, to call require.js from new path using grunt-regex-replace plugin: + // See https://github.com/yeoman/grunt-usemin/issues/192 + 'regex-replace': { + dist: { + src: ['<%= yeoman.dist %>/index.html'], + actions: [{ + name: 'requirejs-newpath', + search: '', + replace(match) { + const regex = /scripts\/.*main/; + const result = regex.exec(match); + return ''; + }, + flags: 'g' + } + ] + } + }}); + + grunt.registerTask('createDefaultTemplate', () => grunt.file.write('.tmp/scripts/templates.js', 'this.JST = this.JST || {}')); + + grunt.registerTask('server', function(target) { + grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); + return grunt.task.run(['serve' + (target != null ? target : ':' + {target : ''})]); +}); + + grunt.registerTask('serve', function(target) { + if (target === 'dist') { + return grunt.task.run(['build', 'open:server', 'connect:dist:keepalive']); + } + + if (target === 'test') { + return grunt.task.run([ + 'markdown:all', + 'clean:server', + 'copy:coffee', + 'copy:packagejson', + 'copy:unicodedatajson', + 'copy:serversjson', + 'coffee:serve', + 'coffee:test', + //'createDefaultTemplate' + //'jst' + 'eco', + //'compass:server' + 'connect:test', + 'open:test', + 'watch' + ]); + } + + return grunt.task.run([ + 'markdown:all', + 'clean:server', + 'copy:coffee', + 'copy:packagejson', + 'copy:unicodedatajson', + 'copy:serversjson', + 'coffee:serve', + //'createDefaultTemplate' + //'jst' + 'eco', + //'compass:server' + 'connect:livereload', + //'open:server' + 'watch' + ]); +}); + + grunt.registerTask('coffeeDist', ['copy:coffee', 'coffee:dist']); + + grunt.registerTask('test', function(isConnected) { + isConnected = Boolean(isConnected); + const testTasks = [ + 'clean:server', + 'coffee', + //'createDefaultTemplate' + //'jst' + 'eco', + //'compass' + 'connect:test', + 'mocha' + ]; + if (!isConnected) { + return grunt.task.run(testTasks); + } else { + // already connected so not going to connect again, remove the connect:test task + testTasks.splice(testTasks.indexOf('connect:test'), 1); + return grunt.task.run(testTasks); + } + }); + + grunt.registerTask('build', [ + 'markdown:all', + 'clean:dist', // remove everything in dist/ and .tmp/ + 'copy:coffee', // copy all .coffee files in app/scripts/ to .tmp/scripts/ + 'copy:packagejson', + 'copy:unicodedatajson', + 'copy:serversjson', + 'coffee:dist', // convert all .coffee files in .tmp/scripts to .js in situ + + // eco: convert all .eco files in app/scripts/templates/ to .js files in + // .tmp/scripts/templates/ + 'eco', + + // permits execution of shell scripts + 'exec:setContinuousDeploymentVersion', + + //'compass:dist' # commented out because not currently using compass + + // useminPrepare: read the `build` comments in index.html and dynamically + // generate concat, uglify, and or cssmin `generated` tasks. + 'useminPrepare', + + // copy:disttmp: my copy task: copies bower_components and jquery-extensions + // to .tmp. This seems to be necessary: SHOW FAILM MSG: ... Error: ENOENT, + // no such file or directory '.tmp/bower_components/lodash/dist/lodash.js' + 'copy:disttmp', + + // copy:distJQueryUIImages: my other copy task: copies images in + // bower_components/jqueryui/ to dist/. + 'copy:distJQueryUIImages', + + // copy:distrequirejs: copy require.js into dist/scripts/vendor/, see + // https://github.com/yeoman/grunt-usemin/issues/192 # + 'copy:distrequirejs', + + // requirejs: creates a single dist/scripts/main.js file containing all of + // my JavaScript + 'requirejs', + + // I have commented-out imagemin because it adds a random prefix to my + // images which screws up my application logic and I don't know how to stop + // that from happening... The images are already quite small, ... + // 'imagemin' + + 'htmlmin', + 'concat', // task configured by `useminPrepare` above. + + 'cssmin', // Concatenate all CSS files into one, configured by useminPrepare + + //'uglify:dist' + //'copy:dist' + //'copy:requirejs' + //'uglify:requirejs' + 'uglify', // JavaScript minification + 'uglify:requirejs', // minify dist/scripts/vendor/require.js + + // rev: Use the rev task together with yeoman/grunt-usemin for cache busting + // of static files in your app. This allows them to be cached forever by the + // browser. See https://github.com/cbas/grunt-rev + 'rev', + + // usemin': two subtasks are configured by useminPrepare: usemin:html and + // usemin:css tasks; the first renames the references to `main.js`, + // `modernizr.js` and `main.css` # # + 'usemin', + + // regex-replace:dist: change `src="bower_components/requirejs/require.js"` + // to `src="scripts/vendor/require.js">` in dist/index.html # + 'regex-replace:dist', // change `src="bower_components/requirejs/require.js"` to `src="scripts/vendor/require.js">` in dist/index.html + + // I don't know why dist/bower_components/ is created (...). In any case, + // I'm just cleaning it up hackily like so. + // 'clean:postdist' + + // copy everything that is supposed ot be in the dist, not sure why there are other copy tasks like disttmp and distJQueryUIImages and distrequirejs + 'copy:dist', + + 'copy:unicodedatajsondist', + 'copy:serversjsondist' + ]); + + grunt.registerTask('default', ['jshint', 'test', 'build']); + + grunt.registerTask('lint', 'coffeelint'); + + grunt.registerTask('docs', ['clean:docs', 'clean:doctmp', 'copy:docco', 'docco', 'clean:doctmp']); + + grunt.registerTask('deploy', ['jshint', 'build', 'exec:setContinuousDeploymentVersion']); + + grunt.registerTask('copydist', 'copy:dist'); + grunt.registerTask('cleandist', 'clean:dist'); + return grunt.registerTask('imagemin', 'clean:dist'); +}; + diff --git a/app/scripts/collections/application-settings.coffee b/app/scripts/collections/application-settings.coffee deleted file mode 100644 index 10277ce..0000000 --- a/app/scripts/collections/application-settings.coffee +++ /dev/null @@ -1,13 +0,0 @@ -define [ - 'backbone', - './../models/application-settings' - ], (Backbone, ApplicationSettingsModel) -> - - # Application Settings Collection - # ------------------------------- - - class ApplicationSettingsCollection extends Backbone.Collection - - model: ApplicationSettingsModel - localStorage: new Backbone.LocalStorage('dativeApplicationSettings') - diff --git a/app/scripts/collections/collections.coffee b/app/scripts/collections/collections.coffee deleted file mode 100644 index 5fe49d6..0000000 --- a/app/scripts/collections/collections.coffee +++ /dev/null @@ -1,23 +0,0 @@ -define [ - './resources' - './../models/collection' -], (ResourcesCollection, CollectionModel) -> - - # Collections Collection - # ---------------------- - # - # Holds models for (OLD) collections, i.e., texts. - - class CollectionsCollection extends ResourcesCollection - - resourceName: 'collection' - model: CollectionModel - - # When an OLD collection resource is added/updated and its contents contain - # bad form references, the attribute will be "forms", though from Dative's - # perspective it should be "contents". - errorAttributeTransformer: (errorAttribute) -> - if errorAttribute == 'forms' - return 'contents' - errorAttribute - diff --git a/app/scripts/collections/corpora.coffee b/app/scripts/collections/corpora.coffee deleted file mode 100644 index f0ef8ad..0000000 --- a/app/scripts/collections/corpora.coffee +++ /dev/null @@ -1,56 +0,0 @@ -define [ - 'backbone', - './../models/corpus' - ], (Backbone, CorpusModel) -> - - # Corpora Collection - # ------------------ - # - # Holds models for FieldDB corpora. - - class CorporaCollection extends Backbone.Collection - - model: CorpusModel - - # Create a new corpus. - # POST `/newcorpus` - newCorpus: (newCorpusName) -> - Backbone.trigger 'newCorpusStart', newCorpusName - payload = - authUrl: @applicationSettings.get?('activeServer')?.get?('url') - username: @applicationSettings.get?('username') - password: @applicationSettings.get?('password') # TODO use confirm identity event instead - serverCode: @applicationSettings.get?('activeServer')?.get?('serverCode') - newCorpusName: newCorpusName - CorpusModel.cors.request( - method: 'POST' - timeout: 10000 - url: "#{payload.authUrl}/newcorpus" - payload: payload - onload: (responseJSON) => - Backbone.trigger 'newCorpusEnd' - if responseJSON.corpusadded - if responseJSON.corpus - corpusObject = responseJSON.corpus - corpusObject.applicationSettings = @applicationSettings - @unshift corpusObject - Backbone.trigger 'newCorpusSuccess', newCorpusName - else - Backbone.trigger 'newCorpusFail', - ["There was an error creating corpus “#{newCorpusName}”.", - "This name is probably already taken.", - "Try a different one."].join ' ' - console.log responseJSON.userFriendlyErrors[0] - else - Backbone.trigger 'newCorpusFail', "Request to create corpus failed: `corpusadded` not truthy." - console.log 'Failed request to /newcorpus: `corpusadded` not truthy.' - onerror: (responseJSON) -> - Backbone.trigger 'newCorpusEnd' - Backbone.trigger 'newCorpusFail', "Request to create corpus failed with an error." - console.log 'Failed request to /newcorpus: error.' - ontimeout: -> - Backbone.trigger 'newCorpusEnd' - Backbone.trigger 'newCorpusFail', "Request to create corpus failed: request timed out." - console.log 'Failed request to /newcorpus: timed out.' - ) - diff --git a/app/scripts/collections/elicitation-methods.coffee b/app/scripts/collections/elicitation-methods.coffee deleted file mode 100644 index 753a703..0000000 --- a/app/scripts/collections/elicitation-methods.coffee +++ /dev/null @@ -1,16 +0,0 @@ -define [ - './resources' - './../models/elicitation-method' -], (ResourcesCollection, ElicitationMethodModel) -> - - # Elicitation Methods Collection - # ------------------------------ - # - # Holds models for elicitation methods. - - class ElicitationMethodsCollection extends ResourcesCollection - - resourceName: 'elicitationMethod' - model: ElicitationMethodModel - - diff --git a/app/scripts/collections/files.coffee b/app/scripts/collections/files.coffee deleted file mode 100644 index f1154c2..0000000 --- a/app/scripts/collections/files.coffee +++ /dev/null @@ -1,84 +0,0 @@ -define [ - './resources' - './../models/file' -], (ResourcesCollection, FileModel) -> - - # Files Collection - # ---------------- - # - # Holds models for files. Note that because of the particular way in which - # OLD web services handle the uploading of large files---i.e., the request - # must be multipart/form-data, not JSON---we make some customizations here. - - class FilesCollection extends ResourcesCollection - - resourceName: 'file' - model: FileModel - - addResource: (resource, options={}) -> - options.monitorProgress = true - options.progressModel = resource - if resource.get('dative_file_type') is 'storedOnTheServer' and - resource.get('base64_encoded_file') is '' - @addFileAsMultipartFormData resource, options - else - super resource, options - - getResourceForServerCreateMultipartFormData: (resource) -> - formData = new FormData() - for attribute of resource.attributes - value = resource.get attribute - if attribute in ['elicitor', 'speaker'] - if value is undefined - value = '' - else - value = value.id - if attribute in ['base64_encoded_file', 'parent_file'] - value = '' - if attribute isnt 'search-term' # `search-term` isn't a file attribute; also, the OLD will choke on it because it'll expect "term" to be a numeric index. - if attribute in ['tags', 'forms'] - for element, index in value - formData.append "#{attribute}-#{index}", element.id - else - formData.append attribute, value - formData - - addFileAsMultipartFormData: (resource, options) -> - resource.trigger "add#{@resourceNameCapitalized}Start" - payload = @getResourceForServerCreateMultipartFormData resource - monitorProgress = options.monitorProgress or false - progressModel = options.progressModel or null - @model.cors.request( - monitorProgress: monitorProgress - progressModel: progressModel - contentType: "multipart/form-data;" # This isn't actually used, it's just a signal to `CORS` that this is multipart/form-data... - method: 'POST' - url: @getAddResourceURL resource - payload: payload - onload: (responseJSON, xhr) => - @addResourceOnloadHandler resource, responseJSON, xhr, payload - onerror: (responseJSON) => - resource.trigger "add#{@resourceNameCapitalized}End" - resource.trigger "add#{@resourceNameCapitalized}Fail", - responseJSON.error, resource - console.log "Error in POST request to /#{@getServerSideResourceName()}" - ) - - # The OLD processes the file create request conditionally based on the - # *presence* of either of the attributes `base64_encoded_file` and `url`. - # Since Dative encodes this file "type" explicitly, we delete these - # attributes from the payload, as appropriate, based on the type. - getResourceForServerCreate: (resource) -> - result = super resource - if resource.get('dative_file_type') is 'storedOnTheServer' - delete result.url - if not resource.get 'base64_encoded_file' - delete result.base64_encoded_file - delete result.parent_file - else if resource.get('dative_file_type') is 'storedOnAnotherServer' - delete result.base64_encoded_file - else if resource.get('dative_file_type') is 'referencesASubintervalOfAnotherFile' - delete result.base64_encoded_file - delete result.url - result - diff --git a/app/scripts/collections/forms.coffee b/app/scripts/collections/forms.coffee deleted file mode 100644 index c1c02ca..0000000 --- a/app/scripts/collections/forms.coffee +++ /dev/null @@ -1,235 +0,0 @@ -define [ - './resources' - './../models/form' - './../utils/globals' - './../utils/utils' -], (ResourcesCollection, FormModel, globals, utils) -> - - # Forms Collection - # ---------------- - # - # Holds models for forms. - - class FormsCollection extends ResourcesCollection - - resourceName: 'form' - model: FormModel - - - ############################################################################ - # FETCH. - ############################################################################ - - # Method to handle the `onload` event of a CORS request to fetch a - # collection of forms from a server. In the OLD case, we use the - # superclass method; in the FieldDB case, we use a custom handler defined - # below. - fetchResourcesOnloadHandler: (responseJSON) -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' then super responseJSON - when 'FieldDB' then @fetchResourcesOnloadHandlerFieldDB responseJSON - - # Method to handle the `onload` event of a CORS request to fetch a - # page of datums from a FieldDB server. - fetchResourcesOnloadHandlerFieldDB: (responseJSON) -> - @trigger "fetch#{@resourceNamePluralCapitalized}End" - if responseJSON.rows - @add @getDativeFormModelsFromFieldDBObjects(responseJSON) - # The view that listens to `fetchFieldDBFormsSuccess` needs as - # argument a "paginator", which in this case just means an object - # with a `count` attribute. - paginator = count: responseJSON.total_rows - @trigger("fetch#{@resourceNamePluralCapitalized}Success", - paginator) - else - reason = responseJSON.reason or 'unknown' - @trigger("fetch#{@resourceNamePluralCapitalized}Fail", - "Failed in fetching the data. #{reason}") - console.log "request to datums_chronological failed; reason: #{reason}" - - ############################################################################ - # CREATE. - ############################################################################ - - # Method to handle the `onload` event of a CORS request to create a - # form. Delegates to the superclass's method in the OLD case and to a method - # defined here in the FieldDB case. - addResourceOnloadHandler: (resource, responseJSON, xhr, payload) -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' - super resource, responseJSON, xhr, payload - when 'FieldDB' - @addResourceOnloadHandlerFieldDB resource, responseJSON, xhr, payload - - # Method to handle the `onload` event of a CORS request to add a - # FieldDB datum (form). - addResourceOnloadHandlerFieldDB: (resource, responseJSON, xhr, payload) -> - resource.trigger "add#{@resourceNameCapitalized}End" - if xhr.status is 201 and responseJSON.ok is true - resource.set - id: responseJSON.id - _id: responseJSON.id - _rev: responseJSON.rev - dateEntered: payload.dateEntered - dateModified: payload.dateModified - timestamp: payload.timestamp - pouchname: payload.pouchname - comments: payload.comments - newEnteredByUser = _.findWhere payload.datumFields, label: 'enteredByUser' - enteredByUser = _.findWhere resource.get('datumFields'), label: 'enteredByUser' - enteredByUser.value = newEnteredByUser.username - enteredByUser.mask = newEnteredByUser.username - enteredByUser.user = newEnteredByUser.user - resource.trigger 'change' - resource.trigger "add#{@resourceNameCapitalized}Success" - else - errors = responseJSON.errors or {} - error = responseJSON.error - resource.trigger "add#{@resourceNameCapitalized}Fail", error, resource - for attribute, error of errors - resource.trigger "validationError:#{attribute}", error - console.log "add (POST) request to - /#{@getAddResourceURLFieldDB resource} failed (status not 201 - and/or `response.ok != true`)" - console.log errors - - ############################################################################ - # UPDATE. - ############################################################################ - - # Method to handle the `onload` event of a CORS request to update a - # form. Delegates to the superclass's method in the OLD case and to a method - # defined here in the FieldDB case. - updateResourceOnloadHandler: (resource, responseJSON, xhr, payload) -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' - super resource, responseJSON, xhr, payload - when 'FieldDB' - @updateResourceOnloadHandlerFieldDB resource, responseJSON, xhr, payload - - # Method to handle the `onload` event of a CORS request to update a - # FieldDB datum (form). - updateResourceOnloadHandlerFieldDB: (resource, responseJSON, xhr, payload) -> - resource.trigger "update#{@resourceNameCapitalized}End" - if xhr.status is 201 and responseJSON.ok is true - # FieldDB does no server-side processing. We just need to update the - # CouchDB revision UUID and add the client-side-created update-related - # attributes ... - resource.set - _rev: responseJSON.rev - dateModified: payload.dateModified - timestamp: payload.timestamp - pouchname: payload.pouchname - comments: payload.comments - newModifiedByUser = _.findWhere payload.datumFields, label: 'modifiedByUser' - modifiedByUser = _.findWhere resource.get('datumFields'), label: 'modifiedByUser' - modifiedByUser.users = newModifiedByUser.users - resource.trigger 'change' - resource.trigger "update#{@resourceNameCapitalized}Success" - else - errors = responseJSON.errors or {} - error = responseJSON.error - resource.trigger "update#{@resourceNameCapitalized}Fail", error, resource - for attribute, error of errors - resource.trigger "validationError:#{attribute}", error - console.log "Update (PUT) request to - /#{@getUpdateResourceURLFieldDB resource} failed (status not 201 - and/or `response.ok != true`)" - console.log errors - - - ############################################################################ - # Helpers. - ############################################################################ - - # Returns the URL for fetching a page of resources. - getResourcesPaginationURL: (options) -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' then super options - when 'FieldDB' then @getFieldDBFormsPaginationURL options - - # Returns the FieldDB URL for fetching a page of datums. - getFieldDBFormsPaginationURL: (options) -> - url = @getFetchAllFieldDBFormsURL() - if options.page and options.page isnt 0 and options.itemsPerPage - skip = (options.page - 1) * options.itemsPerPage - "#{url}?limit=#{options.itemsPerPage}&skip=#{skip}" - else - "#{url}?limit=#{options.itemsPerPage}" - - # Returns the FieldDB URL for fetching all datums in a corpus, in - # chronological order. - getFetchAllFieldDBFormsURL: -> - url = globals.applicationSettings.get('fieldDBApplication').corpus.url - "#{url}/_design/deprecated/_view/datums_chronological" - - # Returns a URL for updating a resource on a web service. - getUpdateResourceURL: (resource) -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' then super resource - when 'FieldDB' then @getUpdateResourceURLFieldDB resource - - # Returns a URL for updating a resource on a FieldDB web service. - # PUT //?rev= - getUpdateResourceURLFieldDB: (resource) -> - url = globals.applicationSettings.get 'baseDBURL' - pouchname = globals.applicationSettings.get 'activeFieldDBCorpus' - "#{url}/#{pouchname}/#{resource.get '_id'}?rev=#{resource.get '_rev'}" - - # Return a URL for adding a resource to a web service. - getAddResourceURL: (resource) -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' then super resource - when 'FieldDB' then @getAddResourceURLFieldDB resource - - # Returns a URL for adding a resource to a FieldDB web service. - # POST // - getAddResourceURLFieldDB: (resource) -> - url = globals.applicationSettings.get 'baseDBURL' - pouchname = globals.applicationSettings.get 'activeFieldDBCorpus' - "#{url}/#{pouchname}" - - - ############################################################################ - # TODOs related to the following two methods. - ############################################################################ - - # TODO: How do I valuate the `version` field of a particular datum field - # object? Example value I've seen in Spreadsheet: "v2.45.02". - # Relatedly, How do I set the `version` attribute of a user in - # `enteredByUser` or `modifiedByUser`? - # Example value seen: '2.45.02.01.33ss Mon Mar 2 01:37:08 EST 2015' - - # TODO: most FieldDB apps treat the session as a many-to-one required - # relation; however, I want to treat them as a many-to-many optional - # relation, i.e., any datum can belong to zero or more sessions. The only - # problem with this is that some session attributes will need to be - # present on datums and the denotation of "session" needs to be more - # general. Here, provisionally, I delete an empty `session` attribute on a - # create request. - - # TODO: blank comments should never be `setToModel` in the first place. - # This needs to be handled in the field view. - - # TODO: stop Dative from valuating the timestamps of previously created - # comments. - - # TODO: the attributes set here for server consumption need to be carefully - # saved to the client-side model, iff save has succeeded. - - # Returns a representation of a form that a server will accept for form - # creation. - getResourceForServerCreate: (resource) -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' then super resource - when 'FieldDB' then resource.toFieldDBForCreate() - - getResourceForServerUpdate: (resource) -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' then super resource - when 'FieldDB' then resource.toFieldDBForUpdate() - - # Return an array of `FormModel` instances built from FieldDB objects. - getDativeFormModelsFromFieldDBObjects: (responseJSON) -> - (new FormModel()).fieldDB2dative(o) for o in responseJSON.rows - diff --git a/app/scripts/collections/keyboards.coffee b/app/scripts/collections/keyboards.coffee deleted file mode 100644 index 3a8b114..0000000 --- a/app/scripts/collections/keyboards.coffee +++ /dev/null @@ -1,32 +0,0 @@ -define [ - './resources' - './../models/keyboard' -], (ResourcesCollection, KeyboardModel) -> - - # Keyboards Collection - # -------------------- - # - # Holds models for keyboards. - - class KeyboardsCollection extends ResourcesCollection - - resourceName: 'keyboard' - model: KeyboardModel - - # Return a representation of `resource` that the server will accept (for a - # create or update request). See `collections/forms.coffee for a - # FieldDB-specific override. - getResourceForServer: (resource) -> - result = resource.toOLD() - result.keyboard = @prepKeyboard result.keyboard - result - - prepKeyboard: (keyboard) -> - keysToDelete = [] - for keycode, keyMap of keyboard - if not _.some(_.values(keyMap)) - keysToDelete.push keycode - for attr in keysToDelete - delete keyboard[attr] - JSON.stringify keyboard - diff --git a/app/scripts/collections/language-models.coffee b/app/scripts/collections/language-models.coffee deleted file mode 100644 index 0fe4735..0000000 --- a/app/scripts/collections/language-models.coffee +++ /dev/null @@ -1,17 +0,0 @@ -define [ - './resources' - './../models/language-model' -], (ResourcesCollection, LanguageModelModel) -> - - # Language Models Collection - # -------------------------- - # - # Holds models for language models. - - class LanguageModelsCollection extends ResourcesCollection - - resourceName: 'languageModel' - model: LanguageModelModel - - serverSideResourceName: 'morphemelanguagemodels' - diff --git a/app/scripts/collections/languages.coffee b/app/scripts/collections/languages.coffee deleted file mode 100644 index 82d3329..0000000 --- a/app/scripts/collections/languages.coffee +++ /dev/null @@ -1,18 +0,0 @@ -define [ - './resources' - './../models/language' -], (ResourcesCollection, LanguageModel) -> - - # Languages Collection - # -------------------- - # - # Holds models for languages. - - class LanguagesCollection extends ResourcesCollection - - resourceName: 'language' - model: LanguageModel - - - - diff --git a/app/scripts/collections/morphological-parsers.coffee b/app/scripts/collections/morphological-parsers.coffee deleted file mode 100644 index fe7b243..0000000 --- a/app/scripts/collections/morphological-parsers.coffee +++ /dev/null @@ -1,17 +0,0 @@ -define [ - './resources' - './../models/morphological-parser' -], (ResourcesCollection, MorphologicalParserModel) -> - - # Morphological Parsers Collection - # -------------------------------- - # - # Holds models for morphological parsers. - - class MorphologicalParsersCollection extends ResourcesCollection - - resourceName: 'morphologicalParser' - model: MorphologicalParserModel - - serverSideResourceName: 'morphologicalparsers' - diff --git a/app/scripts/collections/morphologies.coffee b/app/scripts/collections/morphologies.coffee deleted file mode 100644 index 478e3c9..0000000 --- a/app/scripts/collections/morphologies.coffee +++ /dev/null @@ -1,15 +0,0 @@ -define [ - './resources' - './../models/morphology' -], (ResourcesCollection, MorphologyModel) -> - - # Morphologies Collection - # ----------------------- - # - # Holds models for morphologies. - - class MorphologiesCollection extends ResourcesCollection - - resourceName: 'morphology' - model: MorphologyModel - diff --git a/app/scripts/collections/old-application-settings.coffee b/app/scripts/collections/old-application-settings.coffee deleted file mode 100644 index 537d1ed..0000000 --- a/app/scripts/collections/old-application-settings.coffee +++ /dev/null @@ -1,38 +0,0 @@ -define [ - './resources' - './../models/old-application-settings' -], (ResourcesCollection, OLDApplicationSettingsModel) -> - - # OLD Application Settings Collection - # ----------------------------------- - # - # Holds models for OLD application settings. - # - # *NOTE:* The OLD has no pagination for its application settings `index` - # action. This means that all we can do is retrieve *all* of the application - # settings. As a result, this class overrides the super-class's - # `fetchResourcesOnloadHandler`. - - class OLDApplicationSettingsCollection extends ResourcesCollection - - resourceName: 'oldApplicationSettings' - serverSideResourceName: 'applicationsettings' - model: OLDApplicationSettingsModel - - # Method to handle the `onload` event of a CORS request to fetch all of an - # OLD web service's application settings resources. - # Note the strange spellings of the events triggered here; just go along - # with it ... - fetchResourcesOnloadHandler: (responseJSON) -> - Backbone.trigger 'fetchOldApplicationSettingsesEnd' - if @utils.type(responseJSON) is 'array' - if responseJSON.length isnt 0 - @reset @getDativeResourceModelsFromOLDObjects(responseJSON) - Backbone.trigger 'fetchOldApplicationSettingsesSuccess' - else - # Failure to retrieve application settings indicates that we are not - # logged in. `AppView` hears the following event and sets its - # `@loggedIn` to `false` in response. - Backbone.trigger 'fetchOldApplicationSettingsesFail', - 'failed to fetch all OLD application settings resources' - diff --git a/app/scripts/collections/orthographies.coffee b/app/scripts/collections/orthographies.coffee deleted file mode 100644 index b54ee8c..0000000 --- a/app/scripts/collections/orthographies.coffee +++ /dev/null @@ -1,18 +0,0 @@ -define [ - './resources' - './../models/orthography' -], (ResourcesCollection, OrthographyModel) -> - - # Orthographies Collection - # ------------------------ - # - # Holds models for orthographies. - - class OrthographiesCollection extends ResourcesCollection - - resourceName: 'orthography' - model: OrthographyModel - - - - diff --git a/app/scripts/collections/pages.coffee b/app/scripts/collections/pages.coffee deleted file mode 100644 index 1a0d61b..0000000 --- a/app/scripts/collections/pages.coffee +++ /dev/null @@ -1,33 +0,0 @@ -define [ - './resources' - './../models/page' - './../utils/globals' - ], (ResourcesCollection, PageModel, globals) -> - - # Pages Collection - # ---------------- - # - # Holds models for pages. - - class PagesCollection extends ResourcesCollection - - resourceName: 'page' - model: PageModel - - # If the home page is updated, we need to tell the `AppView` about that. - updateResourceOnloadHandler: (resource, responseJSON, xhr, payload) -> - @checkForHomePageChange responseJSON, xhr - super resource, responseJSON, xhr, payload - - # If a 'home' page has just been created, we need to tell the `AppView` - # about that. - addResourceOnloadHandler: (resource, responseJSON, xhr, payload) -> - @checkForHomePageChange responseJSON, xhr - super resource, responseJSON, xhr, payload - - checkForHomePageChange: (responseJSON, xhr) -> - if xhr.status is 200 and responseJSON.name == 'home' - globals.applicationSettings.set 'homepage', responseJSON - globals.applicationSettings.save() - Backbone.trigger 'homePageChanged' - diff --git a/app/scripts/collections/phonologies.coffee b/app/scripts/collections/phonologies.coffee deleted file mode 100644 index 26e3926..0000000 --- a/app/scripts/collections/phonologies.coffee +++ /dev/null @@ -1,15 +0,0 @@ -define [ - './resources' - './../models/phonology' -], (ResourcesCollection, PhonologyModel) -> - - # Phonollogies Collection - # ----------------------- - # - # Holds models for phonologies. - - class PhonologiesCollection extends ResourcesCollection - - resourceName: 'phonology' - model: PhonologyModel - diff --git a/app/scripts/collections/resources.coffee b/app/scripts/collections/resources.coffee deleted file mode 100644 index 0302724..0000000 --- a/app/scripts/collections/resources.coffee +++ /dev/null @@ -1,299 +0,0 @@ -define [ - 'backbone', - './../models/resource' - './../utils/utils' - './../utils/globals' -], (Backbone, ResourceModel, utils, globals) -> - - # Resources Collection - # --------------------- - # - # Holds models for resources. Houses code for fetching (GET), adding (POST), - # and updating (PUT) arbitrary resources from an OLD RESTful web service. - # - # This is intended to be a base class for other collections, e.g., - # PhonologiesCollection. The minimum work required for subclassing is - # to override `@resourceName` and `@model`. - # - # TODO: make this code work for FieldDB web services (if applicable). - - class ResourcesCollection extends Backbone.Collection - - # Override these two attributes in subclasses. - resourceName: 'resource' - model: ResourceModel - - # If this is non-null, it is expected to be an object with a `query` - # attribute and a `paginator` attribute. The presence of such an attribute - # will change how `fetchResources` behaves. - search: null - - # If this is non-null, it is expected to be a(n OLD-style) corpus model. - # The presence of such an attribute will change how `fetchResources` - # behaves. - corpus: null - - initialize: (models, options) -> - @utils = utils - @resourceNameCapitalized = utils.capitalize @resourceName - @resourceNamePlural = utils.pluralize @resourceName - @resourceNamePluralCapitalized = utils.capitalize @resourceNamePlural - super models, options - - url: 'fake-url' - - ############################################################################ - # FETCH. - ############################################################################ - - # Fetch Resources (with pagination, if needed) - # OLD case: GET `/?page=x&items_per_page=y - # FieldDB case: - # See http://online-linguistic-database.readthedocs.org/en/latest/interface.html#get-resources - fetchResources: (options) -> - @trigger "fetch#{@resourceNamePluralCapitalized}Start" - @model.cors.request( - method: @getResourcesHTTPMethod() - url: @getResourcesPaginationURL options - payload: @getResourcesPayload options - onload: (responseJSON) => - @fetchResourcesOnloadHandler responseJSON - onerror: (responseJSON) => - @trigger "fetch#{@resourceNamePluralCapitalized}End" - @trigger "fetch#{@resourceNamePluralCapitalized}Fail", - "error in fetching #{@getServerSideResourceName()}" - console.log "Error in GET request to /#{@getServerSideResourceName()}" - ) - - getResourcesHTTPMethod: -> - if @search or @corpus then 'SEARCH' else 'GET' - - # Return a payload for the `fetchResources` request. Note: there is only a - # payload if we have a truthy `@search` attribute, which means the fetching - # is based on a search and uses a SEARCH (non-standard) HTTP method with a - # payload describing the search (and pagination, and ordering). - getResourcesPayload: (options) -> - if @search or @corpus - if @corpus - search = - filter: ["Form", "corpora", "id", "in", [@corpus.get('id')]] - order_by: ["Form", "id", "desc" ] - else - search = @search - if options.page and options.itemsPerPage - query: search - paginator: - page: options.page - items_per_page: options.itemsPerPage - else - query: search - else - null - - # Method to handle the `onload` event of a CORS request to fetch a - # collection of resources from a server. The default behaviour currently - # expects an OLD backend. See `collections/forms.coffee` for a FieldDB - # override/fork. - fetchResourcesOnloadHandler: (responseJSON) -> - @trigger "fetch#{@resourceNamePluralCapitalized}End" - if 'items' of responseJSON - @add @getDativeResourceModelsFromOLDObjects(responseJSON.items) - @trigger "fetch#{@resourceNamePluralCapitalized}Success", - responseJSON.paginator - # The OLD returns `[]` if there are no resources. This is - # inconsistent and should probably be changed OLD-side. - else if utils.type(responseJSON) is 'array' and - responseJSON.length is 0 - @trigger "fetch#{@resourceNamePluralCapitalized}Success" - else - reason = responseJSON.reason or 'unknown' - @trigger "fetch#{@resourceNamePluralCapitalized}Fail", - "failed to fetch all #{@getServerSideResourceName()}; reason: - #{reason}" - console.log "GET request to /#{@getServerSideResourceName()} failed; reason: - #{reason}" - - - ############################################################################ - # CREATE. - ############################################################################ - - # Add (create) a resource. - # POST `/` - addResource: (resource, options={}) -> - resource.trigger "add#{@resourceNameCapitalized}Start" - payload = @getResourceForServerCreate resource - monitorProgress = options.monitorProgress or false - progressModel = options.progressModel or null - @model.cors.request( - method: 'POST' - url: @getAddResourceURL resource - payload: payload - monitorProgress: monitorProgress - progressModel: progressModel - onload: (responseJSON, xhr) => - @addResourceOnloadHandler resource, responseJSON, xhr, payload - onerror: (responseJSON) => - resource.trigger "add#{@resourceNameCapitalized}End" - resource.trigger "add#{@resourceNameCapitalized}Fail", - responseJSON.error, resource - console.log "Error in POST request to /#{@getServerSideResourceName()}" - ) - - # Method to handle the `onload` event of a CORS request to add a - # particular resource. The default behaviour currently expects an OLD - # backend. See `collections/forms.coffee` for a FieldDB override/fork. - addResourceOnloadHandler: (resource, responseJSON, xhr, payload) -> - resource.trigger "add#{@resourceNameCapitalized}End" - if xhr.status is 200 - resource.set responseJSON - # Remember the values of the "sticky" attributes of the resource that - # we just added so that subsequent adds will have those values as - # defaults. - resourcesSettings = globals.applicationSettings.get('resources') - resourceSettings = resourcesSettings[@resourceNamePlural] - if resourceSettings - resourceSettings.pastValues = {} - stickyAttributes = resourceSettings.stickyAttributes or [] - for attr in stickyAttributes - resourceSettings.pastValues[attr] = resource.get(attr) - globals.applicationSettings.save() - resource.trigger "add#{@resourceNameCapitalized}Success", resource - else - errors = responseJSON.errors or {} - if utils.type(errors) is 'object' - # TODO: each of the following 2 lines is needed in different - # contexts; find solution! - resource.trigger "add#{@resourceNameCapitalized}Fail", errors.error - # resource.trigger "add#{@resourceNameCapitalized}Fail", errors - for attribute, error of errors - attribute = @errorAttributeTransformer attribute - resource.trigger "validationError:#{attribute}", error - else - resource.trigger "add#{@resourceNameCapitalized}Fail", errors - attribute = @getAttributeForError errors - if attribute - resource.trigger "validationError:#{attribute}", errors - else - resource.trigger "validationError:general", errors - console.log "POST request to /#{@getServerSideResourceName()} failed - (status not 200) ..." - console.log errors - - # Attempt to match an error string to the resource attribute that caused - # the error. Return a `null` if not possible. Override this in sub-classes. - getAttributeForError: (error) -> null - - - ############################################################################ - # UPDATE. - ############################################################################ - - # Update a resource. - # PUT `//` - updateResource: (resource, options) -> - resource.trigger "update#{@resourceNameCapitalized}Start" - payload = @getResourceForServerUpdate resource - @model.cors.request( - method: 'PUT' - url: @getUpdateResourceURL resource - payload: payload - onload: (responseJSON, xhr) => - @updateResourceOnloadHandler resource, responseJSON, xhr, payload - onerror: (responseJSON) => - resource.trigger "update#{@resourceNameCapitalized}End" - resource.trigger("update#{@resourceNameCapitalized}Fail", - responseJSON.error, resource) - console.log "Error in PUT request to /#{@getServerSideResourceName()}" - ) - - # Method to handle the `onload` event of a CORS request to update a - # particular resource. The default behaviour currently expects an OLD - # backend. See `collections/forms.coffee` for a FieldDB override/fork. - updateResourceOnloadHandler: (resource, responseJSON, xhr, payload) -> - resource.trigger "update#{@resourceNameCapitalized}End" - if xhr.status is 200 - # We remove the `search` value from the response before we set it; we - # don't want to assign a new object to this attribute because various - # views are tied to these (mutable) objects. - if 'search' of responseJSON then delete responseJSON.search - resource.set responseJSON - resource.trigger "update#{@resourceNameCapitalized}Success" - else - errors = responseJSON.errors or {} - error = responseJSON.error - resource.trigger "update#{@resourceNameCapitalized}Fail", error, resource - for attribute, error of errors - attribute = @errorAttributeTransformer attribute - resource.trigger "validationError:#{attribute}", error - console.log "PUT request to /#{@getServerSideResourceName()} failed (status - not 200) ..." - console.log errors - - # This is provided so that we can transform the resource attribute - # associated with a particular error to another attribute in sub-classes. - # For instance, when an OLD collection resource is added/updated and its - # contents contain bad form references, the attribute will be "forms", - # though from Dative's perspective it should be "contents". - errorAttributeTransformer: (errorAttribute) -> - return errorAttribute - - - ############################################################################ - # Helpers - ############################################################################ - - getOLDURL: -> - globals.applicationSettings.get('activeServer').get 'url' - - # The default is to just use the plural form of the resource name as the - # server-side name for the resource; however, this can be overridden with - # `@serverSideResourceName`, as is necessary with "subcorpora"/"corpora". - getServerSideResourceName: -> - @serverSideResourceName or @resourceNamePlural.toLowerCase() - - # Return a URL for requesting a page of from an OLD - # web service. GET parameters control pagination and ordering. See - # `collections/forms.coffee` for a FieldDB-specific override. - # Note: other possible parameters: `order_by_model`, `order_by_attribute`, - # and `order_by_direction`. - getResourcesPaginationURL: (options={}) -> - if @search or @corpus - "#{@getOLDURL()}/#{@getServerSideResourceName()}" - else if options.page and options.itemsPerPage - "#{@getOLDURL()}/#{@getServerSideResourceName()}?\ - page=#{options.page}&\ - items_per_page=#{options.itemsPerPage}" - else - "#{@getOLDURL()}/#{@getServerSideResourceName()}" - - # Return a URL for updating a resource on an OLD web service. See - # `collections/forms.coffee for a FieldDB-specific override. - getUpdateResourceURL: (resource) -> - "#{@getOLDURL()}/#{@getServerSideResourceName()}/#{resource.get 'id'}" - - # Return a URL for adding a resource to an OLD web service. See - # `collections/forms.coffee for a FieldDB-specific override. - getAddResourceURL: (resource) -> - "#{@getOLDURL()}/#{@getServerSideResourceName()}" - - # Return a representation of `resource` that the server will accept (for a - # create or update request). See `collections/forms.coffee for a - # FieldDB-specific override. - getResourceForServer: (resource) -> - resource.toOLD() - - # Return a representation of `resource` that the server will accept for a - # create request. - getResourceForServerCreate: (resource) -> - @getResourceForServer resource - - # Return a representation of `resource` that the server will accept for an - # update request. - getResourceForServerUpdate: (resource) -> - @getResourceForServer resource - - # Return an array of `@model` instances built from OLD objects. - getDativeResourceModelsFromOLDObjects: (responseJSON) -> - (new @model(o) for o in responseJSON) - diff --git a/app/scripts/collections/searches.coffee b/app/scripts/collections/searches.coffee deleted file mode 100644 index b089b08..0000000 --- a/app/scripts/collections/searches.coffee +++ /dev/null @@ -1,17 +0,0 @@ -define [ - './resources' - './../models/search' -], (ResourcesCollection, SearchModel) -> - - # Searches Collection - # ----------------------- - # - # Holds models for searches. - - class SearchesCollection extends ResourcesCollection - - resourceName: 'search' - model: SearchModel - - serverSideResourceName: 'formsearches' - diff --git a/app/scripts/collections/servers.coffee b/app/scripts/collections/servers.coffee deleted file mode 100644 index 252eca2..0000000 --- a/app/scripts/collections/servers.coffee +++ /dev/null @@ -1,18 +0,0 @@ -define [ - 'backbone', - './../models/server' - ], (Backbone, ServerModel) -> - - # Servers Collection - # ------------------ - - class ServersCollection extends Backbone.Collection - - model: ServerModel - - initialize: -> - @on 'removeme', @_removeModel - - _removeModel: (model) -> - @remove model - diff --git a/app/scripts/collections/sources.coffee b/app/scripts/collections/sources.coffee deleted file mode 100644 index 7753699..0000000 --- a/app/scripts/collections/sources.coffee +++ /dev/null @@ -1,15 +0,0 @@ -define [ - './resources' - './../models/source' -], (ResourcesCollection, SourceModel) -> - - # Sources Collection - # ------------------ - # - # Holds models for sources, i.e., texts referenced by data points. - - class SourcesCollection extends ResourcesCollection - - resourceName: 'source' - model: SourceModel - diff --git a/app/scripts/collections/speakers.coffee b/app/scripts/collections/speakers.coffee deleted file mode 100644 index f22f717..0000000 --- a/app/scripts/collections/speakers.coffee +++ /dev/null @@ -1,15 +0,0 @@ -define [ - './resources' - './../models/speaker' -], (ResourcesCollection, SpeakerModel) -> - - # Speakers Collection - # ------------------- - # - # Holds models for speakers. - - class SpeakersCollection extends ResourcesCollection - - resourceName: 'speaker' - model: SpeakerModel - diff --git a/app/scripts/collections/subcorpora.coffee b/app/scripts/collections/subcorpora.coffee deleted file mode 100644 index 6dd8d8f..0000000 --- a/app/scripts/collections/subcorpora.coffee +++ /dev/null @@ -1,19 +0,0 @@ -define [ - './resources' - './../models/subcorpus' -], (ResourcesCollection, SubcorpusModel) -> - - # Subcorpora Collection - # ----------------------- - # - # Holds models for subcorpora. - - class SubcorporaCollection extends ResourcesCollection - - resourceName: 'subcorpus' - model: SubcorpusModel - - # When requesting from the OLD, we need to request 'corpora', not - # 'subcorpora', hence this attribute. - serverSideResourceName: 'corpora' - diff --git a/app/scripts/collections/syntactic-categories.coffee b/app/scripts/collections/syntactic-categories.coffee deleted file mode 100644 index 928e17b..0000000 --- a/app/scripts/collections/syntactic-categories.coffee +++ /dev/null @@ -1,15 +0,0 @@ -define [ - './resources' - './../models/syntactic-category' -], (ResourcesCollection, SyntacticCategoryModel) -> - - # Syntactic Categories Collection - # ------------------------------- - # - # Holds models for syntactic categories. - - class SyntacticCategoriesCollection extends ResourcesCollection - - resourceName: 'syntacticCategory' - model: SyntacticCategoryModel - diff --git a/app/scripts/collections/tags.coffee b/app/scripts/collections/tags.coffee deleted file mode 100644 index 8bf834f..0000000 --- a/app/scripts/collections/tags.coffee +++ /dev/null @@ -1,17 +0,0 @@ -define [ - './resources' - './../models/tag' -], (ResourcesCollection, TagModel) -> - - # Tags Collection - # --------------- - # - # Holds models for tags. - - class TagsCollection extends ResourcesCollection - - resourceName: 'tag' - model: TagModel - - - diff --git a/app/scripts/collections/users.coffee b/app/scripts/collections/users.coffee deleted file mode 100644 index e9f3a09..0000000 --- a/app/scripts/collections/users.coffee +++ /dev/null @@ -1,24 +0,0 @@ -define [ - './resources' - './../models/user-old' -], (ResourcesCollection, UserModel) -> - - # Users Collection - # ----------------------- - # - # Holds models for users. - - class UsersCollection extends ResourcesCollection - - resourceName: 'user' - model: UserModel - - # This recognizes an OLD "username-already-taken" error message and returns - # 'username'. - getAttributeForError: (error) -> - regex = /^The (\w+) \w+ is already taken\.$/ - if regex.test error - 'username' - else - null - diff --git a/app/scripts/main.coffee b/app/scripts/main.coffee deleted file mode 100644 index 7d95925..0000000 --- a/app/scripts/main.coffee +++ /dev/null @@ -1,78 +0,0 @@ -#/*global require*/ -'use strict' - -require.config - - shim: - jquery: - exports: '$' - lodash: - exports: '_' - # FieldDB: - # exports: 'FieldDB' - backbone: - exports: 'Backbone' - deps: ['lodash', 'jquery'] - #jqueryui: ['../bower_components/jquery/dist/jquery'] - jqueryui: ['jquery'] - # backboneindexeddb: ['backbone'] - multiselect: ['jquery', 'jqueryui'] - jqueryelastic: ['jquery'] - autosize: ['jquery'] - superfish: ['jquery'] - superclick: ['jquery'] - supersubs: ['jquery'] - backbonerelational: ['backbone'] - backbonelocalstorage: ['backbone'] - - paths: - jquery: '../bower_components/jquery/dist/jquery' - backbone: '../bower_components/backbone/backbone' - lodash: '../bower_components/lodash/dist/lodash' - underscore: '../bower_components/lodash/dist/lodash.underscore' - # backboneindexeddb: - # '../bower_components/indexeddb-backbonejs-adapter/backbone-indexeddb' - bootstrap: '../bower_components/sass-bootstrap/dist/js/bootstrap' - text: '../bower_components/requirejs-text/text' - jqueryui: '../bower_components/jqueryui/jquery-ui' - superfish: '../bower_components/superfish/dist/js/superfish' - superclick: '../bower_components/superclick/dist/js/superclick' - #superfish: '../bower_components/superfish/dist/js/superfish' - #superfish: 'jquery-extensions/superfish/dist/js/superfish' - igt: 'jquery-extensions/igt' - jqueryuicolors: 'jquery-extensions/jqueryui-colors' - tagit: 'jquery-extensions/tag-it' - sfjquimatch: 'jquery-extensions/superfish-jqueryui-match' - # Supersubs plugin removed in v1.6 of superfish. See - # https://github.com/joeldbirch/superfish. - supersubs: '../bower_components/superfish/dist/js/supersubs' - #supersubs: '../bower_components/superfish/dist/js/supersubs' - #supersubs: 'jquery-extensions/superfish/dist/js/supersubs' - multiselect: '../bower_components/multiselect/js/jquery.multi-select' - jqueryelastic: '../bower_components/jakobmattsson-jquery-elastic/jquery.elastic.source' - autosize: '../bower_components/autosize/jquery.autosize' - #betterelastictextarea: '../bower_components/better-elastic-textarea/dist/better-elastic-textarea' - spin: '../bower_components/spin.js/spin' - jqueryspin: '../bower_components/spin.js/jquery.spin' - # FieldDB: '../bower_components/fielddb/fielddb' - backbonerelational: '../bower_components/backbone-relational/backbone-relational' - backbonelocalstorage: '../bower_components/backbone.localStorage/backbone.localStorage' - -require [ - 'views/app', - 'routes/router' - 'multiselect' - 'jqueryelastic' - 'jqueryuicolors' - 'tagit' - 'jqueryspin' - ], (AppView, Workspace) -> - window.debugMode = false - - $ -> - app = new AppView() - window.onbeforeunload = -> - app.close() - window.location.href += window.location.hash - window.location.reload() - diff --git a/app/scripts/models/application-settings.coffee b/app/scripts/models/application-settings.coffee deleted file mode 100644 index 647de77..0000000 --- a/app/scripts/models/application-settings.coffee +++ /dev/null @@ -1,794 +0,0 @@ -define [ - './base' - './server' - './parser-task-set' - './keyboard-preference-set' - './morphology' - './language-model' - './../collections/servers' - './../utils/utils' - './../utils/globals' - # 'FieldDB' -], (BaseModel, ServerModel, ParserTaskSetModel, KeyboardPreferenceSetModel, - MorphologyModel, LanguageModelModel, ServersCollection, utils, globals) -> - - # Application Settings Model - # -------------------------- - # - # Holds Dative's application settings. Persisted in the browser using - # localStorage. - # - # Also contains the authentication logic. - - class ApplicationSettingsModel extends BaseModel - - modelClassName2model: - 'MorphologyModel': MorphologyModel - 'LanguageModelModel': LanguageModelModel - - initialize: -> - @fetch() - # fieldDBTempApp = new (FieldDB.App)(@get('fieldDBApplication')) - # fieldDBTempApp.authentication.eventDispatcher = Backbone - @listenTo Backbone, 'authenticate:login', @authenticate - @listenTo Backbone, 'authenticate:logout', @logout - @listenTo Backbone, 'authenticate:register', @register - @listenTo @, 'change:activeServer', @activeServerChanged - if @get('activeServer') - activeServer = @get 'activeServer' - if activeServer instanceof ServerModel - @listenTo activeServer, 'change:url', @activeServerURLChanged - if not Modernizr.localstorage - throw new Error 'localStorage unavailable in this browser, please upgrade.' - - @setVersion() - - # set app version from package.json - setVersion: -> - if @get('version') is 'da' - $.ajax - url: 'package.json', - type: 'GET' - dataType: 'json' - error: (jqXHR, textStatus, errorThrown) -> - console.log "Ajax request for package.json threw an error: - #{errorThrown}" - success: (packageDetails, textStatus, jqXHR) => - @set 'version', packageDetails.version - - activeServerChanged: -> - #console.log 'active server has changed says the app settings model' - if @get('fieldDBApplication') - @get('fieldDBApplication').website = @get('activeServer').get('website') - @get('fieldDBApplication').brand = @get('activeServer').get('brand') or - @get('activeServer').get('userFriendlyServerName') - @get('fieldDBApplication').brandLowerCase = - @get('activeServer').get('brandLowerCase') or - @get('activeServer').get('serverCode') - - activeServerURLChanged: -> - #console.log 'active server URL has changed says the app settings model' - - getURL: -> - url = @get('activeServer')?.get('url') - if url.slice(-1) is '/' then url.slice(0, -1) else url - - getCorpusServerURL: -> - @get('activeServer')?.get 'corpusUrl' - - getServerCode: -> - @get('activeServer')?.get 'serverCode' - - authenticateAttemptDone: (taskId) -> - Backbone.trigger 'longTask:deregister', taskId - Backbone.trigger 'authenticate:end' - - - # Login (a.k.a. authenticate) - #========================================================================= - - # Attempt to authenticate with the passed-in credentials - authenticate: (username, password) -> - if @get('activeServer')?.get('type') is 'FieldDB' - @authenticateFieldDBAuthService - username: username - password: password - authUrl: @get('activeServer')?.get('url') - else - @authenticateOLD username: username, password: password - - authenticateOLD: (credentials) -> - taskId = @guid() - Backbone.trigger 'longTask:register', 'authenticating', taskId - Backbone.trigger 'authenticateStart' - @constructor.cors.request( - method: 'POST' - timeout: 20000 - url: "#{@getURL()}/login/authenticate" - payload: credentials - onload: (responseJSON) => - @authenticateAttemptDone taskId - Backbone.trigger 'authenticateEnd' - if responseJSON.authenticated is true - @set - username: credentials.username - loggedIn: true - loggedInUser: responseJSON.user - homepage: responseJSON.homepage - @save() - Backbone.trigger 'authenticateSuccess' - else - Backbone.trigger 'authenticateFail', responseJSON - onerror: (responseJSON) => - Backbone.trigger 'authenticateEnd' - Backbone.trigger 'authenticateFail', responseJSON - @authenticateAttemptDone taskId - ontimeout: => - Backbone.trigger 'authenticateEnd' - Backbone.trigger 'authenticateFail', error: 'Request timed out' - @authenticateAttemptDone taskId - ) - - authenticateFieldDBAuthService: (credentials) => - taskId = @guid() - Backbone.trigger 'longTask:register', 'authenticating', taskId - Backbone.trigger 'authenticateStart' - if not @get 'fieldDBApplication' - @set 'fieldDBApplication', FieldDB.FieldDBObject.application - if not @get('fieldDBApplication').authentication or not @get('fieldDBApplication').authentication.login - @get('fieldDBApplication').authentication = new FieldDB.Authentication() - @get('fieldDBApplication').authentication.login(credentials).then( - (promisedResult) => - @set - username: credentials.username, - password: credentials.password, # TODO dont need this! - loggedInUser: @get('fieldDBApplication').authentication.user - @save() - Backbone.trigger 'authenticateEnd' - Backbone.trigger 'authenticateSuccess' - @authenticateAttemptDone taskId - , - (error) => - @authenticateAttemptDone taskId - Backbone.trigger 'authenticateEnd' - Backbone.trigger 'authenticateFail', responseJSON.userFriendlyErrors - ).fail (error) => - Backbone.trigger 'authenticateEnd' - Backbone.trigger 'authenticateFail', error: 'Request timed out' - @authenticateAttemptDone taskId - - # This is the to-be-deprecated version that has been made obsolete by - # `authenticateFieldDBAuthService` above. - authenticateFieldDBAuthService_: (credentials) => - taskId = @guid() - Backbone.trigger 'longTask:register', 'authenticating', taskId - Backbone.trigger 'authenticateStart' - @constructor.cors.request( - method: 'POST' - timeout: 20000 - url: "#{@getURL()}/login" - payload: credentials - onload: (responseJSON) => - if responseJSON.user - # Remember the corpusServiceURL so we can logout. - @get('activeServer')?.set( - 'corpusServerURL', @getFieldDBBaseDBURL(responseJSON.user)) - @set - baseDBURL: @getFieldDBBaseDBURL(responseJSON.user) - username: credentials.username, - password: credentials.password, - gravatar: responseJSON.user.gravatar, - loggedInUser: responseJSON.user - @save() - credentials.name = credentials.username - @authenticateFieldDBCorpusService credentials, taskId - else - Backbone.trigger 'authenticateFail', responseJSON.userFriendlyErrors - @authenticateAttemptDone taskId - onerror: (responseJSON) => - Backbone.trigger 'authenticateEnd' - Backbone.trigger 'authenticateFail', responseJSON - @authenticateAttemptDone taskId - ontimeout: => - Backbone.trigger 'authenticateEnd' - Backbone.trigger 'authenticateFail', error: 'Request timed out' - @authenticateAttemptDone taskId - ) - - authenticateFieldDBCorpusService: (credentials, taskId) -> - @constructor.cors.request( - method: 'POST' - timeout: 3000 - url: "#{@get('baseDBURL')}/_session" - payload: credentials - onload: (responseJSON) => - # TODO @jrwdunham: this responseJSON has a roles Array attribute which - # references more corpora than I'm seeing from the `corpusteam` - # request. Why the discrepancy? - Backbone.trigger 'authenticateEnd' - @authenticateAttemptDone taskId - if responseJSON.ok - @set - loggedIn: true - loggedInUserRoles: responseJSON.roles - @save() - Backbone.trigger 'authenticateSuccess' - else - Backbone.trigger 'authenticateFail', responseJSON - onerror: (responseJSON) => - Backbone.trigger 'authenticateEnd' - Backbone.trigger 'authenticateFail', responseJSON - @authenticateAttemptDone taskId - ontimeout: => - Backbone.trigger 'authenticateEnd' - Backbone.trigger 'authenticateFail', error: 'Request timed out' - @authenticateAttemptDone taskId - ) - - localStorageKey: 'dativeApplicationSettings' - - # An extremely simple re-implementation of `save`: we just JSON-ify the app - # settings and store them in localStorage. - save: -> - localStorage.setItem @localStorageKey, - JSON.stringify(@attributes) - - # Fetching means simply getting the app settings JSON object from - # localStorage and setting it to the present model. Certain attributes are - # evaluated to other Backbone models; handling this conversion is the job - # of `backbonify`. - fetch: (options) -> - if localStorage.getItem @localStorageKey - applicationSettingsObject = - JSON.parse(localStorage.getItem(@localStorageKey)) - applicationSettingsObject = @fix applicationSettingsObject - applicationSettingsObject = @backbonify applicationSettingsObject - @set applicationSettingsObject - @usingDefaults = false - else - @usingDefaults = true - @save() - - # Fix the application settings, if necessary. This is necessary when Dative - # has been updated but the user's application settings object, as persisted - # in their `localStorage`, was built using an older version of Dative. If - # the app settings are out-of-date, then Dative may break. - # Note: `aso` is the Application Settings Object. - # The `ns` (namespace) param is good for debugging. - fix: (aso, defaults=null, ns='') -> - defaults = defaults or @defaults() - if 'attributes' of defaults - defaults = defaults.attributes - for attr, defval of defaults - if attr not of aso - aso[attr] = defval - # Recursively call `@fix` if the default val is an object. - else if ((@utils.type(defval) == 'object') and - not ((defval instanceof Backbone.Model) or - (defval instanceof Backbone.Collection))) - aso[attr] = @fix aso[attr], defval, "#{ns}.#{attr}" - for attr of aso - if attr not of defaults - delete aso[attr] - return aso - - # Logout - #========================================================================= - - logout: -> - if @get('activeServer')?.get('type') is 'FieldDB' - @logoutFieldDB() - else - @logoutOLD() - - logoutOLD: -> - taskId = @guid() - Backbone.trigger 'longTask:register', 'logout', taskId - Backbone.trigger 'logoutStart' - @constructor.cors.request( - url: "#{@getURL()}/login/logout" - method: 'GET' - timeout: 3000 - onload: (responseJSON, xhr) => - @authenticateAttemptDone taskId - Backbone.trigger 'logoutEnd' - if responseJSON.authenticated is false or - (xhr.status is 401 and - responseJSON.error is "Authentication is required to access this - resource.") - @set loggedIn: false, homepage: null - @save() - Backbone.trigger 'logoutSuccess' - else - Backbone.trigger 'logoutFail' - onerror: (responseJSON) => - Backbone.trigger 'logoutEnd' - @authenticateAttemptDone taskId - Backbone.trigger 'logoutFail' - ontimeout: => - Backbone.trigger 'logoutEnd' - @authenticateAttemptDone taskId - Backbone.trigger 'logoutFail', error: 'Request timed out' - ) - - logoutFieldDB: -> - taskId = @guid() - Backbone.trigger 'longTask:register', 'logout', taskId - Backbone.trigger 'logoutStart' - if not @get 'fieldDBApplication' - @set 'fieldDBApplication', FieldDB.FieldDBObject.application - # TODO: @cesine: I'm getting "`authentication.logout` is not a function" - # errors here ... - @get('fieldDBApplication').authentication - .logout({letClientHandleCleanUp: 'dontReloadWeNeedToCleanUpInDativeClient'}) - .then( - (responseJSON) => - @set - fieldDBApplication: null - loggedIn: false - activeFieldDBCorpus: null - activeFieldDBCorpusTitle: null - @save() - Backbone.trigger 'logoutSuccess' - , - (reason) -> - Backbone.trigger 'logoutFail', reason.userFriendlyErrors.join ' ' - ).done( - => - Backbone.trigger 'logoutEnd' - @authenticateAttemptDone taskId - ) - - # Check if logged in - #========================================================================= - - # Check if we are already logged in. - checkIfLoggedIn: -> - if @get('activeServer')?.get('type') is 'FieldDB' - @checkIfLoggedInFieldDB() - else - @checkIfLoggedInOLD() - - # Check with the OLD if we are logged in. We ask for the speakers. and - # trigger 'authenticateFail' if we can't get them. - checkIfLoggedInOLD: -> - taskId = @guid() - Backbone.trigger('longTask:register', 'checking if already logged in', - taskId) - @constructor.cors.request( - url: "#{@getURL()}/speakers" - timeout: 3000 - onload: (responseJSON) => - @authenticateAttemptDone(taskId) - if utils.type(responseJSON) is 'array' - @set 'loggedIn', true - @save() - Backbone.trigger 'authenticateSuccess' - else - @set 'loggedIn', false - @save() - Backbone.trigger 'authenticateFail' - onerror: (responseJSON) => - @set 'loggedIn', false - @save() - Backbone.trigger 'authenticateFail', responseJSON - @authenticateAttemptDone(taskId) - ontimeout: => - @set 'loggedIn', false - @save() - Backbone.trigger 'authenticateFail', error: 'Request timed out' - @authenticateAttemptDone(taskId) - ) - - checkIfLoggedInFieldDB: -> - taskId = @guid() - Backbone.trigger('longTask:register', 'checking if already logged in', - taskId) - FieldDB.Database::resumeAuthenticationSession().then( - (sessionInfo) => - if sessionInfo.ok and sessionInfo.userCtx.name - @set 'loggedIn', true - @save() - Backbone.trigger 'authenticateSuccess' - else - @set 'loggedIn', false - @save() - Backbone.trigger 'authenticateFail' - , - (reason) => - @set 'loggedIn', false - @save() - Backbone.trigger 'authenticateFail', reason - ).done(=> @authenticateAttemptDone taskId) - - - # Register a new user - # ========================================================================= - - # `RegisterDialogView` should never allow an OLD registration attempt. - register: (params) -> - if @get('activeServer')?.get('type') is 'FieldDB' - @registerFieldDB params - - registerFieldDB: (params) -> - taskId = @guid() - Backbone.trigger 'longTask:register', 'registering a new user', taskId - params.authUrl = @getURL() - params.appVersionWhenCreated = "v#{@get('version')}da" - @constructor.cors.request( - url: "#{@getURL()}/register" - payload: params - method: 'POST' - timeout: 10000 # FieldDB auth can take some time to register a new user ... - onload: (responseJSON) => - @authenticateAttemptDone taskId - # TODO @cesine: what other kinds of responses to registration requests - # can the auth service make? - if responseJSON.user? - user = responseJSON.user - Backbone.trigger 'register:success', responseJSON - else - Backbone.trigger 'register:fail', responseJSON.userFriendlyErrors - onerror: (responseJSON) => - @authenticateAttemptDone taskId - Backbone.trigger 'register:fail', 'server responded with error' - ontimeout: => - @authenticateAttemptDone taskId - Backbone.trigger 'register:fail', 'Request timed out' - ) - - - idAttribute: 'id' - - # Transform certain attribute values of the `appSetObj` - # object into Backbone collections/models and return the `appSetObj`. - backbonify: (appSetObj) -> - - serverModelsArray = ((new ServerModel(s)) for s in appSetObj.servers) - appSetObj.servers = new ServersCollection(serverModelsArray) - activeServer = appSetObj.activeServer - if activeServer - appSetObj.activeServer = appSetObj.servers.get activeServer.id - - longRunningTasks = appSetObj.longRunningTasks - for task in appSetObj.longRunningTasks - task.resourceModel = - new @modelClassName2model[task.modelClassName](task.resourceModel) - for task in appSetObj.longRunningTasksTerminated - task.resourceModel = - new @modelClassName2model[task.modelClassName](task.resourceModel) - - appSetObj.parserTaskSet = new ParserTaskSetModel(appSetObj.parserTaskSet) - - appSetObj.keyboardPreferenceSet = - new KeyboardPreferenceSetModel(appSetObj.keyboardPreferenceSet) - - appSetObj - - ############################################################################ - # Defaults - ############################################################################ - - defaults: -> - - # Default servers are provided at runtime using servers.json - server = - new ServerModel - id: @guid() - name: 'OLD Local Development' - type: 'OLD' - url: 'http://127.0.0.1:5000' - serverCode: null - corpusServerURL: null - website: 'http://www.onlinelinguisticdatabase.org' - - servers = new ServersCollection([server]) - - parserTaskSetModel = new ParserTaskSetModel() - - keyboardPreferenceSetModel = new KeyboardPreferenceSetModel() - - id: @guid() - activeServer: servers.at 0 - loggedIn: false - loggedInUser: null - loggedInUserRoles: [] - - # An OLD will send an object representing the home page when a user - # successfully logs in, if there is such a page named 'home'. - homepage: null - - baseDBURL: null - username: '' - - # This gets set to `true` as soon as the user makes modifications to the - # list of servers. This allows us to avoid over-writing the - # user-specified servers with those supplied by Dative in servers.json. - serversModified: false - - # This contains the array of objects contstituting the last set of - # servers that Dative has sent us. We use this to decide whether to - # prompt/annoy the user to merge these servers into their own. - lastSeenServersFromDative: null - - servers: servers - # serverTypes: ['FieldDB', 'OLD'] - serverTypes: ['OLD'] - fieldDBServerCodes: [ - 'localhost' - 'testing' - 'beta' - 'production' - 'mcgill' - 'concordia' - 'dyslexdisorth' - ] - - # TODO: remove the activeFieldDBCorpusTitle and related attributes. We - # should simply store a real `CorpusModel` as the value of - # `activeFieldDBCorpus`. Note that `AppView` adds - # `activeFieldDBCorpusModel` and stores a Backbone model there. This all - # needs to be cleaned up. - activeFieldDBCorpus: null - activeFieldDBCorpusTitle: null - - languagesDisplaySettings: - itemsPerPage: 100 - dataLabelsVisible: true - allFormsExpanded: false - - formsDisplaySettings: - itemsPerPage: 10 - dataLabelsVisible: false - allFormsExpanded: false - - subcorporaDisplaySettings: - itemsPerPage: 10 - dataLabelsVisible: true - allSubcorporaExpanded: false - - phonologiesDisplaySettings: - itemsPerPage: 1 - dataLabelsVisible: true - allPhonologiesExpanded: false - - morphologiesDisplaySettings: - itemsPerPage: 1 - dataLabelsVisible: true - allMorphologiesExpanded: false - - activeJQueryUITheme: 'pepper-grinder' - defaultJQueryUITheme: 'pepper-grinder' - jQueryUIThemes: [ - ['ui-lightness', 'UI lightness'] - ['ui-darkness', 'UI darkness'] - ['smoothness', 'Smoothness'] - ['start', 'Start'] - ['redmond', 'Redmond'] - ['sunny', 'Sunny'] - ['overcast', 'Overcast'] - ['le-frog', 'Le Frog'] - ['flick', 'Flick'] - ['pepper-grinder', 'Pepper Grinder'] - ['eggplant', 'Eggplant'] - ['dark-hive', 'Dark Hive'] - ['cupertino', 'Cupertino'] - ['south-street', 'South Street'] - ['blitzer', 'Blitzer'] - ['humanity', 'Humanity'] - ['hot-sneaks', 'Hot Sneaks'] - ['excite-bike', 'Excite Bike'] - ['vader', 'Vader'] - ['dot-luv', 'Dot Luv'] - ['mint-choc', 'Mint Choc'] - ['black-tie', 'Black Tie'] - ['trontastic', 'Trontastic'] - ['swanky-purse', 'Swanky Purse'] - ] - - # Use this to limit how many "long-running" tasks can be initiated from - # within the app. A "long-running task" is a request to the server that - # requires polling to know when it has terminated, e.g., phonology - # compilation, morphology generation and compilation, etc. - # NOTE !IMPORTANT: the OLD has a single foma worker and all requests to - # compile FST-based resources appear to enter into a queue. This means - # that a 3s request made while a 1h request is ongoing will take 1h1s! - # Not good ... - longRunningTasksMax: 2 - - # An array of objects with keys `resourceName`, `taskName`, - # `taskStartTimestamp`, and `taskPreviousUUID`. - longRunningTasks: [] - - # An array of objects with keys `resourceName`, `taskName`, - # `taskStartTimestamp`, and `taskPreviousUUID`. - longRunningTasksTerminated: [] - - # IME types that can be uploaded (to an OLD server, at least). - # TODO: this should be expanded and/or made backend-specific. - allowedFileTypes: [ - 'application/pdf' - 'image/gif' - 'image/jpeg' - 'image/png' - 'audio/mpeg' - 'audio/mp3' - 'audio/ogg' - 'audio/x-wav' - 'audio/wav' - 'video/mpeg' - 'video/mp4' - 'video/ogg' - 'video/quicktime' - 'video/x-ms-wmv' - ] - - version: 'da' - - parserTaskSet: parserTaskSetModel - - keyboardPreferenceSet: keyboardPreferenceSetModel - - # This object contains metadata about Dative resources, i.e., forms, - # files, etc. - # TODO: resource display settings (e.g., `formsDisplaySettings` above) - # should be migrated here. - resources: - - forms: - - # Array of form attributes that are "sticky", i.e., that will stick - # around and whose values in the most recent add request will be the - # defaults for subsequent add requests. - stickyAttributes: [] - - # Array of form attributes that *may* be specified as "sticky". - possiblyStickyAttributes: [ - 'date_elicited' - 'elicitation_method' - 'elicitor' - 'source' - 'speaker' - 'status' - 'syntactic_category' - 'tags' - ] - - # This will be populated by the resources collection upon successful - # add requests. It will map attribute names in `stickyAttributes` - # above to their values in the most recent successful add request. - pastValues: {} - - # These objects define metadata on the fields of form resources. - # Note that there are separate metadata objects for OLD fields and - # for FieldDB fields. - fieldsMeta: - - OLD: - - grammaticality: [ - 'grammaticality' - ] - - # IGT OLD Form Attributes. - igt: [ - 'narrow_phonetic_transcription' - 'phonetic_transcription' - 'transcription' - 'morpheme_break' - 'morpheme_gloss' - ] - - # Note: this is currently not being used (just being consistent - # with the FieldDB `fieldsMeta` object below) - translation: [ - 'translations' - ] - - # Secondary OLD Form Attributes. - secondary: [ - 'syntactic_category_string' - 'break_gloss_category' - 'comments' - 'speaker_comments' - 'elicitation_method' - 'tags' - 'syntactic_category' - 'date_elicited' - 'speaker' - 'elicitor' - 'enterer' - 'datetime_entered' - 'modifier' - 'datetime_modified' - 'verifier' - 'source' - 'files' - #'collections' # Does the OLD provide the collections when the forms resource is fetched? - 'syntax' - 'semantics' - 'status' - 'UUID' - 'id' - ] - - readonly: [ - 'syntactic_category_string' - 'break_gloss_category' - 'enterer' - 'datetime_entered' - 'modifier' - 'datetime_modified' - 'UUID' - 'id' - ] - - # This array may contain the names of OLD form attributes that should - # be hidden. This is the (for now only client-side-stored) data - # structure that users manipulate in the settings widget of a - # `FormView` instance. - hidden: [ - 'narrow_phonetic_transcription' - 'phonetic_transcription' - 'verifier' - ] - - FieldDB: - - # This is the set of form attributes that are considered by - # Dative to denote grammaticalities. - grammaticality: [ - 'judgement' - ] - - # IGT FieldDB form attributes. - # The returned array defines the "IGT" attributes of a FieldDB - # form (along with their order). These are those that are aligned - # into columns of one word each when displayed in an IGT view. - igt: [ - 'utterance' - 'morphemes' - 'gloss' - ] - - # This is the set of form attributes that are considered by - # Dative to denote a translation. - translation: [ - 'translation' - ] - - # Secondary FieldDB form attributes. - # The returned array defines the order of how the secondary - # attributes are displayed. It is defined in - # models/application-settings because it should ultimately be - # user-configurable. - # QUESTION: @cesine: how is the elicitor of a FieldDB - # datum/session documented? - # TODO: `audioVideo`, `images` - secondary: [ - 'syntacticCategory' - 'comments' - 'tags' - 'dateElicited' # session field - 'language' # session field - 'dialect' # session field - 'consultants' # session field - 'enteredByUser' - 'dateEntered' - 'modifiedByUser' - 'dateModified' - 'syntacticTreeLatex' - 'validationStatus' - 'timestamp' # make this visible? - 'id' - ] - - # These read-only fields will not be given input fields in - # add/update interfaces. - readonly: [ - 'enteredByUser' - 'dateEntered' - 'modifiedByUser' - 'dateModified' - ] - diff --git a/app/scripts/models/base-relational.coffee b/app/scripts/models/base-relational.coffee deleted file mode 100644 index 2072ca8..0000000 --- a/app/scripts/models/base-relational.coffee +++ /dev/null @@ -1,18 +0,0 @@ -define [ - 'backbone' - './../utils/cors' - './../utils/utils' - 'backbonerelational' -], (Backbone, CORS, utils) -> - - # Base Relational Model - # --------------------- - # - # Functionality that all relational models and collections need. - - class BaseRelationalModel extends Backbone.RelationalModel - - guid: utils.guid - @cors: new CORS() - utils: utils - diff --git a/app/scripts/models/base.coffee b/app/scripts/models/base.coffee deleted file mode 100644 index 2019c81..0000000 --- a/app/scripts/models/base.coffee +++ /dev/null @@ -1,20 +0,0 @@ -define [ - 'backbone' - './../utils/cors' - './../utils/utils' -], (Backbone, CORS, utils) -> - - # Base Model - # ---------- - # - # Functionality that all models and collections need. - - class BaseModel extends Backbone.Model - - guid: utils.guid - @cors: new CORS() - utils: utils - - requiredString: (value) -> - if value?.trim?() in ['', undefined] then 'Please enter a value' else null - diff --git a/app/scripts/models/collection.coffee b/app/scripts/models/collection.coffee deleted file mode 100644 index 8c0b315..0000000 --- a/app/scripts/models/collection.coffee +++ /dev/null @@ -1,117 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Collection Model - # ---------------- - # - # A Backbone model for Dative collections, i.e., the text-like objects of the - # OLD. - - class CollectionModel extends ResourceModel - - resourceName: 'collection' - - ############################################################################ - # Collection Schema - ############################################################################ - # - # Note: the OLD also returns `contents_unpacked`. This is a version of the - # `contents` value where all collection-embedding directives are replaced - # by the `contents` value of the referenced collection. Dative may at some - # point need to make use of this `contents_unpacked` value. - - defaults: -> - title: '' # Required, unique among collection - # names, max 255 chars. - description: '' # description of the collection. - type: '' # max 255 chars. - url: '' # max 255 chars. - markup_language: '' # One of "Markdown" or "reStructuredText", - # defaults to "reStructuredText". - contents: '' # a string of lightweight markup that also - # contains references to forms. This defines the - # collection. - html: '' # HTML generated from the user-supplied markup. - source: null # (textual source (e.g., research - # paper, book of texts, pedagogical material, - # etc.) of the collection, if applicable; - # received as an object, returned as an integer - # id.) - speaker: null # A reference to the OLD speaker with whom this - # collection was elicited, if appropriate. - elicitor: null # A reference to the OLD user who elicited this - # collection, if appropriate. - date_elicited: '' # When this collection was elicited, if appropriate. - tags: [] # An array of tags assigned to the collection. - files: [] # An array of files associated to this collection. - enterer: null # an object (attributes: `id`, `first_name`, - # `last_name`, `role`) - modifier: null # an object (attributes: `id`, `first_name`, - # `last_name`, `role`) - datetime_entered: "" # (datetime file was created/entered; - # generated on the server as a UTC datetime; - # communicated in JSON as a UTC ISO 8601 datetime, - # e.g., '2015-02-11T10:50:57.821192'.) - datetime_modified: "" # (datetime file was last modified; - # format and construction same as - # `datetime_entered`.) - UUID: '' # A string UUID - id: null # relational id - - editableAttributes: [ - 'title' - 'description' - 'type' - 'url' - 'markup_language' - 'contents' - 'source' - 'speaker' - 'elicitor' - 'date_elicited' - 'tags' - 'files' - ] - - manyToOneAttributes: ['source', 'elicitor', 'speaker'] - - manyToManyAttributes: ['files', 'tags'] - - getValidator: (attribute) -> - switch attribute - when 'title' then @requiredString - when 'url' then @urlFragment - else null - - urlFragment: (value) -> - if /^[a-zA-Z0-9_\/-]{0,255}$/.test value - null - else - 'Only a-z, A-Z, 0-9, _, /, and - are allowed in the url value.' - - # The collection model overrides the resource model's `fetchResource` - # method so that it can pass GET params in the request. This is necessary - # because it allows us to request a LaTeX representation of the - # collection's contents from the server. - fetchResource: (id, payload=null) -> - @trigger "fetch#{@resourceNameCapitalized}Start" - @constructor.cors.request( - method: 'GET' - url: @getFetchResourceURL id, payload - onload: (responseJSON, xhr) => - @fetchResourceOnloadHandler responseJSON, xhr - onerror: (responseJSON) => - @trigger "fetch#{@resourceNameCapitalized}End" - error = responseJSON.error or 'No error message provided.' - @trigger "fetch#{@resourceNameCapitalized}Fail", error, @ - console.log "Error in GET request to - /#{@getServerSideResourceName()}/#{@get 'id'} (onerror triggered)." - ) - - # Add `payload` to the URL as a GET query string. - getFetchResourceURL: (id, payload=null) -> - if payload - params = ("#{k}=#{v}" for k, v of payload).join '&' - "#{@getOLDURL()}/#{@getServerSideResourceName()}/#{id}?#{params}" - else - "#{@getOLDURL()}/#{@getServerSideResourceName()}/#{id}" - diff --git a/app/scripts/models/corpus.coffee b/app/scripts/models/corpus.coffee deleted file mode 100644 index 2456c98..0000000 --- a/app/scripts/models/corpus.coffee +++ /dev/null @@ -1,254 +0,0 @@ -define [ - 'underscore' - 'backbone' - './base' - './../utils/utils' -], (_, Backbone, BaseModel, utils) -> - - # Corpus Model - # ------------ - # - # A model for FieldDB corpora. A `CorpusModel` is instantiated with a pouchname - # for the corpus. This is a unique, spaceless, lowercase name that begins with - # its creator's username. - # - # A corpus model's data must be retrieved by two requests. (1) retrieves the - # bulk of the corpus data while (2) returns the users with access to the - # corpus: - # - # 1. @fetch - # 2. @fetchUsers - # - # Three other server-request methods are defined here: - # - # 3. @removeUserFromCorpus - # 4. @grantRoleToUser - # 5. @updateCorpus - - class CorpusModel extends BaseModel - - initialize: (options) -> - @applicationSettings = options.applicationSettings - @pouchname = options.pouchname - - ############################################################################ - # CORS methods - ############################################################################ - - # Fetch the corpus data. - # GET `//_design/deprecated/_view/private_corpuses` - fetch: -> - @trigger 'fetchStart' - CorpusModel.cors.request( - method: 'GET' - timeout: 10000 - url: "#{@getCorpusServerURL()}/_design/deprecated/_view/private_corpuses" - onload: (responseJSON) => - fieldDBCorpusObject = @getFieldDBCorpusObject responseJSON - # TODO @jrwdunham: should this `set` be a `save`? - if fieldDBCorpusObject then @set fieldDBCorpusObject - @trigger 'fetchEnd' - onerror: (responseJSON) => - console.log "Failed to fetch a corpus at #{@url()}." - @trigger 'fetchEnd' - ontimeout: => - console.log "Failed to fetch a corpus at #{@url()}. Request timed out." - @trigger 'fetchEnd' - ) - - # Fetch the users with access to a corpus. - # POST `/corpusteam` - fetchUsers: -> - @trigger 'fetchUsersStart' - payload = @getDefaultPayload() - CorpusModel.cors.request( - method: 'POST' - timeout: 10000 - url: "#{payload.authUrl}/corpusteam" - payload: payload - onload: (responseJSON) => - if responseJSON.users - @set 'users', responseJSON.users - @trigger 'fetchUsersEnd' - else - @trigger 'fetchUsersEnd' - console.log 'Failed request to /corpusteam: no users attribute.' - onerror: (responseJSON) => - @trigger 'fetchUsersEnd' - console.log 'Failed request to /corpusteam: error.' - ontimeout: => - @trigger 'fetchUsersEnd' - console.log 'Failed request to /corpusteam: timed out.' - ) - - # Grant a role on a corpus to a user. - # POST `/updateroles` - grantRoleToUser: (role, username) -> - @trigger 'grantRoleToUserStart' - payload = @getDefaultPayload() - payload.userRoleInfo = - admin: if role is 'admin' then true else false - writer: if role is 'reader' then false else true - reader: true - pouchname: payload.pouchname - role: @getFieldDBRole role - usernameToModify: username - CorpusModel.cors.request( - method: 'POST' - timeout: 10000 - url: "#{payload.authUrl}/updateroles" - payload: payload - onload: (responseJSON) => - @trigger 'grantRoleToUserEnd' - if responseJSON.corpusadded - @trigger 'grantRoleToUserSuccess', role, username - else - console.log 'Failed request to /updateroles: no `corpusadded` attribute.' - onerror: (responseJSON) => - @trigger 'grantRoleToUserEnd' - console.log 'Failed request to /updateroles: error.' - ontimeout: => - @trigger 'grantRoleToUserEnd' - console.log 'Failed request to /updateroles: timed out.' - ) - - # Remove a user from a corpus. - # POST `/updateroles` - removeUserFromCorpus: (username) -> - @trigger 'removeUserFromCorpusStart' - payload = @getDefaultPayload() - payload.userRoleInfo = - pouchname: payload.pouchname - removeUser: true - usernameToModify: username - CorpusModel.cors.request( - method: 'POST' - timeout: 10000 - url: "#{payload.authUrl}/updateroles" - payload: payload - onload: (responseJSON) => - @trigger 'removeUserFromCorpusEnd' - if responseJSON.corpusadded - @trigger 'removeUserFromCorpusSuccess', username - else - console.log 'Failed request to /updateroles: no `corpusadded` attribute.' - onerror: (responseJSON) => - @trigger 'removeUserFromCorpusEnd' - console.log 'Failed request to /updateroles: error.' - ontimeout: => - @trigger 'removeUserFromCorpusEnd' - console.log 'Failed request to /updateroles: timed out.' - ) - - # NOTE: @jrwdunham, @cesine: NOT IMPLEMENTED - # See https://github.com/jrwdunham/dative/issues/78 - - # Update the details of a corpus - # PUT `// - # QUESTIONS: - # 1. do I need to manually change `titleAsURL`? What about the other fields? - - # TODO: - # set title to new title - # set description to new description - # update dateModified timestamp: (new Date().getTime() - # allow user to modify gravatar - # show gravatar in corpus list - # - updateCorpus: (title, description) -> - console.log 'in updateCorpus of corpus model' - console.log JSON.stringify(@, undefined, 2) - # for attr in _.keys(@attributes).sort() - # if attr not in ['applicationSettings', 'isActive'] - # console.log '\n' - # console.log attr - # console.log JSON.stringify(@attributes[attr]) - # console.log '\n' - - updateCorpus_: (title, description) -> - @trigger 'updateCorpusStart' - payload = @getDefaultPayload() - payload.userRoleInfo = - pouchname: payload.pouchname - removeUser: true - usernameToModify: username - CorpusModel.cors.request( - method: 'PUT' - timeout: 10000 - url: "#{payload.authUrl}/updateroles" - payload: payload - onload: (responseJSON) => - @trigger 'updateCorpusEnd' - if responseJSON.corpusadded - @trigger 'updateCorpusSuccess', username - else - console.log 'Failed request to /updateroles: no `corpusadded` attribute.' - onerror: (responseJSON) => - @trigger 'updateCorpusEnd' - console.log 'Failed request to /updateroles: error.' - ontimeout: => - @trigger 'updateCorpusEnd' - console.log 'Failed request to /updateroles: timed out.' - ) - - ### - # This is the object that is sent to PUT `// - # on an update request. It is - _id: "63ff8fd7b5be6becbd9e5413b3060dd5" - _rev: "12-d1e6a51f42377dc3803207bbf6a13baa" - api: "private_corpuses" - authUrl: FieldDB.Database.prototype.BASE_AUTH_URL - collection: "private_corpuses" - comments: [] - confidential: {fieldDBtype: "Confidential", secretkey: "e14714cb-ddfb-5e4e-bad9-2a75d573dbe0",…} - conversationFields: [,…] - copyright: "Default: Add names of the copyright holders of the corpus." - *** dateModified: 1421953770590 # timestamp of last modification (I think) - datumFields: [{fieldDBtype: "DatumField", labelFieldLinguists: "Judgement", mask: "grammatical",…},…] - datumStates: [{color: "success", showInSearchResults: "checked", selected: "selected", state: "Checked"},…] - dbname: "jrwdunham-firstcorpus" - description: "Best corpus ever" - fieldDBtype: "Corpus" - *** gravatar: "33b8cbbfd6c49148ad31ed95e67b4390" - license: {title: "Default: Creative Commons Attribution-ShareAlike (CC BY-SA).",…} - modifiedByUser: {value: "jrwdunham, jrwdunham, jrwdunham, jrwdunham, jrwdunham",…} - participantFields: [,…] - pouchname: "jrwdunham-firstcorpus" - publicCorpus: "Private" - searchKeywords: "Froggo" - sessionFields: [{fieldDBtype: "DatumField", labelFieldLinguists: "Goal", value: "", mask: "", encryptedValue: "",…},…] - termsOfUse: {,…} - *** timestamp: 1399303339523 # timestamp of corpus creation (I think) - title: "Big Bear Corpus" - titleAsUrl: "big_bear_corpus" - url: FieldDB.Database.prototype.BASE_DB_URL+"/jrwdunham-firstcorpus" - version: "v2.38.16" - ### - - ############################################################################ - # utility methods - ############################################################################ - - getCorpusServerURL: -> - url = @applicationSettings.get 'baseDBURL' - "#{url}/#{@pouchname}" - - getFieldDBCorpusObject: (responseJSON) -> - result = {} - if responseJSON.rows? - [..., tmp] = responseJSON.rows # Last element in array, a la CoffeeScript - result = tmp.value - - getDefaultPayload: -> - authUrl: @applicationSettings.get?('activeServer')?.get?('url') - username: @applicationSettings.get?('username') - password: @applicationSettings.get?('password') # TODO trigger authenticate:mustconfirmidentity - serverCode: @applicationSettings.get?('activeServer')?.get?('serverCode') - pouchname: @get 'pouchname' - - getFieldDBRole: (role) -> - switch role - when 'admin' then 'admin' - when 'writer' then 'read_write' - when 'reader' then 'read_only' - diff --git a/app/scripts/models/database.coffee b/app/scripts/models/database.coffee deleted file mode 100644 index 7b300f2..0000000 --- a/app/scripts/models/database.coffee +++ /dev/null @@ -1,18 +0,0 @@ -define [ - 'underscore', - 'backbone', - ], (_, Backbone) -> - - database = - id: "fielddb-database" - description: "IndexedDB database to hold FieldDB data locally in the web - browser" - nolog: true # tell backbone-indexeddb to shut up - migrations: [ - version: 1 - migrate: (transaction, next) -> - store = transaction.db.createObjectStore "forms" - store.createIndex "transcriptionIndex", "transcription", {unique: false} - next() - ] - diff --git a/app/scripts/models/elicitation-method.coffee b/app/scripts/models/elicitation-method.coffee deleted file mode 100644 index 4aae3a7..0000000 --- a/app/scripts/models/elicitation-method.coffee +++ /dev/null @@ -1,37 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Elicitation Method Model - # ------------------------ - # - # A Backbone model for Dative elicitation methods. - - class ElicitationMethodModel extends ResourceModel - - resourceName: 'elicitationMethod' - - ############################################################################ - # Elicitation Method Schema - ############################################################################ - - defaults: -> - name: '' # Required, unique - # among elicitation method - # names, max 255 chars. - description: '' # description of the - # elicitation method. - id: null # relational id - datetime_modified: "" # (datetime resource - # was last modified; format - # and construction same as - # `datetime_entered`.) - - editableAttributes: [ - 'name' - 'description' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - else null - diff --git a/app/scripts/models/file.coffee b/app/scripts/models/file.coffee deleted file mode 100644 index ecebfd0..0000000 --- a/app/scripts/models/file.coffee +++ /dev/null @@ -1,289 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # File Model - # --------------- - # - # A Backbone model for Dative files. Note: currently assumes OLD files as the - # server-side counterpart. - # - # NOTE/WARNING: OLD files are more complicated than typical resources because - # there are various types of file resource with different attributes. The - # differences are based on whether the file data are stored elsewhere and on - # whether the file data are sent (during the creation request) to the OLD as - # a Base64-encoded string or as binary data using the multipart/form-data - # content type. See - # https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/controllers/files.py#L128-L193. - - class FileModel extends ResourceModel - - initialize: (attributes, options) -> - super attributes, options - if @get 'url' - @set 'dative_file_type', 'storedOnAnotherServer' - else if @get 'parent_file' - @set 'dative_file_type', 'referencesASubintervalOfAnotherFile' - else - @set 'dative_file_type', 'storedOnTheServer' - - resourceName: 'file' - - editableAttributes: [] - - manyToOneAttributes: ['elicitor', 'speaker', 'parent_file'] - - manyToManyAttributes: ['forms', 'tags'] - - getValidator: (attribute) -> - switch attribute - when 'name' then @validName - when 'url' then @validURL - when 'start' then @validStart - when 'end' then @validEnd - when 'parent_file' then @validParentFile - when 'base64_encoded_file' then @hasFileData - when 'blobURL' then @hasFileData - else null - - hasFileData: (value) -> - if (not @get('id')) and @get('dative_file_type') is 'storedOnTheServer' - if @get('base64_encoded_file') or @get('blobURL') - null - else - 'Please select a file for upload.' - else - null - - validParentFile: (value) -> - if @get('dative_file_type') is 'referencesASubintervalOfAnotherFile' - if value - null - else - 'Please choose a parent file.' - else - null - - validStart: (value) -> - if @get('dative_file_type') is 'referencesASubintervalOfAnotherFile' - validNumberError = @validNumber value - if validNumberError - validNumberError - else - validEndNumberError = @validNumber @get('end') - if validEndNumberError - null - else - if value < @get('end') - null - else - 'The start value must be less than the end value.' - else - null - - validEnd: (value) -> - if @get('dative_file_type') is 'referencesASubintervalOfAnotherFile' - validNumberError = @validNumber value - if validNumberError - validNumberError - else - validStartNumberError = @validNumber @get('start') - if validStartNumberError - null - else - if value > @get('start') - null - else - 'The end value must be greater than the start value.' - else - null - - validNumber: (value) -> - if @get('dative_file_type') is 'referencesASubintervalOfAnotherFile' - try - if value.match /^\d+(\.\d+)?$/ - null - else - 'Please enter a number.' - catch - null - else - null - - validName: (value) -> - if @get('dative_file_type') in ['storedOnAnotherServer', - 'referencesASubintervalOfAnotherFile'] - @requiredString value - else - null - - validURL: (value) -> - if @get('dative_file_type') is 'storedOnAnotherServer' - if @utils.isValidURL value - null - else - 'This does not appear to be a valid URL.' - else - null - - ############################################################################ - # File Schema - ############################################################################ - - # See: - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L1091-L1111 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/corpus.py#L82-L104 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/model.py - - defaults: -> - - # These attributes are relevant to all file types (regardless of where - # the file data are stored or how they are uploaded). They are also - # relevant on update requests. - description: '' # A description of the file. - utterance_type: '' # If the file represents a recording of an - # utterance, then a value here may be - # appropriate; possible values accepted by the - # OLD currently are 'None', 'Object Language - # Utterance', 'Metalanguage Utterance', and - # 'Mixed Utterance'. - speaker: null # A reference to the OLD speaker who was the - # speaker of this file, if appropriate. - elicitor: null # A reference to the OLD user who elicited this - # file, if appropriate. - tags: [] # An array of OLD tags assigned to the file. - forms: [] # An array of forms associated to this file. - date_elicited: '' # When this file was elicited, if appropriate. - - base64_encoded_file: '' # `base64_encoded_file`: When creating a file, - # this attribute may contain a base-64 encoded - # string representation of the file data, so long - # as the file size does not exceed 20MB. - - filename: '' # the filename, cannot be empty, max 255 chars. - # Note: the OLD will remove quotation marks and - # replace spaces with underscores. Note also that - # the OLD will not allow the file to be created - # if the MIMEtype guessed on the basis of the - # filename is different from that guessed on the - # basis of the file data. - name: '' # the name of the file, max 255 chars; This value - # is only valid when the file is created as a - # subinterval-referencing file or as a file whose - # file data are stored elsewhere, i.e., at the - # provided URL. - MIME_type: '' # a string representing the MIME type. - - # Externally hosted file attributes. These attributes relevant only to - # files whose file data are stored elsewhere, i.e., not on an OLD web - # service. - url: '' # a valid URL where that resolves to the file - # data. - password: '' # If needed, this field should contain the value - # of a password needed to access the file on the - # server where it is stored. - - # Subinterval-referencing file attributes. These attributes are relevant - # only to file objects that reference an existing file object for their - # file data and specify, in addition, start and end points within the - # parent file to represent their file content/data. - # NOTE: a valuated `name` attribute is also required when creating a file - # in this way. - parent_file: null # a reference to an existing OLD audio/video - # file; note: the creator must have permission to - # access the parent file, it must exist, it must - # be audio/video, it cannot be empty, and the - # parent file cannot itself be a - # subinterval-referencing file. - start: null # start time of the subinterval; a value must be - # specified, it must be a number, and it must be - # less than `end`. - end: null # end time of the subinterval; a value must be - # specified, it must be a number, and it must be - # greater than `start`. - - - # Attributes that the OLD sends to us, but which the OLD will ignore if - # we try to send them back. - id: null # An integer relational id - UUID: '' # A string UUID - enterer: null # an object (attributes: `id`, `first_name`, - # `last_name`, `role`) - modifier: null # an object (attributes: `id`, `first_name`, - # `last_name`, `role`) - datetime_entered: "" # (datetime file was created/entered; - # generated on the server as a UTC datetime; - # communicated in JSON as a UTC ISO 8601 datetime, - # e.g., '2015-02-11T10:50:57.821192'.) - datetime_modified: "" # (datetime file was last modified; - # format and construction same as - # `datetime_entered`.) - size: null # calculated server-side - lossy_filename: '' # created server-side; name of the lossy copy of - # the file, if applicable. E.g., if you upload a - # 100MB .wav file named "elicitation.wav", then the - # lossy filename may be a 25MB "elicitation.ogg". - - # Dative-only attribute that indicates what kind of file this is. Note - # that the OLD does not persist this attribute/value. Possible values - # here are `'storedOnTheServer'`, `'storedOnAnotherServer'`, and - # `'referencesASubintervalOfAnotherFile'`. - dative_file_type: 'storedOnTheServer' - - blobURL: '' # Dative-only use: a JavaScript/HTML5 BLOB URL so - # that we can (dis)play large selected files - # prior to upload. - filedata: null - - - - # Fetch the file data of this file resource. - # GET `///serve_reduced` or - # GET `///serve` if the full-size - # file is needed. - fetchFileData: (reduced=true) -> - url = @getFetchFileDataURL reduced - @trigger "fetchFileDataStart" - @constructor.cors.request( - method: 'GET' - url: url - onload: (response, xhr) => - @trigger "fetchFileDataEnd" - if xhr.status is 200 - @trigger "fetchFileDataSuccess", response - else - error = response.error or 'No error message provided.' - if xhr.status is 404 and - @utils.startsWith error, "There is no size-reduced copy" - @trigger "fetchFileDataFailNoReduced", error, @ - else - @trigger "fetchFileDataFail", error, @ - console.log "GET request to /#{url} failed (status not 200)." - console.log error - onerror: (response) => - @trigger "fetchFileDataEnd" - error = response.error or 'No error message provided.' - @trigger "fetchFileDataFail", error, @ - console.log "Error in GET request to #{url} (onerror triggered)." - ) - - # The type of URL used to fetch a resource on an OLD backend. - getFetchFileDataURL: (reduced=false) -> - base = "#{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}" - if reduced then "#{base}/serve_reduced" else "#{base}/serve" - - # Returns `true` if the file is audio or video. - isAudioVideo: -> - MIME_type = @get 'MIME_type' - if MIME_type - @utils.startsWith(MIME_type, 'audio') or - @utils.startsWith(MIME_type, 'video') - else - try - if @get('url') - MIME_type = @utils.getMIMEType @get('url') - @utils.startsWith(MIME_type, 'audio') or - @utils.startsWith(MIME_type, 'video') - else - false - catch - false - diff --git a/app/scripts/models/form.coffee b/app/scripts/models/form.coffee deleted file mode 100644 index 2627dea..0000000 --- a/app/scripts/models/form.coffee +++ /dev/null @@ -1,968 +0,0 @@ -define [ - './resource' - './../utils/globals' -], (ResourceModel, globals) -> - - # Form Model - # ---------- - # - # A Backbone model for Dative forms. - - class FormModel extends ResourceModel - - initialize: (attributes, options) -> - super - @setAttributesArrays() - - setAttributesArrays: -> - if @activeServerType is 'OLD' - @manyToOneAttributes = @manyToOneAttributesOLD - @manyToManyAttributes = @manyToManyAttributesOLD - @editableAttributes = @editableAttributesOLD - - resourceName: 'form' - - editableAttributes: [] - - editableAttributesOLD: [ - 'transcription' - 'phonetic_transcription' - 'narrow_phonetic_transcription' - 'morpheme_break' - 'grammaticality' - 'morpheme_gloss' - 'translations' - 'comments' - 'speaker_comments' - 'syntax' - 'semantics' - 'status' - 'elicitation_method' - 'syntactic_category' - 'speaker' - 'elicitor' - 'verifier' - 'source' - 'tags' - 'files' - 'date_elicited' - ] - - manyToOneAttributes: [] - manyToManyAttributes: [] - - manyToOneAttributesOLD: [ - 'elicitation_method' - 'elicitor' - 'source' - 'speaker' - 'syntactic_category' - 'verifier' - ] - - manyToManyAttributesOLD: [ - 'tags' - 'files' - ] - - getValidator: (attribute) -> - switch @activeServerType - when 'FieldDB' then @getValidatorFieldDB attribute - when 'OLD' then @getValidatorOLD attribute - - getValidatorOLD: (attribute) -> - switch attribute - when 'transcription' then @validOLDTranscription - when 'phonetic_transcription' then @validOLDPhoneticTranscription - when 'narrow_phonetic_transcription' then @validOLDNarrowPhoneticTranscription - when 'morpheme_break' then @validOLDMorphemeBreak - when 'translations' then @validOLDTranslations - when 'date_elicited' then @validateOLDDateElicited - # The following validators are redundant when a FormAddWidgetView is - # used because its field views have forced-choice select menus and - # max-length inputs. But they do become necessary during CSV import. - when 'grammaticality' then @inGrammaticalities - when 'grammaticality' then @max255Chars - when 'morpheme_gloss' then @max510Chars - when 'syntax' then @max1023Chars - when 'semantics' then @max1023Chars - when 'status' then @inStatuses - else null - - # Every form must have one of transcription, phonetic_transcription, - # narrow_phonetic_transcription or morpheme_break. - requiredTranscriptionType: (value) -> - if (not @get('transcription').trim()) and - (not @get('morpheme_break').trim()) and - (not @get('phonetic_transcription').trim()) and - (not @get('narrow_phonetic_transcription').trim()) - msg = 'Please enter a value in one of the following fields: - transcription, morpheme break, phonetic transcription or narrow - phonetic transcription' - { - transcription: msg - morpheme_break: msg - phonetic_transcription: msg - narrow_phonetic_transcription: msg - } - else - @max510Chars value - - # Validate the input `value` to an OLD transcription-type form attribute. - # This performs the input validation based on the settings in the OLD - # application settings, if applicable. If not, it defaults to - # `@requiredTranscriptionType`. - # The `validationAttr` param is something like 'orthographic_validation', - # i.e., an attribute of an OLD application settings model that holds a - # validation setting. - # The `attr` param must be someting like 'transcription', i.e., a string - # that `oldApplicationSettingsModel.getInputValidator` can return a - # validator for. - validOLDTranscriptionType: (value, validationAttr, attr) -> - validationSetting = globals.oldApplicationSettings - .get validationAttr - if validationSetting in ['Error', 'Warning'] - validator = globals.oldApplicationSettings.getInputValidator attr - if validator - if validator.test value - @trigger "warning:#{validationAttr}" - @requiredTranscriptionType value - else - msg = "invalid given the input validation settings for #{attr} - values in this database" - if validationSetting is 'Error' - msg - else - @trigger "warning:#{validationAttr}", msg - @requiredTranscriptionType value - else - @requiredTranscriptionType value - else - @requiredTranscriptionType value - - validOLDTranscription: (value) -> - @validOLDTranscriptionType(value, 'orthographic_validation', - 'transcription') - - validOLDPhoneticTranscription: (value) -> - @validOLDTranscriptionType(value, 'broad_phonetic_validation', - 'phonetic transcription') - - validOLDNarrowPhoneticTranscription: (value) -> - @validOLDTranscriptionType(value, 'narrow_phonetic_validation', - 'narrow phonetic transcription') - - validOLDMorphemeBreak: (value) -> - @validOLDTranscriptionType(value, 'morpheme_break_validation', - 'morpheme break') - - max255Chars: (value) -> - if value.length > 255 then return '255 characters max' - null - - max510Chars: (value) -> - if value.length > 510 then return '510 characters max' - null - - max1023Chars: (value) -> - if value.length > 1023 then return '1023 characters max' - null - - inStatuses: (value) -> - if value in ['', 'tested', 'requires testing'] - null - else - 'Only the values “tested” and “requires testing” are permitted' - - validOLDTranslations: (value) -> - error = null - if (t for t in value when t.transcription.trim()).length is 0 - error = 'Please enter one or more translations' - error - - # The standard FormAddWidgetView prevents impossible grammaticalities but - # the import interface makes it possible for the user to enter whatever - # they want. - inGrammaticalities: (value) -> - error = null - if value # '' for grammaticality is always ok - if globals.oldApplicationSettings - try - grammaticalities = - globals.oldApplicationSettings.get('grammaticalities').split(',') - if value not in grammaticalities - error = "Valid grammaticalities are: #{grammaticalities.join ', '}" - error - - validateOLDDateElicited: (value) -> - if value?.trim?() is '' - null - else - if not @validDate value - 'Please enter a valid date in mm/dd/yyyy format' - else - null - - # Return `true` if `date` is a string in dd/mm/yyyy format. (Obviously - # accepts some impossible dates, but shouldn't exclude any possible ones.) - validDate: (date) -> - date_regex = /// - ^ - ( 0 [1-9] | 1 [0-2] ) - \/ - ( 0 [1-9] | 1 \d | 2 \d | 3 [01] ) - \/ - [0-2] \d{3} - $ - /// - date_regex.test date - - getValidatorFieldDB: (attribute) -> - null - - ############################################################################ - # Form Schema - ############################################################################ - - defaults: -> - activeServerType = @getActiveServerType() - switch activeServerType - when 'FieldDB' then @defaultFieldDBDatum() - when 'OLD' then @defaultOLDForm() - - ############################################################################ - # FieldDB Datum Schema - ############################################################################ - - # Default FieldDB Datum - - # `@defaultFieldDBDatum` returns an object representing a default FieldDB - # Datum (i.e., form). Note that most of the "meat" is in `datumFields`, - # an array of objects. - - # The structure of each of these datum field object is as follows, where - # the `label` and `value` are, in general, most important: - - # defaultfield: (presumably indicates whether this is a - # "default" field or a "non-standard" one - # that a user has chosen to use) - # encryptedValue: (= `value` unless encrypted, I think) - # fieldDBtype: ("DatumField" is the standard value, I - # would think) - # help: (help text, e.g., for an HTML title - # attribute) - # id: (usually equals `label`; I don't - # understand the difference; must be - # unique?) - # label: (what kind of field this is, e.g., - # `"utterance"`) - # labelFieldLinguists: (label for field linguists) - # mask: (usually = `value` but when encrypted I - # think this is a more user-friendly - # hidden representation, e.g., "XXX-XX") - # shouldBeEncrypted: (whether the value should be encrypted) - # size: (stringified integer; indicates the max - # length of the possible values?...) - # value: (the value in the field; are values - # other than strings permitted?) - # version: (the version of the client app (e.g., - # Spreadsheet) or the FieldDB server? A - # string like "v2.38.16") - - # Note, however, that a `datumField` object can also have other attributes. - # For example, the datum field with label `modifiedByUser` can have a `user` - # attribute whose value is an object. - - # In fact, it's more complicated since some `datumField` objects have a very - # different set of attributes. The datum fields of the ETI 3 Data Tutorial - # corpus, for instance, has "label", "value", "mask", "encrypted", - # "shouldBeEncrypted", "help", "showToUserTypes", and "userchooseable". - - # The labels of the default datum field objects are: - - # judgement: (equivalent to OLD's `grammaticality`; oddly - # `grammatical` is sometimes a value here; - # "Grammaticality/acceptability judgement - # (*,#,?, etc). Leaving it blank can mean - # grammatical/acceptable, or you can choose a - # new symbol for this meaning.") - # utterance: (roughly equivalent to OLD's - # `transcription`; "Unparsed utterance in the - # language, in orthography or transcription. - # Line 1 in your LaTeXed examples for - # handouts. Sample entry: amigas") - # morphemes: (sequence of morpheme shapes and delimiters; - # equivalent to OLD's `morpheme_break`) - # gloss: (sequence of morpheme glosses and - # delimiters; equivalent to OLD's - # `morpheme_glosses`) - # translation: (no (enforced) conventions for multiple - # translations) - # tags: (tags; just a string with no enforced - # delimiter conventions, at least as far as I - # can tell) - # validationStatus: (status of the datum; note: setting to - # `"Deleted"` is what "deletes" a datum, I - # think. "For example: To be checked with a - # language consultant, Checked with Sebrina, - # Deleted etc...") - # syntacticCategory: (sequence of categories and delimiters, - # isomorphic with morphemes and gloss values, - # equivalent to OLD's - # syntactic_category_string) - # syntacticTreeLatex: (tree in LaTeX Qtree bracket notation) - # enteredByUser: (a username; "The user who originally - # entered the datum") - # modifiedByUser: (***Note: `value` is a string but `users` is - # the real value; it's an array of user objects - # each of which has four attributes: - # appVersion: "2.38.16.07.59ss Fri Jan 16 - # 08:02:30 EST 2015" # - # gravatar: - # "5b7145b0f10f7c09be842e9e4e58826d" - # timestamp: 1423667274803 - # username: "jdunham") - - defaultFieldDBDatum: -> - defaults = - _id: '' # (UUID generated by CouchDB.) - _rev: '' # (UUID with a "digit-" - # prefix; generated by - # CouchDB.) - audioVideo: [] # (of objects, I presume...) - collection: 'datums' # - comments: [] # (of comment objects of form: { - # text: '' - # username: '' - # timestamp: ''}.) - dateEntered: '' # (timestamp in format - # 2015-02-11T15:07:54.803Z.) - dateModified: '' # (timestamp in format - # 2015-02-11T15:07:54.803Z.) - datumFields: # (of objects, all of which - @getCorpusDatumFields() # have `label` and `value` - # attributes, but others too. - # See above.) - datumTags: [] # (of objects, I presume ...) - images: [] # (of objects, I presume ...) - jsonType: 'Datum' # - pouchname: '' # (-, - # e.g., "jrwdunham-firstcorpus".) - session: @defaultFieldDBSession() # (representation of a(n - # elicitation) session; see - # `@defaultFieldDBSession()` - # below.) - timestamp: null # (Unix timestamp, e.g., - # 1423667274803) - # We must clone the defaults otherwise we'll have multiple references to - # the same mutable objects (e.g., arrays). - result = @utils.clone defaults - result - - # Return the datumFields of the currently active corpus, if applicable; - # otherwise []. Cf. non-DRY `/views/base.coffee:getCorpusDatumFields`. - getCorpusDatumFields: -> - try - globals.applicationSettings - .get('activeFieldDBCorpusModel').get 'datumFields' - catch - [] - - # Default FieldDB Session - - # `@defaultFieldDBSession` returns an object representing a default FieldDB - # (elicitation) Session. Here the primary content is in the `sessionFields` - # array of objects. Each session field object has the same keys (and value - # types) as the datum field objects described above (except the `size` - # attribute seems to be # missing). - - # The labels of the default session fields are: - - # goal: (the goal of the elicitation session) - # consultants: (the consultant(s)/speaker(s) of the session) - # dialect: (the dialect of the consultant) - # language: (the language being elicited) - # dateElicited: (when the elicitation session took place; format - # YYY-MM-DD seen, but I don't know if this is - # enforced) - # user: (username) - # dateSEntered: (when the session was created; format "Mon Feb 02 - # 2015 00:18:20 GMT-0800 (PST)" seen) - - defaultFieldDBSession: -> - _id: '' # (UUID generated by CouchDB.) - _rev: '' # (UUID with a "digit-" - collection: 'sessions' # - comments: [] # (of comment objects of form: { - # text: '' - # username: '' - # timestamp: ''}.) - dateCreated: '' # (timestamp in format - # 2015-02-11T15:07:54.803Z.) - dateModified: '' # (timestamp in format - # 2015-02-11T15:07:54.803Z.) - lastModifiedBy: '' # (a username.) - pouchname: '' # (-, e.g., - # "jrwdunham-firstcorpus".) - sessionFields: [] # (of objects, all of which have `label` and - # `value` attributes, but others too. See - # comments above.) - title: '' # (concatenation of `dateElicited` and - # `goal` values from `sessionFields`.) - - - ############################################################################ - # FieldDB-specific getters - ############################################################################ - - - # Datum Field getters - ############################################################################ - - # Return the datum field (an object) such that `label=label`. - # E.g., `@getDatumField 'utterance'`. - getDatumField: (label) -> - try - _.findWhere(@get('datumFields'), label: label) - catch - undefined - - # Return the value of `attribute` on the datum field (an object) such that - # `label=label`. E.g., @getDatumFieldAttributeValue 'utterance', 'value' - getDatumFieldAttributeValue: (label, attribute) -> - @getDatumField(label)?[attribute] - - # Return the `value` value of the first object in `datumFields` - # such that `label=label`. - getDatumFieldValue: (label) -> - @getDatumFieldAttributeValue label, 'value' - - # Get the `help` value of the first object in `datumFields` - # such that `label=label`. - getDatumFieldHelp: (label) -> - @getDatumFieldAttributeValue label, 'help' - - - # Session Field getters - ############################################################################ - - # Return the session field (an object) such that `label=label`. - # E.g., `@getSessionField 'goal'`. - getSessionField: (label) -> - try - _.findWhere(@get('session').sessionFields, label: label) - catch - undefined - - # Return the value of `attribute` on the session field (an object) such that - # `label=label`. - # E.g., @getSessionFieldAttributeValue 'goal', 'value' - getSessionFieldAttributeValue: (label, attribute) -> - @getSessionField(label)?[attribute] - - # Return the `value` value of the first object in `sessionFields` - # such that `label=label`. - getSessionFieldValue: (label) -> - @getSessionFieldAttributeValue label, 'value' - - # Get the `help` value of the first object in `sessionFields` - # such that `label=label`. - getSessionFieldHelp: (label) -> - @getSessionFieldAttributeValue label, 'help' - - - # General Datum getters - ############################################################################ - # - # These methods are conveniences that try to treat a FieldDB datum as a - # simple object. One passes in an "attribute" and these getters will return - # a value by trying to find a match for that "attribute" in a datum/session - # field label or by matching the "attribute" to a true attribute of the - # datum. - - # Return `@attribute`, `@session.sessionFields(label=attribute)` or - # `@datumFields(label=attribute)`, whichever exists first. - getDatumValue: (attribute) -> - directValue = @get attribute - sessionField = @getSessionField attribute - datumField = @getDatumField attribute - _.filter([directValue, sessionField, datumField])[0] - - # Get the value of `subattr` of the value of the datum's `attribute`. - # NOTE: this really only makes sense for session and datum fields, since - # only these evaluate to true objects. - # E.g., `@getDatumValueAttributeValue 'utterance', 'value'` - getDatumValueAttributeValue: (attribute, subattr) -> - try - @getDatumValue(attribute)[subattr] - catch - undefined - - # Attempt to intelligently get the value of `attribute` in the datum, - # where the value is not always `.value` and where `attribute` is - # usually not really an attribute name. - # Examples: - # - `@getDatumValue 'utterance'` # a string (from datumFields) - # - `@getDatumValue 'goal'` # a string (from sessionFields) - # - `@getDatumValue 'comments'` # an array (a direct attribute) - getDatumValueSmart: (attribute) -> - datumValue = @getDatumValue attribute - if @utils.type(datumValue) is 'object' - if attribute is 'modifiedByUser' - datumValue.users - else - datumValue.value - else - datumValue - - # Attempt to intelligently set the value of `attribute` in the datum, - # Examples: - # - `@getDatumValue 'utterance', 'chien'` # a string (into datumFields) - # - `@getDatumValue 'comments', [...]` # an array (a direct attribute) - # NOTE: does not set to sessionFields (contrast with `getDatumValueSmart` - # which does get from sessionFields, since we want these data points in - # form display but we don't want to edit them via the form edit interface - # (at least not yet we don't ...) - setDatumValueSmart: (attribute, value) -> - if typeof attribute is 'object' - for key, value of attribute - @_setDatumValueSmart key, value - else - @_setDatumValueSmart attribute, value - - _setDatumValueSmart: (attribute, value) -> - if attribute in _.keys(@attributes) - @set attribute, value - else - datumField = @getDatumField attribute - if datumField - oldValue = datumField.value - if oldValue isnt value - datumField.value = value - datumField.mask = value - @trigger 'change' - - # Get the `help` value of a FieldDB datum field or session field, if exists. - getDatumHelp: (label) -> - sessionFieldHelp = @getSessionFieldHelp label - datumFieldHelp = @getDatumFieldHelp label - _.filter([sessionFieldHelp, datumFieldHelp])[0] - - fieldDB2dative: (fieldDBDatum) -> - @set 'id', fieldDBDatum.id - @set fieldDBDatum.value - - # Return a representation of the model's state that FieldDB likes: just a - # clone of the attributes with the `collection` removed. - toFieldDB: -> - result = _.clone @attributes - # Not doing this causes a `RangeError: Maximum call stack size exceeded` - # when cors.coffee tries to call `JSON.stringify` on a form model that - # contains a forms collection that contains that same form model, etc. ad - # infinitum. - delete result.collection - result - - # Return a representation of the model's state that FieldDB likes for - # updating. - toFieldDBForUpdate: -> - resource = @toFieldDB() - now = new Date() - resource.dateModified = now.toISOString() - resource.timestamp = now.valueOf() - resource.pouchname = globals.applicationSettings.get 'activeFieldDBCorpus' - resource.comments = (c for c in resource.comments when c.text) - username = globals.applicationSettings.get 'username' - gravatar = globals.applicationSettings.get 'gravatar' - modifiedByUser = _.findWhere resource.datumFields, label: 'modifiedByUser' - modifiedByUser.users = @utils.clone modifiedByUser.users - modifiedByUser.users.push( - username: username - gravatar: gravatar - timestamp: now.valueOf() - appVersion: '' # TODO: how? - ) - resource - - # Return a representation of the model's state that FieldDB likes for - # creating. - toFieldDBForCreate: -> - resource = @toFieldDB() - if 'id' of resource then delete resource.id - if '_id' of resource then delete resource._id - if '_rev' of resource then delete resource._rev - if resource.session and not 'id' of resource.session - delete resource.session - now = new Date() - resource.dateEntered = now.toISOString() - resource.dateModified = now.toISOString() - resource.timestamp = now.valueOf() - resource.pouchname = globals.applicationSettings.get 'activeFieldDBCorpus' - resource.comments = (c for c in resource.comments when c.text) - username = globals.applicationSettings.get 'username' - gravatar = globals.applicationSettings.get 'gravatar' - enteredByUser = _.findWhere resource.datumFields, label: 'enteredByUser' - enteredByUser.value = username - enteredByUser.mask = username - enteredByUser.user = - username: username - gravatar: gravatar - appVersion: '' # TODO: how? - resource - - ############################################################################ - # OLD Schema - ############################################################################ - - # `@defaultOLDForm` returns a default OLD form. For details on this schema, - # see - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/form.py - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/model.py - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L289-L316 - - # Note that the OLD *emits* relational attributes as JSON objects but it - # *accepts* them *only* as integer ids. That is, a GET request for a form - # will return an object whose `enterer` value is an object, e.g., - # - # "enterer": {"id": 37, "first_name": "Joel", ...}, - # - # but PUT and POST requests to update/add a form *must* valuate their - # relational attributes as integer ids only, e.g., - # - # "enterer": 1, - - # Note that some attributes are commented-out below. This is because these - # attributes are valuated server-side and should not have client-side - # defaults. That is, the OLD will send them to us, but we cannot directly - # modify them on the server with a PUT/POST request. Nevertheless, it is - # useful to describe their properties here. - - # For a complete list of the attributes that can be specified when - # creating/updating an OLD form, see - # https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L289-L316 - - defaultOLDForm: -> - # These are the attributes that the OLD will recognize on POST/PUT - # (create/update) requests; others *can* be supplied, but the OLD will - # ignore them. The "REC TYPE" header indicates the data type that the OLD - # sends to us. The notes indicate the type that the OLD expects to - # receive, if different. - - # ATTRIBUTE EXP TYPE NOTES & VALIDATION - transcription: "" # (max length = 510) - phonetic_transcription: "" # (max length = 510) - narrow_phonetic_transcription: "" # (max length = 510) - morpheme_break: "" # (max length = 510; sequence - # of morpheme shapes and - # delimiters; equivalent to - # FieldDB `morphemes`.) - grammaticality: "" # (max length = 255; - # grammaticality judgment; - # note that the OLD stores - # possible values in - # server-side - # application_settings - # resources.) - morpheme_gloss: "" # (max length = 510; sequence - # of morpheme glosses and - # delimiters; # equivalent - # to FieldDB `gloss`.) - translations: [] # (of objects, each of which - # represents a translation, - # with keys for - # `transcription` and - # `grammaticality`, the - # latter being more - # correctly labeled - # `acceptibility`. *WARN*: - # the OLD currently requires - # every form to have at - # least one - # `translation.transcription` - # value and all - # `translation.grammaticality` - # values must be present in - # `application_settings.grammaticalities`.) - comments: "" # (general notes about the - # form.) - speaker_comments: "" # (comments by the - # speaker/consultant.) - syntax: "" # (max length = 1023; assumed - # this would be a PTB tree, - # but no logic yet attached - # to that assumption.) - semantics: "" # (max length = 1023; intended - # for formal semantic - # denotations.) - status: "tested" # (max length = 40; indicates - # whether this is a form - # that has been elicited or - # whether it is one that - # needs to be elicited - # (i.e., is part of an - # elicitation plan); default - # value is "tested". Only other - # licit value is 'requires - # testing'. See https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/utils.py#L1483. - # Similar to - # `validationStatus` in - # FieldDB.) - elicitation_method: null # (method by which the form - # was elicited; - # received as an object, - # returned as an integer id.) - syntactic_category: null # (category of the form; - # received as an object, - # returned as an integer id.) - speaker: null # (speaker/consultant who - # produced/judged the form; - # received as an object, - # returned as an integer id.) - elicitor: null # (elicitor of the form; - # received as an object, - # returned as an integer id.) - verifier: null # (user who has "verified" - # quality/accuracy of the - # form; received as an - # object, returned as an - # integer id.) - source: null # (textual source (e.g., - # research paper, book of - # texts, pedagogical - # material, etc.) of the - # form, if applicable; - # received as an object, - # returned as an integer id.) - tags: [] # (of objects, each of which - # represents a tag that is - # associated to this form. - # Note: sent by OLD as an - # array of objects but must - # be received as an array of - # integer ids.) - files: [] # (of objects, each of which - # represents a file that is - # associated to this form. - # Note: sent by OLD as an - # array of objects but must - # be received as an array of - # integer ids.) - date_elicited: "" # (date elicited; OLD sends - # it in ISO 8601 format, i.e., - # "YYYY-MM-DD" but expects - # to receive it in - # "MM/DD/YYYY" format.) - - # These are attributes that the OLD sends to us, but will ignore if we try - # to send them back. The "REC TYPE" column header indicates the type of - # value that we can expect to receive from the OLD. - - # ATTRIBUTE REC TYPE NOTES & VALIDATION - id: null # (created by RDBMS on server) - UUID: "" # (UUID created by the server; - # used to link forms with - # their deleted/premodified - # copies) - datetime_entered: "" # (datetime form was - # created/entered; generated - # on the server as a UTC - # datetime; communicated in - # JSON as a UTC ISO 8601 - # datetime, e.g., - # '2015-02-11T10:50:57.821192'.) - datetime_modified: "" # (datetime form was last - # modified; format and - # construction same as - # `datetime_entered`.) - syntactic_category_string: "" # (max length = 510; sequence - # of morpheme categories and - # delimiters; isomorphic to - # `morpheme_break` and - # `morpheme_gloss` values; - # equivalent to FieldDB - # `syntacticCategory`. - # *WARN*: the OLD currently - # only constructs this - # server-side and does *not* - # allow for it to be - # user-specified; it may be - # desirable to change this.) - morpheme_break_ids: [] # (array of arrays encoding - # the cross-references - # between morpheme shapes - # in the `morpheme_break` - # value and lexical forms in - # the database.) - morpheme_gloss_ids: [] # (array of arrays encoding - # the cross-references - # between morpheme glosses - # in the `morpheme_break` - # value and lexical forms in - # the database.) - break_gloss_category: "" # (serialized zip of the - # `morpheme_break`, - # `morpheme_gloss`, and - # `syntactic_category_string` - # values; e.g., - # "chien|dog|N-s|PL|Num".) - enterer: null # (enterer of the form.) - modifier: null # (last user to modify the form.) - collections: [] # (of objects, each of which - # represents a collection - # that this form belongs to. - # Note: emitted but not - # received by the OLD; use - # the `collections` - # interface to manipulate - # collection membership.) - - - ############################################################################ - # FieldDB-specific DELETE/DESTROY stuff. - ############################################################################ - - # To destroy a FieldDB datum, you do a PUT request where - # `.trashed='deleted'`. - - destroyResourceOnloadHandler: (responseJSON, xhr) -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' - super responseJSON, xhr - when 'FieldDB' - @destroyResourceOnloadHandlerFieldDB responseJSON, xhr - - destroyResourceOnloadHandlerFieldDB: (responseJSON, xhr) -> - Backbone.trigger "destroy#{@resourceNameCapitalized}End" - if xhr.status is 201 and responseJSON.ok is true - Backbone.trigger "destroy#{@resourceNameCapitalized}Success", @ - else - error = responseJSON.error or 'No error message provided.' - Backbone.trigger "destroy#{@resourceNameCapitalized}Fail", error - console.log "Request to delete FieldDB datum #{@get 'id'} failed - (status not 201)." - console.log error - - getDestroyResourceHTTPMethod: -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' then super - when 'FieldDB' then 'PUT' - - getDestroyResourceURL: -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' then super - when 'FieldDB' then @getDestroyResourceURLFieldDB() - - # Returns a URL for deleting a resource on a FieldDB web service. - # NOTE: you don't really delete FieldDB datums and you don't make a DELETE - # HTTP request; you just set the `trashed` attribute to "deleted". - # PUT //?rev= - getDestroyResourceURLFieldDB: -> - url = globals.applicationSettings.get 'baseDBURL' - pouchname = globals.applicationSettings.get 'activeFieldDBCorpus' - "#{url}/#{pouchname}/#{@get '_id'}?rev=#{@get '_rev'}" - - getDestroyResourcePayload: -> - switch globals.applicationSettings.get('activeServer').get('type') - when 'OLD' then super - when 'FieldDB' then @getDestroyResourcePayloadFieldDB() - - # Return the payload for deleting a FieldDB datum: here I use the same - # object used to update a datum, except I add `datum.trashed='deleted'`. - getDestroyResourcePayloadFieldDB: -> - payload = @toFieldDBForUpdate() - payload.trashed = 'deleted' - payload - - # Destroy an OLD form. - # DELETE `/forms/` - # TODO: I think I can safely delete this since `destroyResource` in the - # super class is doing what this used to. - destroyOLDForm: (options) -> - Backbone.trigger 'destroyOLDFormStart' - @constructor.cors.request( - method: 'DELETE' - url: "#{@getOLDURL()}/forms/#{@get 'id'}" - onload: (responseJSON, xhr) => - Backbone.trigger 'destroyOLDFormEnd' - if xhr.status is 200 - Backbone.trigger 'destroyOLDFormSuccess', @ - else - error = responseJSON.error or 'No error message provided.' - Backbone.trigger 'destroyOLDFormFail', error - console.log "DELETE request to /forms/#{@get 'id'} failed (status not 200)." - console.log error - onerror: (responseJSON) => - Backbone.trigger 'destroyOLDFormEnd' - error = responseJSON.error or 'No error message provided.' - Backbone.trigger 'destroyOLDFormFail', error - console.log "Error in DELETE request to /forms/#{@get 'id'} - (onerror triggered)." - ) - - ############################################################################ - # HISTORY. - ############################################################################ - - fetchHistory: -> - switch @activeServerType - when 'OLD' then @fetchHistoryOLD() - when 'FieldDB' then @fetchHistoryFieldDB() - - # GET /forms//history - # If successful, returns `{"form": { ... }, "previous_versions": [ ... ]}` - fetchHistoryOLD: -> - @trigger 'fetchHistoryFormStart' - @constructor.cors.request( - method: 'GET' - url: "#{@getOLDURL()}/forms/#{@get 'id'}/history" - onload: (responseJSON, xhr) => - @trigger 'fetchHistoryFormEnd' - if xhr.status is 200 - @trigger 'fetchHistoryFormSuccess', responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger 'fetchHistoryFormFail', error - console.log "GET request to /forms/#{@get 'id'}/history failed (status not 200)." - console.log error - onerror: (responseJSON) => - @trigger 'fetchHistoryFormEnd' - error = responseJSON.error or 'No error message provided.' - @trigger 'fetchHistoryFormFail', error - console.log "Error in GET request to /forms/#{@get 'id'}/history - (onerror triggered)." - ) - - # Request the history of the form model, the user can click on a revision - # and see the details preferably by simply showing the other revision as a - # model along side in the same views - fetchHistoryFieldDB: -> - console.log "you want to fetch the history of OLD form #{@get 'id'}" - console.log (new FieldDB.FieldDBObject()).version - console.log 'FieldDB.Datum' - console.log FieldDB.Datum - console.log @id - fielddbHelperModel = new FieldDB.Datum(_id: @id) - console.log 'got fielddbHelperModel' - console.log fielddbHelperModel - console.log 'fielddbHelperModel.fetchRevisions()' - console.log fielddbHelperModel.fetch_revisions - fielddbHelperModel.fetch_revisions().then( - (revisions) -> - # TODO: not sure we want to set this on the model ... - # TODO can we avoid fetching them until the user clicks on the one they want? - console.log 'got revisions' - console.log revisions - previousVersions = revisions.map((revisionUrl) -> url: revisionUrl) - console.log 'got previousVersions' - console.log previousVersions - @set 'previousVersions', previousVersions - , - (error) -> - console.log 'TODO how do you talk to users about errors contacting - the server etc...', error - ).fail( - (error) -> - console.log 'TODO how do you talk to users about errors contacting - the server etc...', error - ) - diff --git a/app/scripts/models/fst-based.coffee b/app/scripts/models/fst-based.coffee deleted file mode 100644 index 4ed17f0..0000000 --- a/app/scripts/models/fst-based.coffee +++ /dev/null @@ -1,129 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # FST-based Model - # --------------- - # - # A Backbone model for resources based on FSTs, i.e., finite-state - # transducers. - - class FSTBasedModel extends ResourceModel - - # Perform a "compile" request on the FST-based resource. - # e.g., PUT `/phonologies/{id}/compile` - compile: -> - @trigger "compileStart" - @constructor.cors.request( - method: 'PUT' - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/compile" - onload: (responseJSON, xhr) => - @trigger "compileEnd" - if xhr.status is 200 - @trigger "compileSuccess", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "compileFail", error - console.log "PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/compile - failed (status not 200)." - console.log error - onerror: (responseJSON) => - @trigger "compileEnd" - error = responseJSON.error or 'No error message provided.' - @trigger "compileFail", error - console.log "Error in PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/compile - (onerror triggered)." - ) - - # Perform an "apply down" request on the FST-based resource. - # For example, ask that a morphological segmentation be phonologized using - # a phonology; that is, converted the morphological segmentation to its - # surface form, given the phonology. - # Example request: PUT `/phonologies/{id}/applydown` (which is an - # alias for PUT # /phonologies/{id}/phonologize). - applyDown: (words) -> @apply words, 'down' - - # Perform an "apply up" request on the FST-based resource. - # For example, ask that an impoverished morphological segmentation be - # converted to a rich representation for input to a candidate ranker during - # parsing. - # Example request: PUT `/morphologies/{id}/applyup`. - applyUp: (words) -> @apply words, 'up' - - # Perform an "apply" request on the FST-based resource, where `direction` - # is "up" or "down", this determining whether the request is an "apply up" - # one or an "apply down" one. - apply: (words, direction='down') -> - directionCapitalized = @utils.capitalize direction - @trigger "apply#{directionCapitalized}Start" - @constructor.cors.request( - method: 'PUT' - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/\ - #{@get 'id'}/apply#{direction}" - payload: @["getApply#{directionCapitalized}Payload"] words - onload: (responseJSON, xhr) => - @applyOnloadHandler responseJSON, xhr, directionCapitalized - onerror: (responseJSON) => - @trigger "apply#{directionCapitalized}End" - error = responseJSON.error or 'No error message provided.' - @trigger "apply#{directionCapitalized}Fail", error - console.log "Error in PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/\ - apply#{direction} (onerror triggered)." - ) - - # Method to handle the 'onload' event of an apply up/down request. This can - # be overridden in sub-classes. - applyOnloadHandler: (responseJSON, xhr, directionCapitalized) -> - @trigger "apply#{directionCapitalized}End" - if xhr.status is 200 - @trigger "apply#{directionCapitalized}Success", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "apply#{directionCapitalized}Fail", error - console.log "PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/\ - apply#{direction} failed (status not 200)." - console.log error - - # Input in body of HTTP request that phonology resources expect: - # ``{'transcriptions': [t1, t2, ...]}``. - getApplyDownPayload: (words) -> - if @utils.type(words) is 'string' - words = words.split /\s+/ - if @resourceName is 'phonology' - {transcriptions: words} - else - {morpheme_sequences: words} - - getApplyUpPayload: (words) -> @getApplyDownPayload words - - # Perform a "serve compiled" request on the fst-based resource. - # Example request: GET `/phonologies/{id}/servecompiled` - serveCompiled: -> - @trigger "serveCompiledStart" - @constructor.cors.request( - responseType: 'blob' - method: 'GET' - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/servecompiled" - onload: (responseJSON, xhr) => - @trigger "serveCompiledEnd" - if xhr.status is 200 - @trigger "serveCompiledSuccess", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "serveCompiledFail", error - console.log "GET request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/servecompiled - failed (status not 200)." - console.log error - onerror: (responseJSON) => - @trigger "serveCompiledEnd" - error = responseJSON.error or 'No error message provided.' - @trigger "serveCompiledFail", error - console.log "Error in GET request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/servecompiled - (onerror triggered)." - ) - - diff --git a/app/scripts/models/keyboard-preference-set.coffee b/app/scripts/models/keyboard-preference-set.coffee deleted file mode 100644 index 11c18e1..0000000 --- a/app/scripts/models/keyboard-preference-set.coffee +++ /dev/null @@ -1,74 +0,0 @@ -define [ - './resource' - './keyboard' -], (ResourceModel, KeyboardModel) -> - - # Keyboard Preferences Model - # -------------------------- - # - # Client-side-stored model for assigning keyboards to specific form fields. - - class KeyboardPreferenceSetModel extends ResourceModel - - # Transform plain keyboard objects into Backbone `KeyboardModel` instances. - objects2models: -> - - systemWideKeyboardObject = @get 'system_wide_keyboard' - if systemWideKeyboardObject - @set('system_wide_keyboard', - (new KeyboardModel(systemWideKeyboardObject))) - - transcriptionKeyboardObject = @get 'transcription_keyboard' - if transcriptionKeyboardObject - @set('transcription_keyboard', - (new KeyboardModel(transcriptionKeyboardObject))) - - phoneticTranscriptionKeyboardObject = - @get 'phonetic_transcription_keyboard' - if phoneticTranscriptionKeyboardObject - @set('phonetic_transcription_keyboard', - (new KeyboardModel(phoneticTranscriptionKeyboardObject))) - - narrowPhoneticTranscriptionKeyboardObject = - @get 'narrow_phonetic_transcription_keyboard' - if narrowPhoneticTranscriptionKeyboardObject - @set('narrow_phonetic_transcription_keyboard', - (new KeyboardModel(narrowPhoneticTranscriptionKeyboardObject))) - - morphemeBreakKeyboardObject = @get 'morpheme_break_keyboard' - if morphemeBreakKeyboardObject - @set('morpheme_break_keyboard', - (new KeyboardModel(morphemeBreakKeyboardObject))) - - resourceName: 'parserTaskSet' - - clientSideOnlyModel: true - - defaults: -> - id: @guid() - - # A system-wide keyboard that will be used when entering data into any - # field. The field-specific keyboard listed below should trump this one - # when those fields are focused. - system_wide_keyboard: null - - # A keyboard for entering transcription values. - transcription_keyboard: null - - # A keyboard for entering *phonetic* transcription values. - phonetic_transcription_keyboard: null - - # A keyboard for entering *narrow* phonetic transcription values. - narrow_phonetic_transcription_keyboard: null - - # A keyboard for entering morpheme break values. - morpheme_break_keyboard: null - - editableAttributes: [ - 'system_wide_keyboard' - 'transcription_keyboard' - 'phonetic_transcription_keyboard' - 'narrow_phonetic_transcription_keyboard' - 'morpheme_break_keyboard' - ] - diff --git a/app/scripts/models/keyboard.coffee b/app/scripts/models/keyboard.coffee deleted file mode 100644 index 639b299..0000000 --- a/app/scripts/models/keyboard.coffee +++ /dev/null @@ -1,50 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Keyboard Model - # -------------- - # - # A Backbone model for Dative keyboards. - - class KeyboardModel extends ResourceModel - - resourceName: 'keyboard' - - ############################################################################ - # Keyboard Schema - ############################################################################ - - defaults: -> - name: '' # Required, unique - # among keyboard - # names, max 255 chars. - description: '' # description of the - # keyboard. - keyboard: {} # maps JavaScript key - # codes to Unicode - # characters/strings. - id: null # relational id - datetime_entered: "" # (datetime resource - # was last entered; generated - # on the server as a UTC - # datetime; communicated in - # JSON as a UTC ISO 8601 - # datetime, e.g., - # '2015-02-11T10:50:57.821192'.) - datetime_modified: "" # (datetime resource - # was last modified; format - # and construction same as - # `datetime_entered`.) - enterer: null # (enterer of the keyboard.) - modifier: null # (last user to modify the keyboard.) - - editableAttributes: [ - 'name' - 'description' - 'keyboard' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - else null - diff --git a/app/scripts/models/language-model.coffee b/app/scripts/models/language-model.coffee deleted file mode 100644 index a4182ac..0000000 --- a/app/scripts/models/language-model.coffee +++ /dev/null @@ -1,173 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Language Model Model - # -------------------- - # - # A Backbone model for Dative language models. - - class LanguageModelModel extends ResourceModel - - resourceName: 'languageModel' - serverSideResourceName: 'morphemelanguagemodels' - - ############################################################################ - # Language Model Schema - ############################################################################ - - # See: - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L1201-L1216 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/morphemelanguagemodel.py#L41-L66 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/controllers/morphemelanguagemodels.py - - defaults: -> - - name: '' # - description: '' # - corpus: null # / Corpus from which to - # extract the morpheme-based N-grams. - vocabulary_morphology: null # / A morphology object from - # which a vocabulary of 1-grams may be extracted. - toolkit: 'mitlm' # Name of the LM estimating toolkit; - # currently the only possible value is - # "mitlm". - order: 3 # bigram to quinquegram: integer between 2 - # and 5, defaults to 3. - smoothing: 'ModKN' # The name of a smoothing algorithm that is - # defined by the specified toolkit; for - # "mitlm" these are 'ML', 'FixKN', - # 'FixModKN', 'FixKNn', 'KN', 'ModKN', and - # 'KNn'. - categorial: false # (default is false); a categorial LM - # scopes over morpheme categories whereas a - # non-categorial one scopes over specific - # morphemes. - - # Attributes that the OLD sends to us, but which the OLD will ignore if - # we try to send them back. - - id: null # relational id - UUID: '' # UUID - enterer: null # attributes: `id`, - # `first_name`, `last_name`, - # `role` - modifier: null # attributes: `id`, - # `first_name`, `last_name`, - # `role` - datetime_entered: "" # (datetime resource - # was created/entered; - # generated on the server as a - # UTC datetime; communicated - # in JSON as a UTC ISO 8601 - # datetime, e.g., - # '2015-02-11T10:50:57.821192'.) - datetime_modified: "" # (datetime resource - # was last modified; format - # and construction same as - # `datetime_entered`.) - generate_succeeded: false # will be true if server has - # generated the LM. - generate_message: '' # the message that the OLD returns - # (indicating success or failure) after trying - # to generate this LM. - generate_attempt: '' # a UUID. - perplexity: 0.0 # the perplexity of the LM's - # corpus according to the LM. The OLD randomly - # divides the corpus into training and test - # sets multiple times and computes the - # perplexity and returns the average. - perplexity_attempt: '' # a UUID. - perplexity_computed: false # will be true if the server has - # computed the perplexity of the LM. - restricted: '' # if true, then only unrestricted - # users can access this LM. - morpheme_delimiters: '' # a of comma-separated morpheme - # delimiters; defined in the OLD's application - # settings. - - editableAttributes: [ - 'name' - 'description' - 'corpus' - 'vocabulary_morphology' - 'toolkit' - 'order' - 'smoothing' - 'categorial' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - when 'corpus' then @requiredCorpus - else null - - requiredCorpus: (value) -> - error = null - if _.isEmpty @get('corpus') - error = 'You must choose a corpus to estimate the language model from' - error - - manyToOneAttributes: [ - 'corpus' - 'vocabulary_morphology' - ] - - # Perform a "generate" request on the LM. - # e.g., PUT `/morpheme_language_model/{id}/generate` - generate: -> - @trigger "generateStart" - @constructor.cors.request( - method: 'PUT' - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/\ - #{@get 'id'}/generate" - onload: (responseJSON, xhr) => - @trigger "generateEnd" - if xhr.status is 200 - @trigger "generateSuccess", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "generateFail", error - console.log "PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/\ - #{@get 'id'}/generate failed (status not 200)." - console.log error - onerror: (responseJSON) => - @trigger "generateEnd" - error = responseJSON.error or 'No error message provided.' - @trigger "generateFail", error - console.log "Error in PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/\ - #{@get 'id'}/generate (onerror triggered)." - ) - - # Perform a `get_probabilities` request against the LM. - # e.g., PUT `/morpheme_language_model/{id}/get_probabilities` - # with a request body JSON object with a `morpheme_sequences` attribute that - # valuates to an array of strings representing analyzed words. - getProbabilities: (words) -> - @trigger "getProbabilitiesStart" - @constructor.cors.request( - method: 'PUT' - payload: words - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/\ - #{@get 'id'}/get_probabilities" - onload: (responseJSON, xhr) => - @trigger "getProbabilitiesEnd" - if xhr.status is 200 - @trigger "getProbabilitiesSuccess", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "getProbabilitiesFail", error - console.log "PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/\ - #{@get 'id'}/get_probabilities failed (status not 200)." - console.log error - onerror: (responseJSON) => - @trigger "getProbabilitiesEnd" - error = responseJSON.error or 'No error message provided.' - @trigger "getProbabilitiesFail", error - console.log "Error in PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/\ - #{@get 'id'}/get_probabilities (onerror triggered)." - ) - diff --git a/app/scripts/models/language.coffee b/app/scripts/models/language.coffee deleted file mode 100644 index fa02e37..0000000 --- a/app/scripts/models/language.coffee +++ /dev/null @@ -1,32 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Language Model - # -------------- - # - # A Backbone model for Dative languages. These are ISO 639-3 language - # objects. They are not user-editable. - - class LanguageModel extends ResourceModel - - resourceName: 'language' - - ############################################################################ - # Language Schema - ############################################################################ - - defaults: -> - Id: '' # , 3-char unique id for language. - Part2B: '' # , 3 chars. - Part2T: '' # , 3 chars. - Part1: '' # , 2 chars. - Scope: '' # , 1 char. - Type: '' # , 1 char. - Ref_Name: '' # , max 150 chars; this is the - # reference name, i.e., the name for the language - # that the Ethnologue (arbitrarily?) chose as the - # standard one. - Comment: '' # , max 150 chars. - datetime_modified: '' # - - editableAttributes: [] - diff --git a/app/scripts/models/morphological-parser.coffee b/app/scripts/models/morphological-parser.coffee deleted file mode 100644 index eef4813..0000000 --- a/app/scripts/models/morphological-parser.coffee +++ /dev/null @@ -1,245 +0,0 @@ -define [ - './resource' - './../utils/globals' -], (ResourceModel, globals) -> - - # Morphological Parser Model - # -------------------------- - # - # A Backbone model for Dative morphological parsers. - - class MorphologicalParserModel extends ResourceModel - - resourceName: 'morphologicalParser' - serverSideResourceName: 'morphologicalparsers' - - initialize: (attributes, options) -> - super attributes, options - @listenTo @, 'change:compile_attempt', @resetParseCache - @parseCache = @fetchParseCache() - - # Each parser resource has a cache in localStorage whose key is a unique - # string constructed from the web service's URL and the parser's last - # compile_attempt value. Note that multiple `MorphologicalParserModel` - # instances can exist in a Dative app and they will (should) all access and - # modify the same localStorage cache, as needed. - getLocalStorageKey: (previous=false) -> - serverURL = globals.applicationSettings.get('activeServer').get 'url' - if previous - compileAttempt = @previousAttributes().compile_attempt - else - compileAttempt = @get 'compile_attempt' - if compileAttempt - "dative-#{serverURL}-morphological-parser-#{compileAttempt}-parse-cache" - else - null - - # Fetch our client-side-stored (in localStorage) cache of parse mappings. - fetchParseCache: -> - key = @getLocalStorageKey() - if key - localStorageCache = localStorage.getItem key - if localStorageCache - JSON.parse localStorageCache - else - localStorage.setItem key, JSON.stringify({}) - {} - else - console.log "WARN: unable to get persisted cache: this parser has no - `compile_attempt` attribute" - {} - - # Save the in-memory cache of parse results to localStorage. - persistParseCache: -> - key = @getLocalStorageKey() - if key - localStorage.setItem key, JSON.stringify(@parseCache) - else - console.log "WARN: unable to persist cache: this parser has no - `compile_attempt` attribute" - - # We delete our old localStorage parse cache and create a new one when - # our MorphologicalParserModel's `compile_attempt` attribute changes. This - # attribute changing usually means that the parser will behave differently, - # though that isn't necessarily true if the compile was made with no change - # to the parser's phonology, morphology, or language model. - # TODO: make this reset more sensitive to actual changes to the behaviour - # of the parser model. - resetParseCache: -> - previousLocalStorageKey = @getLocalStorageKey true - currentLocalStorageKey = @getLocalStorageKey() - localStorage.removeItem previousLocalStorageKey - @parseCache = {} - @persistParseCache() - - # Cache our parse results from the server in memory and in localStorage. - cacheParseResults: (parseResults) -> - for input, output of parseResults - @parseCache[input] = output - @persistParseCache() - - # Request a parse for a word. - # PUT `/morphologicalparsers//parse` - # Note: we try to minimize requests to the server and to minimize how many - # words are sent to the server for parsing on any necessary requests. - parse: (words) -> - wordsNeedingParse = (w for w in words when w not of @parseCache) - if wordsNeedingParse.length > 0 - # We remember the words that we already have (cached) outputs for so we - # can add them to the server's response on a successful request. See - # the `parseOnloadHandler` below. - @wordsCached = (w for w in words when w of @parseCache) - @_parse wordsNeedingParse - else - # Client-side retrieval of cached parse results. Note we use the - # same API, i.e., we trigger the same events that a successful request - # to the server would. - @trigger "parseStart" - result = {} - for word in words - result[word] = @parseCache[word] - @trigger "parseSuccess", result - @trigger "parseEnd" - - # Request a parse for a word. This is the *real* parse method, the one that - # issues a request to the server. - # PUT `/morphologicalparsers//parse` - _parse: (words) -> - @trigger "parseStart" - @constructor.cors.request( - method: 'PUT' - url: "#{@getOLDURL()}/morphologicalparsers/#{@get 'id'}/parse" - payload: @getParsePayload words - onload: (responseJSON, xhr) => @parseOnloadHandler responseJSON, xhr - onerror: (responseJSON) => - @trigger "parseEnd" - error = responseJSON.error or 'No error message provided.' - @trigger "parseFail", error - console.log "Error in PUT request to - #{@getOLDURL()}/morphologicalparsers/#{@get 'id'}/parse - (onerror triggered)." - ) - - # Input in body of HTTP request that morphological parser resources expect: - # ``{'transcriptions': [t1, t2, ...]}``. - getParsePayload: (words) -> - if @utils.type(words) is 'string' - words = words.split /\s+/ - {transcriptions: words} - - # Respond to a successful parse request to the server. Here we ... - # 1. cache the results from the server - # 2. add our cached response to the object passed to any 'parseSuccess' - # listeners. - parseOnloadHandler: (responseJSON, xhr, directionCapitalized) -> - @trigger "parseEnd" - if xhr.status is 200 - @cacheParseResults responseJSON - if @wordsCached and @wordsCached.length > 0 - for word in @wordsCached - responseJSON[word] = @parseCache[word] - @trigger "parseSuccess", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "parseFail", error - console.log "PUT request to - #{@getOLDURL()}/morphologicalparsers/#{@get 'id'}/parse - failed (status not 200)." - console.log error - - - ############################################################################ - # morphological parser Schema - ############################################################################ - - # See: - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L1201-L1216 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/morphologicalparser.py#L145-L166 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/controllers/morphologicalparsers.py - - defaults: -> - - name: '' # - description: '' # - phonology: null # / a phonology resource. - morphology: null # / a morphology resource. - language_model: null # / a language model resource. - - # Attributes that the OLD sends to us, but which the OLD will ignore if - # we try to send them back. - - id: null # relational id - UUID: '' # UUID - enterer: null # attributes: `id`, - # `first_name`, `last_name`, - # `role` - modifier: null # attributes: `id`, - # `first_name`, `last_name`, - # `role` - datetime_entered: "" # (datetime resource - # was created/entered; - # generated on the server as a - # UTC datetime; communicated - # in JSON as a UTC ISO 8601 - # datetime, e.g., - # '2015-02-11T10:50:57.821192'.) - datetime_modified: "" # (datetime resource - # was last modified; format - # and construction same as - # `datetime_entered`.) - generate_succeeded: false # will be true if server has - # generated the parser. - generate_message: '' # the message that the OLD returns - # (indicating success or failure) after trying - # to generate this parser. - generate_attempt: '' # a UUID. - compile_succeeded: false # will be true if server has - # compiled the parser. - compile_message: '' # the message that the OLD returns - # (indicating success or failure) after trying - # to compile this parser. - compile_attempt: '' # a UUID. - - editableAttributes: [ - 'name' - 'description' - 'phonology' - 'morphology' - 'language_model' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - when 'phonology' then @requiredPhonology - when 'morphology' then @requiredMorphology - when 'language_model' then @requiredLanguageModel - else null - - requiredPhonology: (value) -> - error = null - if _.isEmpty @get('phonology') - error = 'You must specify a phonology when creating a morphological - parser' - error - - requiredMorphology: (value) -> - error = null - if _.isEmpty @get('morphology') - error = 'You must specify a morphology when creating a morphological - parser' - error - - requiredLanguageModel: (value) -> - error = null - if _.isEmpty @get('language_model') - error = 'You must specify a language model when creating a morphological - parser' - error - - manyToOneAttributes: [ - 'phonology' - 'morphology' - 'language_model' - ] - diff --git a/app/scripts/models/morphology.coffee b/app/scripts/models/morphology.coffee deleted file mode 100644 index 163db09..0000000 --- a/app/scripts/models/morphology.coffee +++ /dev/null @@ -1,122 +0,0 @@ -define ['./fst-based'], (FSTBasedModel) -> - - # Morphology Model - # --------------- - # - # A Backbone model for Dative morphologies. - - class MorphologyModel extends FSTBasedModel - - resourceName: 'morphology' - - ############################################################################ - # Morphology Schema - ############################################################################ - - # See: - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L1136-L1152 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/morphology.py#L69-L91 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/controllers/morphologies.py - - # Validation: - # A value for either rules or rules_corpus must be specified. - - defaults: -> - name: '' # Required, unique - # among morphology names, max - # 255 chars. - description: '' # description of the - # morphology. - lexicon_corpus: null # An OLD corpus model: - # received as an object, but - # OLD expects to receive an id. - rules_corpus: null # An OLD corpus model: - # received as an object, but - # OLD expects to receive an id. - script_type: '' # forced choice; one of - # 'regex' or 'lexc'. - extract_morphemes_from_rules_corpus: false # . If true, then - # morphemes will be extracted - # from the rules corpus in - # addition to the default - # extraction from the lexicon - # corpus. - rules: '' # Morphotactic rules: - # a string containing rules - # like "V-Agr". - rich_upper: false # if True, the morphemes on - # the upper side of the tape - # are in forma, else f - # format. - rich_lower: false # if True, the morphemes on - # the lower side of the tape - # are in forma, else f - # format - include_unknowns: false # Boolean. If True, morphemes - # of unknown category will be - # added to lexicon. - - # Attributes that the OLD sends to us, but which the OLD will ignore if - # we try to send them back. - id: null # relational id - UUID: '' # UUID - enterer: null # attributes: `id`, - # `first_name`, `last_name`, - # `role` - modifier: null # attributes: `id`, - # `first_name`, `last_name`, - # `role` - datetime_entered: "" # (datetime resource - # was created/entered; - # generated on the server as a - # UTC datetime; communicated - # in JSON as a UTC ISO 8601 - # datetime, e.g., - # '2015-02-11T10:50:57.821192'.) - datetime_modified: "" # (datetime resource - # was last modified; format - # and construction same as - # `datetime_entered`.) - compile_succeeded: false # . If true, then the - # server has successfully compiled the morphology. - compile_message: '' # . Explanation from - # the server about the outcome - # of the compile attempt. - compile_attempt: '' # . A UUID. - generate_attempt: '' # . A UUID. - rules_generated: '' # . Rules generated - # server-side from the - # rules_corpus. - - editableAttributes: [ - 'name' - 'description' - 'lexicon_corpus' - 'rules_corpus' - 'script_type' - 'extract_morphemes_from_rules_corpus' - 'rules' - 'rich_upper' - 'rich_lower' - 'include_unknowns' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - when 'rules' then @rulesOrRulesCorpus - when 'rules_corpus' then @rulesOrRulesCorpus - else null - - rulesOrRulesCorpus: (value) -> - error = null - if @get('rules').trim() is '' and - not @get('rules_corpus')? - error = 'You must either specify rules or a rules corpus' - error - - manyToOneAttributes: [ - 'lexicon_corpus' - 'rules_corpus' - ] - diff --git a/app/scripts/models/old-application-settings.coffee b/app/scripts/models/old-application-settings.coffee deleted file mode 100644 index 820ae4b..0000000 --- a/app/scripts/models/old-application-settings.coffee +++ /dev/null @@ -1,202 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # OLD Application Settings Model - # ------------------------------ - # - # A Backbone model for an OLD server's application-wide settings. - - class OLDApplicationSettingsModel extends ResourceModel - - resourceName: 'oldApplicationSettings' - serverSideResourceName: 'applicationsettings' - - manyToOneAttributes: [ - 'storage_orthography' - 'input_orthography' - 'output_orthography' - ] - - manyToManyAttributes: [ - 'unrestricted_users' - ] - - ############################################################################ - # OLDApplicationSettings Schema - ############################################################################ - - defaults: -> - - id: null - object_language_name: '' # 255 chrs max - object_language_id: '' # 3 chrs max, ISO 639-3 3-char Id code - metalanguage_name: '' # 255 chrs max - metalanguage_id: '' # 3 chrs max, ISO 639-3 3-char Id code - metalanguage_inventory: '' # long text; Don't think this is really used for any OLD-side logic. - orthographic_validation: 'None' # one of 'None', 'Warning', or 'Error' - narrow_phonetic_inventory: '' # long text; should be comma-delimited graphemes - narrow_phonetic_validation: '' # one of 'None', 'Warning', or 'Error' - broad_phonetic_inventory: '' # long text; should be comma-delimited graphemes - broad_phonetic_validation: '' # one of 'None', 'Warning', or 'Error' - morpheme_break_is_orthographic: false # boolean - morpheme_break_validation: '' # one of 'None', 'Warning', or 'Error' - phonemic_inventory: '' # long text; should be comma-delimited graphemes - morpheme_delimiters: '' # 255 chars max; should be COMMA-DELIMITED single chars... - punctuation: '' # long text; should be punctuation chars - grammaticalities: '' # 255 chars max ... - storage_orthography: null # id of an orthography - input_orthography: null # id of an orthography - output_orthography: null # id of an orthography - datetime_modified: '' - unrestricted_users: [] # an array of users who are "unrestricted". In the OLD this is a m2m relation, I think. - # orthographies: [] # OLD's schema suggests this is an attr, but it's app sett model doesn't ... - - editableAttributes: [ - 'object_language_name' - 'object_language_id' - 'metalanguage_name' - 'metalanguage_id' - 'metalanguage_inventory' - 'orthographic_validation' - 'narrow_phonetic_inventory' - 'narrow_phonetic_validation' - 'broad_phonetic_inventory' - 'broad_phonetic_validation' - 'morpheme_break_is_orthographic' - 'morpheme_break_validation' - 'phonemic_inventory' - 'morpheme_delimiters' - 'punctuation' - 'grammaticalities' - 'storage_orthography' - 'input_orthography' - 'output_orthography' - 'unrestricted_users' - ] - - getValidator: (attribute) -> - switch attribute - when 'metalanguage_id' then @realISOLanguageId - when 'object_language_id' then @realISOLanguageId - else null - - realISOLanguageId: (value) -> - if value.trim() - if value in @languageRefNames - null - else - "#{value} is not a valid ISO 639-3 language Id; please enter a valid - Id or nothing at all." - else - null - - ############################################################################ - # Logic for Input Validation - ############################################################################ - - # These methods allow the OLD application settings' orthography, inventory - # and validation-related attributes to effect validation of transcription, - # phonetic transcription, and morpheme break values. - - # Return a validator (a `RegExp` instance) that returns `true` if the input - # of the specified field is valid. - getInputValidator: (targetField) -> - switch targetField - when 'orthographic transcription' - @getOrthographicValidator() - when 'transcription' - @getOrthographicValidator() - when 'narrow phonetic transcription' - @getNarrowPhoneticValidator() - when 'narrow_phonetic_transcription' - @getNarrowPhoneticValidator() - when 'broad phonetic transcription' - @getBroadPhoneticValidator() - when 'phonetic_transcription' - @getBroadPhoneticValidator() - when 'phonetic transcription' - @getBroadPhoneticValidator() - else - @getMorphemeBreakValidator() - - # Return a RegExp that validates orthographic transcription values. This - # allows: - # - graphs from the storage orthography, - # - capitalized graphs from the storage orthography, - # - punctuation characters, and - # - the space character - getOrthographicValidator: -> - inventory = @getInventoryFromStorageOrthography() - if inventory - graphs = inventory.split ',' - escapedGraphs = (@utils.escapeRegexChars(g) for g in graphs) - punctuation = - (@utils.escapeRegexChars(p) for p in @get('punctuation').split('')) - capitalizedGraphs = - (@utils.escapeRegexChars(@utils.capitalize(g)) for g in graphs) - elements = escapedGraphs.concat punctuation, capitalizedGraphs - new RegExp "^(#{elements.join '|'}| )*$" - else - null - - # Get an inventory string (i.e., an orthography) from the storage - # orthography object in application settings. - getInventoryFromStorageOrthography: -> - orthography = @get 'storage_orthography' - if orthography - orthography.orthography - else - null - - # Return a RegExp that validates narrow phonetic transcription values. This - # allows: - # - graphs from the narrow phonetic inventory and - # - the space character - getNarrowPhoneticValidator: -> - inventory = @get 'narrow_phonetic_inventory' - if inventory - graphs = inventory.split ',' - escapedGraphs = (@utils.escapeRegexChars(g) for g in graphs) - new RegExp "^(#{escapedGraphs.join '|'}| )*$" - else - null - - # Return a RegExp that validates phonetic transcription values. This - # allows: - # - graphs from the broad phonetic inventory and - # - the space character - getBroadPhoneticValidator: -> - inventory = @get 'broad_phonetic_inventory' - if inventory - graphs = inventory.split ',' - escapedGraphs = (@utils.escapeRegexChars(g) for g in graphs) - new RegExp "^(#{escapedGraphs.join '|'}| )*$" - else - null - - # Return a RegExp that validates morpheme break values. This allows: - # - graphs from the storage orthography XOR phonemic inventory, - # - capitalized graphs from the storage orthography XOR phonemic inventory, - # - morpheme delimiters, and - # - the space character - getMorphemeBreakValidator: -> - if @get 'morpheme_break_is_orthographic' - inventory = @getInventoryFromStorageOrthography() - else - inventory = @get 'phonemic_inventory' - if inventory - graphs = inventory.split ',' - escapedGraphs = (@utils.escapeRegexChars(g) for g in graphs) - delimiters = - (@utils.escapeRegexChars(d) for d in @get('morpheme_delimiters').split(',')) - # A phonemic inventory should not have its graphs automatically - # capitalized since capitalization may have phonological meaning. - if @get 'morpheme_break_is_orthographic' - capitalizedGraphs = - (@utils.escapeRegexChars(@utils.capitalize(g)) for g in graphs) - elements = escapedGraphs.concat delimiters, capitalizedGraphs - else - elements = escapedGraphs.concat delimiters - new RegExp "^(#{elements.join '|'}| )*$" - else - null - diff --git a/app/scripts/models/orthography.coffee b/app/scripts/models/orthography.coffee deleted file mode 100644 index 19c50cc..0000000 --- a/app/scripts/models/orthography.coffee +++ /dev/null @@ -1,40 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Orthography Model - # --------- - # - # A Backbone model for Dative orthographies. - - class OrthographyModel extends ResourceModel - - resourceName: 'orthography' - - ############################################################################ - # Orthography Schema - ############################################################################ - - defaults: -> - id: null # relational id - name: '' # Required, unique among orthography - # names, max 255 chars. - orthography: '' # = Column(UnicodeText) - lowercase: false # ... - initial_glottal_stops: true # ... - datetime_modified: "" # (datetime resource was last - # modified; format and construction same as - # `datetime_entered`.) - - editableAttributes: [ - 'name' - 'orthography' - 'lowercase' - 'initial_glottal_stops' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - when 'orthography' then @requiredString - else null - - diff --git a/app/scripts/models/page.coffee b/app/scripts/models/page.coffee deleted file mode 100644 index 841b50e..0000000 --- a/app/scripts/models/page.coffee +++ /dev/null @@ -1,53 +0,0 @@ -define [ - './resource' - './../utils/globals' - ], (ResourceModel, globals) -> - - # Page Model - # ---------- - # - # A Backbone model for Dative pages. - - class PageModel extends ResourceModel - - resourceName: 'page' - - ############################################################################ - # Page Schema - ############################################################################ - - defaults: -> - name: '' # Required, unique among page names, - # max 255 chars. - heading: '' # max 255 chars. - markup_language: '' # One of "Markdown" or "reStructuredText", - # defaults to "reStructuredText". - content: '' # a string of lightweight markup, defining the - # page content. - html: '' # HTML generated from the user-supplied markup. - id: null # relational id - datetime_modified: "" # (datetime resource was last - # modified; format and construction same as - # `datetime_entered`.) - - editableAttributes: [ - 'name' - 'heading' - 'markup_language' - 'content' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - else null - - # If the home page is deleted, we need to tell the application settings - # about that. - destroyResourceOnloadHandler: (responseJSON, xhr) -> - if xhr.status is 200 and responseJSON.name == 'home' - globals.applicationSettings.set 'homepage', null - globals.applicationSettings.save() - Backbone.trigger 'homePageChanged' - super responseJSON, xhr - diff --git a/app/scripts/models/parser-task-set.coffee b/app/scripts/models/parser-task-set.coffee deleted file mode 100644 index 6154066..0000000 --- a/app/scripts/models/parser-task-set.coffee +++ /dev/null @@ -1,101 +0,0 @@ -define [ - './resource' - './phonology' - './morphology' - './morphological-parser' -], (ResourceModel, PhonologyModel, MorphologyModel, MorphologicalParserModel) -> - - # Parser Tasks Model - # ------------------ - # - # Client-side-stored model for assigning form-related tasks to parsers and - # their subcomponents, i.e., phonologies and morphologies. - - class ParserTaskSetModel extends ResourceModel - - initialize: (attributes, options) -> - super attributes, options - # @objects2models() - - # Transform plain objects into Backbone models for parsers, phonologies and - # morphologies. - objects2models: -> - transcriptionParserObject = @get 'transcription_parser' - if transcriptionParserObject - @set('transcription_parser', - (new MorphologicalParserModel(transcriptionParserObject))) - - phoneticTranscriptionParserObject = @get 'phonetic_transcription_parser' - if phoneticTranscriptionParserObject - @set('phonetic_transcription_parser', - (new MorphologicalParserModel(phoneticTranscriptionParserObject))) - - narrowPhoneticTranscriptionParserObject = - @get 'narrow_phonetic_transcription_parser' - if narrowPhoneticTranscriptionParserObject - @set('narrow_phonetic_transcription_parser', - (new MorphologicalParserModel(narrowPhoneticTranscriptionParserObject))) - - toTranscriptionPhonologyObject = @get 'to_transcription_phonology' - if toTranscriptionPhonologyObject - @set('to_transcription_phonology', - (new PhonologyModel(toTranscriptionPhonologyObject))) - - toPhoneticTranscriptionPhonologyObject = - @get 'to_phonetic_transcription_phonology' - if toPhoneticTranscriptionPhonologyObject - @set('to_phonetic_transcription_phonology', - (new PhonologyModel(toPhoneticTranscriptionPhonologyObject))) - - toNarrowPhoneticTranscriptionPhonologyObject = - @get 'to_narrow_phonetic_transcription_phonology' - if toNarrowPhoneticTranscriptionPhonologyObject - @set('to_narrow_phonetic_transcription_phonology', - (new PhonologyModel(toNarrowPhoneticTranscriptionPhonologyObject))) - - recognizerMorphologyObject = @get 'recognizer_morphology' - if recognizerMorphologyObject - @set('recognizer_morphology', - (new MorphologyModel(recognizerMorphologyObject))) - - resourceName: 'parserTaskSet' - - clientSideOnlyModel: true - - defaults: -> - id: @guid() - - # A morphological parser for parsing transcription values - transcription_parser: null - - # A morphological parser for parsing *phonetic* transcription values - phonetic_transcription_parser: null - - # A morphological parser for parsing *narrow* phonetic transcription - # values - narrow_phonetic_transcription_parser: null - - # A phonology for generating transcriptions from morphological analyses. - to_transcription_phonology: null - - # A phonology for generating phonetic transcriptions from morphological - # analyses. - to_phonetic_transcription_phonology: null - - # A phonology for generating narrow phonetic transcriptions from - # morphological analyses. - to_narrow_phonetic_transcription_phonology: null - - # A morphology for recognizing morphological analyses. - recognizer_morphology: null - - editableAttributes: [ - 'transcription_parser' - 'phonetic_transcription_parser' - 'narrow_phonetic_transcription_parser' - 'to_transcription_phonology' - 'to_phonetic_transcription_phonology' - 'to_narrow_phonetic_transcription_phonology' - 'recognizer_morphology' - ] - diff --git a/app/scripts/models/phonology.coffee b/app/scripts/models/phonology.coffee deleted file mode 100644 index beee126..0000000 --- a/app/scripts/models/phonology.coffee +++ /dev/null @@ -1,200 +0,0 @@ -define [ - './fst-based' - './../utils/globals' -], (FSTBasedModel, globals) -> - - # Phonology Model - # --------------- - # - # A Backbone model for Dative phonologies. - # - # Note: this model has extensive logic for cacheing "apply down" request - # results in localStorage on the client. This functionality should be - # generalized for other FST-based resources and moved to `FSTBasedModel` in - # the future. - - class PhonologyModel extends FSTBasedModel - - resourceName: 'phonology' - - initialize: (attributes, options) -> - super attributes, options - @listenTo @, 'change:compile_attempt', @resetApplyDownCache - @applyDownCache = @fetchApplyDownCache() - - # Each phonology resource has a cache in localStorage whose key is a unique - # string constructed from the web service's URL and the phonology's last - # compile_attempt value. Note that multiple `PhonologyModel` instances can - # exist in a Dative app and they will (should) all access and modify the - # same localStorage cache, as needed. - getLocalStorageKey: (previous=false) -> - serverURL = globals.applicationSettings.get('activeServer').get 'url' - if previous - compileAttempt = @previousAttributes().compile_attempt - else - compileAttempt = @get 'compile_attempt' - if compileAttempt - "dative-#{serverURL}-phonology-#{compileAttempt}-applydown-cache" - else - null - - # Fetch our client-side-stored (in localStorage) cache of "apply down" - # mappings. - fetchApplyDownCache: -> - key = @getLocalStorageKey() - if key - localStorageCache = localStorage.getItem key - if localStorageCache - JSON.parse localStorageCache - else - localStorage.setItem key, JSON.stringify({}) - {} - else - console.log "WARN: unable to get persisted cache: this phonology has no - `compile_attempt` attribute" - {} - - # Save the in-memory cache of "apply down" results to localStorage. - persistApplyDownCache: -> - key = @getLocalStorageKey() - if key - localStorage.setItem key, JSON.stringify(@applyDownCache) - else - console.log "WARN: unable to persist cache: this phonology has no - `compile_attempt` attribute" - - # We delete our old localStorage apply down cache and create a new one when - # our PhonologyModel's `compile_attempt` attribute changes. This attribute - # changing usually means that the phonology will behave differently, though - # that isn't necessarily true if the compile was made with no change in the - # phonology's script. - # TODO: make this reset sensitive to `PhonologyModel.script` content: if - # script hasn't changed, then don't delete the cache, just copy it over to - # the new localStorage address. - resetApplyDownCache: -> - previousLocalStorageKey = @getLocalStorageKey true - currentLocalStorageKey = @getLocalStorageKey() - localStorage.removeItem previousLocalStorageKey - @applyDownCache = {} - @persistApplyDownCache() - - # Cache our apply down results from the server in memory and in - # localStorage. - cacheApplyDownResults: (applyDownResults) -> - for uf, sfSet of applyDownResults - @applyDownCache[uf] = sfSet - @persistApplyDownCache() - - # The `PhonologyModel` overrides the super-class's `applyDown` method in - # order to provide in-memory and client-side localStorage caching of parser - # results. We try to minimize requests to the server and to minimize how - # many words are sent to the server for "apply down" transformation on all - # necessary requests. - applyDown: (words) -> - wordsNeedingApplyDown = - (w for w in words when w not of @applyDownCache) - if wordsNeedingApplyDown.length > 0 - # We remember the words that we already have (cached) outputs for so we - # can add them to the server's response on a successful request. See - # the `applyOnloadHandler` below. - @wordsCached = (w for w in words when w of @applyDownCache) - super wordsNeedingApplyDown - else - # Client-side retrieval of cached apply down results. Note we use the - # same API, i.e., we trigger the same events that a successful request - # to the server would. - @trigger "applyDownStart" - result = {} - for word in words - result[word] = @applyDownCache[word] - @trigger "applyDownSuccess", result - @trigger "applyDownEnd" - - # Respond to a successful "apply down" request to the server. The override - # here is needed in order to: - # 1. cache the results from the server - # 2. add our cached response to the object passed to any 'applyDownSuccess' - # listeners. - applyOnloadHandler: (responseJSON, xhr, directionCapitalized) -> - @trigger "apply#{directionCapitalized}End" - if xhr.status is 200 - @cacheApplyDownResults responseJSON - if @wordsCached and @wordsCached.length > 0 - for word in @wordsCached - responseJSON[word] = @applyDownCache[word] - @trigger "apply#{directionCapitalized}Success", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "apply#{directionCapitalized}Fail", error - console.log "PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/\ - apply#{direction} failed (status not 200)." - console.log error - - ############################################################################ - # Phonology Schema - ############################################################################ - - # See: - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/phonology.py#L50-L64 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/controllers/phonologies.py - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L1041-L1049 - - defaults: -> - name: '' # required, unique among phonology names, max - # 255 chars - description: '' # - script: '' # The FST script of the phonology. - - # Attributes that the OLD sends to us, but which the OLD will ignore if - # we try to send them back. - id: null # An integer relational id - UUID: '' # A string UUID - enterer: null # an object (attributes: `id`, `first_name`, - # `last_name`, `role`) - modifier: null # an object (attributes: `id`, `first_name`, - # `last_name`, `role`) - datetime_entered: "" # (datetime resource was created/entered; - # generated on the server as a UTC datetime; - # communicated in JSON as a UTC ISO 8601 datetime, - # e.g., '2015-02-11T10:50:57.821192'.) - datetime_modified: "" # (datetime resource was last modified; - # format and construction same as - # `datetime_entered`.) - compile_succeeded: false - compile_message: '' - compile_attempt: '' # A UUID - - editableAttributes: [ - 'name' - 'description' - 'script' - ] - - # Perform a "run tests" request on the phonology. - # GET `/phonologies/{id}/runtests` - runTests: -> - @trigger "runTestsStart" - @constructor.cors.request( - method: 'GET' - url: "#{@getOLDURL()}/phonologies/#{@get 'id'}/runtests" - onload: (responseJSON, xhr) => - @trigger "runTestsEnd" - if xhr.status is 200 - @trigger "runTestsSuccess", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "runTestsFail", error - console.log "PUT request to - #{@getOLDURL()}/phonologies/#{@get 'id'}/runtests - failed (status not 200)." - console.log error - onerror: (responseJSON) => - @trigger "runTestsEnd" - error = responseJSON.error or 'No error message provided.' - @trigger "runTestsFail", error - console.log "Error in PUT request to - #{@getOLDURL()}/phonologies/#{@get 'id'}/runtests - (onerror triggered)." - ) - diff --git a/app/scripts/models/resource.coffee b/app/scripts/models/resource.coffee deleted file mode 100644 index 8f4b5b0..0000000 --- a/app/scripts/models/resource.coffee +++ /dev/null @@ -1,376 +0,0 @@ -define [ - './base' - './../utils/globals' - ], (BaseModel, globals) -> - - # Resource Model - # --------------- - # - # A Backbone model for Dative resources, e.g., OLD corpora. - # - # This model is intended for sub-classing. At a minimum, the `@resourceName` - # attribute should be overridden. - - class ResourceModel extends BaseModel - - # Override this in subclasses. - resourceName: 'resource' - - # Set this to `true` if this is a model that is never saved to a server; - # its persistence is always client-side. - clientSideOnlyModel: false - - clientSideOnlyAttributes: [] - - # Override this in the sub-class with something sensible, i.e., something - # that makes sense for the resource model being represented here. - defaults: -> - - # Override this in subclasses to indicate which attributes can be edited by - # users. - editableAttributes: [] - - initialize: (attributes, options) -> - @lastErrors = null - @resourceNameCapitalized = @utils.capitalize @resourceName - @resourceNamePlural = @utils.pluralize @resourceName - @resourceNamePluralCapitalized = @utils.capitalize @resourceNamePlural - options = options or {} - if options.collection then @collection = options.collection - @activeServerType = @getActiveServerType() - super attributes, options - - # Backbone throws 'A "url" property or function must be specified' if this - # is not present. - url: 'fakeurl' - - getActiveServerType: -> - try - globals.applicationSettings.get('activeServer').get 'type' - catch - 'OLD' - - # Validate the model. If there are errors, returns an object with errored - # attributes as keys and error messages as values; otherwise returns - # `undefined`. Note that we cache/memoize the validation errors, based on - # the attributes being validated. This is because field views can call - # `@validate` many times. - # TODO: why is the `options` parameter necessary here? - validate: (attributes, options) -> - attributes = attributes or @attributes - if @lastErrors isnt null and _.isEqual(@lastErrors.attributes, attributes) - return @lastErrors.errors - errors = {} - for attribute, value of attributes - attributeValidator = @getValidator attribute - if attributeValidator - error = attributeValidator.apply @, [value] - if error - if @utils.type(error) is 'object' - for errorAttr, errorMsg of error - errors[errorAttr] = errorMsg - else - errors[attribute] = error - @lastErrors = attributes: @utils.clone(attributes) - if _.isEmpty errors - @lastErrors.errors = undefined - else - @lastErrors.errors = errors - @lastErrors.errors - - # Override this in subclasses for validation: return a `@validator` method - # for the input `attribute`, or `null` if it shouldn't be validated. - getValidator: (attribute) -> null - - # The OLD web service expects ids or arrays of ids as input for relational - # attributes. However, Dative stores the values of such attributes as - # objects (with id attributes) or arrays of such objects. Specifying the - # relational attributes in these arrays allows `toOLD` to work correctly. - manyToOneAttributes: [] - manyToManyAttributes: [] - - # Return a representation of the model's state that the OLD likes: i.e., - # with relational values as ids or arrays thereof. Note that there is no - # general `toFieldDB` method, since I am unsure a) whether other FieldDB - # objects expose a similar RESTful resource-based interface (sessions?, - # comments?, corpora?, message_feeds?) - toOLD: -> - result = _.clone @attributes - # Not doing this causes a `RangeError: Maximum call stack size exceeded` - # when cors.coffee tries to call `JSON.stringify` on a resource model that - # contains a resources collection that contains that same resource model, - # etc. ad infinitum. - delete result.collection - for attribute in @manyToOneAttributes - result[attribute] = result[attribute]?.id or null - for attribute in @manyToManyAttributes - result[attribute] = (v.id for v in result[attribute] or []) - for attribute in @clientSideOnlyAttributes - delete result[attribute] - result - - ############################################################################ - # Resource Schema - ############################################################################ - - # Returns `true` if the model is empty. - isEmpty: -> - attributes = _.clone @attributes - delete attributes.collection - _.isEqual @defaults(), attributes - - getOLDURL: -> globals.applicationSettings.get('activeServer').get 'url' - - # The default is to just use the plural form of the resource name as the - # server-side name for the resource; however, this can be overridden with - # `@serverSideResourceName`, as is necessary with OLD "corpora" which are - # called "subcorpora" in Dative. - getServerSideResourceName: -> - @serverSideResourceName or @resourceNamePlural.toLowerCase() - - # Fetch a resource by id. - # GET `//` - fetchResource: (id) -> - @trigger "fetch#{@resourceNameCapitalized}Start" - @constructor.cors.request( - method: 'GET' - url: @getFetchResourceURL id - onload: (responseJSON, xhr) => - @fetchResourceOnloadHandler responseJSON, xhr - onerror: (responseJSON) => - @trigger "fetch#{@resourceNameCapitalized}End" - error = responseJSON.error or 'No error message provided.' - @trigger "fetch#{@resourceNameCapitalized}Fail", error, @ - console.log "Error in GET request to - /#{@getServerSideResourceName()}/#{@get 'id'} (onerror triggered)." - ) - - # The type of URL used to fetch a resource on an OLD backend. - getFetchResourceURL: (id) -> - "#{@getOLDURL()}/#{@getServerSideResourceName()}/#{id}" - - fetchResourceOnloadHandler: (responseJSON, xhr) -> - @trigger "fetch#{@resourceNameCapitalized}End" - if xhr.status is 200 - @trigger "fetch#{@resourceNameCapitalized}Success", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "fetch#{@resourceNameCapitalized}Fail", error, @ - console.log "GET request to /#{@getServerSideResourceName()}/#{@get 'id'} - failed (status not 200)." - console.log error - - # Issue a GET request to //new on the active OLD - # server. In the OLD API, this type of request returns a JSON object - # containing the data necessary to create a new OLD resource. - getNewResourceData: -> - @trigger "getNew#{@resourceNameCapitalized}DataStart" - @constructor.cors.request( - method: 'GET' - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/new" - onload: (responseJSON, xhr) => - @trigger "getNew#{@resourceNameCapitalized}DataEnd" - if xhr.status is 200 - @trigger "getNew#{@resourceNameCapitalized}DataSuccess", - responseJSON - else - @trigger "getNew#{@resourceNameCapitalized}DataSuccess", - "Failed in fetching the data required to create new - #{@getServerSideResourceName()}." - onerror: (responseJSON) => - @trigger "getNew#{@resourceNameCapitalized}DataEnd" - @trigger "getNew#{@resourceNameCapitalized}DataFail", - "Error in GET request to OLD server for /#{@getServerSideResourceName()}/new" - console.log "Error in GET request to OLD server for - /#{@getServerSideResourceName()}/new" - ) - - # Issue a GET request to ///edit on the active OLD - # server. In the OLD API, this type of request returns a JSON object - # containing both the resource with id as well as the data necessary - # to update that resource. Example returned object: - # `{form: {...}, data: {...}}`. - getEditResourceData: -> - @trigger "getEdit#{@resourceNameCapitalized}DataStart" - @constructor.cors.request( - method: 'GET' - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/\ - #{@get('id')}/edit" - onload: (responseJSON, xhr) => - @trigger "getEdit#{@resourceNameCapitalized}DataEnd" - if xhr.status is 200 - @trigger "getEdit#{@resourceNameCapitalized}DataSuccess", - responseJSON - else - @trigger "getEdit#{@resourceNameCapitalized}DataSuccess", - "Failed in fetching the data required to create edit - #{@getServerSideResourceName()} #{@get('id')}." - onerror: (responseJSON) => - @trigger "getEdit#{@resourceNameCapitalized}DataEnd" - @trigger "getEdit#{@resourceNameCapitalized}DataFail", - "Error in GET request to OLD server for - /#{@getServerSideResourceName()}/#{@get('id')}/edit" - console.log "Error in GET request to OLD server for - /#{@getServerSideResourceName()}/#{@get('id')}/edit" - ) - - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/new_search" - - # This may need to be overridden in sub-classes. - getNewSearchDataURL: -> - "#{@getOLDURL()}/#{@getServerSideResourceName()}/new_search" - - # Issue a GET request to //new_search on the active OLD - # server. In the OLD API, this type of request returns a JSON object - # containing the data necessary to create a new OLD search over that resource. - getNewSearchData: -> - @trigger "getNew#{@resourceNameCapitalized}SearchDataStart" - @constructor.cors.request( - method: 'GET' - url: @getNewSearchDataURL() - onload: (responseJSON, xhr) => - @trigger "getNew#{@resourceNameCapitalized}SearchDataEnd" - if xhr.status is 200 - @trigger "getNew#{@resourceNameCapitalized}SearchDataSuccess", - responseJSON - else - @trigger "getNew#{@resourceNameCapitalized}SearchDataSuccess", - "Failed in fetching the data required to create a new search over - #{@resourceNamePlural}." - onerror: (responseJSON) => - @trigger "getNew#{@resourceNameCapitalized}SearchDataEnd" - @trigger "getNew#{@resourceNameCapitalized}SearchDataFail", - "Error in GET request to OLD server for /#{@getServerSideResourceName()}/new_search" - console.log "Error in GET request to OLD server for - /#{@getServerSideResourceName()}/new_search" - ) - - # Destroy a resource. - # DELETE `//` - destroyResource: (options) -> - Backbone.trigger "destroy#{@resourceNameCapitalized}Start" - @constructor.cors.request( - method: @getDestroyResourceHTTPMethod() - url: @getDestroyResourceURL() - payload: @getDestroyResourcePayload() - onload: (responseJSON, xhr) => - @destroyResourceOnloadHandler responseJSON, xhr - onerror: (responseJSON) => - Backbone.trigger "destroy#{@resourceNameCapitalized}End" - error = responseJSON.error or 'No error message provided.' - Backbone.trigger "destroy#{@resourceNameCapitalized}Fail", error - console.log "Error in DELETE request to - /#{@getServerSideResourceName()}/#{@get 'id'} (onerror triggered)." - ) - - destroyResourceOnloadHandler: (responseJSON, xhr) -> - Backbone.trigger "destroy#{@resourceNameCapitalized}End" - if xhr.status is 200 - Backbone.trigger "destroy#{@resourceNameCapitalized}Success", @ - @trigger "destroy#{@resourceNameCapitalized}Success" - else - error = responseJSON.error or 'No error message provided.' - Backbone.trigger "destroy#{@resourceNameCapitalized}Fail", error - console.log "DELETE request to /#{@getServerSideResourceName()}/#{@get 'id'} - failed (status not 200)." - console.log error - - # This is its own function because in the FieldDB case it's a PUT request. - getDestroyResourceHTTPMethod: -> - 'DELETE' - - # The type of URL used to destroy a resource on an OLD backend. - getDestroyResourceURL: -> - "#{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}" - - # The JSON payload for destroying a resource; the OLD doesn't use this, but - # FieldDB crucially does. - getDestroyResourcePayload: -> null - - # Perform a "generate and compile" request. - # PUT `/morphologicalparsers/{id}/generate_and_compile` - # NOTE: this is only relevant to FST-based resources that need to be - # generated and compiled, i.e., just morphologies and morphological - # parsers, I think. - # NOTE 2: I purposefully pass `@` here so that events relayed through - # `Backbone` can know which morphology failed/succeeded. - generateAndCompile: -> - @trigger "generateAndCompileStart" - @constructor.cors.request( - method: 'PUT' - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/generate_and_compile" - onload: (responseJSON, xhr) => - @trigger "generateAndCompileEnd" - if xhr.status is 200 - @trigger "generateAndCompileSuccess", responseJSON, @ - else - error = responseJSON.error or 'No error message provided.' - @trigger "generateAndCompileFail", error, @ - console.log "PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/generate_and_compile - failed (status not 200)." - console.log error - onerror: (responseJSON) => - @trigger "generateAndCompileEnd" - error = responseJSON.error or 'No error message provided.' - @trigger "generateAndCompileFail", error, @ - console.log "Error in PUT request to - #{@getOLDURL()}/#{@getServerSideResourceName()}/#{@get 'id'}/generate_and_compile - (onerror triggered)." - ) - - # Perform a search request. - # SEARCH `//` or - # POST `//search` - # Payload guide: - # { - # "query": { - # "filter": [ ... ], - # "order_by": [ ... ] - # }, - # "paginator": { ... } - # } - # TODO: the default `order_by` should not reference the Form model ... - getSearchPayload: (query, paginator, paginate=true) -> - paginator = paginator or {page: 1, items_per_page: 10} - if 'order_by' not of query then query.order_by = ['Form', 'id', 'asc'] - if paginate - query: query - paginator: paginator - else - query: query - - # This may need to be overridden in sub-classes, cf. the complication of - # searching across corpora in the OLD. - getSearchURL: -> - "#{@getOLDURL()}/#{@getServerSideResourceName()}" - - # Perform a search over this type of resource; if a paginator is supplied, - # it will be used, otherwise a default (page 1, 10 ipp) will be used. - # Setting `paginate` to `false` will result in no pagination. - search: (query, paginator=null, paginate=true) -> - @trigger "searchStart" - @constructor.cors.request( - method: 'SEARCH' - url: @getSearchURL() - payload: @getSearchPayload query, paginator, paginate - onload: (responseJSON, xhr) => - @trigger "searchEnd" - if xhr.status is 200 - @trigger "searchSuccess", responseJSON - else - error = responseJSON.error or 'No error message provided.' - @trigger "searchFail", error - console.log "SEARCH request to - #{@getOLDURL()}/#{@getServerSideResourceName()} - failed (status not 200)." - console.log error - onerror: (responseJSON) => - @trigger "searchEnd" - error = responseJSON.error or 'No error message provided.' - @trigger "searchFail", error - console.log "Error in SEARCH request to - #{@getOLDURL()}/#{@getServerSideResourceName()} - (onerror triggered)." - ) - diff --git a/app/scripts/models/search.coffee b/app/scripts/models/search.coffee deleted file mode 100644 index cb6e5a2..0000000 --- a/app/scripts/models/search.coffee +++ /dev/null @@ -1,236 +0,0 @@ -define [ - './resource' - './form' -], (ResourceModel, FormModel) -> - - # Search Model - # --------------- - # - # A Backbone model for Dative searches. - - class SearchModel extends ResourceModel - - resourceName: 'search' - - # Change some or all of the following four attributes if this search model - # is being used to search over a resource other than forms, e.g., over file - # resources. - targetResourceName: 'form' - targetResourcePrimaryAttribute: 'transcription' - targetModelClass: FormModel - - # This may need to be overridden when this SearchModel is used to search - # over models that don't have "id" as their primary key, e.g., OLD - # languages which have the ISO 639-3 "Id" as their primary key. - targetResourcePrimaryKey: 'id' - - getTargetResourceNameCapitalized: -> - @utils.capitalize @targetResourceName - - serverSideResourceName: 'formsearches' - - initialize: (attributes, options) -> - super attributes, options - @targetResourceNameCapitalized = @getTargetResourceNameCapitalized() - @targetResourceNamePlural = @utils.pluralize @targetResourceName - @targetResourceNamePluralCapitalized = - @utils.capitalize @targetResourceNamePlural - - editableAttributes: [ - 'name' - 'description' - 'search' - ] - - manyToOneAttributes: [] - - manyToManyAttributes: [] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - else null - - ############################################################################ - # Search Schema - ############################################################################ - - # See: - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L846-L854 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/formsearch.py - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/model.py - - defaults: -> - name: '' # required, unique among search names, max 255 chars - description: '' # string description - search: - filter: [ - @getTargetResourceNameCapitalized() - @targetResourcePrimaryAttribute - 'regex' - '' - ] - order_by: [ - @getTargetResourceNameCapitalized() - @targetResourcePrimaryKey - 'asc' - ] - - # Attributes that the OLD sends to us, but which the OLD will ignore if - # we try to send them back. - id: null # An integer relational id - enterer: null # an object (attributes: `id`, `first_name`, - # `last_name`, `role`) - datetime_modified: "" # (datetime search was last modified, - # format and construction same as - # `datetime_entered`.) - - # We listen to this once so that we can add the result of calling GET - # //new_search to the result of calling GET - # /formsearches/new. - getNewTargetResourceSearchDataSuccess: (newSearchData) -> - key = "#{@targetResourceName}_search_parameters" - @searchNewData[key] = newSearchData.search_parameters - @trigger "getNew#{@resourceNameCapitalized}DataSuccess", @searchNewData - - # We listen to this once so that we can tell the user that the request to - # GET //new_search failed. - getNewTargetResourceSearchDataFail: -> - @trigger "getNew#{@resourceNameCapitalized}DataFail", - "Error in GET request to OLD server for - /#{@targetResourceNamePlural}/new_search" - - # Get the data necessary to create a new search over - # objects. Note: this is an override of the base `ResourceModel`'s - # implementation of this method since here we need to also request GET - # //new_search. We do this by first requesting GET - # /formsearches/new and then, if that's successful, requesting GET - # //new_search. If that's successful, we trigger - # the standard Backbone-wide success event for this method, passing in an - # integrated/extended object. - getNewResourceData: -> - @trigger "getNew#{@resourceNameCapitalized}DataStart" - @constructor.cors.request( - method: 'GET' - url: "#{@getOLDURL()}/#{@getServerSideResourceName()}/new" - onload: (responseJSON, xhr) => - @trigger "getNew#{@resourceNameCapitalized}DataEnd" - if xhr.status is 200 - @searchNewData = responseJSON - ourTargetModel = new @targetModelClass() - @listenToOnce(ourTargetModel, - "getNew#{@targetResourceNameCapitalized}SearchDataSuccess", - @getNewTargetResourceSearchDataSuccess) - @listenToOnce(ourTargetModel, - "getNew#{@targetResourceNameCapitalized}SearchDataFail", - @getNewTargetResourceSearchDataFail) - ourTargetModel.getNewSearchData() - else - @trigger "getNew#{@resourceNameCapitalized}DataFail", - "Failed in fetching the data required to create new - #{@getServerSideResourceName()}." - onerror: (responseJSON) => - @trigger "getNew#{@resourceNameCapitalized}DataEnd" - @trigger "getNew#{@resourceNameCapitalized}DataFail", - "Error in GET request to OLD server for /#{@getServerSideResourceName()}/new" - console.log "Error in GET request to OLD server for - /#{@getServerSideResourceName()}/new" - ) - - ############################################################################ - # Logic for returning a "patterns object" for the search model. - ############################################################################ - - # A patterns object is an object that maps attributes to regular - # expressions that can be used to highlight the attribute's value in order - # to better indicate why a given resource matches the search model. - - getPatternsObject: -> - patterns = @getPatterns @get('search').filter - @_getPatternsObject patterns - - # Return a "patterns" array with all of the "positive" filter - # expressions (i.e., patterns) in the (OLD-style) filter expression, i.e., - # all of the non-negated filter expressions. - getPatterns: (filter) -> - patterns = [] - if filter.length in [4, 5] - patterns.push filter - else - if filter[0] in ['and', 'or'] - for junct in filter[1] - patterns = patterns.concat @getPatterns(junct) - patterns - - # Return an object with resource attributes as attributes and regular - # expressions for matching search pattern matches as values. Setting the - # `flatten` param to `true` will treat all attribute-values as scalars - # (i.e., strings or numbers). - # The `patterns` param is an array of subarrays, where each subarray is a - # "positive" OLD-style filter expression (positive, meaning that it asserts - # something content-ful/non-negative of the match). The `patternsObject` - # returned maps attributes (and `[attribute, subattribute]` duples) to - # `RegExp` instances. - _getPatternsObject: (patterns, flatten=false) -> - patternsObject = {} - for pattern in patterns - attribute = pattern[1] - if pattern.length is 4 - relation = pattern[2] - term = pattern[3] - else - subattribute = pattern[2] - relation = pattern[3] - term = pattern[4] - regex = @getRegex relation, term - if not regex then continue - if attribute of patternsObject - if (not flatten) and pattern.length is 5 - if subattribute of patternsObject[attribute] - patternsObject[attribute][subattribute].push regex - else - patternsObject[attribute][subattribute] = [regex] - else - patternsObject[attribute].push regex - else - if (not flatten) and pattern.length is 5 - patternsObject[attribute] = {} - patternsObject[attribute][subattribute] = [regex] - else - patternsObject[attribute] = [regex] - for k, v of patternsObject - if @utils.type(v) is 'object' - for kk, vv of v - patternsObject[k][kk] = new RegExp("((?:#{vv.join ')|(?:'}))", 'g') - else - patternsObject[k] = new RegExp("((?:#{v.join ')|(?:'}))", 'g') - patternsObject - - # Take `relation` and `term` and return an appropriate regular expression - # string. Relations currently accounted for: 'regex', 'like', '=', and - # 'in'. TODO: relations still needing work: <=, >=, <, and >. - getRegex: (relation, term) -> - if relation is 'regex' - regex = term - else if relation is 'like' - # Clip off '%' on the edges so that the later `.replace` call - # highlights only the pattern and not the entire value. - if term.length > 1 and term[0] is '%' - term = term[1...] - if term.length > 1 and term[term.length - 1] is '%' - term = term[...-1] - regex = @utils.escapeRegexChars(term).replace(/_/g, '.').replace(/%/g, '.*') - else if relation is '=' - regex = "^#{@utils.escapeRegexChars term}$" - else if relation is 'in' - regex = "(?:^#{term.join ')$|(?:^'})$" - else - regex = null - - # The OLD NFD-Unicode normalizes all data. So we need to normalize our - # regex string in order for all matches to work out correctly. - try - regex.normalize 'NFD' - catch - regex - diff --git a/app/scripts/models/server.coffee b/app/scripts/models/server.coffee deleted file mode 100644 index da80663..0000000 --- a/app/scripts/models/server.coffee +++ /dev/null @@ -1,22 +0,0 @@ -define [ - 'underscore' - 'backbone' - './base' - './../utils/utils' -], (_, Backbone, BaseModel, utils) -> - - # Server Model - # ------------ - - class ServerModel extends BaseModel - - idAttribute: 'id' - - defaults: -> - id: @guid() - name: '' - type: 'OLD' # 'OLD' or 'FieldDB' - url: '' # must be unique - serverCode: '' # FieldDB-specific; see `model/application-settings` for the list. - corpusServerURL: null - diff --git a/app/scripts/models/source.coffee b/app/scripts/models/source.coffee deleted file mode 100644 index 67b3a5d..0000000 --- a/app/scripts/models/source.coffee +++ /dev/null @@ -1,482 +0,0 @@ -define [ - './resource' - './../utils/bibtex' -], (ResourceModel, BibTeXUtils) -> - - # Source Model - # ------------ - # - # A Backbone model for Dative sources. - - class SourceModel extends ResourceModel - - resourceName: 'source' - - # Note that `crossref_source` is not in here because the user does not - # specify it directly. It is by selecting a `crossref` value that the - # `crossref_source` relational value gets specified server-side. - manyToOneAttributes: [ - 'file' - ] - - ############################################################################ - # Source Schema - ############################################################################ - - defaults: -> - - file: null # a reference to a file (e.g., a pdf) for this source - - crossref_source: null # a reference to another source model for - # cross-referencing, cf. the BibTeX spec - - crossref: '' # The `key` value of another source to be - # cross-referenced. Any attribute values that - # are missing from the source model are inherited - # from the source cross-referenced via the - # cross-reference attribute. Maximum length is - # 1000 characters. Note: OLD will return an error - # if a non-existent key value is supplied here. - # QUESTION: how do `crossref_source` and - # `crossref` interact? I think that specifying the - # latter determines the former. - - type: '' # the BibTeX entry type, e.g., “article”, - # “book”, etc. A valid type value is - # obligatory for all source models. The chosen - # type value will determine which other attributes - # must also possess non-empty values. See p. 391 - # of Dunham 2014. - - key: '' # the BibTeX key, i.e., the unique string used to - # unambiguously identify a source. E.g., “chomsky57”. - - address: '' # Usually the address of the publisher or other - # type of institution. Maximum length is 1000 - # characters. - - annote: '' # An annotation. It is not used by the standard - # bibliography styles, but may be used by others - # that produce an annotated bibliography. - - author: '' # The name(s) of the author(s), in the format - # described in Kopka and Daly (2004). There are - # two basic formats: (1) Given Names Surname and - # (2) Surname, Given Names. For multiple authors, - # use the formats just specified and separate each - # such formatted name by the word “and”. - # Maximum length is 255 characters. - - booktitle: '' # Title of a book, part of which is being cited. - # See Kopka and Daly (2004) for details on how to - # type titles. For book entries, use the title - # field instead. Maximum length is 255 characters. - - chapter: '' # A chapter (or section or whatever) number. - # Maximum length is 255 characters. - - edition: '' # The edition of a book—for example, - # “Second”. This should be an ordinal, and - # should have the first letter capitalized, as - # shown here; the standard styles convert to lower - # case when necessary. Maximum length is 255 - # characters. - - editor: '' # Name(s) of editor(s), typed as indicated in - # Kopka and Daly (2004). At its most basic, this - # means either as Given Names Surname or Surname, - # Given Names and using “and” to separate - # multiple editor names. If there is also a value - # for the author attribute, then the editor - # attribute gives the editor of the book or - # collection in which the reference appears. - # Maximum length is 255 characters. - - howpublished: '' # How something has been published. The first word - # should be capitalized. Maximum length is 255 - # characters. - - institution: '' # The sponsoring institution of a technical - # report. Maximum length is 255 characters. - - journal: '' # A journal name. Abbreviations are provided for - # many journals. Maximum length is 255 characters. - - key_field: '' # Used for alphabetizing, cross referencing, and - # creating a label when the author information is - # missing. This field should not be confused with - # the source’s key attribute. Maximum length is - # 255 characters. - - month: '' # The month in which the work was published or, - # for an unpublished work, in which it was - # written. Maximum length is 100 characters. - - note: '' # Any additional information that can help the - # reader. The first word should be capitalized. - # Maximum length is 1000 characters. - - number: '' # The number of a journal, magazine, technical - # report, or of a work in a series. An issue of a - # journal or magazine is usually identified by its - # volume and number; the organization that issues - # a technical report usually gives it a number; - # and sometimes books are given numbers in a named - # series. Maximum length is 100 characters. - - organization: '' # The organization that sponsors a conference or - # that publishes a manual. Maximum length is 255 - # characters. - - pages: '' # One or more page numbers or range of numbers, - # such as 42–111 or 7,41,73– 97 or 43+ (the - # “+” in this last example indicates pages - # following that don’t form a simple range). - # Maximum length is 100 characters. - - publisher: '' # The publisher’s name. Maximum length is 255 - # characters. - - school: '' # The name of the school where a thesis was - # written. Maximum length is 255 characters. - - series: '' # The name of a series or set of books. When - # citing an entire book, the title attribute gives - # its title and an optional series attribute gives - # the name of a series or multi-volume set in - # which the book is published. Maximum length is - # 255 characters. - - title: '' # The work’s title, typed as explained in Kopka - # and Daly (2004). Maximum length is 255 - # characters. - - type_field: '' # The type of a technical report—for example, - # “Research Note”. Maximum length is 255 - # characters. - - url: '' # The universal resource locator for online - # documents; this is not standard but supplied by - # more modern bibliography styles. Maximum length - # is 1000 characters. - - volume: '' # The volume of a journal or multi-volume book. - # Maximum length is 100 characters. - - year: '' # The year of publication or, for an unpublished - # work, the year it was written. Generally it - # should consist of four numerals, such as 1984. - - affiliation: '' # The author’s affiliation. Maximum length is - # 255 characters. - - abstract: '' # An abstract of the work. Maximum length is 1000 - # characters. - - contents: '' # A table of contents. Maximum length is 255 - # characters. - - copyright: '' # Copyright information. Maximum length is 255 - # characters. - - ISBN: '' # The International Standard Book Number. Maximum - # length is 20 characters. - - ISSN: '' # The International Standard Serial Number. Used - # to identify a journal. Maximum length is 20 - # characters. - - keywords: '' # Key words used for searching or possibly for - # annotation. Maximum length is 255 characters. - - language: '' # The language the document is in. Maximum length - # is 255 characters. - - location: '' # A location associated with the entry, such as - # the city in which a conference took place. - # Maximum length is 255 characters. - - LCCN: '' # The Library of Congress Call Number. Maximum - # length is 20 characters. - - mrnumber: '' # The Mathematical Reviews number. Maximum length - # is 25 characters. - - price: '' # The price of the document. Maximum length is 100 - # characters. - - size: '' # The physical dimensions of a work. Maximum - # length is 255 characters. - - - id: null # relational id - datetime_modified: "" # (datetime resource was last modified, - # format and construction same as - # `datetime_entered`.) - - editableAttributes: [ - 'file' - 'crossref_source' - 'crossref' - 'type' - 'key' - 'address' - 'annote' - 'author' - 'booktitle' - 'chapter' - 'edition' - 'editor' - 'howpublished' - 'institution' - 'journal' - 'key_field' - 'month' - 'note' - 'number' - 'organization' - 'pages' - 'publisher' - 'school' - 'series' - 'title' - 'type_field' - 'url' - 'volume' - 'year' - 'affiliation' - 'abstract' - 'contents' - 'copyright' - 'ISBN' - 'ISSN' - 'keywords' - 'language' - 'location' - 'LCCN' - 'mrnumber' - 'price' - 'size' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - when 'key' then @validBibTeXKey - when 'type' then @validBibTeXType - else null - - validBibTeXKey: (value) -> - regex = /^([-!$%^&*()_+|~=`{}\[\]:";'<>?.\/]|[a-zA-Z0-9])+$/ - if regex.test value - null - else - 'Source keys can only contain letters, numerals and symbols (except the - comma)' - - # Given the BibTeX entry type in `value`, make sure that all of the required - # fields (as specified in `@entryTypes[value].required` are valuated. - validBibTeXType: (value) -> - if not value then return 'You must select a type for this source' - invalid = false - - # BibTeX type determines required fields. Get them here. - [requiredFields, disjunctivelyRequiredFields, msg] = - @parseRequirements value - - # Here we set error messages on zero or more attributes, given the typed - # requirements of BibTeX. - requiredFieldsValues = - (@getRequiredValue(rf) for rf in requiredFields) - requiredFieldsValues = (rf for rf in requiredFieldsValues when rf) - if requiredFieldsValues.length isnt requiredFields.length - invalid = true - else - for dr in disjunctivelyRequiredFields - drValues = (@getRequiredValue(rf) for rf in dr) - drValues = (rf for rf in drValues when rf) - if drValues.length is 0 then invalid = true - if invalid - errorObject = type: msg - for field in requiredFields - if not @getRequiredValue(field) - errorObject[field] = "Please enter a value" - for fieldsArray in disjunctivelyRequiredFields - values = (@getRequiredValue(f) for f in fieldsArray) - values = (v for v in values when v) - if values.length is 0 - for field in fieldsArray - errorObject[field] = "Please enter a value for - #{@coordinate fieldsArray, 'or'}" - errorObject - else - null - - getRequiredValue: (requiredField) -> - """Try to get a value for the required field `requiredField`; if it's - not there, try the cross-referenced source model. - """ - val = @get requiredField - if val - val - else - crossReferencedSource = @get 'crossref_source' - if crossReferencedSource - val = crossReferencedSource[requiredField] - if val then val else null - else - null - - coordinate: (array, coordinator='and') -> - if array.length > 1 - "#{array[...-1].join ', '} #{coordinator} #{array[array.length - 1]}" - else if array.length is 1 - array[0] - else - '' - - # Given a BibTeX `type` value, return a 3-tuple array consisting of the - # required fields, the disjunctively required fields, and a text message - # declaring those requirements. - parseRequirements: (typeValue) -> - - conjugateValues = (requiredFields) -> - if requiredFields.length > 1 then 'values' else 'a value' - - required = @entryTypes[typeValue].required - requiredFields = (r for r in required when @utils.type(r) is 'string') - disjunctivelyRequiredFields = - (r for r in required when @utils.type(r) is 'array') - - msg = "Sources of type #{typeValue} require #{conjugateValues requiredFields} - for #{@coordinate requiredFields}" - if disjunctivelyRequiredFields.length > 0 - tmp = ("at least one of #{@coordinate dr}" for dr in disjunctivelyRequiredFields) - msg = "#{msg} as well as a value for #{@coordinate tmp}" - - [requiredFields, disjunctivelyRequiredFields, "#{msg}."] - - # Maps each BibTeX `type` value to the array of other attributes that must - # (`required`) and may (`optional`) be valuated for that type. - entryTypes: - - article: - required: ['author', 'title', 'journal', 'year'] - optional: ['volume', 'number', 'pages', 'month', 'note'] - - book: - required: [['author', 'editor'], 'title', 'publisher', 'year'] - optional: [['volume', 'number'], 'series', 'address', 'edition', - 'month', 'note'] - - booklet: - required: ['title'] - optional: ['author', 'howpublished', 'address', 'month', 'year', 'note'] - - conference: - required: ['author', 'title', 'booktitle', 'year'] - optional: ['editor', ['volume', 'number'], 'series', 'pages', - 'address', 'month', 'organization', 'publisher', 'note'] - - inbook: - required: [['author', 'editor'], 'title', ['chapter', 'pages'], - 'publisher', 'year'] - optional: [['volume', 'number'], 'series', 'type', 'address', - 'edition', 'month', 'note'] - - incollection: - required: ['author', 'title', 'booktitle', 'publisher', 'year'] - optional: ['editor', ['volume', 'number'], 'series', 'type', 'chapter', - 'pages', 'address', 'edition', 'month', 'note'] - - inproceedings: - required: ['author', 'title', 'booktitle', 'year'] - optional: ['editor', ['volume', 'number'], 'series', 'pages', - 'address', 'month', 'organization', 'publisher', 'note'] - - manual: - required: ['title'] - optional: ['author', 'organization', 'address', 'edition', 'month', - 'year', 'note'] - - mastersthesis: - required: ['author', 'title', 'school', 'year'] - optional: ['type', 'address', 'month', 'note'] - - misc: - required: [] - optional: ['author', 'title', 'howpublished', 'month', 'year', 'note'] - - phdthesis: - required: ['author', 'title', 'school', 'year'] - optional: ['type', 'address', 'month', 'note'] - - proceedings: - required: ['title', 'year'] - optional: ['editor', ['volume', 'number'], 'series', 'address', - 'month', 'publisher', 'organization', 'note'] - - techreport: - required: ['author', 'title', 'institution', 'year'] - optional: ['type', 'number', 'address', 'month', 'note'] - - unpublished: - required: ['author', 'title', 'note'] - optional: ['month', 'year'] - - - ############################################################################ - # String Conveniences - ############################################################################ - # - # Call these methods to get string-like representations of the source; - # needed because BibTeX ain't simple. - - getAuthor: -> BibTeXUtils.getAuthor @attributes - - getYear: -> BibTeXUtils.getYear @attributes - - # Get `attr` from this source's `crossref_source` value. - getCrossrefAttr: (attr) -> - crossref = @get 'crossref_source' - if crossref then crossref[attr] else null - - # Return a string like "Chomsky and Halle (1968)" - # Setting `crossref` to `false` will result in `crossref` not being used - # for empty value. - getAuthorYear: (crossref=true) -> - author = @get 'author' - if (not author) and crossref then author = @getCrossrefAttr 'author' - authorCitation = BibTeXUtils.getNameInCitationForm author - year = @get 'year' - if (not year) and crossref then year = @getCrossrefAttr 'year' - "#{authorCitation} (#{year})" - - # Return a string like "Chomsky and Halle (1968)", using editor names if - # authors are unavailable. - getAuthorEditorYear: (crossref=true) -> - name = @getAuthorEditor crossref - nameCitation = BibTeXUtils.getNameInCitationForm name - "#{nameCitation} (#{@get 'year'})" - - # Get author, else crossref.author, else editor, else crossref.editor - getAuthorEditor: (crossref=true) -> - name = @get 'author' - if (not name) and crossref then name = @getCrossrefAttr 'author' - if not name then name = @get 'editor' - if (not name) and crossref then name = @getCrossrefAttr 'editor' - name - - # Try to return a string like "Chomsky and Halle (1968)", but replace - # either the author/editor or the year with filler text, if needed. - getAuthorEditorYearDefaults: (crossref=true) -> - auth = @getAuthorEditor crossref - if auth - auth = BibTeXUtils.getNameInCitationForm auth - else - auth = 'no author' - year = @get 'year' - if (not year) and crossref then year = @getCrossrefAttr 'year' - yr = if year then year else 'no year' - "#{auth} (#{yr})" - diff --git a/app/scripts/models/speaker.coffee b/app/scripts/models/speaker.coffee deleted file mode 100644 index f89df69..0000000 --- a/app/scripts/models/speaker.coffee +++ /dev/null @@ -1,44 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Speaker Model - # ------------- - # - # A Backbone model for Dative speakers. - - class SpeakerModel extends ResourceModel - - resourceName: 'speaker' - - ############################################################################ - # Speaker Schema - ############################################################################ - - defaults: -> - first_name: '' # Max 255 chars, can't be empty. - last_name: '' # Max 255 chars, can't be empty. - dialect: '' # Max 255 chars, - markup_language: '' # One of "Markdown" or "reStructuredText", - # defaults to "reStructuredText". - page_content: '' # A string of lightweight markup. - - # Attributes that the OLD sends to us, but which the OLD will ignore if - # we try to send them back. - id: null # The relational id given to the speaker model. - datetime_modified: '' # When the speaker was last modified. - html: '' # HTML generated from the speaker-supplied - # markup. - - editableAttributes: [ - 'first_name' - 'last_name' - 'dialect' - 'markup_language' - 'page_content' - ] - - getValidator: (attribute) -> - switch attribute - when 'first_name' then @requiredString - when 'last_name' then @requiredString - else null - diff --git a/app/scripts/models/subcorpus.coffee b/app/scripts/models/subcorpus.coffee deleted file mode 100644 index 1142bd5..0000000 --- a/app/scripts/models/subcorpus.coffee +++ /dev/null @@ -1,84 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Subcorpus Model - # --------------- - # - # A Backbone model for Dative subcorpora, i.e., OLD corpora. - # - # At present Dative `SubcorpusModel`s represent OLD corpora and have no - # equivalent in the FieldDB data structure. They are called "subcorpora" - # because the term "corpora" is used for FieldDB corpora, which are different. - - class SubcorpusModel extends ResourceModel - - resourceName: 'subcorpus' - - # When requesting from the OLD, we need to request 'corpora', not - # 'subcorpora', hence this attribute. - serverSideResourceName: 'corpora' - - editableAttributes: [ - 'name' - 'description' - 'content' - 'tags' - 'form_search' - ] - - manyToOneAttributes: ['form_search'] - - manyToManyAttributes: ['tags'] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - else null - - ############################################################################ - # Subcorpus Schema - ############################################################################ - - # See: - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L1091-L1111 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/corpus.py#L82-L104 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/model.py - - defaults: -> - name: '' # required, unique among corpus names, max 255 chars - description: '' # string description - content: '' # string containing form references - tags: [] # OLD sends this as an array of objects - # (attributes: `id`, `name`) but receives it as an - # array of integer relational ids, all of which must - # be valid tag ids. - form_search: null # OLD sends this as an object (attributes: `id`, - # `name`) but receives it as a relational integer - # id; must be a valid form search id. - - # Attributes that the OLD sends to us, but which the OLD will ignore if - # we try to send them back. - id: null # An integer relational id - UUID: '' # A string UUID - enterer: null # an object (attributes: `id`, `first_name`, - # `last_name`, `role`) - modifier: null # an object (attributes: `id`, `first_name`, - # `last_name`, `role`) - datetime_entered: "" # (datetime subcorpus was created/entered; - # generated on the server as a UTC datetime; - # communicated in JSON as a UTC ISO 8601 datetime, - # e.g., '2015-02-11T10:50:57.821192'.) - datetime_modified: "" # (datetime subcorpus was last modified; - # format and construction same as - # `datetime_entered`.) - files: [] # an array of objects (attributes: `id`, `name`, - # `filename`, `MIME_type`, `size`, `url`, - # `lossy_filename`) - - # OLD corpora are searched by issuing a SEARCH request against - # /corpora/searchcorpora - getSearchURL: -> "#{@getOLDURL()}/corpora/searchcorpora" - - # Getting the data needed to perform a search across OLD corpora requires - # issuing a GET request to /corpora/new_search_corpora - getNewSearchDataURL: -> "#{@getOLDURL()}/corpora/new_search_corpora" - diff --git a/app/scripts/models/syntactic-category.coffee b/app/scripts/models/syntactic-category.coffee deleted file mode 100644 index 53cebe5..0000000 --- a/app/scripts/models/syntactic-category.coffee +++ /dev/null @@ -1,43 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Syntactic Category Model - # ------------------------ - # - # A Backbone model for Dative syntactic categories. - - class SyntacticCategoryModel extends ResourceModel - - resourceName: 'syntacticCategory' - - ############################################################################ - # Syntactic Category Schema - ############################################################################ - - defaults: -> - name: '' # Required, unique - # among syntactic category - # names, max 255 chars. - type: '' # According to the OLD, this - # should be one of the - # following values: 'lexical', - # 'phrasal' or 'sentential'. - description: '' # description of the - # syntactic category. - id: null # relational id - datetime_modified: "" # (datetime resource - # was last modified; format - # and construction same as - # `datetime_entered`.) - - editableAttributes: [ - 'name' - 'type' - 'description' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - else null - - diff --git a/app/scripts/models/tag.coffee b/app/scripts/models/tag.coffee deleted file mode 100644 index 572bcb4..0000000 --- a/app/scripts/models/tag.coffee +++ /dev/null @@ -1,37 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # Tag Model - # --------- - # - # A Backbone model for Dative tags. - - class TagModel extends ResourceModel - - resourceName: 'tag' - - ############################################################################ - # Tag Schema - ############################################################################ - - defaults: -> - name: '' # Required, unique - # among tag - # names, max 255 chars. - description: '' # description of the - # tag. - id: null # relational id - datetime_modified: "" # (datetime resource - # was last modified; format - # and construction same as - # `datetime_entered`.) - - editableAttributes: [ - 'name' - 'description' - ] - - getValidator: (attribute) -> - switch attribute - when 'name' then @requiredString - else null - diff --git a/app/scripts/models/user-old.coffee b/app/scripts/models/user-old.coffee deleted file mode 100644 index 241eb66..0000000 --- a/app/scripts/models/user-old.coffee +++ /dev/null @@ -1,171 +0,0 @@ -define ['./resource'], (ResourceModel) -> - - # User Model - # --------------- - # - # A Backbone model for Dative users. - - class UserModel extends ResourceModel - - resourceName: 'user' - - ############################################################################ - # User Schema - ############################################################################ - - # See: - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/lib/schemata.py#L1027-L1047 - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/model/user.py#L64-L78# - # - https://github.com/jrwdunham/old/blob/master/onlinelinguisticdatabase/controllers/users.py - - defaults: -> - - username: '' # Max 255 chars. Must be unique among usernames. - # May contain only letters of the English - # alphabet, numbers and the underscore. Required - # during user creation. Only administrators can - # update usernames. - password: '' # Max 255 chars. Required during user creation. - # - must contain at least 8 chars - # - must either - # - contain at least one character that is not - # in the printable ASCII range', or - # - contain at least one symbol, one digit, - # one uppercase letter and one lowercase - # letter - # - must match `passwordConfirm` value. - # - password_confirm: '' # Max 255 chars. - first_name: '' # Max 255 chars, can't be empty. - last_name: '' # Max 255 chars, can't be empty. - email: '' # Valid email, max 255 chars, can't be empty. - affiliation: '' # Max 255 chars. - role: '' # One of "viewer", "contributor", or - # "administrator", can't be empty. Can only be - # changed by an administrator. - markup_language: '' # One of "Markdown" or "reStructuredText", - # defaults to "reStructuredText". - page_content: '' # A string of lightweight markup. - input_orthography: null # An OLD orthography object. - output_orthography: null # An OLD orthography object. - - # Attributes that the OLD sends to us, but which the OLD will ignore if - # we try to send them back. - id: null # The relational id given to the user model. - datetime_modified: '' # When the user was last modified. - html: '' # HTML generated from the user-supplied markup. - - editableAttributes: [ - 'first_name' - 'last_name' - 'email' - 'affiliation' - 'markup_language' - 'page_content' - 'input_orthography' - 'output_orthography' - 'role' # ONLY EDITABLE BY ADMINS - 'username' # ONLY EDITABLE BY ADMINS - 'password' # ONLY EDITABLE BY ADMINS - 'password_confirm' # ONLY EDITABLE BY ADMINS - ] - - getValidator: (attribute) -> - switch attribute - when 'first_name' then @requiredString - when 'last_name' then @requiredString - when 'email' then @validEmail - when 'username' then @validUsername - when 'password' then @validPassword - when 'password_confirm' then @validPasswordConfirm - else null - - validUsername: (value) -> - if @get('id') - if value - if @containsOnlyWordCharacters value - null - else - 'The username may contain only letters of the English alphabet, - numbers and the underscore.' - else - null - else - emptyError = @requiredString value - if emptyError - emptyError - else - if @containsOnlyWordCharacters value - null - else - 'The username may contain only letters of the English alphabet, - numbers and the underscore.' - - containsOnlyWordCharacters: (string) -> - /[^\w]/.test(string) is false - - validPassword: (value) -> - @_validPassword value - - validPasswordConfirm: (value) -> - @_validPassword value, false - - _validPassword: (value, validatingPassword=true) -> - if value - if validatingPassword - counterpart = 'password_confirm' - else - counterpart = 'password' - if value is @get(counterpart) - if value.length < 8 - 'Passwords must contain at least 8 characters.' - else if not @passwordIsSecure(value) - 'Passwords must either contain at least one character that is not - in the printable ASCII range or else they must contain at least - one symbol, one digit, one uppercase letter, and one lowercase - letter.' - else - null - else - 'The “password” and “password confirm” values do not match.' - else - if @get 'id' - null - else - 'A password must be provided when creating a new user.' - - passwordIsSecure: (password) -> - if @containsNonASCIIChars password - true - else if @isHighEntropyASCII password - true - else - false - - containsNonASCIIChars: (password) -> - (c for c in password when c.charCodeAt(0) not in [32...127]).length > 0 - - # Returns `true` if the password has a lowercase character, an uppercase - # character, a digit and a symbol. - isHighEntropyASCII: (password) -> - /[a-z]/.test(password) and - /[A-Z]/.test(password) and - /[0-9]/.test(password) and - /[-!$%^&*()_+|~=`{}\[\]:";'<>?,./]/.test(password) - - validEmail: (value) -> - emptyError = @requiredString value - if emptyError - emptyError - else - emailIsValid = @utils.emailIsValid value - if emailIsValid - null - else - 'This does not appear to be a valid email address.' - - manyToOneAttributes: [ - 'input_orthography' - 'output_orthography' - ] - diff --git a/app/scripts/models/user.coffee b/app/scripts/models/user.coffee deleted file mode 100644 index f80e48f..0000000 --- a/app/scripts/models/user.coffee +++ /dev/null @@ -1,13 +0,0 @@ -define [ - 'underscore' - 'backbone' - './base' - './../utils/utils' -], (_, Backbone, BaseModel, utils) -> - - # User Model - # ------------ - # - - class UserModel extends BaseModel - diff --git a/app/scripts/routes/router.coffee b/app/scripts/routes/router.coffee deleted file mode 100644 index fa4b72f..0000000 --- a/app/scripts/routes/router.coffee +++ /dev/null @@ -1,39 +0,0 @@ -define [ - 'jquery', - 'backbone' - './../utils/utils' -], ($, Backbone, utils) -> - - class Workspace extends Backbone.Router - - initialize: (options) -> - @mainMenuView = options.mainMenuView - @resources = options.resources - @createResourceRoutes() - - createResourceRoutes: -> - for resourceName of @resources - do => - - resourcePlural = utils.pluralize resourceName - resourcePluCap = utils.capitalize resourcePlural - resourceCapitalized = utils.capitalize resourceName - - route1 = utils.camel2hyphen resourcePlural - methodName1 = "request#{resourcePluCap}Browse" - eventName1 = "request:#{resourcePlural}Browse" - @[methodName1] = => @mainMenuView.trigger eventName1 - @route route1, methodName1 - - route2 = "#{utils.camel2hyphen resourceName}/:resourceId" - methodName2 = "request#{resourceCapitalized}View" - eventName2 = "request:#{resourceCapitalized}View" - @[methodName2] = (resourceId) -> - Backbone.trigger eventName2, resourceId - @route route2, methodName2 - - routes: - 'home': 'home' - 'login': 'openLoginDialogBox' - 'register': 'openRegisterDialogBox' - diff --git a/app/scripts/utils/bibtex.coffee b/app/scripts/utils/bibtex.coffee deleted file mode 100644 index c5d9780..0000000 --- a/app/scripts/utils/bibtex.coffee +++ /dev/null @@ -1,129 +0,0 @@ -define [], -> - - # BibTeX Utilities - # ---------------- - # - # Logic for handling sources/bibliographic references in BibTeX format. - - class BibTeXUtils - - # Return a name-type BibTeX value (e.g., 'author', or 'editor') as a - # conjunction (with commas) of last names, e.g., "Mozart, Brahms and von - # Beethoven". - @getNameInCitationForm: (name) -> - parsedName = @parseBibTeXName name - if parsedName - @getLastNames parsedName - else - name - - # Given a parsed BibTeX name (an array of arrays of arrays), return a - # string of conjoined last names, e.g., "Smith, Yang, and Moore". - @getLastNames: (parsedName) -> - lastNamesArray = (author[2].join(' ') for author in parsedName) - switch lastNamesArray.length - when 1 - lastNamesArray[0] - when 2 - "#{lastNamesArray[0]} and #{lastNamesArray[1]}" - else - "#{lastNamesArray[...-1].join ', '} and - #{lastNamesArray[lastNamesArray.length - 1]}" - - # Return an array of arrays representing the parse of the BibTeX name. - # The parse is an array that contains a name-array, one for each name - # in the name string. Each name-array contains four part-arrays: - # part-array 1 is for first names, part-array 2 is for "von" names, - # part-array 3 is for last names, and part-array 4 is for "Jr." names. - # See http://nwalsh.com/tex/texhelp/bibtx-23.html. - # Note: this may not work exactly as BibTeX does it, but it should be good - # enough. - @parseBibTeXName: (input) -> - try - @parseBibTeXName_ input - catch - null - - @parseBibTeXName_: (input) -> - cleanPart = (part) -> - if part[0] is '{' and part[part.length - 1] is '}' - part[1...-1] - else - part - output = [] - names = input.split ' and ' - r = /// - \{[^\{\}]+\} | # sequence of non-braces between braces - [^\x20,]+ | # any non-space or non-comma (\x20 is space char) - , # comma - ///g - for name in names - authorOutput = [[], [], [], []] - parts = name.match r - commaCount = (p for p in parts when p is ',').length - switch commaCount - when 0 # Type 1: "First von Last" - vonPartSeen = false - for part, index in parts - part = cleanPart part - if index is 0 - authorOutput[0].push part - else if index is (parts.length - 1) - authorOutput[2].push part - else - if part is part.toLowerCase() - vonPartSeen = true - authorOutput[1].push part - else if vonPartSeen - authorOutput[2].push part - else - authorOutput[0].push part - when 1 # Type 2: "von Last, First" - section = 1 - for part, index in parts - part = cleanPart part - if part is ',' - section = 2 - else if section is 1 - if part is part.toLowerCase() - authorOutput[1].push part - else - authorOutput[2].push part - else - authorOutput[0].push part - when 2 # Type 3: "von Last, Jr, First" - section = 1 - for part, index in parts - part = cleanPart part - if part is ',' - section += 1 - else if section is 1 - if part is part.toLowerCase() - authorOutput[1].push part - else - authorOutput[2].push part - else if section is 2 - authorOutput[3].push part - else - authorOutput[0].push part - else - throw new Error("BibTeX name #{input} cannot be parsed") - output.push authorOutput - output - - # Return an "author" for supplied source object. We return the author in - # citation form if there is an author; otherwise we return the editor in - # citation form or the title or 'no author'. - @getAuthor: (sourceObject) -> - if sourceObject.author - BibTeXUtils.getNameInCitationForm sourceObject.author - else if sourceObject.editor - BibTeXUtils.getNameInCitationForm sourceObject.editor - else if sourceObject.title - sourceObject.title - else - 'no author' - - @getYear: (sourceObject) -> - if sourceObject.year then sourceObject.year else 'no year' - diff --git a/app/scripts/utils/cors.coffee b/app/scripts/utils/cors.coffee deleted file mode 100644 index 9883ea9..0000000 --- a/app/scripts/utils/cors.coffee +++ /dev/null @@ -1,159 +0,0 @@ -define [], -> - - # CORS Class - # ---------- - # - # For making CORS requests. - - class CORS - - vocal: false - - request: (options={}) -> - try - @_request options - catch error - if options.onerror - options.onerror error: error - else - console.log error - - updateProgress: (event) -> - percentComplete = (event.loaded / event.total * 100) - @progressModel.trigger 'uploadProgress', percentComplete - - # Perform a CORS request, sending JSON to and receiving JSON from a RESTful - # web service - _request: (options={}) -> - - url = options.url or throw new Error 'A URL is required for CORS requests' - method = options.method or 'GET' - timeout = options.timeout or undefined - contentType = options.contentType or 'application/json;charset=UTF-8' - monitorProgress = options.monitorProgress or false - @progressModel = options.progressModel or null - - [onload, onerror, onloadstart, onabort, onprogress, ontimeout, - onloadend] = @_getHandlers options, method, url - - xhr = @_getXHR url, method - if timeout then xhr.timeout = timeout - - if options.responseType - xhr.responseType = options.responseType - - xhr.withCredentials = true - - if monitorProgress and xhr.upload - xhr.upload.onprogress = (event) => @updateProgress event - - if contentType[...9] is 'multipart' - # This is a multipart/form-data request so we let the browser set the - # content-type header on its own. - payload = options.payload - else - # This is a JSON request (the default) so we set the content-type - # header ourselves and JSON-ify the payload. - # Note: apparently "You cannot add custom headers to an XDR object", cf. - # http://stackoverflow.com/questions/2657180/setting-headers-in-xdomainrequest-or-activexobjectmicrosoft-xmlhttp - xhr.setRequestHeader 'Content-Type', contentType - payload = JSON.stringify(options.payload) or "{}" - - xhr.send(payload) - - xhr.onload = onload - xhr.onerror = onerror - - xhr.onloadstart = onloadstart - xhr.onabort = onabort - xhr.onprogress = onprogress - xhr.ontimeout = ontimeout - xhr.onloadend = onloadend - - # Return a new XHR (cross-browser-wise) - # From http://www.html5rocks.com/en/tutorials/cors/ - _getXHR: (url, method) -> - xhr = new XMLHttpRequest() - # Check if the XMLHttpRequest object has a "withCredentials" property. - # "withCredentials" only exists on XMLHTTPRequest2 objects. - if 'withCredentials' of xhr - xhr.open method, url, true - # Otherwise, check if XDomainRequest. XDomainRequest only exists in IE, - # and is IE's way of making CORS requests. - else if typeof XDomainRequest isnt 'undefined' - xhr = new XDomainRequest() - xhr.open method, url - # Otherwise, CORS is not supported by the browser. - else - throw new Error 'CORS is not supported by this browser. Try Chrome or - Firefox' - xhr - - # Get default request handlers for those not supplied; also, modify some - # of the handlers so that they receive an object representation of the - # response body. - _getHandlers: (options, method, url) -> - - vocal = @vocal - - if 'parseJSON' of options - parseJSON = options.parseJSON - else - parseJSON = true - - onload = options.onload or do (vocal, method, url) -> - -> if vocal then console.log "Successful request to #{method} #{url}." - onload = @_jsonify onload, parseJSON - - onerror = options.onerror or do (vocal, method, url) -> - -> if vocal then console.log "Error requesting #{method} #{url}." - onerror = @_jsonify onerror, parseJSON - - # Default non-standard request handlers (JSON.parse response bodies?) - - onloadstart = options.onloadstart or do (vocal) -> - -> if vocal then console.log 'onloadstart: the request has started' - - onabort = options.onabort or do (vocal) -> - -> - if vocal - console.log 'onabort: the request has been aborted. For instance, by - invoking the abort() method.' - - onprogress = options.onprogress or do (vocal) -> - -> - if vocal - console.log 'onprogress: while loading and sending data.' - - ontimeout = options.ontimeout or do (vocal) -> - -> - if vocal - console.log 'ontimeout: author-specified timeout has passed before - the request could complete.' - - onloadend = options.onloadend or do (vocal) -> - -> - if vocal - console.log 'onloadend: the request has completed (either in success - or failure).' - - [onload, onerror, onloadstart, onabort, onprogress, ontimeout, onloadend] - - # Wrap a CORS XHR response event handler so that it receives the response - # as JSON as its first argument (and the XHR as its second). - _jsonify: (callback, parseJSON=true) -> - (xhrProgressEvent) -> - xhr = xhrProgressEvent.target # previously had `.currentTarget` ... - try - if parseJSON - responseJSON = JSON.parse xhr.responseText - else - responseJSON = xhr.responseText - catch error - try - responseJSON = xhr.responseText - catch error - responseJSON = xhr.response - callback responseJSON, xhr - - diff --git a/app/scripts/utils/globals.coffee b/app/scripts/utils/globals.coffee deleted file mode 100644 index dca1885..0000000 --- a/app/scripts/utils/globals.coffee +++ /dev/null @@ -1,216 +0,0 @@ -define [ - 'backbone' - './utils' -], (Backbone, utils) -> - - # This module returns a single `Backbone.Model` instance that is passed - # around in order to store global data. Special-purpose logic allows for - # arrays of resource objects to be stored and to react to alterations by - # issuing events that views can listen for. - # - # TODOs: - # - # - sort options (here or in views?) - # - add timestamps when modifying `globals` - # - centralize control of `globals` modification in this module - - class Globals extends Backbone.Model - - # This object will have the names of resources that we are tracking as its - # attributes. The values will be objects that contain metadata about the - # resource being tracked. These tracked resources are the resource - # collections that are needed globally to keep various views in sync. E.g., - # when a syntactic category is added or updated we want that change to - # be reflected in various interfaces that allow users to specify a category - # for another resource. - trackedResources: {} - - # Every time we set a set of resources on `globals`, we begin "tracking" - # changes to that resource collection, if we're not doing so already. - set: (key, val, options) -> - if utils.type(key) is 'object' - @track(key2, val2) for key2, val2 of key - else - @track key, val - super key, val, options - - # If we're not already tracking `resourceName`, then we begin tracking it - # by listening for update/add/delete events on it. - track: (resourceName, val) -> - if resourceName not of @trackedResources - if val.data.length > 0 - @trackedResources[resourceName] = {attributes: _.keys(val.data[0])} - else - @trackedResources[resourceName] = {} - - @listenForResourceChange resourceName - - # Listen for changes on this resource collection. E.g., add, update, or - # delete events. - listenForResourceChange: (resourceName) -> - - tmp = utils.capitalize( - utils.snake2camel(utils.singularize(resourceName))) - - addEvent = "add#{tmp}Success" - addMethod = (model) => - @addResource resourceName, model - @listenTo Backbone, addEvent, addMethod - - updateEvent = "update#{tmp}Success" - updateMethod = (model) => - @updateResource resourceName, model - @listenTo Backbone, updateEvent, updateMethod - - deleteEvent = "destroy#{tmp}Success" - deleteMethod = (model) => - @deleteResource resourceName, model - @listenTo Backbone, deleteEvent, deleteMethod - - # A new resource has been added. `resourceName` is the attribute name for - # that resource on this `globals` model; e.g., `@get(resourceName)` will - # return an array of elicitation method objects, if `resourceName` is - # `"elicitation_methods"`. - addResource: (resourceName, resourceModel) -> - resourceObject = @getResourceObjectFromModel resourceName, resourceModel - @get(resourceName).data.push resourceObject - @trigger "change:#{resourceName}" - - # An existing resource has been updated. - updateResource: (resourceName, resourceModel) -> - resourceObject = @getResourceObjectFromModel resourceName, resourceModel - resourcesArray = @get(resourceName).data - storedResourceObject = _.findWhere resourcesArray, {id: resourceObject.id} - for attr, val of resourceObject - storedResourceObject[attr] = val - @trigger "change:#{resourceName}" - - # An existing resource has been deleted. - deleteResource: (resourceName, resourceModel) -> - resourcesArray = @get(resourceName).data - for resourceObject, index in resourcesArray - try - if resourceObject.id is resourceModel.get('id') - resourcesArray.splice index, 1 - @trigger "change:#{resourceName}" - - # Return an object based on the BB model `resourceModel` that only has the - # attributes that we are tracking. - getResourceObjectFromModel: (resourceName, resourceModel) -> - try - attributes = @trackedResources[resourceName].attributes - catch - attributes = [] - resourceObject = {} - for attribute in attributes - resourceObject[attribute] = resourceModel.get attribute - resourceObject - - relatedResources: - collection: [ - 'speakers' - 'users' - 'tags' - 'sources' - 'collection_types' - 'markup_languages' - ] - - collection_search: [ - 'collection_search_parameters' - ] - - file: [ - 'tags' - 'speakers' - 'users' - 'utterance_types' - 'allowed_file_types' - ] - - file_search: [ - 'file_search_parameters' - ] - - form: [ - 'elicitation_methods' - 'grammaticalities' - 'sources' - 'speakers' - 'syntactic_categories' - 'tags' - 'users' - ] - - form_search: [ - 'form_search_parameters' - ] - - language_search: [ - 'language_search_parameters' - ] - - languageModel: [ - 'corpora' - 'morphologies' - 'toolkits' - # We add these "resources" client-side, cf. `fixToolkits` - 'smoothingAlgorithms' - 'orders' - 'booleans' - ] - - morphologicalParser: [ - 'morpheme_language_models' - 'phonologies' - 'morphologies' - ] - - morphology: [ - 'corpora' - 'script_types' - 'booleans' - ] - - oldApplicationSettings: [ - 'languages' - 'orthographies' - 'users' - ] - - page: [ - 'markup_languages' - ] - - search: [ - 'search_parameters' - 'form_search_parameters' - ] - - source: [ - 'types' - ] - - speaker: [ - 'markup_languages' - ] - - subcorpus: [ - 'form_searches' - 'users' - 'tags' - 'corpus_formats' - ] - - syntacticCategory: [ - 'syntactic_category_types' - ] - - user: [ - 'orthographies' - 'roles' - 'markup_languages' - ] - - new Globals() - diff --git a/app/scripts/utils/help.coffee b/app/scripts/utils/help.coffee deleted file mode 100644 index cf0b461..0000000 --- a/app/scripts/utils/help.coffee +++ /dev/null @@ -1,6 +0,0 @@ -define [], -> - - views_forms_browse: - title: "Forms Browse Interface" - content: "this is the help text for the forms-browse view." - diff --git a/app/scripts/utils/indexeddb-utils.coffee b/app/scripts/utils/indexeddb-utils.coffee deleted file mode 100644 index c17730d..0000000 --- a/app/scripts/utils/indexeddb-utils.coffee +++ /dev/null @@ -1,213 +0,0 @@ -# Functionality for interacting with IndexedDB. -# -# Based heavily upon Matt West's IndexedDB example todo application: -# -# http://blog.teamtreehouse.com/create-your-own-to-do-app-with-html5-and-indexeddb -# -# Defines a FieldDBIDB class that simplifies IndexedDB interactions. -# -# See also the IndexedDB API spec: -# -# http://www.w3.org/TR/IndexedDB/ -# -# API to implement (basically a rewrite of the OLD's Atom-based API) -# search {query: {filter: [], order_by: []}, paginator: {}} -# new_search -# index (get all; order_by and pagination params, optional) -# create :param Object form: -# update :param Number id: :param Object form: -# delete :param Number id: -# show :param Number id: - -define (require) -> - - {clone} = require './utils' - - # A wrapper around IndexedDB with FieldDB conveniences. - class FieldDBIDB - - # :param String id: name of the IndexedDB database as defined in - # `models/database`. - # :param String version: IDB version, for migrations. - constructor: (@id, @version) -> - @indexedDB = window.indexedDB or window.webkitIndexedDB or \ - window.mozIndexedDB or window.msIndexedDB - @IDBKeyRange = window.IDBKeyRange or window.webkitIDBKeyRange - @datastore = null - - s4: -> - (((1 + Math.random()) * 0x10000) | 0).toString(16).substring 1 - - guid: -> - "#{@s4()}#{@s4()}-#{@s4()}-#{@s4()}-#{@s4()}-#{@s4()}#{@s4()}#{@s4()}" - - # Open a connection to the datastore - open: (handler) -> - if @datastore instanceof IDBDatabase - return handler.onsuccess() - request = @indexedDB.open @id, @version - - # Handle datastore upgrades. - # TODO: write tests to handle upgrades - request.onupgradeneeded = (event) -> - db = event.target.result - event.target.transaction.onerror = handler.onerror - if db.objectStoreNames.contains 'forms' - db.deleteObjectStore 'forms' - store = db.createObjectStore 'forms' - - request.onsuccess = (event) => - @datastore = event.target.result - handler.onsuccess() - request.onerror = handler.onerror - - defaultHandler: - onsuccess: (->) - onerror: (->) - - # Retrieve a single item via its key. - show: (key, handler, options) -> - @open @defaultHandler - transaction = @datastore.transaction [options.storeName], 'readwrite' - objStore = transaction.objectStore options.storeName - request = objStore.get key - request.onsuccess = (event) -> - handler.onsuccess event.target.result - request.onerror = handler.onerror - - # Retrieve all items from a given store - # TODO: allow for order_by and pagination options. - index: (handler, options) -> - @open @defaultHandler - transaction = @datastore.transaction [options.storeName], 'readwrite' - objStore = transaction.objectStore options.storeName - keyRange = @IDBKeyRange.lowerBound 0 - cursorRequest = objStore.openCursor keyRange - items = [] - - transaction.oncomplete = (event) -> - handler.onsuccess items - - cursorRequest.onsuccess = (event) -> - result = event.target.result - if not result - return - items.push result.value - result.continue() - - cursorRequest.onerror = handler.onerror - - # Create a new item. - # :param Object itemObject: the item data. - create: (itemObject, handler, options) -> - @open( - onsuccess: => - transaction = @datastore.transaction [options.storeName], 'readwrite' - objStore = transaction.objectStore options.storeName - itemObject.id = @guid() - request = objStore.put itemObject, itemObject.id - request.onsuccess = -> - handler.onsuccess itemObject - request.onerror = handler.onerror - onerror: handler.onerror - ) - - # Update an existing item - # Note: error if `key` does not correspond to an existing item. - update: (key, newObject, handler, options) -> - @open @defaultHandler - @open( - onsuccess: => - transaction = @datastore.transaction [options.storeName], 'readwrite' - objStore = transaction.objectStore options.storeName - getRequest = objStore.get key - getRequest.onsuccess = (event) -> - objectToEnter = _.extend event.target.result, newObject - putRequest = objStore.put objectToEnter, key - putRequest.onsuccess = -> - handler.onsuccess objectToEnter - putRequest.onerror = -> - handler.onerror "Update request failed" - getRequest.onerror = -> - handler.onerror "There is no #{options.storeName} with key #{key}" - onerror: handler.onerror - ) - - # Delete an item. - # :param Object itemObject: the form data. - delete: (key, handler, options) -> - @open @defaultHandler - transaction = @datastore.transaction [options.storeName], 'readwrite' - objStore = transaction.objectStore options.storeName - request = objStore.delete key - request.onsuccess = handler.onsuccess - request.onerror = handler.onerror - - # Search across forms. - # TODO: model the query object on the OLD's search API - search: (query, handler, options) -> - @open @defaultHandler - - # Delete entire indexedDB database. - # FIXME: this messes up the indexedDB database in ways I don't understand... - # WARNING: do not use this! - deleteDatabase: (callback) -> - try - request = @indexedDB.deleteDatabase @id - request.onsuccess = (event) -> - db = event.result - callback true - request.onerror = (event) -> - console.error "indexedDB.delete Error: #{event.message}" - callback false - catch e - console.error "Error: #{e.message}" - # prefer change id of database than to start on new instance - @id = @id + '.' + @guid() - callback false - - - # An form store-specific interface to a FieldDBIDB instance. - class FormStore - - # Requires a FieldDBIDB instance - constructor: (@db) -> - - # Create a new form. - # :param Object formObject: the form data. - create: (formObject, handler, options) -> - options ?= {} - options.storeName = 'forms' - @db.create formObject, handler, options - - # Get all forms. - index: (handler, options) -> - options ?= {} - options.storeName = 'forms' - @db.index handler, options - - # Get a form by id. - show: (formId, handler, options) -> - options ?= {} - options.storeName = 'forms' - @db.show formId, handler, options - - # Update a form. - update: (formId, formObject, handler, options) -> - options ?= {} - options.storeName = 'forms' - @db.update formId, formObject, handler, options - - # Delete a form. - delete: (formId, handler, options) -> - options ?= {} - options.storeName = 'forms' - @db.delete formId, handler, options - - search: (query, handler, options) -> - - - # The Object that we export. - FieldDBIDB: FieldDBIDB - FormStore: FormStore - diff --git a/app/scripts/utils/keyboard-shortcuts.coffee b/app/scripts/utils/keyboard-shortcuts.coffee deleted file mode 100644 index 181da8c..0000000 --- a/app/scripts/utils/keyboard-shortcuts.coffee +++ /dev/null @@ -1,95 +0,0 @@ -define [], -> - - # Application-wide keyboard shortcuts are defined here. `MainMenuView` uses - # this array of objects in its `keyboardShortcuts` method. - # - # Note: many of these are arbitrary and are just a convenience during - # development. It may be a good idea to remove some of these shortcuts and/or - # make the shortcuts completely user-configurable. - - [ - shortcut: 'ctrl+,' - event: 'request:applicationSettingsBrowse' - , - shortcut: 'ctrl+h' - event: 'request:home' - , - shortcut: 'ctrl+p' - event: 'request:pagesBrowse' - , - shortcut: 'alt+c' - old: - event: 'request:subcorporaBrowse' - fielddb: - event: 'request:corporaBrowse' - , - shortcut: 'ctrl+r' - event: 'request:openRegisterDialogBox' - , - shortcut: 'ctrl+a' - event: 'request:formAdd' - , - shortcut: 'ctrl+s' - event: 'request:searchesBrowse' - , - shortcut: 'ctrl+b' - event: 'request:formsBrowse' - , - shortcut: 'alt+x' - event: 'request:phonologiesBrowse' - , - shortcut: 'ctrl+m' - event: 'request:morphologiesBrowse' - , - shortcut: 'ctrl+z' - event: 'request:languageModelsBrowse' - , - shortcut: 'ctrl+y' - event: 'request:morphologicalParsersBrowse' - , - shortcut: 'ctrl+?' - event: 'request:toggleHelpDialogBox' - , - shortcut: 'ctrl+l' - event: 'request:openLoginDialogBox' - , - shortcut: 'ctrl+t' - event: 'request:toggleTasksDialog' - , - shortcut: 'ctrl+u' - event: 'request:usersBrowse' - , - shortcut: 'ctrl+e' - event: 'request:elicitationMethodsBrowse' - , - shortcut: 'ctrl+g' - event: 'request:tagsBrowse' - , - shortcut: 'alt+v' - event: 'request:syntacticCategoriesBrowse' - , - shortcut: 'ctrl+n' - event: 'request:languagesBrowse' - , - shortcut: 'ctrl+o' - event: 'request:orthographiesBrowse' - , - shortcut: 'ctrl+k' - event: 'request:speakersBrowse' - , - shortcut: 'ctrl+j' - event: 'request:sourcesBrowse' - , - shortcut: 'ctrl+i' - event: 'request:collectionsBrowse' - , - shortcut: 'ctrl+f' - event: 'request:filesBrowse' - , - shortcut: 'ctrl+q' - event: 'request:formsImport' - , - shortcut: 'alt+k' - event: 'request:keyboardsBrowse' - ] - diff --git a/app/scripts/utils/paginator.coffee b/app/scripts/utils/paginator.coffee deleted file mode 100644 index 7af19bc..0000000 --- a/app/scripts/utils/paginator.coffee +++ /dev/null @@ -1,137 +0,0 @@ -define ['./../utils/utils'], (utils) -> - - # Paginator - # --------- - # - # Holds attributes and logic for manipulating pagination. - - class Paginator - - constructor: ( - @page = 1 - @items = 0 - @itemsPerPage = 10 - @possibleItemsPerPage = @_defaultPossibleItemsPerPage) -> - - # computed attributes - @itemsDisplayed = 0 - @pages = 1 - @start = 0 - @end = 1 - - @setItemsCalled = false - - # Public methods - - setItems: (newItems) -> - @items = newItems - @setItemsCalled = true - @_refresh() - - # Set `itemsPerPage` manually. - # If you manually change the items per page, things become a bit complicated. - # You want your page to start to be near its current value, but you also - # need it to be a multiple of `itemsPerPage`. This method handles these details - # of readjusting the paginator given a new items per page. - setItemsPerPage: (newItemsPerPage) -> - [@start, @page] = @_getClosestStartValueGivenNewItemsPerPage( - newItemsPerPage) - @itemsPerPage = newItemsPerPage - @_setEnd() - @_setPages() - @_setItemsDisplayed() - - setPage: (newPage) -> - @page = newPage - @_refresh() - - setPageToFirst: -> - @page = 1 - @_refresh() - - setPageToPrevious: -> - if (@page - 1) > 0 - @page = @page - 1 - @_refresh() - - setPageToNext: -> - if (@page + 1) <= @pages - @page = @page + 1 - @_refresh() - - setPageToLast: -> - @page = @pages - @_refresh() - - incrementPage: (n) -> - @page = @page + n - if @page > @pages then @page = @pages - @_refresh() - - decrementPage: (n) -> - @page = @page - n - if @page < 1 then @page = 1 - @_refresh() - - setPossibleItemsPerPage: (newPossibleItemsPerPage) -> - if utils.type newPossibleItemsPerPage is 'array' - @possibleItemsPerPage = newPossibleItemsPerPage - else - @possibleItemsPerPage = @_defaultPossibleItemsPerPage - - # Private methods - - _refresh: -> - @_setPages() - @_setPage() - @_setStart() - @_setEnd() - @_setItemsDisplayed() - - _setStart: -> - @start = (@page - 1) * @itemsPerPage - - _setEnd: -> - end = @start + @itemsPerPage - 1 - if end >= @items - @end = @items - 1 - else - @end = end - - _setPages: -> - @pages = Math.ceil(@items / @itemsPerPage) - - _setPage: -> - if @page > @pages then @page = @pages - if @page is 0 then @page = 1 - - _setItemsDisplayed: -> - if @itemsPerPage > @items - @itemsDisplayed = @items - else - @itemsDisplayed = @itemsPerPage - - _defaultPossibleItemsPerPage: [1, 2, 3, 5, 10, 25, 50, 100] - - # Return the highest multiple of `newItemsPerPage` such that it is less than - # or equal to the current `start` value. Also return the `page` value that - # corresponds to that `start` value. Returns an array of two integers. - _getClosestStartValueGivenNewItemsPerPage: (newItemsPerPage) -> - possibleStartValues = @_getPossibleStartValuesGivenNewItemsPerPage(newItemsPerPage) - currentStartValue = @start - tmp = (v for v in possibleStartValues when v <= currentStartValue) - if tmp.length - [tmp[tmp.length - 1], tmp.length] - else - [0, 1] - - # Return the multiples of newItemsPerPage that are less than the number of - # items in the paginator. - _getPossibleStartValuesGivenNewItemsPerPage: (newItemsPerPage) -> - possibleStartValues = [] - possibleStartValue = 0 - while possibleStartValue < @items - possibleStartValues.push possibleStartValue - possibleStartValue = possibleStartValue + newItemsPerPage - possibleStartValues - diff --git a/app/scripts/utils/tooltips.coffee b/app/scripts/utils/tooltips.coffee deleted file mode 100644 index dd4550c..0000000 --- a/app/scripts/utils/tooltips.coffee +++ /dev/null @@ -1,1268 +0,0 @@ -define ['./utils'], (utils) -> - - # Tooltips defined here, in one place. These are the HTML "title" attributes - # that are transformed into jQueryUI tooltips. Consolidating them here is - # good for future translation/localization, etc. - # - # This module returns a function that, when passed in a "dot notation" string - # will return the appropriate tooltip function (or a default one, if the - # requested tooltip does not exist. For example, to get the tooltip for the - # syntactic category field of OLD forms: - # - # tooltips('old.forms.syntactic_category')() # - # - # Some tooltips are functions that take an `options` argument. These functions - # (generally) expect a `options.value` attribute that is the value of the - # item being "tooltipped". Thus, if you pass a `Date` instance to the - # `dateElicited` tooltip function, it will be used in the returned tooltip: - # - # tooltips('fieldDB.forms.dateEntered') value: new Date() - - dateElicited = - eng: (options) -> - if options.value - "This form was elicited on #{utils.humanDate options.value}" - else - 'The date this form was elicited' - - datetimeEntered = - eng: (options) -> - if options.value and options.resourceName - "This #{utils.camel2regular(utils.singularize(options.resourceName))} - was entered on #{utils.humanDatetime options.value}" - else - "The date and time when this resource was entered" - - generateSucceeded = - eng: (options) -> - if 'value' of options - if options.value is true - "The most recent attempt to generate a FST script for this - #{utils.camel2regular(utils.singularize(options.resourceName))} was - successful." - else - "The most recent attempt to generate a FST script for this - #{utils.camel2regular(utils.singularize(options.resourceName))} was - NOT successful." - else - 'Will be true if the last generate attempt succeeded; false otherwise.' - - compileSucceeded = - eng: (options) -> - if 'value' of options - if options.value is true - "The most recent attempt to compile the FST script of this - #{utils.camel2regular(utils.singularize(options.resourceName))} was - successful." - else - "The most recent attempt to compile the FST script for this - #{utils.camel2regular(utils.singularize(options.resourceName))} was - NOT successful." - else - 'Will be true if the last generate attempt succeeded; false otherwise.' - - compileMessage = - eng: (options) -> - "The message returned by the OLD after an attempt to compile this - #{utils.camel2regular(utils.singularize(options.resourceName))}." - - compileAttempt = - eng: (options) -> - "A unique value (a UUID) created by the OLD after each attempt to compile - a #{utils.camel2regular(utils.singularize(options.resourceName))}." - - datetimeModified = - eng: (options) -> - if options.value and options.resourceName - "This #{utils.camel2regular(utils.singularize(options.resourceName))} - was last modified on #{utils.humanDatetime options.value}" - else - "The date and time when this resource was last modified" - - enterer = - eng: (options) -> - if options.resourceName - tmp = utils.camel2regular(utils.singularize(options.resourceName)) - else - tmp = 'resource' - "The user who entered/created this #{tmp}. This value is - specified automatically by the application." - - modifier = - eng: (options) -> - if options.resourceName - tmp = utils.camel2regular(utils.singularize(options.resourceName)) - else - tmp = 'resource' - "The user who made the most recent modification to this #{tmp}. This - value is specified automatically by the application." - - name = - eng: (options) -> - if options.resourceName - resourceName = - utils.camel2regular(utils.singularize(options.resourceName)) - resourceNamePlural = utils.camel2regular(options.resourceName) - else - resourceName = 'resource' - resourceNamePlural = 'resources' - "A name for the #{resourceName}. Each #{resourceName} must have a name and - it must be unique among #{resourceNamePlural}." - - description = - eng: (options) -> - if options.resourceName - "A description of the - #{utils.camel2regular(utils.singularize(options.resourceName))}." - else - "A description of the resource." - - id = - eng: (options) -> - "The id of the - #{utils.camel2regular(utils.singularize(options.resourceName))}. This is - an integer generated by the relational database that is used by the OLD. - This value can be used to uniquely identify the - #{utils.camel2regular(utils.singularize(options.resourceName))}." - - uuid = - eng: (options) -> - "The UUID (universally unique identifier) of the - #{utils.camel2regular(utils.singularize(options.resourceName))}. This is - a unique value generated by the OLD. It is used to create references - between #{utils.camel2regular(options.resourceName)} and their previous - versions." - - tooltips = - - fieldDB: - - forms: - - dateElicited: dateElicited - - dateEntered: - eng: (options) -> - "This form was entered on #{utils.humanDatetime options.value}" - - dateModified: - eng: (options) -> - "This form was last modified on #{utils.humanDatetime options.value}" - - id: - eng: "A unique identifier for this form (a UUID)." - - comments: - eng: "Any user with the “commenter” role may add comments to a form. - The date and time the comment was made and the commenter are - automatically saved when a comment is created." - - text: - eng: "The content of the comment." - - old: - - keyboardPreferenceSets: - - system_wide_keyboard: - eng: 'The keyboard that you want to use, by default, when entering - data into any input field.' - - transcription_keyboard: - eng: 'The keyboard that you want to use when entering data into the - transcription fields of forms.' - - phonetic_transcription_keyboard: - eng: 'The keyboard that you want to use when entering data into the - phonetic transcription fields of forms.' - - narrow_phonetic_transcription_keyboard: - eng: 'The keyboard that you want to use when entering data into the - narrow phonetic transcription fields of forms.' - - morpheme_break_keyboard: - eng: 'The keyboard that you want to use when entering data into the - morpheme break fields of forms.' - - keyboards: - name: name - description: description - id: id - datetime_modified: datetimeModified - enterer: enterer - modifier: modifier - datetime_entered: datetimeEntered - - keyboard: - eng: "A mapping from keyboard key codes to Unicode characters or - strings." - - parserTaskSets: - - transcription_parser: - eng: '“Transcription parser”: the morphological parser that you - use to parse transcription values.' - - phonetic_transcription_parser: - eng: '“Phonetic transcription parser”: the morphological parser - that you use to parse phonetic transcription values.' - - narrow_phonetic_transcription_parser: - eng: '“Narrow phonetic transcription parser”: the morphological - parser that you use to parser narrow phonetic transcription values.' - - to_transcription_phonology: - eng: '“To transcription phonology”: the phonology that you use to - generate transcriptions from morphological analyses.' - - to_phonetic_transcription_phonology: - eng: '“To phonetic transcription phonology”: the phonology that - you use to generate phonetic transcriptions from morphological - analyses.' - - to_narrow_phonetic_transcription_phonology: - eng: '“To narrow phonetic transcription phonology”: the phonology - that you use to generate narrow phonetic transcriptions from - morphological analyses.' - - recognizer_morphology: - eng: '“Recognizer morphology”: the morphology that you use to - recognize (i.e., validate) morphological analyses.' - - pages: - - heading: - eng: 'The text of the primary heading of this page.' - - markup_language: - eng: 'The markup language (“Markdown” or - “reStructuredText”) that is used to generate HTML from the - “content” value of this page.' - - content: - eng: 'This is text that defines the page in Dative; - use markup conventions from the selected “markup language” - in this field and the output will be rendered as HTML.' - - html: - eng: 'The HTML of the page; this is generated from the - “content” value using the specified “markup language”.' - - name: name - id: id - datetime_modified: datetimeModified - - - speakers: - first_name: - eng: 'The first (given) name of this speaker.' - - last_name: - eng: 'The last name (surname) of this speaker.' - - dialect: - eng: 'This speaker’s dialect.' - - markup_language: - eng: 'The markup language (“Markdown” or - “reStructuredText”) that is used to generate HTML from the - speaker’s “page content” value.' - - page_content: - eng: 'This is text that defines the speaker’s page in Dative; - use markup conventions from the selected “markup language” - in this field and the output will be rendered as HTML.' - - html: - eng: 'The HTML of the speaker’s page; this is generated from the - “page content” using the specified “markup language”.' - - id: id - datetime_modified: datetimeModified - - collections: - - title: - eng: 'A title for the collection.' - - description: description - - type: - eng: 'The type of this collection, one of “story”, - “elicitation”, “paper”, “discourse”, or “other”.' - - url: - eng: 'The URL path that can be used to navigate to this collection.' - - markup_language: - eng: 'The markup language (“Markdown” or - “reStructuredText”) that is used to generate HTML from the - “contents” value.' - - html: - eng: "The HTML generated from the “contents” value using the - specified “markup language”." - - contents: - eng: "The string of lightweight markup and references to forms that - defines the contents of this collection." - - source: - eng: "The textual source (e.g., research paper, text collection, book - of learning materials) from which the collection was drawn, if - applicable." - - speaker: - eng: "The speaker (consultant) with whom this collection was - elicited, if appropriate." - - elicitor: - eng: 'The person who elicited this collection, if appropriate.' - - date_elicited: - eng: (options) -> - if options.value - "This collection was elicited on #{utils.humanDate options.value}" - else - 'The date this collection was elicited' - - tags: - eng: "Tags for categorizing collections. (These are the same tags that - are used throughout an OLD application; i.e., the same tag can be - used to categorize a form and a collection.)" - - files: - eng: "File resources that are associated to this collection." - - enterer: enterer - modifier: modifier - datetime_entered: datetimeEntered - datetime_modified: datetimeModified - UUID: uuid - id: id - - sources: - - file: - eng: 'A file resource (e.g., a PDF) containing a digital - representation of this source.' - - crossref_source: - eng: 'Another source model for cross-referencing.' - - crossref: - eng: 'The “key” value of another source to be cross-referenced. Any - attribute values that are missing from the source model are - inherited from the source cross-referenced via this attribute.' - - type: - eng: 'The BibTeX entry type, e.g., “article”, “book”, etc. A - valid type value is obligatory for all source models. The chosen - type value will determine which other attributes must also possess - non-empty values.' - - key: - eng: 'The BibTeX key, i.e., the unique string used to unambiguously - identify a source. E.g., “chomsky57”.' - - address: - eng: 'Usually the address of the publisher or other type of - institution.' - - annote: - eng: 'An annotation. It is not used by the standard bibliography - styles, but may be used by others that produce an annotated - bibliography.' - - author: - eng: 'The name(s) of the author(s), in the format described in Kopka - and Daly (2004), i.e., either Given Names Surname or Surname, Given - Names. For multiple authors, use the formats just specified and - separate each such formatted name by the word “and”.' - - booktitle: - eng: 'Title of a book, part of which is being cited. See Kopka and - Daly (2004) for details on how to type titles. For book entries, - use the title field instead.' - - chapter: - eng: 'A chapter (or section or whatever) number.' - - edition: - eng: 'The edition of a book—for example, “Second”. This should - be an ordinal, and should have the first letter capitalized, as - shown here; the standard styles convert to lower case when - necessary.' - - editor: - eng: 'Name(s) of editor(s), typed as indicated in Kopka and Daly - (2004). At its most basic, this means either as Given Names Surname - or Surname, Given Names and using “and” to separate multiple - editor names. If there is also a value for the author attribute, - then the editor attribute gives the editor of the book or - collection in which the reference appears.' - - howpublished: - eng: 'How something has been published. The first word should be - capitalized.' - - institution: - eng: 'The sponsoring institution of a technical report.' - - journal: - eng: 'A journal name. Abbreviations are provided for many journals.' - - key_field: - eng: 'Used for alphabetizing, cross referencing, and creating a label - when the author information is missing. This field should not be - confused with the source’s key attribute.' - - month: - eng: 'The month in which the work was published or, for an - unpublished work, in which it was written.' - - note: - eng: 'Any additional information that can help the reader. The first - word should be capitalized.' - - number: - eng: 'The number of a journal, magazine, technical report, or of a - work in a series. An issue of a journal or magazine is usually - identified by its volume and number; the organization that issues a - technical report usually gives it a number; and sometimes books are - given numbers in a named series.' - - organization: - eng: 'The organization that sponsors a conference or that publishes a - manual.' - - pages: - eng: 'One or more page numbers or range of numbers, such as 42–111 - or 7,41,73–97 or 43+ (the “+” in this last example indicates - pages following that don’t form a simple range).' - - publisher: - eng: 'The publisher’s name.' - - school: - eng: 'The name of the school where a thesis was written.' - - series: - eng: 'The name of a series or set of books. When citing an entire - book, the title attribute gives its title and an optional series - attribute gives the name of a series or multi-volume set in which - the book is published.' - - title: - eng: 'The work’s title, typed as explained in Kopka and Daly (2004).' - - type_field: - eng: 'The type of a technical report—for example, “Research Note”.' - - url: - eng: 'The universal resource locator for online documents; this is - not standard but supplied by more modern bibliography styles.' - - volume: - eng: 'The volume of a journal or multi-volume book.' - - year: - eng: 'The year of publication or, for an unpublished work, the year - it was written. Generally it should consist of four numerals, such - as 1984.' - - affiliation: - eng: 'The author’s affiliation.' - - abstract: - eng: 'An abstract of the work.' - - contents: - eng: 'A table of contents.' - - copyright: - eng: 'Copyright information.' - - ISBN: - eng: 'The International Standard Book Number.' - - ISSN: - eng: 'The International Standard Serial Number. Used to identify a - journal.' - - keywords: - eng: 'Key words used for searching or possibly for annotation.' - - language: - eng: 'The language the document is in.' - - location: - eng: 'A location associated with the entry, such as the city in which - a conference took place.' - - LCCN: - eng: 'The Library of Congress Call Number.' - - mrnumber: - eng: 'The Mathematical Reviews number.' - - price: - eng: 'The price of the document.' - - size: - eng: 'The physical dimensions of a work.' - - id: id - datetime_modified: datetimeModified - - orthographies: - name: name - datetime_modified: datetimeModified - id: id - - orthography: - eng: 'A comma-delimited sequence of characters that defines the - graphemes/polygraphs of this orthography.' - - lowercase: - eng: 'When set to “true” (the default), the system assumes that only - lowercase graphemes are used in this orthography. When set to - “false”, the system tries to guess uppercase alternants for the - graphemes in this orthography.' - - initial_glottal_stops: - eng: 'When set to “true” (the default), the system assumes that - glottal stops are written (overtly) at the beginning of a word in - this orthography. When set to “false”, the system removes - initial glottal stops when translating strings into this - orthography.' - - languages: - datetime_modified: datetimeModified - - Id: - eng: 'The three-letter 639-3 identifier.' - - Part2B: - eng: 'Equivalent 639-2 identifier of the bibliographic applications - code set, if there is one.' - - Part2T: - eng: 'Equivalent 639-2 identifier of the terminology applications - code set, if there is one.' - - Part1: - eng: 'Equivalent 639-1 identifier, if there is one.' - - Scope: - eng: 'I(ndividual), M(acrolanguage), or S(pecial).' - - Type: - eng: 'A(ncient), C(onstructed), E(xtinct), H(istorical), L(iving), or - S(pecial).' - - Ref_Name: - eng: 'Reference language name.' - - Comment: - eng: 'Comment relating to one or more of the columns.' - - syntacticCategories: - name: name - description: description - id: id - datetime_modified: datetimeModified - - type: - eng: "The type of syntactic category; one of “lexical”, - “phrasal” or “sentential”." - - oldApplicationSettingses: - - id: id - datetime_modified: datetimeModified - - object_language_name: - eng: 'The name of the language that is being documented and analyzed - by means of this OLD web service. This may be the ISO 639-3 - “reference name” but this is not required.' - - object_language_id: - eng: 'The three-letter ISO 639-3 identifier for the language that is - being documented and analyzed by means of this OLD web service.' - - metalanguage_name: - eng: 'The name of the language that is being used to translate, - document and analyze the object language. This may be the ISO 639-3 - “reference name” but this is not required.' - - metalanguage_id: - eng: 'The three-letter ISO 639-3 identifier for the language that is - being used to translate, document and analyze the object language.' - - metalanguage_inventory: - eng: 'A comma-delimited list of graphemes that should be used when - writing in the metalanguage.' - - orthographic_validation: - eng: 'How to validate user input in the “transcription” field. - “None” means no validation. “Warning” means a warning is - generated when a user tries invalid input. “Error” means - invalid input is forbidden.' - - narrow_phonetic_inventory: - eng: 'A comma-delimited list of graphemes - that should be used when entering data into the narrow phonetic - transcription field.' - - narrow_phonetic_validation: - eng: 'How to validate user input in the “narrow phonetic - transcription” field. “None” means no validation. - “Warning” means a warning is generated when a user tries - invalid input. “Error” means invalid input is forbidden.' - - broad_phonetic_inventory: - eng: 'A comma-delimited list of graphemes - that should be used when entering data into the phonetic - transcription field.' - - broad_phonetic_validation: - eng: 'How to validate user input in the “phonetic - transcription” field. “None” means no validation. - “Warning” means a warning is generated when a user tries - invalid input. “Error” means invalid input is forbidden.' - - morpheme_break_is_orthographic: - eng: 'The morpheme break is orthographic means that the morpheme - break field should be validated against the storage orthography. - If the morpheme break is not orthographic, that means that it - should be validated against the phonemic inventory.' - - morpheme_break_validation: - eng: 'How to validate user input in the “morpheme - break” field. “None” means no validation. - “Warning” means a warning is generated when a user tries - invalid input. “Error” means invalid input is forbidden.' - - phonemic_inventory: - eng: 'A comma-delimited list of phonemes/graphemes that should be - used when entering data into the morpheme break field (assuming - “morpheme break is orthographic” is set to false).' - - morpheme_delimiters: - eng: 'A comma-delimited list of delimiter characters that should be - used to separate morphemes in the morpheme break field and morpheme - glosses in the morpheme gloss field.' - - punctuation: - eng: 'A string of punctuation characters that should define, along - with the graphemes in the storage orthography, the licit strings in - the transcription field.' - - grammaticalities: - eng: 'A comma-delimited list of characters that will define the - options in the grammaticality fields. Example: “*,?,#”.' - - storage_orthography: - eng: 'The orthography that transcription values should be stored in. - This orthography may affect how orthographic validation works - and/or how orthography conversion works.' - - input_orthography: - eng: 'The orthography that transcription values should be entered in. - If specified and if different from the storage orthography, then the - system should convert user input in the input orthography to - strings in the storage orthography.' - - output_orthography: - eng: 'The orthography that transcription values should be displayed in. - If specified and if different from the storage orthography, then the - system should convert stored data in the storage orthography to - strings in the output orthography.' - - unrestricted_users: - eng: 'A list of users that the OLD server considers to be - “unrestricted”. These users are able to access data that has - been tagged with the “restricted” tag.' - - tags: - name: name - description: description - id: id - datetime_modified: datetimeModified - - elicitationMethods: - name: name - description: description - id: id - datetime_modified: datetimeModified - - users: - id: id - first_name: - eng: 'The first (given) name of this user.' - - last_name: - eng: 'The last name (surname) of this user.' - - email: - eng: 'The user’s email address.' - - role: - eng: 'The user’s role, which determines their level of access: one - of “administrator”, “contributor”, or “viewer”.' - - username: - eng: 'The user’s username, a unique identifier.' - - password: - eng: 'The user’s password. Leave this field blank when updating a - user and their password will be left unchanged.' - - password_confirm: - eng: 'Retype the user’s password here.' - - affiliation: - eng: 'The academic institution, First Nation, museum, etc. that the - user is affiliated with.' - - markup_language: - eng: 'The markup language—Markdown or - reStructuredText—that will be used to generate HTML from the - user’s “page content”.' - - page_content: - eng: 'This is text that defines the user’s page in Dative; users - may use markup conventions from the selected “markup language” - in this field and the output will be rendered as HTML.' - - html: - eng: 'The HTML of the user’s page; this is generated from the - “page content” using the specified “markup language”.' - - datetime_modified: datetimeModified - - files: - id: id - description: description - datetime_entered: datetimeEntered - datetime_modified: datetimeModified - enterer: enterer - modifier: modifier - - tags: - eng: "Tags for categorizing files. (These are the same tags that - are used throughout an OLD application; i.e., the same tag can be - used to categorize a form and a file.)" - - filename: - eng: 'The name of the file.' - - name: - eng: 'The name of the file; relevant for externally hosted files or - subinterval files.' - - lossy_filename: - eng: 'The name given to the reduced-size copy that was made of this - file.' - - size: - eng: (options) -> - "The size of the file (#{utils.integerWithCommas options.value} - bytes)" - - MIME_type: - eng: "The type of the file; technically, this is the MIME - (Multipurpose Internet Mail Extensions) type or Internet media - type." - - utterance_type: - eng: 'If this file represents an utterance, then this value indicates - whether that utterance is in the object language, the metalanguage, - or both.' - - speaker: - eng: 'The speaker of the content encoded in this file, if relevant.' - - elicitor: - eng: 'The person who elicited/recorded this file, if appropriate.' - - date_elicited: - eng: (options) -> - if options.value - "This file was elicited on #{utils.humanDate options.value}" - else - 'The date this file was elicited' - - url: - eng: "The URL where this file’s data are stored." - - password: - eng: "The password needed to access this file’s data on the external - server, if needed." - - parent_file: - eng: "The audio or video file that this (subinterval-referencing) - file refers to for its file data." - - start: - eng: "The time in the parent file where this file’s data begins." - - end: - eng: "The time in the parent file where this file’s data ends." - - dative_file_type: - eng: "Indicates whether the file’s data are stored on the server, - are hosted elsewhere, or are a subinterval of another “parent” - file." - - file_data: - eng: "Click this button to choose a file for upload." - - morphologicalParsers: - id: id - UUID: uuid - name: name - description: description - datetime_entered: datetimeEntered - datetime_modified: datetimeModified - enterer: enterer - modifier: modifier - generate_succeeded: generateSucceeded - compile_succeeded: compileSucceeded - - phonology: - eng: 'The phonology of the morphological parser.' - - morphology: - eng: 'The morphology of the morphological parser.' - - language_model: - eng: 'The language model of the morphological parser.' - - generate_message: - eng: 'The message returned by the OLD after an attempt to generate - this morphological parser based on the values specified here.' - - generate_attempt: - eng: 'A unique value (a UUID) created by the OLD after each attempt - to generate a morphological parser based on the values specified - here.' - - compile_message: compileMessage - compile_attempt: compileAttempt - - morphology_rare_delimiter: - eng: 'A Unicode character used to separate shape, gloss and category - in the morpheme representations of the morphology of this - morphological parser.' - - languageModels: - id: id - UUID: uuid - name: name - description: description - datetime_entered: datetimeEntered - datetime_modified: datetimeModified - enterer: enterer - modifier: modifier - - restricted: - eng: 'The system will set this to true if any of this corpus’ forms - are tagged as restricted.' - - generate_succeeded: - eng: 'Will be set to true if this language model has been - successfully generated, i.e., estimated.' - - perplexity_computed: - eng: 'Will be set to true if there has been an attempt to compute the - perplexity of this language model.' - - corpus: - eng: 'A corpus that will be used to estimate the language model.' - - vocabulary_morphology: - eng: 'A morphology that will be used to determine the vocabulary of - unigrams for the language model.' - - toolkit: - eng: 'The name of the N-gram language model toolkit to be used to - create the LM; currently only MITLM is supported.' - - order: - eng: 'The order of the language model, i.e. "2" for a bigram model or - "3" for a trigram one.' - - smoothing: - eng: 'The smoothing algorithm to be used to estimate the language - model; currently, the only options are the algorithms made - available by MITLM.' - - categorial: - eng: 'If set to true, then the elements of the model will be simple - morpheme categories; if set to false (the default), then the - elements of the model will be fully specified morphemes, i.e., - shape-gloss-category triples.' - - generate_message: - eng: 'The message returned by the OLD after an attempt to - estimate/generate a language model based on the specified - configuration.' - - generate_attempt: - eng: 'A unique value (a UUID) created by the OLD after each attempt - to estimate/generate a language model based on the specified - configuration.' - - perplexity: - eng: "The perplexity of the language model’s corpus according to the - language model." - - morphologies: - id: id - UUID: uuid - name: name - description: description - datetime_entered: datetimeEntered - datetime_modified: datetimeModified - enterer: enterer - modifier: modifier - - lexicon_corpus: - eng: 'An OLD corpus from which the lexicon of the morphology will be - extracted.' - - rules_corpus: - eng: 'An OLD corpus from which the morphotactic rules of the - morphology will be extracted.' - - script_type: - eng: 'The type of FST script that the generated morphology should be - written in: one of “regex” or “lexc”.' - - extract_morphemes_from_rules_corpus: - eng: 'If set to true, then morphemes for the morphology will be - extracted from the rules corpus in addition to also being extracted - from the lexicon corpus.' - - rules: - eng: 'Instead of having the system extract morphotactic rules from a - specified rules corpus, you can specify morphotactic rules in this - field. Morphotactic rules are strings like “V-Agr”, i.e., - sequences of categories and delimiters.' - - rich_upper: - eng: 'If set to true, then the morphology script will represent - morphemes on the upper side of the tape as (shape, gloss, category) - triples; if set to false (the default), then morphemes on the upper - side of the tape will be represented by their shapes only. See - Dunham (2014).' - - rich_lower: - eng: 'If set to true, then the morphology script will represent - morphemes on the lower side of the tape as (shape, gloss, category) - triples; if set to false (the default), then morphemes on the lower - side of the tape will be represented by their shapes only. See - Dunham (2014).' - - include_unknowns: - eng: 'If set to true, then morphemes of unknown category will be - added to lexicon of the morphology.' - - compile_succeeded: - eng: 'The server will set this to true if a request to compile the - morphology has completed successfully.' - - compile_message: - eng: 'Once a compile attempt has terminated, the compile message will - contain details of the outcome of that attempt.' - - compile_attempt: - eng: 'After each compile attempt, the server sets the “compile - attempt” attribute with a new unique (UUID) value.' - - generate_attempt: - eng: 'After each generate attempt, the server sets the “generate - attempt” attribute with a new unique (UUID) value.' - - rules_generated: - eng: 'The morphotactic rules generated by the server during a - generate attempt.' - - searches: - name: name - description: description - datetime_modified: datetimeModified - enterer: enterer - id: id - - search: - eng: 'The search expression that defines what forms are to be - returned and their ordering.' - - phonologies: - name: name - description: description - datetime_entered: datetimeEntered - datetime_modified: datetimeModified - enterer: enterer - modifier: modifier - UUID: uuid - id: id - compile_succeeded: compileSucceeded - compile_message: compileMessage - compile_attempt: compileAttempt - - script: - eng: "The FST script that defines the phonology. An ordered set of - rewrite rules that conforms to the foma regular - expression/rewrite rule syntax. See Hulden (2012) and Beesley and - Karttunen (2003)." - - subcorpora: - - name: - eng: "A name for the corpus. Each corpus must have a name and - it must be unique among corpora." - - description: - eng: "A description of the corpus." - - content: - eng: "The content of the corpus: a block of text containing - references to the forms that constitute the content of the - corpus." - - tags: - eng: "Tags for categorizing corpora. (These are the same tags that - are used throughout an OLD application; i.e., the same tag can be - used to categorize a form and a corpus.)" - - form_search: - eng: "An OLD form search object which defines the set of forms that - constitute the corpus." - - id: - eng: "The id of the corpus. This is an integer generated by the - relational database that is used by the OLD. This value can be used - to uniquely identify the corpus." - - UUID: - eng: "The UUID (universally unique identifier) of the corpus. This - is a unique value generated by the OLD. It is used to create - references between corpora and their previous versions." - - enterer: - eng: "The OLD user who entered/created the corpus. This value is - specified automatically by the application." - - modifier: - eng: "The OLD user who made the most recent modification to this - corpus. This value is specified automatically by the - application." - - datetime_entered: - eng: (options) -> - "This corpus was entered on #{utils.humanDatetime options.value}" - - datetime_modified: - eng: (options) -> - "This corpus was last modified on - #{utils.humanDatetime options.value}" - - files: - eng: "A list of files associated with this corpus. These are binary - representations of the corpus in various formats, e.g., NLTK-style - corpora or PTB-style treebanks. (TODO: verify this!)" - - forms: - - grammaticality: - eng: "The grammaticality of the form, e.g., grammatical, ungrammatical, - questionable, infelicitous in a given context. Possible values are - defined in the application settings and can be modified by - administrators." - - # TODO: put in help document: "A sequence of categories and morpheme - # delimiters (a sequence of parts-of-speech) corresponding to the - # morphological composition of the words in the form. If the form is - # mono-morphemic, then this value should be the same as the syntactic - # category value. The syntactic category string value is generated by - # the OLD based on the morpheme break and morpheme gloss values - # supplied by the user and the syntactic category of the morphemes - # implicit in those values. If, for example, a form has a morpheme - # break value of “chien-s” and a morpheme gloss value of - # “dog-PL”, and if the database contains lexical entries for - # “chien/dog” and “s/PL” with categories “N” and “Num”, respectively, - # then the syntactic category string value generated will be - # “N-Num”. Note that the OLD does not allow the syntactic category - # string to be explicitly defined by the user; this is by design: the - # idea is to encourage you to build a lexicon, a verbicon, a phrasicon, - # and a text collection simultaneously. You get more accurate syntactic - # category strings by increasing the consistency between your lexicon - # of morphemes and your lexicon of morphologically analyzed - # phrase-level forms." - syntactic_category_string: - eng: "A sequence of categories (and morpheme delimiters) that is - auto-generated by the system based on the morphemes/glosses entered - into the morpheme break and morpheme gloss fields and the - categories of matching lexical items in the database." - - # TODO: put in help docs: Some examples: “N”, “S”, “Phi”, “Asp”, “JJ”, etc." - syntactic_category: - eng: "The category (syntactic and/or morphological) of the form." - - comments: - eng: "General-purpose field for notes and commentary about the form." - - speaker_comments: - eng: "Field specifically for comments about the form made by the - speaker/consultant." - - elicitation_method: - eng: "How the form was elicited. Examples: “volunteered”, - “judged elicitor’s utterance”, “translation task”, etc." - - # TODO: put in help docs: "Note that the tags “foreign word” and - # “restricted” have special meaning in the OLD." - tags: - eng: "Tags for categorizing your forms." - - speaker: - eng: "The speaker (consultant) who produced or judged the form." - - elicitor: - eng: "The linguistic fieldworker who elicited the form with the help - of the consultant." - - enterer: - eng: "The user who entered/created the form. This value is - specified automatically by the application." - - modifier: - eng: "The user who made the most recent modification to this - form. This value is specified automatically by the application." - - verifier: - eng: "The user who has verified the reliability/accuracy of - this form." - - source: - eng: "The textual source (e.g., research paper, text collection, book - of learning materials) from which the form was drawn, if applicable." - - files: - eng: "Digital files (e.g., audio, video, image or text) that are - associated to this form." - - collections: - eng: "Collections, i.e., texts (e.g., papers, elicitation records, - etc.), that this form is a part of." - - - # TODO: put in help document: "The break/gloss/category value is - # generated by the OLD based on the morpheme break and morpheme gloss - # values and the (also auto-generated) syntactic category string value. - # The break/gloss/category value is a serialization of these three - # values. A form with “chien-s” as morpheme break, “dog-PL” as morpheme - # gloss and “N-Num” as category string will have - # “chien|dog|N-s|PL|Num” as its break/gloss/category value. This - # value is useful for search since it allows one to search through forms - # according to exactly specified morphemes." - break_gloss_category: - eng: "The morpheme break, morpheme gloss and (syntactic) category - string values all interleaved into a single string. This value is - auto-generated by the application." - - date_elicited: dateElicited - - datetime_entered: - eng: (options) -> - "This form was entered on #{utils.humanDatetime options.value}" - - datetime_modified: - eng: (options) -> - "This form was last modified on #{utils.humanDatetime options.value}" - - # TODO: put in help document: "The OLD assumes that this will be a tree - # in bracket notation using Penn Treebank conventions." - syntax: - eng: "A syntactic phrase structure representation in some kind of - string-based format." - - semantics: - eng: "A semantic representation of the meaning of the form in some - string-based format." - - status: - eng: "The status of the form: “tested” for data that have been - elicited/tested/verified with a consultant or “requires - testing” for data that are posited and still need - testing/elicitation." - - UUID: - eng: "The UUID (universally unique identifier) of the form. A unique - value generated by the system, this is used to create references - between forms and their previous versions." - - id: - eng: "The id of the form. An system-generated integer that is unique - to each form." - - narrow_phonetic_transcription: - eng: "A narrow phonetic transcription, probably in IPA." - - phonetic_transcription: - eng: "A phonetic transcription, probably in IPA." - - transcription: - eng: "A transcription, probably orthographic." - - morpheme_break: - eng: "A sequence of morpheme shapes and delimiters. The OLD assumes - phonemic shapes (e.g., “in-perfect”), but phonetic (i.e., - allomorphic, e.g., “im-perfect”) ones are ok." - - morpheme_gloss: - eng: "A sequence of morpheme glosses and delimiters, isomorphic to - the morpheme break sequence, e.g., “NEG-parfait”." - - translations: - eng: "One or more translations for the form. Each translation may - have its own grammaticality/acceptibility specification." - - transcription: - eng: "A transcription of a possible translation of this form." - - # Note: this should be changed to "appropriateness" in the OLD. - grammaticality: - eng: "The appropriateness of this translation with respect to this - form." - - # This is the anonymous function that we return. It returns a second function - # which returns the tooltip string when called. You use "dot notation" to get - # the tooltip, e.g., `tooltips('old.forms.syntactic_category')()`. - (namespace) -> - parts = namespace.split '.' - current = tooltips - for part, index in parts - if current[part] - current = current[part] - else - current = {} - break - - (options) -> - language = options?.language or 'eng' - if language of current - tooltip = current[language] - else if 'eng' of current - tooltip = current.eng - else - tooltip = 'No tooltip.' - if utils.type(tooltip) is 'function' - tooltip options - else - tooltip - diff --git a/app/scripts/utils/utils.coffee b/app/scripts/utils/utils.coffee deleted file mode 100644 index d8ef894..0000000 --- a/app/scripts/utils/utils.coffee +++ /dev/null @@ -1,522 +0,0 @@ -# Utility functions and classes - -define (require) -> - - # Clone an Object (deep copy) - # From http://coffeescriptcookbook.com/chapters/classes_and_objects/cloning - clone = (obj) -> - if not obj? or typeof obj isnt 'object' - return obj - - if obj instanceof Date - return new Date(obj.getTime()) - - if obj instanceof RegExp - flags = '' - flags += 'g' if obj.global? - flags += 'i' if obj.ignoreCase? - flags += 'm' if obj.multiline? - flags += 'y' if obj.sticky? - return new RegExp(obj.source, flags) - - # You can't really copy a Blob instance, so let's not try. - # TODO: check whether this cause issues with an add view being able to tell - # whether it's model has been altered. - if obj instanceof Blob - return null - - newInstance = new obj.constructor() - - for key of obj - newInstance[key] = clone obj[key] - - return newInstance - - # Type function which is superior to typeof. - # See http://coffeescriptcookbook.com/chapters/classes_and_objects/type-function - type = (obj) -> - if obj == undefined or obj == null - return String obj - classToType = { - '[object Boolean]': 'boolean', - '[object Number]': 'number', - '[object String]': 'string', - '[object Function]': 'function', - '[object Array]': 'array', - '[object Date]': 'date', - '[object RegExp]': 'regexp', - '[object Object]': 'object' - } - return classToType[Object.prototype.toString.call(obj)] - - s4 = -> - (((1 + Math.random()) * 0x10000) | 0).toString(16).substring 1 - - guid = -> - "#{s4()}#{s4()}-#{s4()}-#{s4()}-#{s4()}-#{s4()}#{s4()}#{s4()}" - - # Email validator. This is the second regex of the second answer to - # http://stackoverflow.com/questions/46155/validate-email-address-in-javascript - emailIsValid = (email) -> - regex = /[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?/ - return regex.test(email) - - startsWith = (s, prefix) -> s[...prefix.length] is prefix - - endsWith = (s, suffix) -> suffix is '' or s[-suffix.length..] is suffix - - integerWithCommas = (integer) -> - try - integer.toString().replace /\B(?=(\d{3})+(?!\d))/g, ',' - catch - integer - - singularize = (noun) -> - try - if endsWith(noun, 'ies') - "#{noun[0..-4]}y" - else if endsWith(noun, 'hes') - noun[...-2] - else - noun[...-1] - catch - noun - - # A very ad hoc pluralize function; patch it up as needed but beware: it is - # used extensively in the core logic. - pluralize = (noun) -> - if endsWith noun, 'y' - "#{noun[...-1]}ies" - else if endsWith noun, 'tatus' # status - "#{noun}es" - else if endsWith noun, 'us' # "corpus/corpora" - "#{noun[...-2]}ora" - else if endsWith(noun, 'z') or - endsWith(noun, 's') or - endsWith(noun, 'sh') or - endsWith(noun, 'ch') - "#{noun}es" - else - "#{noun}s" - - pluralizeByNum = (noun, numeral) -> - switch numeral - when 1 then noun - else pluralize noun - - indefiniteDeterminer = (complement) -> - if complement[0].toLowerCase() in ['a', 'e', 'i', 'o', 'u'] - if startsWith complement.toLowerCase(), 'user' - 'a' - else - 'an' - else - 'a' - - # Parses a date(time) string to a Date instance - dateString2object = (dateString) -> - try # Some FieldDB dates are enclosed in double quotation marks - dateString = dateString.replace /"/g, '' - date = new Date(Date.parse(dateString)) - if date.toString() is 'Invalid Date' then dateString else date - - # Return `unknown` as a JavaScript `Date` instance, if possible. - asDateObject = (unknown) -> - if type(unknown) is 'date' - unknown - else if type(unknown) is 'string' - try - dateString2object unknown - catch - unknown - else - null - - # Returns a `Date` instance as "January 1, 2015 at 5:45 p.m.", etc. - humanDatetime = (dateObject, showSeconds=false) -> - dateObject = asDateObject dateObject - if type(dateObject) in ['string', 'null'] then return dateObject - humanDateString = humanDate dateObject - if not humanDateString then return null - "#{humanDateString} at #{humanTime dateObject, showSeconds}" - - # Returns a `Date` instance as "January 1, 2015", etc. - humanDate = (dateObject) -> - dateObject = asDateObject dateObject - if type(dateObject) in ['string', 'null'] then return dateObject - try - monthNames = ["January", "February", "March", "April", "May", "June", - "July", "August", "September", "October", "November", "December"] - ["#{monthNames[dateObject.getMonth()]}", - "#{dateObject.getDate()},", - "#{dateObject.getFullYear()}"].join ' ' - catch - null - - # Returns the time portion of a `Date` instance as "5:45 p.m.", etc. - humanTime = (dateObject, showSeconds=false) -> - try - hours = dateObject.getHours() - minutes = dateObject.getMinutes() - ampm = if hours >= 12 then 'p.m.' else 'a.m.' - hours = hours % 12 - hours = if hours then hours else 12 # the hour '0' should be '12' - minutes = if minutes < 10 then "0#{minutes}" else minutes - if showSeconds - seconds = dateObject.getSeconds() - seconds = if seconds < 10 then "0#{seconds}" else seconds - "#{hours}:#{minutes}:#{seconds} #{ampm}" - else - "#{hours}:#{minutes} #{ampm}" - catch - return null - - # Takes a Date instance and returns a string indicating how long ago it was - # from now. - timeSince = (dateObject) -> - dateObject = asDateObject dateObject - if type(dateObject) in ['string', 'null'] then return dateObject - try - date = dateObject.getTime() - catch - return null - if isNaN date then return '' - seconds = Math.floor((new Date() - date) / 1000) - - # Handle future dates - if seconds < 0 - prefix = 'in ' - suffix = '' - else - prefix = '' - suffix = ' ago' - seconds = Math.abs(seconds) - - interval = Math.floor(seconds / 31536000) - if interval > 1 then return "#{prefix}#{interval} years#{suffix}" - interval = Math.floor(seconds / 2592000) - if interval > 1 then return "#{prefix}#{interval} months#{suffix}" - interval = Math.floor(seconds / 86400) - if interval > 1 then return "#{prefix}#{interval} days#{suffix}" - interval = Math.floor(seconds / 3600) - if interval > 1 then return "#{prefix}#{interval} hours#{suffix}" - interval = Math.floor(seconds / 60) - if interval > 1 then return "#{prefix}#{interval} minutes#{suffix}" - "#{prefix}#{Math.floor(seconds)} seconds#{suffix}" - - # Convert a number of milliseconds to a string formatted as 00h00m00s. - millisecondsToTimeString = (milliseconds) -> - hours = Math.floor(milliseconds / 36e5) - minutes = Math.floor((milliseconds % 36e5) / 6e4) - seconds = Math.floor((milliseconds % 6e4) / 1000) - "#{leftPad hours, 2}h#{leftPad minutes, 2}m#{leftPad seconds, 2}s" - - # Add `padding` number of "0"s to the left of `value`. - leftPad = (value, padding=2) -> - value = String value - value = '0' + value while ('' + value).length < padding - value - - # "snake_case" to "camelCase" - snake2camel = (string) -> - string.replace(/(_[a-z])/g, ($1) -> - $1.toUpperCase().replace('_','')) - - # "snake_case" to "hyphen-case" - snake2hyphen = (string) -> - string.replace /_/g, '-' - - # "snake_case" to "regular case" - snake2regular = (string) -> - string.replace /_/g, ' ' - - # "regular case" to "snake_case" - regular2snake = (string) -> - string.toLowerCase().replace(/( )/g, '_') - - # "camelCase" to "snake_case" - camel2snake = (string) -> - string - .replace /([A-Z])/g, ($1) -> "_#{$1.toLowerCase()}" - .replace /^_/, '' - - # "camelCase" to "camel case". - camel2regular = (string) -> - string - .replace /([A-Z])/g, ' $1' - .toLowerCase() - .trim() - - # "camelCase" to "camel Case". Insert a space before all caps and uppercase - # the first character - camel2regularUpper = (string) -> - string - .replace /([A-Z])/g, ' $1' - .replace /^./, (str) -> str.toUpperCase() - .trim() - - # "camelCase" to "camel-case". Insert a hyphen before all caps and lowercase everything. - camel2hyphen = (string) -> - string - .replace /([A-Z])/g, '-$1' - .replace /^-/, '' - .toLowerCase() - .trim() - - hyphen2camel = (string) -> - array = (x for x in string.split('-') when x) - if array.length == 0 - '' - else if array.length == 1 - array[0] - else - "#{array[0]}#{("#{b[0].toUpperCase()}#{b[1...]}" for b in \ - array[1...]).join('')}" - - capitalize = (string) -> - try - "#{string[0].toUpperCase()}#{string[1..-1]}" - catch - string - - # Enclose `enclosee` in `start` and `end` characters, only if they're not - # already there. - encloseIfNotAlready = (enclosee, start, end) -> - if not enclosee? then return enclosee - [first, ..., last] = enclosee - start = if first is start then '' else start - end = if last is end then '' else end - "#{start}#{enclosee}#{end}" - - log = (thingToLog) -> - console.log JSON.stringify(thingToLog, undefined, 2) - - getTimestamp = -> new Date().getTime() - - # Returns `string` with all of its acronyms enclosed in ``. NOTE: assumes that an acronyms is any sequence of two - # or more uppercase ASCII characters. - smallCapsAcronyms = (string) -> - string.replace(/([A-Z]{2,})/g, (match, $1) -> - "#{$1.toLowerCase()}") - - isoDateRegex = /^\d{4}-\d{2}-\d{2}$/ - - # Convert a date like "2015-03-16" to "03/16/2015". Note that the OLD - # accepts dates in the latter format (because of how Pylons' FormEncode - # validation works), but returns them in the former (ISO 8601) format. - convertDateISO2mdySlash = (date) -> - if isoDateRegex.test date - [year, month, day] = date.split '-' - "#{month}/#{day}/#{year}" - else - date - - # See http://stackoverflow.com/questions/985272/selecting-text-in-an-element-akin-to-highlighting-with-your-mouse - selectText = (element) -> - if document.body.createTextRange - range = document.body.createTextRange() - range.moveToElementText element - range.select() - else if window.getSelection - selection = window.getSelection() - range = document.createRange() - range.selectNodeContents element - selection.removeAllRanges() - selection.addRange range - - isValidURL = (url) -> - if url - regex = /// ^ - (?:(?:https?|ftp):\/\/) - (?:\S+(?::\S*)?@)? - (?: - (?!10(?:\.\d{1,3}){3}) - (?!127(?:\.‌​\d{1,3}){3}) - (?!169\.254(?:\.\d{1,3}){2}) - (?!192\.168(?:\.\d{1,3}){2}) - (?!172\.(?:1[‌​6-9]|2\d|3[0-1])(?:\.\d{1,3}){2}) - (?:[1-9]\d?|1\d\d|2[01]\d|22[0-3]) - (?:\.(?:1?\d{1‌​,2}|2[0-4]\d|25[0-5])){2} - (?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4])) - | (?:(?:[a-z\u00‌​a1-\uffff0-9]+-?)*[a-z\u00a1-\uffff0-9]+) - (?:\.(?:[a-z\u00a1-\uffff0-9]+-?)*[a-z\u‌​00a1-\uffff0-9]+)* - (?:\.(?:[a-z\u00a1-\uffff]{2,})) - ) - (?::\d{2,5})? - (?:\/[^\s]*)? - $ ///i - if url.match regex then true else false - else - false - - # Maps file extensions to MIME types. Potentially problematic parts include - # the audio/mp3 type for .mp3 files ... - extensions = - mpeg: 'video/mpeg' - mp4: 'video/mp4' - ogv: 'video/ogg' - qt: 'video/quicktime' - mov: 'video/quicktime' - wmv: 'video/x-ms-wmv' - pdf: 'application/pdf' - gif: 'image/gif' - jpeg: 'image/jpeg' - jpg: 'image/jpeg' - png: 'image/png' - mpga: 'audio/mpeg' - mp3: 'audio/mp3' - ogg: 'audio/ogg' - oga: 'audio/ogg' - wav: 'audio/x-wav' - - getExtension = (path) -> - try - path.split('.')[-1..][0] - catch - null - - getFilenameAndExtension = (path) -> - try - parts = path.split('.') - if parts.length is 1 - [path, null] - else - [parts[...-1].join('.'), parts[-1..][0]] - catch - [path, null] - - getFilenameWithoutExtension = (path) -> - getFilenameAndExtension(path)[0] - - getMIMEType = (path) -> - try - extensions[getExtension(path)] - catch - null - - # From http://stackoverflow.com/questions/10420352/converting-file-size-in-bytes-to-human-readable - humanFileSize = (bytes, si=true) -> - thresh = if si then 1000 else 1024 - if Math.abs(bytes) < thresh then return "#{bytes} B" - if si - units = ['kB','MB','GB','TB','PB','EB','ZB','YB'] - else - units = ['KiB','MiB','GiB','TiB','PiB','EiB','ZiB','YiB'] - u = -1 - loop - bytes /= thresh - u += 1 - if not ((Math.abs(bytes) >= thresh) and (u < (units.length - 1))) - break - "#{bytes.toFixed(1)} #{units[u]}" - - # Use `regex` to wrap `value` in tags that will make it render as - # highlighted. Note: this assumes the type of regex generated in the - # `SearchModel` class. - highlightSearchMatch = (regex, value) -> - try - String(value).replace(regex, (a, b) -> - "#{b}") - catch - value - - # Return `array` with no duplicates. - unique = (array) -> - output = {} - output[array[key]] = array[key] for key in [0...array.length] - value for key, value of output - - # Convert a string so that all strings of whitespace are replaced by a single - # space character and so that there is no leading or trailing whitespace. - singleSpace = (string) -> - try - string.trim().split(/\s+/).join ' ' - catch - string - - number2word = (number) -> - switch number - when 0 then 'zero' - when 1 then 'one' - when 2 then 'two' - when 3 then 'three' - when 4 then 'four' - when 5 then 'five' - when 6 then 'six' - when 7 then 'seven' - when 8 then 'eight' - when 9 then 'nine' - when 10 then 'ten' - when 11 then 'eleven' - when 12 then 'twelve' - else integerWithCommas number - - # Cf. http://stackoverflow.com/a/9310752/992730 - escapeRegexChars = (input) -> - try - input.replace /[-[\]{}()*+?.,\\^$|#]/g, "\\$&" - catch - try - String(input).replace /[-[\]{}()*+?.,\\^$|#]/g, "\\$&" - catch - input - - # Return the decimal number `decimal` has a hex string with left-padding 0s - # to minimum length 4. - decimal2hex = (decimal) -> - hex = Number(decimal).toString 16 - if hex.length < 4 - "#{Array(5 - hex.length).join '0'}#{hex}" - else - hex - - decimal2hex: decimal2hex - escapeRegexChars: escapeRegexChars - number2word: number2word - singleSpace: singleSpace - unique: unique - highlightSearchMatch: highlightSearchMatch - humanFileSize: humanFileSize - isValidURL: isValidURL - clone: clone - type: type - guid: guid - emailIsValid: emailIsValid - startsWith: startsWith - endsWith: endsWith - integerWithCommas: integerWithCommas - singularize: singularize - pluralize: pluralize - pluralizeByNum: pluralizeByNum - timeSince: timeSince - humanDatetime: humanDatetime - humanDate: humanDate - humanTime: humanTime - dateString2object: dateString2object - snake2camel: snake2camel - snake2hyphen: snake2hyphen - snake2regular: snake2regular - regular2snake: regular2snake - camel2snake: camel2snake - camel2regular: camel2regular - camel2regularUpper: camel2regularUpper - camel2hyphen: camel2hyphen - hyphen2camel: hyphen2camel - capitalize: capitalize - encloseIfNotAlready: encloseIfNotAlready - log: log - getTimestamp: getTimestamp - smallCapsAcronyms: smallCapsAcronyms - convertDateISO2mdySlash: convertDateISO2mdySlash - selectText: selectText - millisecondsToTimeString: millisecondsToTimeString - leftPad: leftPad - getExtension: getExtension - getFilenameWithoutExtension: getFilenameWithoutExtension - getFilenameAndExtension: getFilenameAndExtension - getMIMEType: getMIMEType - extensions: extensions - indefiniteDeterminer: indefiniteDeterminer - asDateObject: asDateObject - diff --git a/app/scripts/views/active-server.coffee b/app/scripts/views/active-server.coffee deleted file mode 100644 index 082f8dc..0000000 --- a/app/scripts/views/active-server.coffee +++ /dev/null @@ -1,111 +0,0 @@ -define [ - 'backbone' - './base' - './../templates/active-server' -], (Backbone, BaseView, activeServerTemplate) -> - - # Active Server View - # ------------------ - # - # Purpose: the "Active Server" select reflects the state of the servers - # collection. - # - # A view dedicated to a single jQueryUI selectmenu which indicates the active - # server. The model is the entire `applicationSettings` instance initialized - # in `app.coffee`. - - class ActiveServerView extends BaseView - - template: activeServerTemplate - - initialize: (options) -> - @width = options.width or 540 - @label = options.label or 'Active Server' - @tooltipPosition = options.tooltipPosition or { - my: "left top+15", at: "left bottom", collision: "flipfit"} - @tooltipContent = options.tooltipContent or 'select the active server' - - events: - 'selectmenuchange': 'setModelFromGUI' - - listenToEvents: -> - @listenTo @model.get('servers'), 'add', @newServerAdded - @listenTo @model.get('servers'), 'remove', @serverRemoved - @listenTo @model.get('servers'), 'change', @serverChanged - @listenTo @model, 'change:loggedIn', @loggedInChanged - @listenTo @model, 'change:activeServer', @activeServerChanged - @delegateEvents() - - loggedInChanged: -> - @selectmenuify() - - loggedIn: -> - @model.get 'loggedIn' - - selectmenuify: -> - disabled = if @loggedIn() then true else false - @$('select.activeServer') - .selectmenu( - width: @width - disabled: disabled - ) - .data 'ui-selectmenu' - ._renderMenu = (ul,items) -> - # CSS class forces selectmenu to have a max-height. - ul.addClass 'activeServerSMUL' - for item in items - @_renderItemData ul, item - - render: -> - context = - label: @label - activeServerId: @model.get('activeServer')?.get?('id') - servers: @model.get('servers').toJSON() - @$el.html @template(context) - @selectmenuify() - @$('.ui-selectmenu-button') - .addClass 'dative-tooltip dative-select-active-server' - .tooltip - items: 'span' - content: @tooltipContent - position: @tooltipPosition - @listenToEvents() - @ - - activeServerChanged: -> - activeServer = @model.get 'activeServer' - if activeServer - @$('select.activeServer').val activeServer.get('id') - else - @$('select.activeServer').val 'null' - @$('select.activeServer').selectmenu 'refresh' - - serverChanged: (serverModel) -> - @$('select.activeServer') - .find("option[value=#{serverModel.get('id')}]") - .text(serverModel.get('name')).end() - .selectmenu('refresh') - - newServerAdded: (newServerModel) -> - newOptionElement = $('