diff --git a/services/web/public/coffee/base.coffee b/services/web/public/coffee/base.coffee index c3847c9342..370823703a 100644 --- a/services/web/public/coffee/base.coffee +++ b/services/web/public/coffee/base.coffee @@ -17,14 +17,19 @@ define [ "ErrorCatcher" "localStorage" "ngTagsInput" - ]).config ($qProvider, sixpackProvider, $httpProvider)-> + "ui.select" + ]).config ($qProvider, sixpackProvider, $httpProvider, uiSelectConfig) -> $qProvider.errorOnUnhandledRejections(false) + uiSelectConfig.spinnerClass = 'fa fa-refresh ui-select-spin' sixpackProvider.setOptions({ debug: false baseUrl: window.sharelatex.sixpackDomain client_id: window.user_id }) + App.run ($templateCache) -> + $templateCache.put "bootstrap/match.tpl.html", "
" + sl_debugging = window.location?.search?.match(/debug=true/)? window.sl_console = log: (args...) -> console.log(args...) if sl_debugging diff --git a/services/web/public/coffee/libraries.coffee b/services/web/public/coffee/libraries.coffee index e4532d7187..bddc5a2ac9 100644 --- a/services/web/public/coffee/libraries.coffee +++ b/services/web/public/coffee/libraries.coffee @@ -11,5 +11,6 @@ define [ "libs/sixpack" "libs/angular-sixpack" "libs/ng-tags-input-3.0.0" + "libs/select/select" ], () -> diff --git a/services/web/public/coffee/main.coffee b/services/web/public/coffee/main.coffee index 786bf1a94b..a52a490491 100644 --- a/services/web/public/coffee/main.coffee +++ b/services/web/public/coffee/main.coffee @@ -20,6 +20,8 @@ define [ "main/subscription/team-invite-controller" "main/contact-us" "main/learn" + "main/affiliations/controllers/UserAffiliationsController" + "main/affiliations/factories/UserAffiliationsDataService" "analytics/AbTestingManager" "directives/asyncForm" "directives/stopPropagation" @@ -34,6 +36,7 @@ define [ "services/queued-http" "services/validateCaptcha" "filters/formatDate" + "components/inputSuggestions" "__MAIN_CLIENTSIDE_INCLUDES__" ], () -> angular.module('SharelatexApp').config( diff --git a/services/web/public/js/libs/select/select.css b/services/web/public/js/libs/select/select.css new file mode 100755 index 0000000000..0e713bb1b9 --- /dev/null +++ b/services/web/public/js/libs/select/select.css @@ -0,0 +1,362 @@ +/*! + * ui-select + * http://github.com/angular-ui/ui-select + * Version: 0.19.7 - 2017-04-15T14:28:36.790Z + * License: MIT + */ + + +/* Style when highlighting a search. */ +.ui-select-highlight { + font-weight: bold; +} + +.ui-select-offscreen { + clip: rect(0 0 0 0) !important; + width: 1px !important; + height: 1px !important; + border: 0 !important; + margin: 0 !important; + padding: 0 !important; + overflow: hidden !important; + position: absolute !important; + outline: 0 !important; + left: 0px !important; + top: 0px !important; +} + + +.ui-select-choices-row:hover { + background-color: #f5f5f5; +} + +/* Select2 theme */ + +/* Mark invalid Select2 */ +.ng-dirty.ng-invalid > a.select2-choice { + border-color: #D44950; +} + +.select2-result-single { + padding-left: 0; +} + +.select2-locked > .select2-search-choice-close{ + display:none; +} + +.select-locked > .ui-select-match-close{ + display:none; +} + +body > .select2-container.open { + z-index: 9999; /* The z-index Select2 applies to the select2-drop */ +} + +/* Handle up direction Select2 */ +.ui-select-container[theme="select2"].direction-up .ui-select-match, +.ui-select-container.select2.direction-up .ui-select-match { + border-radius: 4px; /* FIXME hardcoded value :-/ */ + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.ui-select-container[theme="select2"].direction-up .ui-select-dropdown, +.ui-select-container.select2.direction-up .ui-select-dropdown { + border-radius: 4px; /* FIXME hardcoded value :-/ */ + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + + border-top-width: 1px; /* FIXME hardcoded value :-/ */ + border-top-style: solid; + + box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); + + margin-top: -4px; /* FIXME hardcoded value :-/ */ +} +.ui-select-container[theme="select2"].direction-up .ui-select-dropdown .select2-search, +.ui-select-container.select2.direction-up .ui-select-dropdown .select2-search { + margin-top: 4px; /* FIXME hardcoded value :-/ */ +} +.ui-select-container[theme="select2"].direction-up.select2-dropdown-open .ui-select-match, +.ui-select-container.select2.direction-up.select2-dropdown-open .ui-select-match { + border-bottom-color: #5897fb; +} + +.ui-select-container[theme="select2"] .ui-select-dropdown .ui-select-search-hidden, +.ui-select-container[theme="select2"] .ui-select-dropdown .ui-select-search-hidden input{ + opacity: 0; + height: 0; + min-height: 0; + padding: 0; + margin: 0; + border:0; +} + +/* Selectize theme */ + +/* Helper class to show styles when focus */ +.selectize-input.selectize-focus{ + border-color: #007FBB !important; +} + +/* Fix input width for Selectize theme */ +.selectize-control.single > .selectize-input > input { + width: 100%; +} + +/* Fix line break when there's at least one item selected with the Selectize theme */ +.selectize-control.multi > .selectize-input > input { + margin: 0 !important; +} + +/* Fix dropdown width for Selectize theme */ +.selectize-control > .selectize-dropdown { + width: 100%; +} + +/* Mark invalid Selectize */ +.ng-dirty.ng-invalid > div.selectize-input { + border-color: #D44950; +} + +/* Handle up direction Selectize */ +.ui-select-container[theme="selectize"].direction-up .ui-select-dropdown { + box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); + margin-top: -2px; /* FIXME hardcoded value :-/ */ +} + +.ui-select-container[theme="selectize"] input.ui-select-search-hidden{ + opacity: 0; + height: 0; + min-height: 0; + padding: 0; + margin: 0; + border:0; + width: 0; +} + +/* Bootstrap theme */ + +/* Helper class to show styles when focus */ +.btn-default-focus { + color: #333; + background-color: #EBEBEB; + border-color: #ADADAD; + text-decoration: none; + outline: 5px auto -webkit-focus-ring-color; + outline-offset: -2px; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); +} + +.ui-select-bootstrap .ui-select-toggle { + position: relative; +} + +.ui-select-bootstrap .ui-select-toggle > .caret { + position: absolute; + height: 10px; + top: 50%; + right: 10px; + margin-top: -2px; +} + +/* Fix Bootstrap dropdown position when inside a input-group */ +.input-group > .ui-select-bootstrap.dropdown { + /* Instead of relative */ + position: static; +} + +.input-group > .ui-select-bootstrap > input.ui-select-search.form-control { + border-radius: 4px; /* FIXME hardcoded value :-/ */ + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.input-group > .ui-select-bootstrap > input.ui-select-search.form-control.direction-up { + border-radius: 4px !important; /* FIXME hardcoded value :-/ */ + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} + +.ui-select-bootstrap .ui-select-search-hidden{ + opacity: 0; + height: 0; + min-height: 0; + padding: 0; + margin: 0; + border:0; +} + +.ui-select-bootstrap > .ui-select-match > .btn{ + /* Instead of center because of .btn */ + text-align: left !important; +} + +.ui-select-bootstrap > .ui-select-match > .caret { + position: absolute; + top: 45%; + right: 15px; +} + +/* See Scrollable Menu with Bootstrap 3 http://stackoverflow.com/questions/19227496 */ +.ui-select-bootstrap > .ui-select-choices ,.ui-select-bootstrap > .ui-select-no-choice { + width: 100%; + height: auto; + max-height: 200px; + overflow-x: hidden; + margin-top: -1px; +} + +body > .ui-select-bootstrap.open { + z-index: 1000; /* Standard Bootstrap dropdown z-index */ +} + +.ui-select-multiple.ui-select-bootstrap { + height: auto; + padding: 3px 3px 0 3px; +} + +.ui-select-multiple.ui-select-bootstrap input.ui-select-search { + background-color: transparent !important; /* To prevent double background when disabled */ + border: none; + outline: none; + height: 1.666666em; + margin-bottom: 3px; +} + +.ui-select-multiple.ui-select-bootstrap .ui-select-match .close { + font-size: 1.6em; + line-height: 0.75; +} + +.ui-select-multiple.ui-select-bootstrap .ui-select-match-item { + outline: 0; + margin: 0 3px 3px 0; +} + +.ui-select-multiple .ui-select-match-item { + position: relative; +} + +.ui-select-multiple .ui-select-match-item.dropping .ui-select-match-close { + pointer-events: none; +} + +.ui-select-multiple:hover .ui-select-match-item.dropping-before:before { + content: ""; + position: absolute; + top: 0; + right: 100%; + height: 100%; + margin-right: 2px; + border-left: 1px solid #428bca; +} + +.ui-select-multiple:hover .ui-select-match-item.dropping-after:after { + content: ""; + position: absolute; + top: 0; + left: 100%; + height: 100%; + margin-left: 2px; + border-right: 1px solid #428bca; +} + +.ui-select-bootstrap .ui-select-choices-row>span { + cursor: pointer; + display: block; + padding: 3px 20px; + clear: both; + font-weight: 400; + line-height: 1.42857143; + color: #333; + white-space: nowrap; +} + +.ui-select-bootstrap .ui-select-choices-row>span:hover, .ui-select-bootstrap .ui-select-choices-row>span:focus { + text-decoration: none; + color: #262626; + background-color: #f5f5f5; +} + +.ui-select-bootstrap .ui-select-choices-row.active>span { + color: #fff; + text-decoration: none; + outline: 0; + background-color: #428bca; +} + +.ui-select-bootstrap .ui-select-choices-row.disabled>span, +.ui-select-bootstrap .ui-select-choices-row.active.disabled>span { + color: #777; + cursor: not-allowed; + background-color: #fff; +} + +/* fix hide/show angular animation */ +.ui-select-match.ng-hide-add, +.ui-select-search.ng-hide-add { + display: none !important; +} + +/* Mark invalid Bootstrap */ +.ui-select-bootstrap.ng-dirty.ng-invalid > button.btn.ui-select-match { + border-color: #D44950; +} + +/* Handle up direction Bootstrap */ +.ui-select-container[theme="bootstrap"].direction-up .ui-select-dropdown { + box-shadow: 0 -4px 8px rgba(0, 0, 0, 0.25); +} + +.ui-select-bootstrap .ui-select-match-text { + width: 100%; + padding-right: 1em; +} +.ui-select-bootstrap .ui-select-match-text span { + display: inline-block; + width: 100%; + overflow: hidden; +} +.ui-select-bootstrap .ui-select-toggle > a.btn { + position: absolute; + height: 10px; + right: 10px; + margin-top: -2px; +} + +/* Spinner */ +.ui-select-refreshing.glyphicon { + position: absolute; + right: 0; + padding: 8px 27px; + } + +@-webkit-keyframes ui-select-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} +@keyframes ui-select-spin { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 100% { + -webkit-transform: rotate(359deg); + transform: rotate(359deg); + } +} + +.ui-select-spin { + -webkit-animation: ui-select-spin 2s infinite linear; + animation: ui-select-spin 2s infinite linear; +} + +.ui-select-refreshing.ng-animate { + -webkit-animation: none 0s; +} diff --git a/services/web/public/js/libs/select/select.js b/services/web/public/js/libs/select/select.js new file mode 100755 index 0000000000..5ac4e0b702 --- /dev/null +++ b/services/web/public/js/libs/select/select.js @@ -0,0 +1,2427 @@ +/*! + * ui-select + * http://github.com/angular-ui/ui-select + * Version: 0.19.7 - 2017-04-15T14:28:36.649Z + * License: MIT + */ + + +(function () { +"use strict"; +var KEY = { + TAB: 9, + ENTER: 13, + ESC: 27, + SPACE: 32, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + SHIFT: 16, + CTRL: 17, + ALT: 18, + PAGE_UP: 33, + PAGE_DOWN: 34, + HOME: 36, + END: 35, + BACKSPACE: 8, + DELETE: 46, + COMMAND: 91, + + MAP: { 91 : "COMMAND", 8 : "BACKSPACE" , 9 : "TAB" , 13 : "ENTER" , 16 : "SHIFT" , 17 : "CTRL" , 18 : "ALT" , 19 : "PAUSEBREAK" , 20 : "CAPSLOCK" , 27 : "ESC" , 32 : "SPACE" , 33 : "PAGE_UP", 34 : "PAGE_DOWN" , 35 : "END" , 36 : "HOME" , 37 : "LEFT" , 38 : "UP" , 39 : "RIGHT" , 40 : "DOWN" , 43 : "+" , 44 : "PRINTSCREEN" , 45 : "INSERT" , 46 : "DELETE", 48 : "0" , 49 : "1" , 50 : "2" , 51 : "3" , 52 : "4" , 53 : "5" , 54 : "6" , 55 : "7" , 56 : "8" , 57 : "9" , 59 : ";", 61 : "=" , 65 : "A" , 66 : "B" , 67 : "C" , 68 : "D" , 69 : "E" , 70 : "F" , 71 : "G" , 72 : "H" , 73 : "I" , 74 : "J" , 75 : "K" , 76 : "L", 77 : "M" , 78 : "N" , 79 : "O" , 80 : "P" , 81 : "Q" , 82 : "R" , 83 : "S" , 84 : "T" , 85 : "U" , 86 : "V" , 87 : "W" , 88 : "X" , 89 : "Y" , 90 : "Z", 96 : "0" , 97 : "1" , 98 : "2" , 99 : "3" , 100 : "4" , 101 : "5" , 102 : "6" , 103 : "7" , 104 : "8" , 105 : "9", 106 : "*" , 107 : "+" , 109 : "-" , 110 : "." , 111 : "/", 112 : "F1" , 113 : "F2" , 114 : "F3" , 115 : "F4" , 116 : "F5" , 117 : "F6" , 118 : "F7" , 119 : "F8" , 120 : "F9" , 121 : "F10" , 122 : "F11" , 123 : "F12", 144 : "NUMLOCK" , 145 : "SCROLLLOCK" , 186 : ";" , 187 : "=" , 188 : "," , 189 : "-" , 190 : "." , 191 : "/" , 192 : "`" , 219 : "[" , 220 : "\\" , 221 : "]" , 222 : "'" + }, + + isControl: function (e) { + var k = e.which; + switch (k) { + case KEY.COMMAND: + case KEY.SHIFT: + case KEY.CTRL: + case KEY.ALT: + return true; + } + + if (e.metaKey || e.ctrlKey || e.altKey) return true; + + return false; + }, + isFunctionKey: function (k) { + k = k.which ? k.which : k; + return k >= 112 && k <= 123; + }, + isVerticalMovement: function (k){ + return ~[KEY.UP, KEY.DOWN].indexOf(k); + }, + isHorizontalMovement: function (k){ + return ~[KEY.LEFT,KEY.RIGHT,KEY.BACKSPACE,KEY.DELETE].indexOf(k); + }, + toSeparator: function (k) { + var sep = {ENTER:"\n",TAB:"\t",SPACE:" "}[k]; + if (sep) return sep; + // return undefined for special keys other than enter, tab or space. + // no way to use them to cut strings. + return KEY[k] ? undefined : k; + } + }; + +function isNil(value) { + return angular.isUndefined(value) || value === null; +} + +/** + * Add querySelectorAll() to jqLite. + * + * jqLite find() is limited to lookups by tag name. + * TODO This will change with future versions of AngularJS, to be removed when this happens + * + * See jqLite.find - why not use querySelectorAll? https://github.com/angular/angular.js/issues/3586 + * See feat(jqLite): use querySelectorAll instead of getElementsByTagName in jqLite.find https://github.com/angular/angular.js/pull/3598 + */ +if (angular.element.prototype.querySelectorAll === undefined) { + angular.element.prototype.querySelectorAll = function(selector) { + return angular.element(this[0].querySelectorAll(selector)); + }; +} + +/** + * Add closest() to jqLite. + */ +if (angular.element.prototype.closest === undefined) { + angular.element.prototype.closest = function( selector) { + var elem = this[0]; + var matchesSelector = elem.matches || elem.webkitMatchesSelector || elem.mozMatchesSelector || elem.msMatchesSelector; + + while (elem) { + if (matchesSelector.bind(elem)(selector)) { + return elem; + } else { + elem = elem.parentElement; + } + } + return false; + }; +} + +var latestId = 0; + +var uis = angular.module('ui.select', []) + +.constant('uiSelectConfig', { + theme: 'bootstrap', + searchEnabled: true, + sortable: false, + placeholder: '', // Empty by default, like HTML tag