diff --git a/akvo/rsr/static/lib/scripts/react-typeahead-1.0.8.js b/akvo/rsr/static/lib/scripts/react-typeahead-1.0.8.js deleted file mode 100644 index b629a8de04..0000000000 --- a/akvo/rsr/static/lib/scripts/react-typeahead-1.0.8.js +++ /dev/null @@ -1,960 +0,0 @@ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.ReactTypeahead=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;olah' // The rendered string -// , index: 2 // The index of the element in `arr` -// , original: 'blah' // The original element in `arr` -// }] -// -// `opts` is an optional argument bag. Details: -// -// opts = { -// // string to put before a matching character -// pre: '' -// -// // string to put after matching character -// , post: '' -// -// // Optional function. Input is an element from the passed in -// // `arr`, output should be the string to test `pattern` against. -// // In this example, if `arr = [{crying: 'koala'}]` we would return -// // 'koala'. -// , extract: function(arg) { return arg.crying; } -// } -fuzzy.filter = function(pattern, arr, opts) { - opts = opts || {}; - return arr - .reduce(function(prev, element, idx, arr) { - var str = element; - if(opts.extract) { - str = opts.extract(element); - } - var rendered = fuzzy.match(pattern, str, opts); - if(rendered != null) { - prev[prev.length] = { - string: rendered.rendered - , score: rendered.score - , index: idx - , original: element - }; - } - return prev; - }, []) - - // Sort by score. Browsers are inconsistent wrt stable/unstable - // sorting, so force stable by using the index in the case of tie. - // See http://ofb.net/~sethml/is-sort-stable.html - .sort(function(a,b) { - var compare = b.score - a.score; - if(compare) return compare; - return a.index - b.index; - }); -}; - - -}()); - - -},{}],3:[function(require,module,exports){ -/** - * PolyFills make me sad - */ -var KeyEvent = KeyEvent || {}; -KeyEvent.DOM_VK_UP = KeyEvent.DOM_VK_UP || 38; -KeyEvent.DOM_VK_DOWN = KeyEvent.DOM_VK_DOWN || 40; -KeyEvent.DOM_VK_BACK_SPACE = KeyEvent.DOM_VK_BACK_SPACE || 8; -KeyEvent.DOM_VK_RETURN = KeyEvent.DOM_VK_RETURN || 13; -KeyEvent.DOM_VK_ENTER = KeyEvent.DOM_VK_ENTER || 14; -KeyEvent.DOM_VK_ESCAPE = KeyEvent.DOM_VK_ESCAPE || 27; -KeyEvent.DOM_VK_TAB = KeyEvent.DOM_VK_TAB || 9; - -module.exports = KeyEvent; - - -},{}],4:[function(require,module,exports){ -var Typeahead = require('./typeahead'); -var Tokenizer = require('./tokenizer'); - -module.exports = { - Typeahead: Typeahead, - Tokenizer: Tokenizer -}; - - -},{"./tokenizer":5,"./typeahead":7}],5:[function(require,module,exports){ -/** - * @jsx React.DOM - */ - -var React = window.React || require('react'); -var Token = require('./token'); -var KeyEvent = require('../keyevent'); -var Typeahead = require('../typeahead'); -var classNames = require('classnames'); - -function _arraysAreDifferent(array1, array2) { - if (array1.length != array2.length){ - return true; - } - for (var i = array2.length - 1; i >= 0; i--) { - if (array2[i] !== array1[i]){ - return true; - } - } -} - -/** - * A typeahead that, when an option is selected, instead of simply filling - * the text entry widget, prepends a renderable "token", that may be deleted - * by pressing backspace on the beginning of the line with the keyboard. - */ -var TypeaheadTokenizer = React.createClass({displayName: "TypeaheadTokenizer", - propTypes: { - name: React.PropTypes.string, - options: React.PropTypes.array, - customClasses: React.PropTypes.object, - allowCustomValues: React.PropTypes.number, - defaultSelected: React.PropTypes.array, - defaultValue: React.PropTypes.string, - placeholder: React.PropTypes.string, - inputProps: React.PropTypes.object, - onTokenRemove: React.PropTypes.func, - onTokenAdd: React.PropTypes.func, - filterOption: React.PropTypes.func, - maxVisible: React.PropTypes.number - }, - - getInitialState: function() { - return { - // We need to copy this to avoid incorrect sharing - // of state across instances (e.g., via getDefaultProps()) - selected: this.props.defaultSelected.slice(0) - }; - }, - - getDefaultProps: function() { - return { - options: [], - defaultSelected: [], - customClasses: {}, - allowCustomValues: 0, - defaultValue: "", - placeholder: "", - inputProps: {}, - onTokenAdd: function() {}, - onTokenRemove: function() {} - }; - }, - - componentWillReceiveProps: function(nextProps){ - // if we get new defaultProps, update selected - if (_arraysAreDifferent(this.props.defaultSelected, nextProps.defaultSelected)){ - this.setState({selected: nextProps.defaultSelected.slice(0)}) - } - }, - - // TODO: Support initialized tokens - // - _renderTokens: function() { - var tokenClasses = {}; - tokenClasses[this.props.customClasses.token] = !!this.props.customClasses.token; - var classList = classNames(tokenClasses); - var result = this.state.selected.map(function(selected) { - return ( - React.createElement(Token, {key: selected, className: classList, - onRemove: this._removeTokenForValue, - name: this.props.name}, - selected - ) - ); - }, this); - return result; - }, - - _getOptionsForTypeahead: function() { - // return this.props.options without this.selected - return this.props.options; - }, - - _onKeyDown: function(event) { - // We only care about intercepting backspaces - if (event.keyCode !== KeyEvent.DOM_VK_BACK_SPACE) { - return; - } - - // No tokens - if (!this.state.selected.length) { - return; - } - - // Remove token ONLY when bksp pressed at beginning of line - // without a selection - var entry = this.refs.typeahead.refs.entry.getDOMNode(); - if (entry.selectionStart == entry.selectionEnd && - entry.selectionStart == 0) { - this._removeTokenForValue( - this.state.selected[this.state.selected.length - 1]); - event.preventDefault(); - } - }, - - _removeTokenForValue: function(value) { - var index = this.state.selected.indexOf(value); - if (index == -1) { - return; - } - - this.state.selected.splice(index, 1); - this.setState({selected: this.state.selected}); - this.props.onTokenRemove(this.state.selected, value); - return; - }, - - _addTokenForValue: function(value) { - if (this.state.selected.indexOf(value) != -1) { - return; - } - this.state.selected.push(value); - this.setState({selected: this.state.selected}); - this.refs.typeahead.setEntryText(""); - this.props.onTokenAdd(this.state.selected, value); - }, - - render: function() { - var classes = {}; - classes[this.props.customClasses.typeahead] = !!this.props.customClasses.typeahead; - var classList = classNames(classes); - return ( - React.createElement("div", {className: "typeahead-tokenizer"}, - this._renderTokens(), - React.createElement(Typeahead, {ref: "typeahead", - className: classList, - placeholder: this.props.placeholder, - inputProps: this.props.inputProps, - allowCustomValues: this.props.allowCustomValues, - customClasses: this.props.customClasses, - options: this._getOptionsForTypeahead(), - defaultValue: this.props.defaultValue, - maxVisible: this.props.maxVisible, - onOptionSelected: this._addTokenForValue, - onKeyDown: this._onKeyDown, - filterOption: this.props.filterOption}) - ) - ); - } -}); - -module.exports = TypeaheadTokenizer; - - -},{"../keyevent":3,"../typeahead":7,"./token":6,"classnames":1,"react":"react"}],6:[function(require,module,exports){ -/** - * @jsx React.DOM - */ - -var React = window.React || require('react'); -var classNames = require('classnames'); - -/** - * Encapsulates the rendering of an option that has been "selected" in a - * TypeaheadTokenizer - */ -var Token = React.createClass({displayName: "Token", - propTypes: { - className: React.PropTypes.string, - name: React.PropTypes.string, - children: React.PropTypes.string, - onRemove: React.PropTypes.func - }, - - render: function() { - var className = classNames([ - "typeahead-token", - this.props.className - ]); - - return ( - React.createElement("div", {className: className}, - this._renderHiddenInput(), - this.props.children, - this._renderCloseButton() - ) - ); - }, - - _renderHiddenInput: function() { - // If no name was set, don't create a hidden input - if (!this.props.name) { - return null; - } - - return ( - React.createElement("input", { - type: "hidden", - name: this.props.name + '[]', - value: this.props.children} - ) - ); - }, - - _renderCloseButton: function() { - if (!this.props.onRemove) { - return ""; - } - return ( - React.createElement("a", {className: "typeahead-token-close", href: "#", onClick: function(event) { - this.props.onRemove(this.props.children); - event.preventDefault(); - }.bind(this)}, "×") - ); - } -}); - -module.exports = Token; - - -},{"classnames":1,"react":"react"}],7:[function(require,module,exports){ -/** - * @jsx React.DOM - */ - -var React = window.React || require('react'); -var TypeaheadSelector = require('./selector'); -var KeyEvent = require('../keyevent'); -var fuzzy = require('fuzzy'); -var classNames = require('classnames'); - -var IDENTITY_FN = function(input) { return input; }; -var _generateAccessor = function(field) { - return function(object) { return object[field]; }; -}; - -/** - * A "typeahead", an auto-completing text input - * - * Renders an text input that shows options nearby that you can use the - * keyboard or mouse to select. Requires CSS for MASSIVE DAMAGE. - */ -var Typeahead = React.createClass({displayName: "Typeahead", - propTypes: { - name: React.PropTypes.string, - customClasses: React.PropTypes.object, - maxVisible: React.PropTypes.number, - options: React.PropTypes.array, - allowCustomValues: React.PropTypes.number, - defaultValue: React.PropTypes.string, - placeholder: React.PropTypes.string, - inputProps: React.PropTypes.object, - onOptionSelected: React.PropTypes.func, - onChange: React.PropTypes.func, - onKeyDown: React.PropTypes.func, - onKeyUp: React.PropTypes.func, - onFocus: React.PropTypes.func, - onBlur: React.PropTypes.func, - filterOption: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.func - ]), - displayOption: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.func - ]), - formInputOption: React.PropTypes.oneOfType([ - React.PropTypes.string, - React.PropTypes.func - ]) - }, - - getDefaultProps: function() { - return { - options: [], - customClasses: {}, - allowCustomValues: 0, - defaultValue: "", - placeholder: "", - inputProps: {}, - onOptionSelected: function(option) {}, - onChange: function(event) {}, - onKeyDown: function(event) {}, - onKeyUp: function(event) {}, - onFocus: function(event) {}, - onBlur: function(event) {}, - filterOption: null - }; - }, - - getInitialState: function() { - return { - // The currently visible set of options - visible: this.getOptionsForValue(this.props.defaultValue, this.props.options), - - // This should be called something else, "entryValue" - entryValue: this.props.defaultValue, - - // A valid typeahead value - selection: null - }; - }, - - getOptionsForValue: function(value, options) { - var filterOptions = this._generateFilterFunction(); - var result = filterOptions(value, options); - if (this.props.maxVisible) { - result = result.slice(0, this.props.maxVisible); - } - return result; - }, - - setEntryText: function(value) { - this.refs.entry.getDOMNode().value = value; - this._onTextEntryUpdated(); - }, - - _hasCustomValue: function() { - if (this.props.allowCustomValues > 0 && - this.state.entryValue.length >= this.props.allowCustomValues && - this.state.visible.indexOf(this.state.entryValue) < 0) { - return true; - } - return false; - }, - - _getCustomValue: function() { - if (this._hasCustomValue()) { - return this.state.entryValue; - } - return null - }, - - _renderIncrementalSearchResults: function() { - // Nothing has been entered into the textbox - if (!this.state.entryValue) { - return ""; - } - - // Something was just selected - if (this.state.selection) { - return ""; - } - - // There are no typeahead / autocomplete suggestions - if (!this.state.visible.length && !(this.props.allowCustomValues > 0)) { - return ""; - } - - if (this._hasCustomValue()) { - return ( - React.createElement(TypeaheadSelector, { - ref: "sel", options: this.state.visible, - customValue: this.state.entryValue, - onOptionSelected: this._onOptionSelected, - customClasses: this.props.customClasses, - displayOption: this._generateOptionToStringFor(this.props.displayOption)}) - ); - } - - return ( - React.createElement(TypeaheadSelector, { - ref: "sel", options: this.state.visible, - onOptionSelected: this._onOptionSelected, - customClasses: this.props.customClasses, - displayOption: this._generateOptionToStringFor(this.props.displayOption)}) - ); - }, - - _onOptionSelected: function(option, event) { - var nEntry = this.refs.entry.getDOMNode(); - nEntry.focus(); - - var displayOption = this._generateOptionToStringFor(this.props.displayOption); - var optionString = displayOption(option, 0); - - var formInputOption = this._generateOptionToStringFor(this.props.formInputOption || displayOption); - var formInputOptionString = formInputOption(option); - - nEntry.value = optionString; - this.setState({visible: this.getOptionsForValue(optionString, this.props.options), - selection: formInputOptionString, - entryValue: optionString}); - return this.props.onOptionSelected(option, event); - }, - - _onTextEntryUpdated: function() { - var value = this.refs.entry.getDOMNode().value; - this.setState({visible: this.getOptionsForValue(value, this.props.options), - selection: null, - entryValue: value}); - }, - - _onEnter: function(event) { - if (!this.refs.sel.state.selection) { - return this.props.onKeyDown(event); - } - return this._onOptionSelected(this.refs.sel.state.selection, event); - }, - - _onEscape: function() { - this.refs.sel.setSelectionIndex(null) - }, - - _onTab: function(event) { - var option = this.refs.sel.state.selection ? - this.refs.sel.state.selection : (this.state.visible.length > 0 ? this.state.visible[0] : null); - - if (option === null && this._hasCustomValue()) { - option = this._getCustomValue(); - } - - if (option !== null) { - return this._onOptionSelected(option, event); - } - }, - - eventMap: function(event) { - var events = {}; - - events[KeyEvent.DOM_VK_UP] = this.refs.sel.navUp; - events[KeyEvent.DOM_VK_DOWN] = this.refs.sel.navDown; - events[KeyEvent.DOM_VK_RETURN] = events[KeyEvent.DOM_VK_ENTER] = this._onEnter; - events[KeyEvent.DOM_VK_ESCAPE] = this._onEscape; - events[KeyEvent.DOM_VK_TAB] = this._onTab; - - return events; - }, - - _onChange: function(event) { - if (this.props.onChange) { - this.props.onChange(event); - } - - this._onTextEntryUpdated(); - }, - - _onKeyDown: function(event) { - // If there are no visible elements, don't perform selector navigation. - // Just pass this up to the upstream onKeydown handler - if (!this.refs.sel) { - return this.props.onKeyDown(event); - } - - var handler = this.eventMap()[event.keyCode]; - - if (handler) { - handler(event); - } else { - return this.props.onKeyDown(event); - } - // Don't propagate the keystroke back to the DOM/browser - event.preventDefault(); - }, - - componentWillReceiveProps: function(nextProps) { - this.setState({ - visible: this.getOptionsForValue(this.state.entryValue, nextProps.options) - }); - }, - - render: function() { - var inputClasses = {} - inputClasses[this.props.customClasses.input] = !!this.props.customClasses.input; - var inputClassList = classNames(inputClasses); - - var classes = { - typeahead: true - } - classes[this.props.className] = !!this.props.className; - var classList = classNames(classes); - - return ( - React.createElement("div", {className: classList}, - this._renderHiddenInput(), - React.createElement("input", React.__spread({ref: "entry", type: "text"}, - this.props.inputProps, - {placeholder: this.props.placeholder, - className: inputClassList, - value: this.state.entryValue, - defaultValue: this.props.defaultValue, - onChange: this._onChange, - onKeyDown: this._onKeyDown, - onKeyUp: this.props.onKeyUp, - onFocus: this.props.onFocus, - onBlur: this.props.onBlur}) - ), - this._renderIncrementalSearchResults() - ) - ); - }, - - _renderHiddenInput: function() { - if (!this.props.name) { - return null; - } - - return ( - React.createElement("input", { - type: "hidden", - name: this.props.name, - value: this.state.selection} - ) - ); - }, - - _generateFilterFunction: function() { - var filterOptionProp = this.props.filterOption; - if (typeof filterOptionProp === 'function') { - return function(value, options) { - return options.filter(function(o) { return filterOptionProp(value, o); }); - }; - } else { - var mapper; - if (typeof filterOptionProp === 'string') { - mapper = _generateAccessor(filterOptionProp); - } else { - mapper = IDENTITY_FN; - } - return function(value, options) { - var transformedOptions = options.map(mapper); - return fuzzy - .filter(value, transformedOptions) - .map(function(res) { return options[res.index]; }); - }; - } - }, - - _generateOptionToStringFor: function(prop) { - if (typeof prop === 'string') { - return _generateAccessor(prop); - } else if (typeof prop === 'function') { - return prop; - } else { - return IDENTITY_FN; - } - } -}); - -module.exports = Typeahead; - - -},{"../keyevent":3,"./selector":9,"classnames":1,"fuzzy":2,"react":"react"}],8:[function(require,module,exports){ -/** - * @jsx React.DOM - */ - -var React = window.React || require('react'); -var classNames = require('classnames'); - -/** - * A single option within the TypeaheadSelector - */ -var TypeaheadOption = React.createClass({displayName: "TypeaheadOption", - propTypes: { - customClasses: React.PropTypes.object, - customValue: React.PropTypes.string, - onClick: React.PropTypes.func, - children: React.PropTypes.string, - hover: React.PropTypes.bool - }, - - getDefaultProps: function() { - return { - customClasses: {}, - onClick: function(event) { - event.preventDefault(); - } - }; - }, - - getInitialState: function() { - return {}; - }, - - render: function() { - var classes = {}; - classes[this.props.customClasses.hover || "hover"] = !!this.props.hover; - classes[this.props.customClasses.listItem] = !!this.props.customClasses.listItem; - - if (this.props.customValue) { - classes[this.props.customClasses.customAdd] = !!this.props.customClasses.customAdd; - } - - var classList = classNames(classes); - - return ( - React.createElement("li", {className: classList, onClick: this._onClick}, - React.createElement("a", {href: "javascript: void 0;", className: this._getClasses(), ref: "anchor"}, - this.props.children - ) - ) - ); - }, - - _getClasses: function() { - var classes = { - "typeahead-option": true, - }; - classes[this.props.customClasses.listAnchor] = !!this.props.customClasses.listAnchor; - - return classNames(classes); - }, - - _onClick: function(event) { - event.preventDefault(); - return this.props.onClick(event); - } -}); - - -module.exports = TypeaheadOption; - - -},{"classnames":1,"react":"react"}],9:[function(require,module,exports){ -/** - * @jsx React.DOM - */ - -var React = window.React || require('react'); -var TypeaheadOption = require('./option'); -var classNames = require('classnames'); - -/** - * Container for the options rendered as part of the autocompletion process - * of the typeahead - */ -var TypeaheadSelector = React.createClass({displayName: "TypeaheadSelector", - propTypes: { - options: React.PropTypes.array, - customClasses: React.PropTypes.object, - customValue: React.PropTypes.string, - selectionIndex: React.PropTypes.number, - onOptionSelected: React.PropTypes.func, - displayOption: React.PropTypes.func.isRequired - }, - - getDefaultProps: function() { - return { - selectionIndex: null, - customClasses: {}, - customValue: null, - onOptionSelected: function(option) { } - }; - }, - - getInitialState: function() { - return { - selectionIndex: this.props.selectionIndex, - selection: this.getSelectionForIndex(this.props.selectionIndex) - }; - }, - - render: function() { - var classes = { - "typeahead-selector": true - }; - classes[this.props.customClasses.results] = this.props.customClasses.results; - var classList = classNames(classes); - - var results = []; - // CustomValue should be added to top of results list with different class name - if (this.props.customValue !== null) { - - results.push( - React.createElement(TypeaheadOption, {ref: this.props.customValue, key: this.props.customValue, - hover: this.state.selectionIndex === results.length, - customClasses: this.props.customClasses, - customValue: this.props.customValue, - onClick: this._onClick.bind(this, this.props.customValue)}, - this.props.customValue - )); - } - - this.props.options.forEach(function(result, i) { - var displayString = this.props.displayOption(result, i); - results.push ( - React.createElement(TypeaheadOption, {ref: displayString, key: displayString, - hover: this.state.selectionIndex === results.length, - customClasses: this.props.customClasses, - onClick: this._onClick.bind(this, result)}, - displayString - ) - ); - }, this); - - - return React.createElement("ul", {className: classList}, results ); - }, - - setSelectionIndex: function(index) { - this.setState({ - selectionIndex: index, - selection: this.getSelectionForIndex(index), - }); - }, - - getSelectionForIndex: function(index) { - if (index === null) { - return null; - } - if (index === 0 && this.props.customValue !== null) { - return this.props.customValue; - } - - if (this.props.customValue !== null) { - index -= 1; - } - - return this.props.options[index]; - }, - - _onClick: function(result, event) { - return this.props.onOptionSelected(result, event); - }, - - _nav: function(delta) { - if (!this.props.options && this.props.customValue === null) { - return; - } - var newIndex = this.state.selectionIndex === null ? (delta == 1 ? 0 : delta) : this.state.selectionIndex + delta; - var length = this.props.options.length; - if (this.props.customValue !== null) { - length += 1; - } - - if (newIndex < 0) { - newIndex += length; - } else if (newIndex >= length) { - newIndex -= length; - } - - var newSelection = this.getSelectionForIndex(newIndex); - this.setState({selectionIndex: newIndex, - selection: newSelection}); - }, - - navDown: function() { - this._nav(1); - }, - - navUp: function() { - this._nav(-1); - } - -}); - -module.exports = TypeaheadSelector; - - -},{"./option":8,"classnames":1,"react":"react"}]},{},[4])(4) -}); \ No newline at end of file diff --git a/akvo/rsr/static/lib/scripts/react-typeahead-1.1.5.js b/akvo/rsr/static/lib/scripts/react-typeahead.js similarity index 100% rename from akvo/rsr/static/lib/scripts/react-typeahead-1.1.5.js rename to akvo/rsr/static/lib/scripts/react-typeahead.js diff --git a/akvo/rsr/static/lib/scripts/typeahead-0.10.5.js b/akvo/rsr/static/lib/scripts/typeahead.js similarity index 100% rename from akvo/rsr/static/lib/scripts/typeahead-0.10.5.js rename to akvo/rsr/static/lib/scripts/typeahead.js diff --git a/akvo/rsr/static/scripts-src/my-details-employments.js b/akvo/rsr/static/scripts-src/my-details-employments.js index aa09751c00..56a09520d8 100644 --- a/akvo/rsr/static/scripts-src/my-details-employments.js +++ b/akvo/rsr/static/scripts-src/my-details-employments.js @@ -7,13 +7,12 @@ /* Global variables */ -var Typeahead = ReactTypeahead.Typeahead; - var countries, i18n, initial_data, organisations, - request_link; + request_link, + Typeahead; var orgsAPIUrl = '/rest/v1/typeaheads/organisations?format=json', countriesAPIUrl = '/rest/v1/typeaheads/countries?format=json'; @@ -37,353 +36,356 @@ function getCookie(name) { var csrftoken = getCookie('csrftoken'); -var Employment = React.createClass({displayName: 'Employment', - getInitialState: function() { - return { - visible: true, - deleting: false - }; - }, - - handleDelete: function() { - // Delete current employment - this.setState({deleting: true}); - - var xmlHttp = new XMLHttpRequest(); - var thisEmployment = this; - xmlHttp.onreadystatechange = function() { - if (xmlHttp.readyState == XMLHttpRequest.DONE) { - thisEmployment.setState({deleting: false}); - if (xmlHttp.status >= 200 && xmlHttp.status < 400){ - thisEmployment.props.removeEmployment(thisEmployment.props.employment.id); - return false; +/* Build React components of Employments app */ +function buildEmploymentsApp() { + // Load globals + Typeahead = ReactTypeahead.Typeahead; + + var Employment = React.createClass({displayName: 'Employment', + getInitialState: function() { + return { + visible: true, + deleting: false + }; + }, + + handleDelete: function() { + // Delete current employment + this.setState({deleting: true}); + + var xmlHttp = new XMLHttpRequest(); + var thisEmployment = this; + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState == XMLHttpRequest.DONE) { + thisEmployment.setState({deleting: false}); + if (xmlHttp.status >= 200 && xmlHttp.status < 400){ + thisEmployment.props.removeEmployment(thisEmployment.props.employment.id); + return false; + } } + }; + xmlHttp.open("DELETE", "/rest/v1/employment/" + this.props.employment.id + "/", true); + xmlHttp.setRequestHeader("X-CSRFToken", csrftoken); + xmlHttp.send(); + }, + + render: function() { + if (!this.state.visible) { + return React.createElement("li", {}); } - }; - xmlHttp.open("DELETE", "/rest/v1/employment/" + this.props.employment.id + "/", true); - xmlHttp.setRequestHeader("X-CSRFToken", csrftoken); - xmlHttp.send(); - }, - - render: function() { - if (!this.state.visible) { - return React.createElement("li", {}); - } - var deleteButton; - if (!this.state.deleting) { - var buttonStyle = {cursor: 'pointer'}; - deleteButton = React.createElement("i", { - className: 'fa fa-times help-block-error', - style: buttonStyle, - onClick: this.handleDelete - }); - } else { - deleteButton = React.createElement("i", {className: 'fa fa-spinner fa-spin'}); - } + var deleteButton; + if (!this.state.deleting) { + var buttonStyle = {cursor: 'pointer'}; + deleteButton = React.createElement("i", { + className: 'fa fa-times help-block-error', + style: buttonStyle, + onClick: this.handleDelete + }); + } else { + deleteButton = React.createElement("i", {className: 'fa fa-spinner fa-spin'}); + } - if (this.props.employment.is_approved) { - var groupName = React.createElement("i", null, '(', this.props.employment.group.name.slice(0, -1), ')'); - var employmentNode = React.createElement("li", {}, this.props.employment.organisation_full.name, ' ', groupName, ' ', deleteButton); - return React.createElement("b", null, employmentNode); - } else { - var notApproved = React.createElement("i", null, '(', i18n.not_approved_text, ')'); - return React.createElement("li", {}, this.props.employment.organisation_full.name, ' ', notApproved, ' ', deleteButton); - } + if (this.props.employment.is_approved) { + var groupName = React.createElement("i", null, '(', this.props.employment.group.name.slice(0, -1), ')'); + var employmentNode = React.createElement("li", {}, this.props.employment.organisation_full.name, ' ', groupName, ' ', deleteButton); + return React.createElement("b", null, employmentNode); + } else { + var notApproved = React.createElement("i", null, '(', i18n.not_approved_text, ')'); + return React.createElement("li", {}, this.props.employment.organisation_full.name, ' ', notApproved, ' ', deleteButton); + } - } -}); + } + }); -var EmploymentList = React.createClass({displayName: 'EmploymentList', - render: function() { - var thisEmploymentList = this; - var employments = this.props.employments.map(function(job) { - return React.createElement(Employment, {key: job.id, employment: job, removeEmployment: thisEmploymentList.props.removeEmployment}); - }); + var EmploymentList = React.createClass({displayName: 'EmploymentList', + render: function() { + var thisEmploymentList = this; + var employments = this.props.employments.map(function(job) { + return React.createElement(Employment, {key: job.id, employment: job, removeEmployment: thisEmploymentList.props.removeEmployment}); + }); - return React.createElement("ul", null, employments); - } -}); + return React.createElement("ul", null, employments); + } + }); -var OrganisationInput = React.createClass({displayName: 'OrganisationInput', - typeaheadCallback: function(option) { - document.getElementById('organisationInput').setAttribute('value', option.id); - }, + var OrganisationInput = React.createClass({displayName: 'OrganisationInput', + typeaheadCallback: function(option) { + document.getElementById('organisationInput').setAttribute('value', option.id); + }, - render: function() { - var inputProps = {id: 'organisationInput'}; - if (this.props.loading) { - inputProps.disabled = true; + render: function() { + var inputProps = {id: 'organisationInput'}; + if (this.props.loading) { + inputProps.disabled = true; + } + var orgTypeahead = React.createElement(Typeahead, { + placeholder: i18n.organisation_text, + maxVisible: 10, + options: organisations, + onOptionSelected: this.typeaheadCallback, + displayOption: 'displayOption', + filterOption: 'filterOption', + customClasses: {input: 'form-control'}, + inputProps: inputProps + }); + var div = React.createFactory('div'); + var formGroupClass = this.props.orgError ? 'form-group has-error' : 'form-group'; + return div({className: formGroupClass}, orgTypeahead); } - var orgTypeahead = React.createElement(Typeahead, { - placeholder: i18n.organisation_text, - maxVisible: 10, - options: organisations, - onOptionSelected: this.typeaheadCallback, - displayOption: 'displayOption', - filterOption: 'filterOption', - customClasses: {input: 'form-control'}, - inputProps: inputProps - }); - var div = React.createFactory('div'); - var formGroupClass = this.props.orgError ? 'form-group has-error' : 'form-group'; - return div({className: formGroupClass}, orgTypeahead); - } -}); + }); -var ErrorNode = React.createClass({displayName: 'ErrorNode', - render: function() { - if (this.props.visible) { - return React.createElement("div", {className: 'help-block-error'}, '* ' + this.props.errorText); - } else { - return React.createElement("span"); + var ErrorNode = React.createClass({displayName: 'ErrorNode', + render: function() { + if (this.props.visible) { + return React.createElement("div", {className: 'help-block-error'}, '* ' + this.props.errorText); + } else { + return React.createElement("span"); + } } - } -}); + }); -var CountryInput = React.createClass({displayName: 'CountryInput', - typeaheadCallback: function(option) { - document.getElementById('countryInput').setAttribute('value', option.id); - }, + var CountryInput = React.createClass({displayName: 'CountryInput', + typeaheadCallback: function(option) { + document.getElementById('countryInput').setAttribute('value', option.id); + }, - render: function() { - var inputProps = {id: 'countryInput'}; - if (this.props.loading) { - inputProps.disabled = true; + render: function() { + var inputProps = {id: 'countryInput'}; + if (this.props.loading) { + inputProps.disabled = true; + } + var countryTypeahead = React.createElement(Typeahead, { + placeholder: i18n.country_text, + maxVisible: 5, + options: countries, + onOptionSelected: this.typeaheadCallback, + displayOption: 'name', + filterOption: 'name', + customClasses: {input: 'form-control'}, + inputProps: inputProps + }); + var div = React.createFactory('div'); + return div({className: 'form-group'}, countryTypeahead); } - var countryTypeahead = React.createElement(Typeahead, { - placeholder: i18n.country_text, - maxVisible: 5, - options: countries, - onOptionSelected: this.typeaheadCallback, - displayOption: 'name', - filterOption: 'name', - customClasses: {input: 'form-control'}, - inputProps: inputProps - }); - var div = React.createFactory('div'); - return div({className: 'form-group'}, countryTypeahead); - } -}); + }); -var JobTitleInput = React.createClass({displayName: 'JobTitleInput', - render: function() { - var input = React.createFactory('input'); - var inputProps = {className: 'form-control', type: 'text', 'placeholder': i18n.job_title_text, id: "jobtitleInput"}; - if (this.props.loading) { - inputProps.disabled = true; + var JobTitleInput = React.createClass({displayName: 'JobTitleInput', + render: function() { + var input = React.createFactory('input'); + var inputProps = {className: 'form-control', type: 'text', 'placeholder': i18n.job_title_text, id: "jobtitleInput"}; + if (this.props.loading) { + inputProps.disabled = true; + } + var jobTitleInput = input(inputProps); + var div = React.createFactory('div'); + return div({className: 'form-group'}, jobTitleInput); } - var jobTitleInput = input(inputProps); - var div = React.createFactory('div'); - return div({className: 'form-group'}, jobTitleInput); - } -}); + }); -var FormButton = React.createClass({displayName: 'FormButton', - handleAddEmployment: function () { - this.props.addEmployment(); - }, - - render: function() { - var loadingIcon = React.createElement("i", {className: 'fa fa-spinner fa-spin'}); - var button = React.createFactory('button'); - if (this.props.loading) { - return button( - { - onClick: this.handleAddEmployment, - className: 'btn btn-primary', - type: "button", - disabled: true - }, - loadingIcon, - ' ', - i18n.sending_request_text - ); - } else { - return button( - { - onClick: this.handleAddEmployment, - className: 'btn btn-default btn-sm', - type: "button" - }, - i18n.request_join_text - ); + var FormButton = React.createClass({displayName: 'FormButton', + handleAddEmployment: function () { + this.props.addEmployment(); + }, + + render: function() { + var loadingIcon = React.createElement("i", {className: 'fa fa-spinner fa-spin'}); + var button = React.createFactory('button'); + if (this.props.loading) { + return button( + { + onClick: this.handleAddEmployment, + className: 'btn btn-primary', + type: "button", + disabled: true + }, + loadingIcon, + ' ', + i18n.sending_request_text + ); + } else { + return button( + { + onClick: this.handleAddEmployment, + className: 'btn btn-default btn-sm', + type: "button" + }, + i18n.request_join_text + ); + } } - } -}); + }); -var AddEmploymentForm = React.createClass({displayName: 'AddEmploymentForm', - getInitialState: function () { - return { - buttonText: i18n.request_join_text, - loading: false, - showError: false, - errorText: '' - }; - }, - - handleAddEmployment: function (employment) { - this.props.addEmployment(employment); - }, - - checkExistingEmployment: function(organisationId) { - return this.props.existingEmployment(organisationId); - }, - - getFormData: function () { - var orgValue = document.getElementById('organisationInput').getAttribute('value'); - var countryValue = document.getElementById('countryInput').getAttribute('value'); - var jobTitleValue = document.getElementById('jobtitleInput').value; - - return { - organisation: orgValue, - country: countryValue, - job_title: jobTitleValue - }; - }, - - addEmployment: function () { - // Retrieve form data - var formData = this.getFormData(); - - // Disable button and form - this.setState({ - loading: true, - showError: false, - errorText: '' - }); - - // Check if organisation is filled in - if (formData.organisation === '') { - this.setState({ + var AddEmploymentForm = React.createClass({displayName: 'AddEmploymentForm', + getInitialState: function () { + return { + buttonText: i18n.request_join_text, loading: false, - showError: true, - errorText: i18n.required_text + showError: false, + errorText: '' + }; + }, + + handleAddEmployment: function (employment) { + this.props.addEmployment(employment); + }, + + checkExistingEmployment: function(organisationId) { + return this.props.existingEmployment(organisationId); + }, + + getFormData: function () { + var orgValue = document.getElementById('organisationInput').getAttribute('value'); + var countryValue = document.getElementById('countryInput').getAttribute('value'); + var jobTitleValue = document.getElementById('jobtitleInput').value; + + return { + organisation: orgValue, + country: countryValue, + job_title: jobTitleValue + }; + }, + + addEmployment: function () { + // Retrieve form data + var formData = this.getFormData(); + + // Disable button and form + this.setState({ + loading: true, + showError: false, + errorText: '' }); - return false; - } - // Check if organisation is a valid Integer and if an employment with that organisation - // already exists - try { - var organisationId = parseInt(formData.organisation); - if (this.checkExistingEmployment(organisationId)) { + // Check if organisation is filled in + if (formData.organisation === '') { this.setState({ loading: false, showError: true, - errorText: i18n.already_connected_text + errorText: i18n.required_text }); return false; } - } catch (err) { - this.setState({ - loading: false, - showError: true, - errorText: i18n.not_connected_text - }); - return false; - } - // POST a new employment record - var xmlHttp = new XMLHttpRequest(); - var thisForm = this; - xmlHttp.onreadystatechange = function() { - if (xmlHttp.readyState == XMLHttpRequest.DONE) { - thisForm.setState({loading: false}); - var response = JSON.parse(xmlHttp.responseText); - if (xmlHttp.status == 200){ - thisForm.setState({loading: false, showError: false, errorText: ''}); - thisForm.handleAddEmployment(response); - return false; - } else if (xmlHttp.status == 409) { - thisForm.setState({ + // Check if organisation is a valid Integer and if an employment with that organisation + // already exists + try { + var organisationId = parseInt(formData.organisation); + if (this.checkExistingEmployment(organisationId)) { + this.setState({ loading: false, showError: true, errorText: i18n.already_connected_text }); return false; - } else { - thisForm.setState({ - loading: false, - showError: true, - errorText: i18n.not_connected_text - }); - return false; } + } catch (err) { + this.setState({ + loading: false, + showError: true, + errorText: i18n.not_connected_text + }); + return false; } - }; - xmlHttp.open("POST", this.props.link + "?format=json", true); - xmlHttp.setRequestHeader("X-CSRFToken", csrftoken); - xmlHttp.setRequestHeader("Content-type", "application/json; charset=UTF-8"); - xmlHttp.send(JSON.stringify(formData)); - }, - - render: function() { - var heading = React.createElement("h4", null, i18n.connect_employer_text); - var errorMessage = React.createElement(ErrorNode, {visible: this.state.showError, errorText: this.state.errorText}); - var orgInput = React.createElement(OrganisationInput, {orgError: this.state.showError, loading: this.state.loading}); - var countryInput = React.createElement(CountryInput, {loading: this.state.loading}); - var jobTitleInput = React.createElement(JobTitleInput, {loading: this.state.loading}); - var formButton = React.createElement(FormButton, {addEmployment: this.addEmployment, loading: this.state.loading}); - var form = React.createElement("form", null, errorMessage, orgInput, countryInput, jobTitleInput, formButton); - return React.createElement("span", null, heading, form); - } -}); + // POST a new employment record + var xmlHttp = new XMLHttpRequest(); + var thisForm = this; + xmlHttp.onreadystatechange = function() { + if (xmlHttp.readyState == XMLHttpRequest.DONE) { + thisForm.setState({loading: false}); + var response = JSON.parse(xmlHttp.responseText); + if (xmlHttp.status == 200){ + thisForm.setState({loading: false, showError: false, errorText: ''}); + thisForm.handleAddEmployment(response); + return false; + } else if (xmlHttp.status == 409) { + thisForm.setState({ + loading: false, + showError: true, + errorText: i18n.already_connected_text + }); + return false; + } else { + thisForm.setState({ + loading: false, + showError: true, + errorText: i18n.not_connected_text + }); + return false; + } + } + }; + xmlHttp.open("POST", this.props.link + "?format=json", true); + xmlHttp.setRequestHeader("X-CSRFToken", csrftoken); + xmlHttp.setRequestHeader("Content-type", "application/json; charset=UTF-8"); + xmlHttp.send(JSON.stringify(formData)); + }, + + render: function() { + var heading = React.createElement("h4", null, i18n.connect_employer_text); + var errorMessage = React.createElement(ErrorNode, {visible: this.state.showError, errorText: this.state.errorText}); + var orgInput = React.createElement(OrganisationInput, {orgError: this.state.showError, loading: this.state.loading}); + var countryInput = React.createElement(CountryInput, {loading: this.state.loading}); + var jobTitleInput = React.createElement(JobTitleInput, {loading: this.state.loading}); + var formButton = React.createElement(FormButton, {addEmployment: this.addEmployment, loading: this.state.loading}); + var form = React.createElement("form", null, errorMessage, orgInput, countryInput, jobTitleInput, formButton); + return React.createElement("span", null, heading, form); + } -var EmploymentApp = React.createClass({displayName: 'EmploymentApp', - getInitialState: function() { - return {employments: []}; - }, + }); - componentDidMount: function() { - if (this.isMounted()) { - this.setState( - {employments: this.props.employments} - ); - } - }, + var EmploymentApp = React.createClass({displayName: 'EmploymentApp', + getInitialState: function() { + return {employments: []}; + }, - existingEmployment: function(organisationId) { - for (var i=0; i < this.state.employments.length; i++) { - if (this.state.employments[i].organisation_full.id === organisationId && - this.state.employments[i].group.name === 'Users') { - return true; + componentDidMount: function() { + if (this.isMounted()) { + this.setState( + {employments: this.props.employments} + ); } - } - return false; - }, - - addEmployment: function(employment) { - this.setState( - {employments: this.state.employments.concat([employment])} - ); - }, - - removeEmployment: function(employmentId) { - for (var i=0; i < this.state.employments.length; i++) { - if (this.state.employments[i].id === employmentId) { - var employments = this.state.employments; - employments.splice(i, 1); - this.setState({employments: employments}); - break; + }, + + existingEmployment: function(organisationId) { + for (var i=0; i < this.state.employments.length; i++) { + if (this.state.employments[i].organisation_full.id === organisationId && + this.state.employments[i].group.name === 'Users') { + return true; + } + } + return false; + }, + + addEmployment: function(employment) { + this.setState( + {employments: this.state.employments.concat([employment])} + ); + }, + + removeEmployment: function(employmentId) { + for (var i=0; i < this.state.employments.length; i++) { + if (this.state.employments[i].id === employmentId) { + var employments = this.state.employments; + employments.splice(i, 1); + this.setState({employments: employments}); + break; + } } + }, + + render: function() { + var icon = React.createFactory("i"); + var headingIcon = icon({className: 'fa fa-users'}); + var heading = React.createElement("h4", null, ' ', i18n.my_organisations_text); + var employmentList = React.createElement(EmploymentList, {employments: this.state.employments, removeEmployment: this.removeEmployment}); + var addEmploymentForm = React.createElement(AddEmploymentForm, {link: this.props.link, org_link: this.props.org_link, country_link: this.props.country_link, addEmployment: this.addEmployment, existingEmployment: this.existingEmployment}); + return React.createElement("span", null, heading, employmentList, addEmploymentForm); } - }, - - render: function() { - var icon = React.createFactory("i"); - var headingIcon = icon({className: 'fa fa-users'}); - var heading = React.createElement("h4", null, ' ', i18n.my_organisations_text); - var employmentList = React.createElement(EmploymentList, {employments: this.state.employments, removeEmployment: this.removeEmployment}); - var addEmploymentForm = React.createElement(AddEmploymentForm, {link: this.props.link, org_link: this.props.org_link, country_link: this.props.country_link, addEmployment: this.addEmployment, existingEmployment: this.existingEmployment}); - return React.createElement("span", null, heading, employmentList, addEmploymentForm); - } -}); + }); -/* Build React components of Employments app */ -function buildEmploymentsApp() { ReactDOM.render( React.createElement( EmploymentApp, @@ -453,8 +455,43 @@ function getInitialData() { i18n = JSON.parse(document.getElementById("my-details-text").innerHTML); } +var loadJS = function(url, implementationCode, location){ + //url is URL of external file, implementationCode is the code + //to be called from the file, location is the location to + //insert the - {% block react_js %} - {# React v0.10.0 #} - - {% endblock react_js %} - + {# RSR default libraries #} {% compressed_js 'rsr' %} {% compressed_js 'rsr_lib' %} @@ -110,9 +106,9 @@ } - {% piwik_tracking_code %} + {# RSR support center help #} + - {# RSR support center help, disabled since it causes errors #} -{# #} + {% piwik_tracking_code %} diff --git a/akvo/templates/myrsr/my_details.html b/akvo/templates/myrsr/my_details.html index 95ee80b62b..dbbc5db01d 100644 --- a/akvo/templates/myrsr/my_details.html +++ b/akvo/templates/myrsr/my_details.html @@ -58,37 +58,42 @@

{% trans 'Change your password' %}

{% endblock %} {% block js %} - {{ block.super }} + {{ block.super }} - {# React 0.14.5 #} - - + {# React #} + + + - {# App data #} - - - - - - - - + "numberOfCountries": {{ country_count }}, + "numberOfOrganisations": {{ organisation_count }} + } + + + + + {# User data #} + + + + + {# Translation strings #} - {# App data #} - - {# Translation strings #} - + {# App data #} + + + {# Translation strings #} + {% compressed_js 'my_change_password' %} - {% compressed_js 'react_typeahead_1_1_5' %} {% compressed_js 'my_details' %} {% endblock js %} diff --git a/akvo/templates/myrsr/my_iati.html b/akvo/templates/myrsr/my_iati.html index aa3e28a07b..6d044c0007 100644 --- a/akvo/templates/myrsr/my_iati.html +++ b/akvo/templates/myrsr/my_iati.html @@ -98,13 +98,13 @@

{% trans 'Existing IATI exports' %}

{% endif %} {% endblock %} -{% block react_js %} - -{% endblock react_js %} - {% block js %} {{ block.super }} + {# React #} + + + {# Translation strings #} - - {% compressed_js 'react_typeahead_1_1_5' %} + + + {# Initial data endpoints #} - + + {# Initial data endpoints #} - - {% compressed_js 'react_typeahead_1_1_5' %} + + + {# Initial data endpoints #} + + {# React CSS #} + {% compressed_css 'react_datepicker_style' %} + + {# React #} + + + + + + {% with project_id_string=project.pk|stringformat:"s" text_input="myrsr/project_editor/fields/text_input.html" textarea_input="myrsr/project_editor/fields/textarea_input.html" choice_input="myrsr/project_editor/fields/choice_input.html" boolean_input="myrsr/project_editor/fields/boolean_input.html" date_input="myrsr/project_editor/fields/date_input.html" typeahead_input="myrsr/project_editor/fields/typeahead_input.html" currency_input="myrsr/project_editor/fields/currency_input.html" percentage_input="myrsr/project_editor/fields/percentage_input.html" file_input="myrsr/project_editor/fields/file_input.html" manytomany_input="myrsr/project_editor/fields/manytomany_input.html" %} {# Section 1 #} @@ -106,15 +118,11 @@

{% trans 'Project Editor' %} {% include "myrsr/project_editor/related_objects/link_input.html" with link='Link.'|add:project_id_string %} - +{# #} {# Section 10 #} {% endwith %} - - {% block react_js %} - - {% endblock react_js %} - - - {% compressed_css 'react_datepicker_style' %} - - {% compressed_js 'react_typeahead' %} - - {% compressed_js 'project_editor' %} {% endblock %} diff --git a/akvo/templates/myrsr/user_management.html b/akvo/templates/myrsr/user_management.html index f6c2afe4d5..8fd24eb307 100644 --- a/akvo/templates/myrsr/user_management.html +++ b/akvo/templates/myrsr/user_management.html @@ -5,48 +5,46 @@ {% block title %}{% trans 'MyRSR - User management' %}{% endblock %} {% block myrsr_main %} - -
-

{% trans 'User management' %}

- {% has_perm 'rsr.change_employment' user as can_change_employments %} - {% if not can_change_employments %} -

- {% trans "You don't have the rights to manage users." %} -

- {% else %} - -
+
+

{% trans 'User management' %}

+ {% has_perm 'rsr.change_employment' user as can_change_employments %} + {% if not can_change_employments %} +

+ {% trans "You don't have the rights to manage users." %} +

+ {% else %} +
-
- {% endblock %} -{% block react_js %} - -{% endblock react_js %} - {% block js %} - {{ block.super }} - - - + {{ block.super }} + + {# Twitter Typeahead #} + + + {# React #} + + + + + {# Google Maps API #} + + + + + {# Translation strings #} + + + {# Descriptions #} + + + {# Translation strings #}