<commit_before>import React from 'react';
import Select from 'react-select';

var ValuesAsNumbersField = React.createClass({
	displayName: 'ValuesAsNumbersField',
	propTypes: {
		label: React.PropTypes.string
	},
	getInitialState () {
		return {
			options: [
				{ value: 10, label: 'Ten' },
				{ value: 11, label: 'Eleven' },
				{ value: 12, label: 'Twelve' },
				{ value: 23, label: 'Twenty-three' },
				{ value: 24, label: 'Twenty-four' }
			],
			matchPos: 'any',
			matchValue: true,
			matchLabel: true,
			value: null,
			multi: false
		};
	},
	onChangeMatchStart(event) {
		this.setState({
			matchPos: event.target.checked ? 'start' : 'any'
		});
	},
	onChangeMatchValue(event) {
		this.setState({
			matchValue: event.target.checked
		});
	},
	onChangeMatchLabel(event) {
		this.setState({
			matchLabel: event.target.checked
		});
	},
	onChange(value) {
		this.setState({ value });
		console.log('Numeric Select value changed to', value);
	},
	onChangeMulti(event) {
		this.setState({
			multi: event.target.checked
		});
	},
	render () {
		var matchProp = 'any';
		if (this.state.matchLabel && !this.state.matchValue) {
			matchProp = 'label';
		}
		if (!this.state.matchLabel && this.state.matchValue) {
			matchProp = 'value';
		}
		return (

{this.props.label}

				<Select
					matchPos={this.state.matchPos}
					matchProp={matchProp}
					multi={this.state.multi}
					onChange={this.onChange}
					options={this.state.options}
					simpleValue
					value={this.state.value}
					/>

						<input type="checkbox" className="checkbox-control" checked={this.state.multi} onChange={this.onChangeMulti} />
						Multi-Select

						<input type="checkbox" className="checkbox-control" checked={this.state.matchValue} onChange={this.onChangeMatchValue} />
						Match value

						<input type="checkbox" className="checkbox-control" checked={this.state.matchLabel} onChange={this.onChangeMatchLabel} />
						Match label

						<input type="checkbox" className="checkbox-control" checked={this.state.matchPos === 'start'} onChange={this.onChangeMatchStart} />
						Only include matches from the start of the string

This example uses simple numeric values

		);
	}
});

module.exports = ValuesAsNumbersField;
<commit_msg>More cleanup<commit_after><|endoftext|>


<commit_before>'use strict';

function AdagiosConfig(data) {
    this.data = data;
}

angular.module('adagios.config', [])

    .provider('readConfig', function ReadConfigProvider() {

        var data = {};

        this.loadJSON = function (value) {
            data = value;
        };

        this.$get = [function getConfigFactory() {
            return new AdagiosConfig(data);
        }];
    });
<commit_msg>Mini refactoring of config data to use native javascript types<commit_after>'use strict';

function AdagiosConfig(data) {
    this.data = data;
}

angular.module('adagios.config', [])

    .provider('readConfig', function ReadConfigProvider() {

        var data = {};

        this.loadJSON = function (value) {
            data = value;
        };

        this.$get = [function () {
            return new AdagiosConfig(data);
        }];
    });
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.Component.extend({

	document: null,

	didRender: function(){
		console.log('json-document.didRender()');
		this._super(...arguments);
		this.renderDocument();
	},

	didUpdateAttrs: function(){
		console.log('json-document.didUpdateAttrs()');
		this._super(...arguments);
		this.renderDocument();
	},

	renderDocument: function() {
		let doc = this.get('document');
		let elemId = this.get('elementId');
		let elem = document.getElementById(elemId);
		if( elem ){
			elem.innerHTML = '';
			let prettyHtml = Ember.String.htmlSafe('
' + this.jsonToHTML(JSON.stringify(doc, undefined, 4)) + '
');
			let d = document.createElement('div');
			d.innerHTML = prettyHtml;
			elem.appendChild(d);
		}
	},

	jsonToHTML: function(json){
		if( json ){
			if (typeof json !== 'string') {
				json = JSON.stringify(json, undefined, 2);
			}
			json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
			return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,  (match) => {
				let cls = 'number';
				if (/^"/.test(match)) {
					if (/:$/.test(match)) {
			  			cls = 'key';
					} else {
						cls = 'string';
					}
				} else if (/true|false/.test(match)) {
					cls = 'boolean';
				} else if (/null/.test(match)) {
					cls = 'null';
				}
				return '' + match + '';
			});
		}
		else{
			return 'Empty';
		}
	}

});<commit_msg>cleanup<commit_after>import Ember from 'ember';

export default Ember.Component.extend({

	document: null,

	didRender: function(){
		this._super(...arguments);
		this.renderDocument();
	},

	didUpdateAttrs: function(){
		this._super(...arguments);
		this.renderDocument();
	},

	renderDocument: function() {
		let doc = this.get('document');
		let elemId = this.get('elementId');
		let elem = document.getElementById(elemId);
		if( elem ){
			elem.innerHTML = '';
			let prettyHtml = Ember.String.htmlSafe('
' + this.jsonToHTML(JSON.stringify(doc, undefined, 4)) + '
');
			let d = document.createElement('div');
			d.innerHTML = prettyHtml;
			elem.appendChild(d);
		}
	},

	jsonToHTML: function(json){
		if( json ){
			if (typeof json !== 'string') {
				json = JSON.stringify(json, undefined, 2);
			}
			json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
			return json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g,  (match) => {
				let cls = 'number';
				if (/^"/.test(match)) {
					if (/:$/.test(match)) {
			  			cls = 'key';
					} else {
						cls = 'string';
					}
				} else if (/true|false/.test(match)) {
					cls = 'boolean';
				} else if (/null/.test(match)) {
					cls = 'null';
				}
				return '' + match + '';
			});
		}
		else{
			return 'Empty';
		}
	}

});<|endoftext|>


<commit_before>/*
 *
 * Youtube
 *
 */

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import shouldPureComponentUpdate from 'react-pure-render/function';

import { Flex, Item } from 'react-flex';

import mapStateToProps from './selectors';

import YoutubeSearch from 'containers/YoutubeSearch';
import YoutubePlaylist from 'containers/YoutubePlaylist';
import YoutubePlayer from 'containers/YoutubePlayer';
import YoutubeControl from 'containers/YoutubeControl';
import YoutubeNotice from 'components/YoutubeNotice';

import { setUpSocket } from './utils';

import styles from './styles.css';

class Youtube extends Component {
  componentWillMount() {
    const { roomName, dispatch } = this.props;
    setUpSocket(roomName, dispatch);
  }

  shouldComponentUpdate = shouldPureComponentUpdate;

  getSidebar() {
    const { showSidebar, showSearch } = this.props;

    if (!showSidebar) {
      return null;
    }

    return (
      <Item
        flex={1}
        className={styles.sidebar}
      >
        {showSearch ? <YoutubeSearch /> : <YoutubePlaylist />}
      </Item>
    );
  }

  render() {
    const style = this.props.currentApp === 'youtube' ? {} : { display: 'none' };

    return (
      <Flex
        className={styles.container}
        style={style}
      >

          <YoutubePlayer />
          <YoutubeControl />
          <YoutubeNotice />

        {this.getSidebar()}
      </Flex>
    );
  }
}

Youtube.propTypes = {
  roomName: PropTypes.string.isRequired,
  currentApp: PropTypes.string.isRequired,
  showSidebar: PropTypes.bool.isRequired,
  showSearch: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired,
};

export default connect(mapStateToProps)(Youtube);
<commit_msg>Fix youtube layout style<commit_after>/*
 *
 * Youtube
 *
 */

import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import shouldPureComponentUpdate from 'react-pure-render/function';

import { Flex, Item } from 'react-flex';

import mapStateToProps from './selectors';

import YoutubeSearch from 'containers/YoutubeSearch';
import YoutubePlaylist from 'containers/YoutubePlaylist';
import YoutubePlayer from 'containers/YoutubePlayer';
import YoutubeControl from 'containers/YoutubeControl';
import YoutubeNotice from 'components/YoutubeNotice';

import { setUpSocket } from './utils';

import styles from './styles.css';

class Youtube extends Component {
  componentWillMount() {
    const { roomName, dispatch } = this.props;
    setUpSocket(roomName, dispatch);
  }

  shouldComponentUpdate = shouldPureComponentUpdate;

  getSidebar() {
    const { showSidebar, showSearch } = this.props;

    if (!showSidebar) {
      return null;
    }

    return (
      <Item
        flex={1}
        className={styles.sidebar}
      >
        {showSearch ? <YoutubeSearch /> : <YoutubePlaylist />}
      </Item>
    );
  }

  render() {
    const style = this.props.currentApp === 'youtube' ? {} : { display: 'none' };

    return (
      <Flex
        alignItems="stretch"
        className={styles.container}
        style={style}
      >

          <YoutubePlayer />
          <YoutubeControl />
          <YoutubeNotice />

        {this.getSidebar()}
      </Flex>
    );
  }
}

Youtube.propTypes = {
  roomName: PropTypes.string.isRequired,
  currentApp: PropTypes.string.isRequired,
  showSidebar: PropTypes.bool.isRequired,
  showSearch: PropTypes.bool.isRequired,
  dispatch: PropTypes.func.isRequired,
};

export default connect(mapStateToProps)(Youtube);
<|endoftext|>


<commit_before>'use strict';

angular.module('arachne.controllers')

    .controller('EntityController', ['$rootScope', '$stateParams', 'searchService', '$scope', '$uibModal', 'Entity',
        '$location', 'arachneSettings', 'Catalog', 'CatalogEntry', 'authService', 'categoryService', 'Query',
        'messageService', 'searchScope',
        function ($rootScope, $stateParams, searchService, $scope, $uibModal, Entity,
                  $location, arachneSettings, Catalog, CatalogEntry, authService, categoryService, Query,
                  messages, searchScope) {

            searchService.initQuery();

            $rootScope.tinyFooter = false;

            $scope.user = authService.getUser();
            $scope.serverUri = "http://" + document.location.host + document.getElementById('baseLink').getAttribute("href");

            categoryService.getCategoriesAsync().then(function (categories) {
                $scope.categories = categories;
            });

            $scope.currentQuery = searchService.currentQuery();

            //if id is not a number (=> string)
            if (isNaN($stateParams.id)) {
                var live = $location.search()["live"] == "true";
                var params;

                if(typeof $stateParams.suffix !== 'undefined')
                {
                    params = {
                        id: $stateParams.id,
                        suffix: $stateParams.suffix,
                        live: live
                    };
                } else{
                    params = {
                        id: $stateParams.id,
                        live: live
                    };
                }

                Entity.books(params, function (data) {
                    $scope = data;

                    if($scope.entityId == null) {
                        $location.url('404');
                    } else {
                        $location.url('entity/' + $scope.entityId);
                    }
                });
            }

            // if no id given, but query get id from search and reload
            if (!$stateParams.id && $scope.currentQuery.hasOwnProperty('resultIndex')) {

                var resultIndex = parseInt($scope.currentQuery.resultIndex);
                searchService.getEntity(resultIndex).then(function (entity) {
                    $location.url('/' + searchScope.currentScopePath() + 'entity/' + entity.entityId + $scope.currentQuery.toString());
                    $location.replace();
                });

            } else {

                var live = $location.search()["live"] == "true";
                var lang = null;
                if(localStorage.lang)
                    lang = localStorage.lang;
                Entity.get({id: $stateParams.id, live: live, lang: lang}, function (data) {

                    $scope.entity = data;
                    // very ugly exception for Berliner Skulpturennetzwerk
                    // hide all data not in datenblatt_berlin
                    if (data.sections[0].label == 'Datenblatt Berlin') {
                        data.sections = data.sections.slice(0,1);
                    }
                    categoryService.getCategoryHref($scope.entity.type).then(function (categoryHref) {
                        $scope.entity.categoryHref = categoryHref;
                    });
                    categoryService.getCategoryKey($scope.entity.type).then(function (key) {
                        $scope.entity.categoryKey = key;
                    });

                    /**
                     * Hide map widget if no marker coordinates are provided
                     * Jan G. Wieners
                     */
                    if (data.places !== undefined) {

                        var cur, locationsExist = false, len = data.places.length;
                        for (var j = len; j--;) {

                            cur = data.places[j].location;
                            if (cur && cur.lat && cur.lon) {
                                locationsExist = true;
                                break;
                            }
                        }
                    }
                    if (!locationsExist) {
                        $scope.entity.places = false;
                    }

                    if (data.lastModified) {
                        $scope.entity.lastModified = new Date(data.lastModified).toISOString();
                    } else {
                        $scope.entity.lastModified = "";
                    }

                    document.title = $scope.entity.title + " | Arachne";

                }, function (response) {
                    $scope.error = true;
                    messages.add("entity_" + response.status);
                });

                $scope.contextQuery = new Query();
                $scope.contextQuery.label = "Mit " + $stateParams.id + " verknüpfte Objekte";
                $scope.contextQuery.q = "connectedEntities:" + $stateParams.id;
                $scope.contextQuery.limit = 0;

                if ($scope.currentQuery.hasOwnProperty('resultIndex')) {

                    $scope.resultIndex = parseInt($scope.currentQuery.resultIndex);
                    $scope.resultIndexInput = $scope.resultIndex;
                    searchService.getCurrentPage().then(function (results) {
                        $scope.searchresults = results;
                        $scope.resultSize = searchService.getSize();
                    }, function (response) {
                        $scope.searchresults = {size: 0};
                        messages.add('search_' + response.status);
                    });

                    var prevIndex = $scope.resultIndex - 1;
                    $scope.prevEntity = searchService.getEntity(prevIndex).then(function (entity) {
                        $scope.prevEntity = entity;
                    }, function () {
                        $scope.prevEntity = false;
                    });
                    var nextIndex = $scope.resultIndex + 1;
                    $scope.nextEntity = searchService.getEntity(nextIndex).then(function (entity) {
                        $scope.nextEntity = entity;
                    }, function () {
                        $scope.prevEntity = false;
                    });

                }
            }
        }
    ]);
<commit_msg>Fix faulty conditional<commit_after>'use strict';

angular.module('arachne.controllers')

    .controller('EntityController', ['$rootScope', '$stateParams', 'searchService', '$scope', '$uibModal', 'Entity',
        '$location', 'arachneSettings', 'Catalog', 'CatalogEntry', 'authService', 'categoryService', 'Query',
        'messageService', 'searchScope',
        function ($rootScope, $stateParams, searchService, $scope, $uibModal, Entity,
                  $location, arachneSettings, Catalog, CatalogEntry, authService, categoryService, Query,
                  messages, searchScope) {

            searchService.initQuery();

            $rootScope.tinyFooter = false;

            $scope.user = authService.getUser();
            $scope.serverUri = "http://" + document.location.host + document.getElementById('baseLink').getAttribute("href");

            categoryService.getCategoriesAsync().then(function (categories) {
                $scope.categories = categories;
            });

            $scope.currentQuery = searchService.currentQuery();

            //if id is not a number (=> string)
            if (isNaN($stateParams.id)) {
                var live = $location.search()["live"] == "true";
                var params;

                if(typeof $stateParams.suffix !== 'undefined')
                {
                    params = {
                        id: $stateParams.id,
                        suffix: $stateParams.suffix,
                        live: live
                    };
                } else{
                    params = {
                        id: $stateParams.id,
                        live: live
                    };
                }

                Entity.books(params, function (data) {
                    $scope = data;

                    if($scope.entityId == null) {
                        $location.url('404');
                    } else {
                        $location.url('entity/' + $scope.entityId);
                    }
                });
            }
            // if no id given, but query get id from search and reload
            else if (!$stateParams.id && $scope.currentQuery.hasOwnProperty('resultIndex')) {

                var resultIndex = parseInt($scope.currentQuery.resultIndex);
                searchService.getEntity(resultIndex).then(function (entity) {
                    $location.url('/' + searchScope.currentScopePath() + 'entity/' + entity.entityId + $scope.currentQuery.toString());
                    $location.replace();
                });
            } else {

                var live = $location.search()["live"] == "true";
                var lang = null;
                if(localStorage.lang)
                    lang = localStorage.lang;
                Entity.get({id: $stateParams.id, live: live, lang: lang}, function (data) {

                    $scope.entity = data;
                    // very ugly exception for Berliner Skulpturennetzwerk
                    // hide all data not in datenblatt_berlin
                    if (data.sections[0].label == 'Datenblatt Berlin') {
                        data.sections = data.sections.slice(0,1);
                    }
                    categoryService.getCategoryHref($scope.entity.type).then(function (categoryHref) {
                        $scope.entity.categoryHref = categoryHref;
                    });
                    categoryService.getCategoryKey($scope.entity.type).then(function (key) {
                        $scope.entity.categoryKey = key;
                    });

                    /**
                     * Hide map widget if no marker coordinates are provided
                     * Jan G. Wieners
                     */
                    if (data.places !== undefined) {

                        var cur, locationsExist = false, len = data.places.length;
                        for (var j = len; j--;) {

                            cur = data.places[j].location;
                            if (cur && cur.lat && cur.lon) {
                                locationsExist = true;
                                break;
                            }
                        }
                    }
                    if (!locationsExist) {
                        $scope.entity.places = false;
                    }

                    if (data.lastModified) {
                        $scope.entity.lastModified = new Date(data.lastModified).toISOString();
                    } else {
                        $scope.entity.lastModified = "";
                    }

                    document.title = $scope.entity.title + " | Arachne";

                }, function (response) {
                    $scope.error = true;
                    messages.add("entity_" + response.status);
                });

                $scope.contextQuery = new Query();
                $scope.contextQuery.label = "Mit " + $stateParams.id + " verknüpfte Objekte";
                $scope.contextQuery.q = "connectedEntities:" + $stateParams.id;
                $scope.contextQuery.limit = 0;

                if ($scope.currentQuery.hasOwnProperty('resultIndex')) {

                    $scope.resultIndex = parseInt($scope.currentQuery.resultIndex);
                    $scope.resultIndexInput = $scope.resultIndex;
                    searchService.getCurrentPage().then(function (results) {
                        $scope.searchresults = results;
                        $scope.resultSize = searchService.getSize();
                    }, function (response) {
                        $scope.searchresults = {size: 0};
                        messages.add('search_' + response.status);
                    });

                    var prevIndex = $scope.resultIndex - 1;
                    $scope.prevEntity = searchService.getEntity(prevIndex).then(function (entity) {
                        $scope.prevEntity = entity;
                    }, function () {
                        $scope.prevEntity = false;
                    });
                    var nextIndex = $scope.resultIndex + 1;
                    $scope.nextEntity = searchService.getEntity(nextIndex).then(function (entity) {
                        $scope.nextEntity = entity;
                    }, function () {
                        $scope.prevEntity = false;
                    });

                }
            }
        }
    ]);
<|endoftext|>


<commit_before>// Import TinyMCE
import tinymce from 'tinymce/tinymce';
// Import TinyMCE theme
import 'tinymce/themes/modern/theme';
import 'tinymce/skins/lightgray/skin.min.css';

// Plugins
import 'tinymce/plugins/table';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/autoresize';
import 'tinymce/plugins/link';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/advlist';
// Other dependencies
import { isObject, isString } from './isType';

// // Configuration extracted from
// // https://www.tinymce.com/docs/advanced/usage-with-module-loaders/
export const defaultOptions = {
  selector: '.tinymce',
  statusbar: false,
  menubar: false,
  toolbar: 'bold italic | bullist numlist | link | table',
  plugins: 'table autoresize link paste advlist lists',
  advlist_bullet_styles: 'circle,disc,square', // Only disc bullets display on htmltoword
  target_list: false,
  autoresize_min_height: 130,
  autoresize_bottom_margin: 10,
  branding: false,
  extended_valid_elements: 'iframe[tooltip] , a[href|target=_blank]',
  paste_auto_cleanup_on_paste: true,
  paste_remove_styles: true,
  paste_retain_style_properties: 'none',
  paste_convert_middot_lists: true,
  paste_remove_styles_if_webkit: true,
  paste_remove_spans: true,
  paste_strip_class_attributes: 'all',
  max_height: 350,
  table_default_attributes: {
    border: 1,
  },
  // editorManager.baseURL is not resolved properly for IE since document.currentScript
  // is not supported, see issue https://github.com/tinymce/tinymce/issues/358
  skin_url: '/tinymce/skins/lightgray',
};
/*
  This function is invoked anytime a new editor is initialised (e.g. Tinymce.init())
  and shrinks a tinymce editor to the minimum height specified at autoresize_min_height
  editor's settings. Since there are cases that tinymce editor is loaded in the DOM
  but has display:none style, the iframe associated gets the height of the screen's device
  and using this function there is no need to wait until the tinymce gains focus to be autoresized.
*/
const resizeEditors = (editors) => {
  editors.forEach((editor) => {
    $(editor.iframeElement).height(editor.settings.autoresize_min_height);
  });
};

export const Tinymce = {
  /*
    Initialises a tinymce editor given the object passed. If a non-valid object is passed,
    the defaultOptions object is used instead
    @param options - An object with tinyMCE properties
  */
  init(options = {}) {
    if (isObject(options)) {
      tinymce.init($.extend(true, defaultOptions, options)).then(resizeEditors);
    } else {
      tinymce.init(defaultOptions).then(resizeEditors);
    }
  },
  /*
    Finds any tinyMCE editor whose target element/textarea has the className passed
    @param className - A string representing the class name of the tinyMCE editor
    target element/textarea to look for
    @return An Array of tinymce.Editor objects
  */
  findEditorsByClassName(className) {
    if (isString(className)) {
      return tinymce.editors.reduce((acc, e) => {
        if ($(e.getElement()).hasClass(className)) {
          return acc.concat([e]);
        }
        return acc;
      }, []);
    }
    return [];
  },
  /*
    Finds a tinyMCE editor whose target element/textarea has the id passed
    @param id - A string representing the id of the tinyMCE editor target
    element/textarea to look for
    @return tinymce.Editor object, otherwise undefined
  */
  findEditorById(id) {
    if (isString(id)) {
      return tinymce.editors.find(el => el.id === id);
    }
    return undefined;
  },
  /*
    Destroy every editor instance whose target element/textarea has the className passed. This
    method executes for each editor the method defined at tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy).
    @param className - A string representing the class name of the tinyMCE editor
    target element/textarea to look for
    @return undefined
  */
  destroyEditorsByClassName(className) {
    const editors = this.findEditorsByClassName(className);
    editors.forEach(ed => ed.destroy(false));
  },
  /*
    Destroy an editor instance whose target element/textarea has HTML id passed. This method
    executes tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy) for a successfull id found.
    @return undefined
  */
  destroyEditorById(id) {
    const editor = this.findEditorById(id);
    if (editor) {
      editor.destroy(false);
    }
  },
};
<commit_msg>backed out change to tinymce.js<commit_after>// Import TinyMCE
import tinymce from 'tinymce/tinymce';
// Import TinyMCE theme
import 'tinymce/themes/modern/theme';

// Plugins
import 'tinymce/plugins/table';
import 'tinymce/plugins/lists';
import 'tinymce/plugins/autoresize';
import 'tinymce/plugins/link';
import 'tinymce/plugins/paste';
import 'tinymce/plugins/advlist';
// Other dependencies
import { isObject, isString } from './isType';

// // Configuration extracted from
// // https://www.tinymce.com/docs/advanced/usage-with-module-loaders/
export const defaultOptions = {
  selector: '.tinymce',
  statusbar: false,
  menubar: false,
  toolbar: 'bold italic | bullist numlist | link | table',
  plugins: 'table autoresize link paste advlist lists',
  advlist_bullet_styles: 'circle,disc,square', // Only disc bullets display on htmltoword
  target_list: false,
  autoresize_min_height: 130,
  autoresize_bottom_margin: 10,
  branding: false,
  extended_valid_elements: 'iframe[tooltip] , a[href|target=_blank]',
  paste_auto_cleanup_on_paste: true,
  paste_remove_styles: true,
  paste_retain_style_properties: 'none',
  paste_convert_middot_lists: true,
  paste_remove_styles_if_webkit: true,
  paste_remove_spans: true,
  paste_strip_class_attributes: 'all',
  max_height: 350,
  table_default_attributes: {
    border: 1,
  },
  // editorManager.baseURL is not resolved properly for IE since document.currentScript
  // is not supported, see issue https://github.com/tinymce/tinymce/issues/358
  skin_url: '/tinymce/skins/lightgray',
};
/*
  This function is invoked anytime a new editor is initialised (e.g. Tinymce.init())
  and shrinks a tinymce editor to the minimum height specified at autoresize_min_height
  editor's settings. Since there are cases that tinymce editor is loaded in the DOM
  but has display:none style, the iframe associated gets the height of the screen's device
  and using this function there is no need to wait until the tinymce gains focus to be autoresized.
*/
const resizeEditors = (editors) => {
  editors.forEach((editor) => {
    $(editor.iframeElement).height(editor.settings.autoresize_min_height);
  });
};

export const Tinymce = {
  /*
    Initialises a tinymce editor given the object passed. If a non-valid object is passed,
    the defaultOptions object is used instead
    @param options - An object with tinyMCE properties
  */
  init(options = {}) {
    if (isObject(options)) {
      tinymce.init($.extend(true, defaultOptions, options)).then(resizeEditors);
    } else {
      tinymce.init(defaultOptions).then(resizeEditors);
    }
  },
  /*
    Finds any tinyMCE editor whose target element/textarea has the className passed
    @param className - A string representing the class name of the tinyMCE editor
    target element/textarea to look for
    @return An Array of tinymce.Editor objects
  */
  findEditorsByClassName(className) {
    if (isString(className)) {
      return tinymce.editors.reduce((acc, e) => {
        if ($(e.getElement()).hasClass(className)) {
          return acc.concat([e]);
        }
        return acc;
      }, []);
    }
    return [];
  },
  /*
    Finds a tinyMCE editor whose target element/textarea has the id passed
    @param id - A string representing the id of the tinyMCE editor target
    element/textarea to look for
    @return tinymce.Editor object, otherwise undefined
  */
  findEditorById(id) {
    if (isString(id)) {
      return tinymce.editors.find(el => el.id === id);
    }
    return undefined;
  },
  /*
    Destroy every editor instance whose target element/textarea has the className passed. This
    method executes for each editor the method defined at tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy).
    @param className - A string representing the class name of the tinyMCE editor
    target element/textarea to look for
    @return undefined
  */
  destroyEditorsByClassName(className) {
    const editors = this.findEditorsByClassName(className);
    editors.forEach(ed => ed.destroy(false));
  },
  /*
    Destroy an editor instance whose target element/textarea has HTML id passed. This method
    executes tinymce.Editor.destroy (e.g. https://www.tinymce.com/docs/api/tinymce/tinymce.editor/#destroy) for a successfull id found.
    @return undefined
  */
  destroyEditorById(id) {
    const editor = this.findEditorById(id);
    if (editor) {
      editor.destroy(false);
    }
  },
};
<|endoftext|>


<commit_before>"use strict";

// Provides several classes frequently used in Arethusa

angular.module('arethusa.util').service('commons', [
  function ArethusaClasses() {
    // Used to define plugin settings
    function Setting(label, model, change) {
      this.label = label;
      this.model = model;
      this.change = change;
    }

    this.setting = function(l, m, c) { return new Setting(l, m, c); };

    // Used by retrievers to place documents in the document store
    function Doc(xml, json, conf) {
      this.xml = xml;
      this.json = json;
      this.conf = conf;
    }

    this.doc = function(x, j, c) { return new Doc(x, j, c); };

    // Used by retrievers to define sentences
    function Sentence(tokens, constituents, cite) {
      var self = this;

      this.tokens = tokens;
      this.constituents = constituents;
      this.cite = cite || '';

      this.toString = function() {
        return arethusaUtil.inject([], self.tokens, function(memo, id, token) {
          memo.push(token.string);
        }).join(' ');
      };
    }

    this.sentence = function(t, c, cite) { return new Sentence(t, c, cite); };

    // Used by retrievers to define constituents
    function Constituent(cl, role, id, sentenceId, parentId) { // might want to add more here
      this.class = cl;
      this.role = role;
      this.id = id;
      this.sentenceId = sentenceId;
      this.parent = parentId;
    }

    this.constituent = function(c, r, i, sId, h) { return new Constituent(c, r, i, sId, h); };

    // A simple token container
    function Token(string, sentenceId) {
      this.string = string;
      this.sentenceId = sentenceId;
    }

    this.token = function (s, sentenceId) { return new Token(s, sentenceId); };
  }
]);
<commit_msg>Polymorphic addition to Constituent class<commit_after>"use strict";

// Provides several classes frequently used in Arethusa

angular.module('arethusa.util').service('commons', [
  function ArethusaClasses() {
    // Used to define plugin settings
    function Setting(label, model, change) {
      this.label = label;
      this.model = model;
      this.change = change;
    }

    this.setting = function(l, m, c) { return new Setting(l, m, c); };

    // Used by retrievers to place documents in the document store
    function Doc(xml, json, conf) {
      this.xml = xml;
      this.json = json;
      this.conf = conf;
    }

    this.doc = function(x, j, c) { return new Doc(x, j, c); };

    // Used by retrievers to define sentences
    function Sentence(tokens, constituents, cite) {
      var self = this;

      this.tokens = tokens;
      this.constituents = constituents;
      this.cite = cite || '';

      this.toString = function() {
        return arethusaUtil.inject([], self.tokens, function(memo, id, token) {
          memo.push(token.string);
        }).join(' ');
      };
    }

    this.sentence = function(t, c, cite) { return new Sentence(t, c, cite); };

    // Used by retrievers to define constituents
    function Constituent(cl, role, id, sentenceId, parentId) { // might want to add more here
      this.class = cl;
      this.role = role;
      this.id = id;
      this.sentenceId = sentenceId;
      this.parent = parentId;

      // To make a constituent conform to a token, we also save the parentId
      // at the exact same spot as a token would do.
      this.constituency = {};
      this.constituency.id = parentId;
    }

    this.constituent = function(c, r, i, sId, h) { return new Constituent(c, r, i, sId, h); };

    // A simple token container
    function Token(string, sentenceId) {
      this.string = string;
      this.sentenceId = sentenceId;
    }

    this.token = function (s, sentenceId) { return new Token(s, sentenceId); };
  }
]);
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import I18n from 'i18n!move_select'
import React from 'react'
import { func, arrayOf } from 'prop-types'
import Select from '@instructure/ui-core/lib/components/Select'
import Button from '@instructure/ui-buttons/lib/components/Button'
import View from '@instructure/ui-layout/lib/components/View'
import Text from '@instructure/ui-elements/lib/components/Text'
import ScreenReaderContent from '@instructure/ui-a11y/lib/components/ScreenReaderContent'

import ConnectorIcon from './ConnectorIcon'
import { itemShape, moveOptionsType } from './propTypes'
import { positions } from './positions'

export default class MoveSelect extends React.Component {
  static propTypes = {
    items: arrayOf(itemShape).isRequired,
    moveOptions: moveOptionsType.isRequired,
    onSelect: func.isRequired,
    onClose: func.isRequired
  }

  constructor(props) {
    super(props)
    this.state = {
      selectedGroup: this.props.moveOptions.groups && this.getFilteredGroups()[0],
      selectedPosition: positions.first,
      selectedSibling: 0,
    }
  }

  selectGroup = (e) => {
    this.setState({ selectedGroup: this.props.moveOptions.groups.find(group => group.id === e.target.value) || null })
  }

  selectPosition = (e) => {
    this.setState({ selectedPosition: positions[e.target.value] || null })
  }

  selectSibling = (e) => {
    this.setState({ selectedSibling: e.target.value === '' ? 0 : Number(e.target.value) })
  }

  submitSelection = () => {
    const { items, moveOptions } = this.props
    const { selectedGroup, selectedPosition, selectedSibling } = this.state
    let order = items.map(({ id }) => id)
    if (selectedPosition) {
      const itemsInGroup = selectedGroup ? selectedGroup.items : moveOptions.siblings
      order = selectedPosition.apply({
        items: items.map(({ id }) => id),
        order: itemsInGroup.map(({ id }) => id),
        relativeTo: selectedSibling,
      })
    }

    this.props.onSelect({
      groupId: moveOptions.groups ? selectedGroup.id : null,
      itemIds: items.map(({ id }) => id),
      order,
    })
  }

  hasSelectedPosition () {
    const { selectedSibling, selectedPosition } = this.state
    const isAbsolute = selectedPosition && selectedPosition.type === 'absolute'
    return !!selectedPosition && (isAbsolute || selectedSibling !== null)
  }

  getFilteredGroups() {
    const { moveOptions, items } = this.props
    let { groups } = moveOptions
    if (moveOptions.excludeCurrent && items[0].groupId) {
      groups = groups.filter(group => group.id !== items[0].groupId)
    }
    return groups
  }

  isDoneSelecting () {
    const { selectedGroup } = this.state
    if (this.props.moveOptions.groups) {
      if (selectedGroup && selectedGroup.items && selectedGroup.items.length) {
        return this.hasSelectedPosition()
      } else {
        return !!selectedGroup
      }
    } else {
      return this.hasSelectedPosition()
    }
  }

  renderSelect ({ label, onChange, options, className,  selectOneDefault}) {
    return (
      <View margin="medium 0" display="block" className={className}>
        <Select
          label={<ScreenReaderContent>{label}</ScreenReaderContent>}
          onChange={onChange}
          >
          {selectOneDefault && (<option>{I18n.t('Select one')}</option>)}
          {options}
        </Select>
      </View>
    )
  }

  renderSelectGroup () {
    const { selectedGroup } = this.state
    const selectPosition = !!(selectedGroup && selectedGroup.items && selectedGroup.items.length)
    const groups = this.getFilteredGroups(this.props)
    return (

        {this.renderSelect({
          label: I18n.t('Group Select'),
          className: 'move-select__group',
          onChange: this.selectGroup,
          options: groups.map(group =>
            <option key={group.id} value={group.id}>{group.title}</option>),
          selectOneDefault: false
        })}
        {selectPosition ? this.renderSelectPosition(selectedGroup.items) : null}

    )
  }

  renderSelectPosition (items) {
    const { selectedPosition } = this.state
    const selectSibling = !!(selectedPosition && selectedPosition.type === 'relative')
    return (

        {this.renderPlaceTitle()}
        {this.renderSelect({
          label: I18n.t('Position Select'),
          className: 'move-select__position',
          onChange: this.selectPosition,
          options: Object.keys(positions).map((pos) =>
            <option key={pos} value={pos}>{positions[pos].label}</option>),
          selectOneDefault: false
        })}
        {selectSibling ? (

            <ConnectorIcon aria-hidden style={{ position: 'absolute', transform: 'translate(-15px, -35px)' }} />
            {this.renderSelectSibling(items)}

) : null}

    )
  }

  renderSelectSibling (items) {
    const filteredItems = items.filter(item => item.id !== this.props.items[0].id)
    return this.renderSelect({
      label: I18n.t('Item Select'),
      className: 'move-select__sibling',
      onChange: this.selectSibling,
      options: filteredItems.map((item, index) =>
        <option key={item.id} value={index}>{item.title}</option>),
      selectOneDefault: false
    })
  }

  renderPlaceTitle() {
    const title = (this.props.moveOptions.groups) ?
      I18n.t('Place') :
      I18n.t('Place "%{title}"', { title: this.props.items[0].title })
    return (
      <Text weight='bold'>{title}</Text>
    )
  }

  render () {
    const { siblings, groups } = this.props.moveOptions
    return (

        {this.props.moveOptions.groupsLabel &&
          <Text weight='bold'>{this.props.moveOptions.groupsLabel}</Text>}
        {groups
          ? this.renderSelectGroup()
          : this.renderSelectPosition(siblings)}
        {(
          <View textAlign="end" display="block">

            <Button id="move-item-tray-cancel-button" onClick={this.props.onClose} margin="0 x-small 0 0">{I18n.t('Cancel')}</Button>
            <Button
              id="move-item-tray-submit-button"
              disabled={!this.isDoneSelecting()}
              type="submit" variant="primary"
              onClick={this.submitSelection}
              margin="0 x-small 0 0">{I18n.t('Move')}</Button>
          </View>
        )}

    )
  }
}
<commit_msg>handle Select deprecation in MoveSelect<commit_after>/*
 * Copyright (C) 2017 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import {arrayOf, func} from 'prop-types'
import ConnectorIcon from './ConnectorIcon'
import I18n from 'i18n!move_select'
import {itemShape, moveOptionsType} from './propTypes'
import {positions} from './positions'
import React from 'react'

import Button from '@instructure/ui-buttons/lib/components/Button'
import FormField from '@instructure/ui-form-field/lib/components/FormField'
import ScreenReaderContent from '@instructure/ui-a11y/lib/components/ScreenReaderContent'
import Text from '@instructure/ui-elements/lib/components/Text'
import View from '@instructure/ui-layout/lib/components/View'

export default class MoveSelect extends React.Component {
  static propTypes = {
    items: arrayOf(itemShape).isRequired,
    moveOptions: moveOptionsType.isRequired,
    onSelect: func.isRequired,
    onClose: func.isRequired
  }

  constructor(props) {
    super(props)
    this.state = {
      selectedGroup: this.props.moveOptions.groups && this.getFilteredGroups()[0],
      selectedPosition: positions.first,
      selectedSibling: 0,
    }
  }

  selectGroup = (e) => {
    this.setState({ selectedGroup: this.props.moveOptions.groups.find(group => group.id === e.target.value) || null })
  }

  selectPosition = (e) => {
    this.setState({ selectedPosition: positions[e.target.value] || null })
  }

  selectSibling = (e) => {
    this.setState({ selectedSibling: e.target.value === '' ? 0 : Number(e.target.value) })
  }

  submitSelection = () => {
    const { items, moveOptions } = this.props
    const { selectedGroup, selectedPosition, selectedSibling } = this.state
    let order = items.map(({ id }) => id)
    if (selectedPosition) {
      const itemsInGroup = selectedGroup ? selectedGroup.items : moveOptions.siblings
      order = selectedPosition.apply({
        items: items.map(({ id }) => id),
        order: itemsInGroup.map(({ id }) => id),
        relativeTo: selectedSibling,
      })
    }

    this.props.onSelect({
      groupId: moveOptions.groups ? selectedGroup.id : null,
      itemIds: items.map(({ id }) => id),
      order,
    })
  }

  hasSelectedPosition () {
    const { selectedSibling, selectedPosition } = this.state
    const isAbsolute = selectedPosition && selectedPosition.type === 'absolute'
    return !!selectedPosition && (isAbsolute || selectedSibling !== null)
  }

  getFilteredGroups() {
    const { moveOptions, items } = this.props
    let { groups } = moveOptions
    if (moveOptions.excludeCurrent && items[0].groupId) {
      groups = groups.filter(group => group.id !== items[0].groupId)
    }
    return groups
  }

  isDoneSelecting () {
    const { selectedGroup } = this.state
    if (this.props.moveOptions.groups) {
      if (selectedGroup && selectedGroup.items && selectedGroup.items.length) {
        return this.hasSelectedPosition()
      } else {
        return !!selectedGroup
      }
    } else {
      return this.hasSelectedPosition()
    }
  }

  renderSelect ({ label, onChange, options, className,  selectOneDefault}) {
    return (
      <View margin="medium 0" display="block" className={className}>
        <FormField
          id="move-select-form"
          label={<ScreenReaderContent>{label}</ScreenReaderContent>}
        >
          <select
            onChange={onChange}
            style={{
              margin: '0',
              width: '100%'
            }}
          >
            {selectOneDefault && (<option>{I18n.t('Select one')}</option>)}
            {options}
          </select>
        </FormField>
      </View>
    )
  }

  renderSelectGroup () {
    const { selectedGroup } = this.state
    const selectPosition = !!(selectedGroup && selectedGroup.items && selectedGroup.items.length)
    const groups = this.getFilteredGroups(this.props)
    return (

        {this.renderSelect({
          label: I18n.t('Group Select'),
          className: 'move-select__group',
          onChange: this.selectGroup,
          options: groups.map(group =>
            <option key={group.id} value={group.id}>{group.title}</option>),
          selectOneDefault: false
        })}
        {selectPosition ? this.renderSelectPosition(selectedGroup.items) : null}

    )
  }

  renderSelectPosition (items) {
    const { selectedPosition } = this.state
    const selectSibling = !!(selectedPosition && selectedPosition.type === 'relative')
    return (

        {this.renderPlaceTitle()}
        {this.renderSelect({
          label: I18n.t('Position Select'),
          className: 'move-select__position',
          onChange: this.selectPosition,
          options: Object.keys(positions).map((pos) =>
            <option key={pos} value={pos}>{positions[pos].label}</option>),
          selectOneDefault: false
        })}
        {selectSibling ? (

            <ConnectorIcon aria-hidden style={{ position: 'absolute', transform: 'translate(-15px, -35px)' }} />
            {this.renderSelectSibling(items)}

) : null}

    )
  }

  renderSelectSibling (items) {
    const filteredItems = items.filter(item => item.id !== this.props.items[0].id)
    return this.renderSelect({
      label: I18n.t('Item Select'),
      className: 'move-select__sibling',
      onChange: this.selectSibling,
      options: filteredItems.map((item, index) =>
        <option key={item.id} value={index}>{item.title}</option>),
      selectOneDefault: false
    })
  }

  renderPlaceTitle() {
    const title = (this.props.moveOptions.groups) ?
      I18n.t('Place') :
      I18n.t('Place "%{title}"', { title: this.props.items[0].title })
    return (
      <Text weight='bold'>{title}</Text>
    )
  }

  render () {
    const { siblings, groups } = this.props.moveOptions
    return (

        {this.props.moveOptions.groupsLabel &&
          <Text weight='bold'>{this.props.moveOptions.groupsLabel}</Text>}
        {groups
          ? this.renderSelectGroup()
          : this.renderSelectPosition(siblings)}
        {(
          <View textAlign="end" display="block">

            <Button id="move-item-tray-cancel-button" onClick={this.props.onClose} margin="0 x-small 0 0">{I18n.t('Cancel')}</Button>
            <Button
              id="move-item-tray-submit-button"
              disabled={!this.isDoneSelecting()}
              type="submit" variant="primary"
              onClick={this.submitSelection}
              margin="0 x-small 0 0">{I18n.t('Move')}</Button>
          </View>
        )}

    )
  }
}
<|endoftext|>


<commit_before>const path = require('path')
const fse = require('fs-extra')

const appPaths = require('../../app-paths')
const artifacts = require('../../artifacts')

module.exports = function (chain, cfg) {
  const rootPath = cfg.ctx.dev ? appPaths.bexDir : cfg.build.distDir
  const outputPath = path.join(rootPath, 'www')

  // Add a copy config to copy the static folder for both dev and build.
  const copyPatterns = [{
    from: appPaths.resolve.app('public'),
    to: path.join(outputPath),
    noErrorOnMissing: true
  }]

  // Copy our entry BEX files to the .quasar/bex folder.
  fse.copySync(appPaths.resolve.cli('templates/entry/bex'), appPaths.resolve.app('.quasar/bex'))

  chain.output
    .path(outputPath) // Output to our src-bex/www folder or dist/bex/unpacked/www.

  // Bundle our bex files for inclusion via the manifest.json
  chain.entry('bex-init')
    .add(appPaths.resolve.app('.quasar/bex/init/index.js'))

  if (cfg.ctx.dev) {
    // Clean old dir
    artifacts.clean(outputPath)

    // Extensions need to be manually added to the browser
    // so we need the dev files available for them to be targeted.
    const WriteFilePlugin = require('write-file-webpack-plugin')
    chain.plugin('write-file-webpack-plugin')
      .use(WriteFilePlugin)
  }
  else {
    // We need this bundled in with the rest of the source to match the manifest instructions.
    // Could use Webpack Copy here but this is more straight forward.
    cfg.build.htmlFilename = path.join('www', 'index.html')

    // Register our plugin, update the manifest and package the browser extension.
    const BexPackager = require('./plugin.bex-packager')
    chain.plugin('webpack-bex-packager')
      .use(BexPackager, [{
        src: cfg.bex.builder.directories.input,
        dest: cfg.bex.builder.directories.output,
        name: require(path.join(appPaths.appDir, 'package.json')).name
      }])

    // Copy our user edited BEX files to the dist dir (excluding the already built www folder)
    copyPatterns.push({
      from: appPaths.bexDir,
      to: cfg.build.distDir,
      noErrorOnMissing: true,
      globOptions: {
        ignore: [
          appPaths.resolve.bex('/**/.*'),
          appPaths.resolve.bex('www'),
          appPaths.resolve.bex('bex-flag.d.ts')
        ]
      }
    })
  }

  // Copy any files we've registered during the chain.
  const CopyWebpackPlugin = require('copy-webpack-plugin')
  chain.plugin('copy-webpack')
    .use(CopyWebpackPlugin, [{ patterns: copyPatterns }])
}
<commit_msg>fix(bex): Don't minify rendered code. (#7185)<commit_after>const path = require('path')
const fse = require('fs-extra')

const appPaths = require('../../app-paths')
const artifacts = require('../../artifacts')

module.exports = function (chain, cfg) {
  const rootPath = cfg.ctx.dev ? appPaths.bexDir : cfg.build.distDir
  const outputPath = path.join(rootPath, 'www')

  // Add a copy config to copy the static folder for both dev and build.
  const copyPatterns = [{
    from: appPaths.resolve.app('public'),
    to: path.join(outputPath),
    noErrorOnMissing: true
  }]

  // Copy our entry BEX files to the .quasar/bex folder.
  fse.copySync(appPaths.resolve.cli('templates/entry/bex'), appPaths.resolve.app('.quasar/bex'))

  chain.output
    .path(outputPath) // Output to our src-bex/www folder or dist/bex/unpacked/www.

  // Bundle our bex files for inclusion via the manifest.json
  chain.entry('bex-init')
    .add(appPaths.resolve.app('.quasar/bex/init/index.js'))

  // We shouldn't minify BEX code. This option is disabled by default for BEX mode in quasar-conf.js.
  // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/Source_Code_Submission#Provide_your_extension_source_code
  chain.optimization.minimize(cfg.build.minify)

  if (cfg.ctx.dev) {
    // Clean old dir
    artifacts.clean(outputPath)

    // Extensions need to be manually added to the browser
    // so we need the dev files available for them to be targeted.
    const WriteFilePlugin = require('write-file-webpack-plugin')
    chain.plugin('write-file-webpack-plugin')
      .use(WriteFilePlugin)
  }
  else {
    // We need this bundled in with the rest of the source to match the manifest instructions.
    // Could use Webpack Copy here but this is more straight forward.
    cfg.build.htmlFilename = path.join('www', 'index.html')

    // Register our plugin, update the manifest and package the browser extension.
    const BexPackager = require('./plugin.bex-packager')
    chain.plugin('webpack-bex-packager')
      .use(BexPackager, [{
        src: cfg.bex.builder.directories.input,
        dest: cfg.bex.builder.directories.output,
        name: require(path.join(appPaths.appDir, 'package.json')).name
      }])

    // Copy our user edited BEX files to the dist dir (excluding the already built www folder)
    copyPatterns.push({
      from: appPaths.bexDir,
      to: cfg.build.distDir,
      noErrorOnMissing: true,
      globOptions: {
        ignore: [
          appPaths.resolve.bex('/**/.*'),
          appPaths.resolve.bex('www'),
          appPaths.resolve.bex('bex-flag.d.ts')
        ]
      }
    })
  }

  // Copy any files we've registered during the chain.
  const CopyWebpackPlugin = require('copy-webpack-plugin')
  chain.plugin('copy-webpack')
    .use(CopyWebpackPlugin, [{ patterns: copyPatterns }])
}
<|endoftext|>


<commit_before>const
  path = require('path'),
  webpack = require('webpack'),
  WebpackChain = require('webpack-chain'),
  VueLoaderPlugin = require('vue-loader/lib/plugin'),
  WebpackProgress = require('./plugin.progress'),
  BootDefaultExport = require('./plugin.boot-default-export')

const
  appPaths = require('../app-paths'),
  injectStyleRules = require('./inject.style-rules')

module.exports = function (cfg, configName) {
  const
    chain = new WebpackChain(),
    needsHash = !cfg.ctx.dev && !['electron', 'cordova', 'capacitor'].includes(cfg.ctx.modeName),
    fileHash = needsHash ? '.[hash:8]' : '',
    chunkHash = needsHash ? '.[contenthash:8]' : '',
    resolveModules = [
      'node_modules',
      appPaths.resolve.app('node_modules'),
      appPaths.resolve.cli('node_modules')
    ]

  if (configName === 'Capacitor') {
    // need to also look into /src-capacitor
    // for deps like @capacitor/core
    resolveModules.push(
      appPaths.resolve.capacitor('node_modules')
    )
  }

  chain.entry('app').add(appPaths.resolve.app('.quasar/client-entry.js'))
  chain.mode(cfg.ctx.dev ? 'development' : 'production')
  chain.devtool(cfg.build.sourceMap ? cfg.build.devtool : false)

  if (cfg.ctx.prod || cfg.ctx.mode.ssr) {
    chain.output
      .path(
        cfg.ctx.mode.ssr
          ? path.join(cfg.build.distDir, 'www')
          : cfg.build.distDir
      )
      .publicPath(cfg.build.publicPath)
      .filename(`js/[name]${fileHash}.js`)
      .chunkFilename(`js/[name]${chunkHash}.js`)
  }

  chain.resolve.symlinks(false)

  chain.resolve.extensions
    .merge([ '.js', '.vue', '.json' ])

  chain.resolve.modules
    .merge(resolveModules)

  chain.resolve.alias
    .merge({
      src: appPaths.srcDir,
      app: appPaths.appDir,
      components: appPaths.resolve.src(`components`),
      layouts: appPaths.resolve.src(`layouts`),
      pages: appPaths.resolve.src(`pages`),
      assets: appPaths.resolve.src(`assets`),
      boot: appPaths.resolve.src(`boot`)
    })

  if (cfg.framework.all === true) {
    chain.resolve.alias.set('quasar$', 'quasar/dist/quasar.esm.js')
  }
  if (cfg.build.vueCompiler) {
    chain.resolve.alias.set('vue$', 'vue/dist/vue.esm.js')
  }

  chain.resolveLoader.modules
    .merge(resolveModules)

  chain.module.noParse(/^(vue|vue-router|vuex|vuex-router-sync)$/)

  const vueRule = chain.module.rule('vue')
    .test(/\.vue$/)

  if (cfg.framework.all === 'auto') {
    vueRule.use('quasar-auto-import')
      .loader(path.join(__dirname, 'loader.auto-import.js'))
      .options(cfg.framework.autoImportComponentCase)
  }

  vueRule.use('vue-loader')
    .loader('vue-loader')
    .options({
      productionMode: cfg.ctx.prod,
      compilerOptions: {
        preserveWhitespace: false
      },
      transformAssetUrls: {
        video: 'src',
        source: 'src',
        img: 'src',
        image: 'xlink:href'
      }
    })

  chain.module.rule('babel')
    .test(/\.jsx?$/)
    .exclude
      .add(filepath => {
        // always transpile js(x) in Vue files
        if (/\.vue\.jsx?$/.test(filepath)) {
          return false
        }

        if (filepath.match(/[\\/]node_modules[\\/]quasar[\\/]/)) {
          if (configName === 'Server') {
            // transpile only if not from 'quasar/dist' folder
            if (!filepath.match(/[\\/]node_modules[\\/]quasar[\\/]dist/)) {
              return false
            }
          }
          else {
            // always transpile Quasar
            return false
          }
        }

        if (cfg.build.transpileDependencies.some(dep => filepath.match(dep))) {
          return false
        }

        // Don't transpile anything else in node_modules
        return /[\\/]node_modules[\\/]/.test(filepath)
      })
      .end()
    .use('babel-loader')
      .loader('babel-loader')
        .options({
          extends: appPaths.resolve.app('babel.config.js'),
          plugins: cfg.framework.all !== true && configName !== 'Server' ? [
            [
              'transform-imports', {
                quasar: {
                  transform: `quasar/dist/babel-transforms/imports.js`,
                  preventFullImport: true
                }
              }
            ]
          ] : []
        })

  chain.module.rule('images')
    .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
    .use('url-loader')
      .loader('url-loader')
      .options({
        limit: 10000,
        name: `img/[name]${fileHash}.[ext]`
      })

  chain.module.rule('fonts')
    .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/)
    .use('url-loader')
      .loader('url-loader')
      .options({
        limit: 10000,
        name: `fonts/[name]${fileHash}.[ext]`
      })

  chain.module.rule('media')
    .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
    .use('url-loader')
      .loader('url-loader')
      .options({
        limit: 10000,
        name: `media/[name]${fileHash}.[ext]`
      })

  injectStyleRules(chain, {
    rtl: cfg.build.rtl,
    sourceMap: cfg.build.sourceMap,
    extract: cfg.build.extractCSS,
    serverExtract: configName === 'Server' && cfg.build.extractCSS,
    minify: cfg.build.minify,
    stylusLoaderOptions: cfg.build.stylusLoaderOptions,
    sassLoaderOptions: cfg.build.sassLoaderOptions,
    scssLoaderOptions: cfg.build.scssLoaderOptions,
    lessLoaderOptions: cfg.build.lessLoaderOptions
  })

  chain.plugin('vue-loader')
    .use(VueLoaderPlugin)

  chain.plugin('define')
    .use(webpack.DefinePlugin, [ cfg.build.env ])

  if (cfg.build.showProgress) {
    chain.plugin('progress')
      .use(WebpackProgress, [{ name: configName }])
  }

  chain.plugin('boot-default-export')
    .use(BootDefaultExport)

  chain.performance
    .hints(false)
    .maxAssetSize(500000)

  // DEVELOPMENT build
  if (cfg.ctx.dev) {
    const
      FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'),
      { devCompilationSuccess } = require('../helpers/banner')

    chain.optimization
      .noEmitOnErrors(true)

    chain.plugin('friendly-errors')
      .use(FriendlyErrorsPlugin, [{
        clearConsole: true,
        compilationSuccessInfo: ['spa', 'pwa', 'ssr'].includes(cfg.ctx.modeName)
          ? { notes: [ devCompilationSuccess(cfg.ctx, cfg.build.APP_URL, appPaths.appDir) ] }
          : undefined
      }])
  }
  // PRODUCTION build
  else {
    // keep module.id stable when vendor modules does not change
    chain.plugin('hashed-module-ids')
      .use(webpack.HashedModuleIdsPlugin, [{
        hashDigest: 'hex'
      }])

    // keep chunk ids stable so async chunks have consistent hash
    const hash = require('hash-sum')
    chain
      .plugin('named-chunks')
        .use(webpack.NamedChunksPlugin, [
          chunk => chunk.name || hash(
            Array.from(chunk.modulesIterable, m => m.id).join('_')
          )
        ])

    if (configName !== 'Server') {
      const
        add = cfg.vendor.add,
        rem = cfg.vendor.remove,
        regex = /[\\/]node_modules[\\/]/

      chain.optimization
        .splitChunks({
          cacheGroups: {
            vendors: {
              name: 'vendor',
              chunks: 'initial',
              priority: -10,
              // a module is extracted into the vendor chunk if...
              test: add.length > 0 || rem.length > 0
                ? module => {
                  if (module.resource) {
                    if (add.length > 0 && add.test(module.resource)) { return true }
                    if (rem.length > 0 && rem.test(module.resource)) { return false }
                  }
                  return regex.test(module.resource)
                }
                : module => regex.test(module.resource)
            },
            common: {
              name: `chunk-common`,
              minChunks: 2,
              priority: -20,
              chunks: 'initial',
              reuseExistingChunk: true
            }
          }
        })

      // extract webpack runtime and module manifest to its own file in order to
      // prevent vendor hash from being updated whenever app bundle is updated
      if (cfg.build.webpackManifest) {
        chain.optimization.runtimeChunk('single')
      }

      // copy statics to dist folder
      const CopyWebpackPlugin = require('copy-webpack-plugin')
      chain.plugin('copy-webpack')
        .use(CopyWebpackPlugin, [
          [{
            from: appPaths.resolve.src('statics'),
            to: 'statics',
            ignore: ['.*'],
            ignore: ['.*'].concat(
              // avoid useless files to be copied
              ['electron', 'cordova', 'capacitor'].includes(cfg.ctx.modeName)
                ? [ 'icons/*', 'app-logo-128x128.png' ]
                : []
            )
          }]
        ])
    }

    // Scope hoisting ala Rollupjs
    if (cfg.build.scopeHoisting) {
      chain.optimization
        .concatenateModules(true)
    }

    if (cfg.ctx.debug) {
      // reset default webpack 4 minimizer
      chain.optimization.minimizers.delete('js')
      // also:
      chain.optimization.minimize(false)
    }
    else if (cfg.build.minify) {
      const TerserPlugin = require('terser-webpack-plugin')

      chain.optimization
        .minimizer('js')
        .use(TerserPlugin, [{
          terserOptions: cfg.build.uglifyOptions,
          cache: true,
          parallel: true,
          sourceMap: cfg.build.sourceMap
        }])
    }

    // configure CSS extraction & optimize
    if (configName !== 'Server' && cfg.build.extractCSS) {
      const MiniCssExtractPlugin = require('mini-css-extract-plugin')

      // extract css into its own file
      chain.plugin('mini-css-extract')
        .use(MiniCssExtractPlugin, [{
          filename: 'css/[name].[contenthash:8].css'
        }])

      // dedupe & minify CSS (only if extracted)
      if (cfg.build.minify) {
        const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

        const cssProcessorOptions = {
          parser: require('postcss-safe-parser'),
          autoprefixer: { disable: true }
        }
        if (cfg.build.sourceMap) {
          cssProcessorOptions.map = { inline: false }
        }

        // We are using this plugin so that possible
        // duplicated CSS = require(different components) can be deduped.
        chain.plugin('optimize-css')
          .use(OptimizeCSSPlugin, [{
            canPrint: false,
            cssProcessor: require('cssnano'),
            cssProcessorOptions,
            cssProcessorPluginOptions: {
              preset: ['default', {
                mergeLonghand: false,
                convertValues: false,
                cssDeclarationSorter: false,
                reduceTransforms: false
              }]
            }
          }])
      }
    }

    if (configName !== 'Server') {
      // also produce a gzipped version
      if (cfg.build.gzip) {
        const CompressionWebpackPlugin = require('compression-webpack-plugin')
        chain.plugin('compress-webpack')
          .use(CompressionWebpackPlugin, [ cfg.build.gzip ])
      }

      if (cfg.build.analyze) {
        const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
        chain.plugin('bundle-analyzer')
          .use(BundleAnalyzerPlugin, [ Object.assign({}, cfg.build.analyze) ])
      }
    }
  }

  return chain
}
<commit_msg>fix(app): Make webpack-terser-plugin not extract comments into .LICENSE files #5429<commit_after>const
  path = require('path'),
  webpack = require('webpack'),
  WebpackChain = require('webpack-chain'),
  VueLoaderPlugin = require('vue-loader/lib/plugin'),
  WebpackProgress = require('./plugin.progress'),
  BootDefaultExport = require('./plugin.boot-default-export')

const
  appPaths = require('../app-paths'),
  injectStyleRules = require('./inject.style-rules')

module.exports = function (cfg, configName) {
  const
    chain = new WebpackChain(),
    needsHash = !cfg.ctx.dev && !['electron', 'cordova', 'capacitor'].includes(cfg.ctx.modeName),
    fileHash = needsHash ? '.[hash:8]' : '',
    chunkHash = needsHash ? '.[contenthash:8]' : '',
    resolveModules = [
      'node_modules',
      appPaths.resolve.app('node_modules'),
      appPaths.resolve.cli('node_modules')
    ]

  if (configName === 'Capacitor') {
    // need to also look into /src-capacitor
    // for deps like @capacitor/core
    resolveModules.push(
      appPaths.resolve.capacitor('node_modules')
    )
  }

  chain.entry('app').add(appPaths.resolve.app('.quasar/client-entry.js'))
  chain.mode(cfg.ctx.dev ? 'development' : 'production')
  chain.devtool(cfg.build.sourceMap ? cfg.build.devtool : false)

  if (cfg.ctx.prod || cfg.ctx.mode.ssr) {
    chain.output
      .path(
        cfg.ctx.mode.ssr
          ? path.join(cfg.build.distDir, 'www')
          : cfg.build.distDir
      )
      .publicPath(cfg.build.publicPath)
      .filename(`js/[name]${fileHash}.js`)
      .chunkFilename(`js/[name]${chunkHash}.js`)
  }

  chain.resolve.symlinks(false)

  chain.resolve.extensions
    .merge([ '.js', '.vue', '.json' ])

  chain.resolve.modules
    .merge(resolveModules)

  chain.resolve.alias
    .merge({
      src: appPaths.srcDir,
      app: appPaths.appDir,
      components: appPaths.resolve.src(`components`),
      layouts: appPaths.resolve.src(`layouts`),
      pages: appPaths.resolve.src(`pages`),
      assets: appPaths.resolve.src(`assets`),
      boot: appPaths.resolve.src(`boot`)
    })

  if (cfg.framework.all === true) {
    chain.resolve.alias.set('quasar$', 'quasar/dist/quasar.esm.js')
  }
  if (cfg.build.vueCompiler) {
    chain.resolve.alias.set('vue$', 'vue/dist/vue.esm.js')
  }

  chain.resolveLoader.modules
    .merge(resolveModules)

  chain.module.noParse(/^(vue|vue-router|vuex|vuex-router-sync)$/)

  const vueRule = chain.module.rule('vue')
    .test(/\.vue$/)

  if (cfg.framework.all === 'auto') {
    vueRule.use('quasar-auto-import')
      .loader(path.join(__dirname, 'loader.auto-import.js'))
      .options(cfg.framework.autoImportComponentCase)
  }

  vueRule.use('vue-loader')
    .loader('vue-loader')
    .options({
      productionMode: cfg.ctx.prod,
      compilerOptions: {
        preserveWhitespace: false
      },
      transformAssetUrls: {
        video: 'src',
        source: 'src',
        img: 'src',
        image: 'xlink:href'
      }
    })

  chain.module.rule('babel')
    .test(/\.jsx?$/)
    .exclude
      .add(filepath => {
        // always transpile js(x) in Vue files
        if (/\.vue\.jsx?$/.test(filepath)) {
          return false
        }

        if (filepath.match(/[\\/]node_modules[\\/]quasar[\\/]/)) {
          if (configName === 'Server') {
            // transpile only if not from 'quasar/dist' folder
            if (!filepath.match(/[\\/]node_modules[\\/]quasar[\\/]dist/)) {
              return false
            }
          }
          else {
            // always transpile Quasar
            return false
          }
        }

        if (cfg.build.transpileDependencies.some(dep => filepath.match(dep))) {
          return false
        }

        // Don't transpile anything else in node_modules
        return /[\\/]node_modules[\\/]/.test(filepath)
      })
      .end()
    .use('babel-loader')
      .loader('babel-loader')
        .options({
          extends: appPaths.resolve.app('babel.config.js'),
          plugins: cfg.framework.all !== true && configName !== 'Server' ? [
            [
              'transform-imports', {
                quasar: {
                  transform: `quasar/dist/babel-transforms/imports.js`,
                  preventFullImport: true
                }
              }
            ]
          ] : []
        })

  chain.module.rule('images')
    .test(/\.(png|jpe?g|gif|svg)(\?.*)?$/)
    .use('url-loader')
      .loader('url-loader')
      .options({
        limit: 10000,
        name: `img/[name]${fileHash}.[ext]`
      })

  chain.module.rule('fonts')
    .test(/\.(woff2?|eot|ttf|otf)(\?.*)?$/)
    .use('url-loader')
      .loader('url-loader')
      .options({
        limit: 10000,
        name: `fonts/[name]${fileHash}.[ext]`
      })

  chain.module.rule('media')
    .test(/\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/)
    .use('url-loader')
      .loader('url-loader')
      .options({
        limit: 10000,
        name: `media/[name]${fileHash}.[ext]`
      })

  injectStyleRules(chain, {
    rtl: cfg.build.rtl,
    sourceMap: cfg.build.sourceMap,
    extract: cfg.build.extractCSS,
    serverExtract: configName === 'Server' && cfg.build.extractCSS,
    minify: cfg.build.minify,
    stylusLoaderOptions: cfg.build.stylusLoaderOptions,
    sassLoaderOptions: cfg.build.sassLoaderOptions,
    scssLoaderOptions: cfg.build.scssLoaderOptions,
    lessLoaderOptions: cfg.build.lessLoaderOptions
  })

  chain.plugin('vue-loader')
    .use(VueLoaderPlugin)

  chain.plugin('define')
    .use(webpack.DefinePlugin, [ cfg.build.env ])

  if (cfg.build.showProgress) {
    chain.plugin('progress')
      .use(WebpackProgress, [{ name: configName }])
  }

  chain.plugin('boot-default-export')
    .use(BootDefaultExport)

  chain.performance
    .hints(false)
    .maxAssetSize(500000)

  // DEVELOPMENT build
  if (cfg.ctx.dev) {
    const
      FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin'),
      { devCompilationSuccess } = require('../helpers/banner')

    chain.optimization
      .noEmitOnErrors(true)

    chain.plugin('friendly-errors')
      .use(FriendlyErrorsPlugin, [{
        clearConsole: true,
        compilationSuccessInfo: ['spa', 'pwa', 'ssr'].includes(cfg.ctx.modeName)
          ? { notes: [ devCompilationSuccess(cfg.ctx, cfg.build.APP_URL, appPaths.appDir) ] }
          : undefined
      }])
  }
  // PRODUCTION build
  else {
    // keep module.id stable when vendor modules does not change
    chain.plugin('hashed-module-ids')
      .use(webpack.HashedModuleIdsPlugin, [{
        hashDigest: 'hex'
      }])

    // keep chunk ids stable so async chunks have consistent hash
    const hash = require('hash-sum')
    chain
      .plugin('named-chunks')
        .use(webpack.NamedChunksPlugin, [
          chunk => chunk.name || hash(
            Array.from(chunk.modulesIterable, m => m.id).join('_')
          )
        ])

    if (configName !== 'Server') {
      const
        add = cfg.vendor.add,
        rem = cfg.vendor.remove,
        regex = /[\\/]node_modules[\\/]/

      chain.optimization
        .splitChunks({
          cacheGroups: {
            vendors: {
              name: 'vendor',
              chunks: 'initial',
              priority: -10,
              // a module is extracted into the vendor chunk if...
              test: add.length > 0 || rem.length > 0
                ? module => {
                  if (module.resource) {
                    if (add.length > 0 && add.test(module.resource)) { return true }
                    if (rem.length > 0 && rem.test(module.resource)) { return false }
                  }
                  return regex.test(module.resource)
                }
                : module => regex.test(module.resource)
            },
            common: {
              name: `chunk-common`,
              minChunks: 2,
              priority: -20,
              chunks: 'initial',
              reuseExistingChunk: true
            }
          }
        })

      // extract webpack runtime and module manifest to its own file in order to
      // prevent vendor hash from being updated whenever app bundle is updated
      if (cfg.build.webpackManifest) {
        chain.optimization.runtimeChunk('single')
      }

      // copy statics to dist folder
      const CopyWebpackPlugin = require('copy-webpack-plugin')
      chain.plugin('copy-webpack')
        .use(CopyWebpackPlugin, [
          [{
            from: appPaths.resolve.src('statics'),
            to: 'statics',
            ignore: ['.*'],
            ignore: ['.*'].concat(
              // avoid useless files to be copied
              ['electron', 'cordova', 'capacitor'].includes(cfg.ctx.modeName)
                ? [ 'icons/*', 'app-logo-128x128.png' ]
                : []
            )
          }]
        ])
    }

    // Scope hoisting ala Rollupjs
    if (cfg.build.scopeHoisting) {
      chain.optimization
        .concatenateModules(true)
    }

    if (cfg.ctx.debug) {
      // reset default webpack 4 minimizer
      chain.optimization.minimizers.delete('js')
      // also:
      chain.optimization.minimize(false)
    }
    else if (cfg.build.minify) {
      const TerserPlugin = require('terser-webpack-plugin')

      chain.optimization
        .minimizer('js')
        .use(TerserPlugin, [{
          terserOptions: cfg.build.uglifyOptions,
          extractComments: false,
          cache: true,
          parallel: true,
          sourceMap: cfg.build.sourceMap
        }])
    }

    // configure CSS extraction & optimize
    if (configName !== 'Server' && cfg.build.extractCSS) {
      const MiniCssExtractPlugin = require('mini-css-extract-plugin')

      // extract css into its own file
      chain.plugin('mini-css-extract')
        .use(MiniCssExtractPlugin, [{
          filename: 'css/[name].[contenthash:8].css'
        }])

      // dedupe & minify CSS (only if extracted)
      if (cfg.build.minify) {
        const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')

        const cssProcessorOptions = {
          parser: require('postcss-safe-parser'),
          autoprefixer: { disable: true }
        }
        if (cfg.build.sourceMap) {
          cssProcessorOptions.map = { inline: false }
        }

        // We are using this plugin so that possible
        // duplicated CSS = require(different components) can be deduped.
        chain.plugin('optimize-css')
          .use(OptimizeCSSPlugin, [{
            canPrint: false,
            cssProcessor: require('cssnano'),
            cssProcessorOptions,
            cssProcessorPluginOptions: {
              preset: ['default', {
                mergeLonghand: false,
                convertValues: false,
                cssDeclarationSorter: false,
                reduceTransforms: false
              }]
            }
          }])
      }
    }

    if (configName !== 'Server') {
      // also produce a gzipped version
      if (cfg.build.gzip) {
        const CompressionWebpackPlugin = require('compression-webpack-plugin')
        chain.plugin('compress-webpack')
          .use(CompressionWebpackPlugin, [ cfg.build.gzip ])
      }

      if (cfg.build.analyze) {
        const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
        chain.plugin('bundle-analyzer')
          .use(BundleAnalyzerPlugin, [ Object.assign({}, cfg.build.analyze) ])
      }
    }
  }

  return chain
}
<|endoftext|>