<commit_before>/**
 * ownCloud - core
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Jörn Friedrich Dreyer <jfd@owncloud.com>
 * @copyright Jörn Friedrich Dreyer 2014
 */

(function () {
	/**
	 * @class OCA.Search
	 * @classdesc
	 *
	 * The Search class manages a search queries and their results
	 *
	 * @param $searchBox container element with existing markup for the #searchbox form
	 * @param $searchResults container element for results und status message
	 */
	var Search = function($searchBox, $searchResults) {
		this.initialize($searchBox, $searchResults);
	};
	/**
	 * @memberof OC
	 */
	Search.prototype = {

		/**
		 * Initialize the search box
		 *
		 * @param $searchBox container element with existing markup for the #searchbox form
		 * @param $searchResults container element for results und status message
		 * @private
		 */
		initialize: function($searchBox, $searchResults) {

			var self = this;

			/**
			 * contains closures that are called to filter the current content
			 */
			var filters = {};
			this.setFilter = function(type, filter) {
				filters[type] = filter;
			};
			this.hasFilter = function(type) {
				return typeof filters[type] !== 'undefined';
			};
			this.getFilter = function(type) {
				return filters[type];
			};

			/**
			 * contains closures that are called to render search results
			 */
			var renderers = {};
			this.setRenderer = function(type, renderer) {
				renderers[type] = renderer;
			};
			this.hasRenderer = function(type) {
				return typeof renderers[type] !== 'undefined';
			};
			this.getRenderer = function(type) {
				return renderers[type];
			};

			/**
			 * contains closures that are called when a search result has been clicked
			 */
			var handlers = {};
			this.setHandler = function(type, handler) {
				handlers[type] = handler;
			};
			this.hasHandler = function(type) {
				return typeof handlers[type] !== 'undefined';
			};
			this.getHandler = function(type) {
				return handlers[type];
			};

			var currentResult = -1;
			var lastQuery = '';
			var lastInApps = [];
			var lastPage = 0;
			var lastSize = 30;
			var lastResults = [];
			var timeoutID = null;

			this.getLastQuery = function() {
				return lastQuery;
			};

			/**
			 * Do a search query and display the results
			 * @param {string} query the search query
			 */
			this.search = function(query, inApps, page, size) {
				if (query) {
					OC.addStyle('core/search','results');
					if (typeof page !== 'number') {
						page = 1;
					}
					if (typeof size !== 'number') {
						size = 30;
					}
					if (typeof inApps !== 'object') {
						var currentApp = getCurrentApp();
						if(currentApp) {
							inApps = [currentApp];
						} else {
							inApps = [];
						}
					}
					// prevent double pages
					if ($searchResults && query === lastQuery && page === lastPage && size === lastSize) {
						return;
					}
					window.clearTimeout(timeoutID);
					timeoutID = window.setTimeout(function() {
						lastQuery = query;
						lastInApps = inApps;
						lastPage = page;
						lastSize = size;

						//show spinner
						$searchResults.removeClass('hidden');
						$status.addClass('status');
						$status.html(t('core', 'Searching other places')+'');

						// do the actual search query
						$.getJSON(OC.generateUrl('core/search'), {query:query, inApps:inApps, page:page, size:size }, function(results) {
							lastResults = results;
							if (page === 1) {
								showResults(results);
							} else {
								addResults(results);
							}
						});
					}, 500);
				}
			};

			//TODO should be a core method, see https://github.com/owncloud/core/issues/12557
			function getCurrentApp() {
				var content = document.getElementById('content');
				if (content) {
					var classList = document.getElementById('content').className.split(/\s+/);
					for (var i = 0; i < classList.length; i++) {
						if (classList[i].indexOf('app-') === 0) {
							return classList[i].substr(4);
						}
					}
				}
				return false;
			}

			var $status = $searchResults.find('#status');
			// summaryAndStatusHeight is a constant
			var summaryAndStatusHeight = 118;

			function isStatusOffScreen() {
				return $searchResults.position() && ($searchResults.position().top + summaryAndStatusHeight > window.innerHeight);
			}

			function placeStatus() {
				if (isStatusOffScreen()) {
					$status.addClass('fixed');
				} else {
					$status.removeClass('fixed');
				}
			}
			function showResults(results) {
				lastResults = results;
				$searchResults.find('tr.result').remove();
				$searchResults.removeClass('hidden');
				addResults(results);
			}
			function addResults(results) {
				var $template = $searchResults.find('tr.template');
				jQuery.each(results, function (i, result) {
					var $row = $template.clone();
					$row.removeClass('template');
					$row.addClass('result');

					$row.data('result', result);

					// generic results only have four attributes
					$row.find('td.info div.name').text(result.name);
					$row.find('td.info a').attr('href', result.link);

					/**
					 * Give plugins the ability to customize the search results. see result.js for examples
					 */
					if (self.hasRenderer(result.type)) {
						$row = self.getRenderer(result.type)($row, result);
					} else {
						// for backward compatibility add text div
						$row.find('td.info div.name').addClass('result');
						$row.find('td.result div.name').after('

');
						$row.find('td.result div.text').text(result.name);
						if (OC.search.customResults && OC.search.customResults[result.type]) {
							OC.search.customResults[result.type]($row, result);
						}
					}
					if ($row) {
						$searchResults.find('tbody').append($row);
					}
				});
				var count = $searchResults.find('tr.result').length;
				$status.data('count', count);
				if (count === 0) {
					$status.addClass('emptycontent').removeClass('status');
					$status.html('');
					$status.append('

');
					$status.append('
' + t('core', 'No search result in other places') + '
');
				} else {
					$status.removeClass('emptycontent').addClass('status');
					$status.text(n('core', '{count} search result in other places', '{count} search results in other places', count, {count:count}));
				}
			}
			function renderCurrent() {
				var result = $searchResults.find('tr.result')[currentResult];
				if (result) {
					var $result = $(result);
					var currentOffset = $('#app-content').scrollTop();
					$('#app-content').animate({
						// Scrolling to the top of the new result
						scrollTop: currentOffset + $result.offset().top - $result.height() * 2
					}, {
						duration: 100
					});
					$searchResults.find('tr.result.current').removeClass('current');
					$result.addClass('current');
				}
			}
			this.hideResults = function() {
				$searchResults.addClass('hidden');
				$searchResults.find('tr.result').remove();
				lastQuery = false;
			};
			this.clear = function() {
				self.hideResults();
				if(self.hasFilter(getCurrentApp())) {
					self.getFilter(getCurrentApp())('');
				}
				$searchBox.val('');
				$searchBox.blur();
			};

			/**
			 * Event handler for when scrolling the list container.
			 * This appends/renders the next page of entries when reaching the bottom.
			 */
			function onScroll(e) {
				if ($searchResults && lastQuery !== false && lastResults.length > 0) {
					var resultsBottom = $searchResults.offset().top + $searchResults.height();
					var containerBottom = $searchResults.offsetParent().offset().top + $searchResults.offsetParent().height();
					if ( resultsBottom < containerBottom * 1.2 ) {
						self.search(lastQuery, lastInApps, lastPage + 1);
					}
					placeStatus();
				}
			}

			$('#app-content').on('scroll', _.bind(onScroll, this));

			/**
			 * scrolls the search results to the top
			 */
			function scrollToResults() {
				setTimeout(function() {
					if (isStatusOffScreen()) {
						var newScrollTop = $('#app-content').prop('scrollHeight') - $searchResults.height();
						console.log('scrolling to ' + newScrollTop);
						$('#app-content').animate({
							scrollTop: newScrollTop
						}, {
							duration: 100,
							complete: function () {
								scrollToResults();
							}
						});
					}
				}, 150);
			}

			$('form.searchbox').submit(function(event) {
				event.preventDefault();
			});

			$searchBox.on('search', function (event) {
				if($searchBox.val() === '') {
					if(self.hasFilter(getCurrentApp())) {
						self.getFilter(getCurrentApp())('');
					}
					self.hideResults();
				}
			});
			$searchBox.keyup(function(event) {
				if (event.keyCode === 13) { //enter
					if(currentResult > -1) {
						var result = $searchResults.find('tr.result a')[currentResult];
						window.location = $(result).attr('href');
					}
				} else if(event.keyCode === 38) { //up
					if(currentResult > 0) {
						currentResult--;
						renderCurrent();
					}
				} else if(event.keyCode === 40) { //down
					if(lastResults.length > currentResult + 1){
						currentResult++;
						renderCurrent();
					}
				} else {
					var query = $searchBox.val();
					if (lastQuery !== query) {
						currentResult = -1;
						if (query.length > 2) {
							self.search(query);
						} else {
							self.hideResults();
						}
						if(self.hasFilter(getCurrentApp())) {
							self.getFilter(getCurrentApp())(query);
						}
					}
				}
			});
			$(document).keyup(function(event) {
				if(event.keyCode === 27) { //esc
					$searchBox.val('');
					if(self.hasFilter(getCurrentApp())) {
						self.getFilter(getCurrentApp())('');
					}
					self.hideResults();
				}
			});

			$searchResults.on('click', 'tr.result', function (event) {
				var $row = $(this);
				var item = $row.data('result');
				if(self.hasHandler(item.type)){
					var result = self.getHandler(item.type)($row, item, event);
					$searchBox.val('');
					if(self.hasFilter(getCurrentApp())) {
						self.getFilter(getCurrentApp())('');
					}
					self.hideResults();
					return result;
				}
			});
			$searchResults.on('click', '#status', function (event) {
				event.preventDefault();
				scrollToResults();
				return false;
			});
			placeStatus();

			OC.Plugins.attach('OCA.Search', this);
		}
	};
	OCA.Search = Search;
})();

$(document).ready(function() {
	var $searchResults = $('#searchresults');
	if ($searchResults.length) {
		$searchResults.addClass('hidden');
		$('#app-content')
			.find('.viewcontainer').css('min-height', 'initial');
	} else {
		$searchResults = $('
');
		$('#app-content')
			.append($searchResults)
			.find('.viewcontainer').css('min-height', 'initial');
	}
	$searchResults.load(OC.webroot + '/core/search/templates/part.results.html', function () {
		OC.Search = new OCA.Search($('#searchbox'), $('#searchresults'));
	});
});

/**
 * @deprecated use get/setRenderer() instead
 */
OC.search.customResults = {};
/**
 * @deprecated use get/setRenderer() instead
 */
OC.search.resultTypes = {};
<commit_msg>Fix grammar of search results phrase<commit_after>/**
 * ownCloud - core
 *
 * This file is licensed under the Affero General Public License version 3 or
 * later. See the COPYING file.
 *
 * @author Jörn Friedrich Dreyer <jfd@owncloud.com>
 * @copyright Jörn Friedrich Dreyer 2014
 */

(function () {
	/**
	 * @class OCA.Search
	 * @classdesc
	 *
	 * The Search class manages a search queries and their results
	 *
	 * @param $searchBox container element with existing markup for the #searchbox form
	 * @param $searchResults container element for results und status message
	 */
	var Search = function($searchBox, $searchResults) {
		this.initialize($searchBox, $searchResults);
	};
	/**
	 * @memberof OC
	 */
	Search.prototype = {

		/**
		 * Initialize the search box
		 *
		 * @param $searchBox container element with existing markup for the #searchbox form
		 * @param $searchResults container element for results und status message
		 * @private
		 */
		initialize: function($searchBox, $searchResults) {

			var self = this;

			/**
			 * contains closures that are called to filter the current content
			 */
			var filters = {};
			this.setFilter = function(type, filter) {
				filters[type] = filter;
			};
			this.hasFilter = function(type) {
				return typeof filters[type] !== 'undefined';
			};
			this.getFilter = function(type) {
				return filters[type];
			};

			/**
			 * contains closures that are called to render search results
			 */
			var renderers = {};
			this.setRenderer = function(type, renderer) {
				renderers[type] = renderer;
			};
			this.hasRenderer = function(type) {
				return typeof renderers[type] !== 'undefined';
			};
			this.getRenderer = function(type) {
				return renderers[type];
			};

			/**
			 * contains closures that are called when a search result has been clicked
			 */
			var handlers = {};
			this.setHandler = function(type, handler) {
				handlers[type] = handler;
			};
			this.hasHandler = function(type) {
				return typeof handlers[type] !== 'undefined';
			};
			this.getHandler = function(type) {
				return handlers[type];
			};

			var currentResult = -1;
			var lastQuery = '';
			var lastInApps = [];
			var lastPage = 0;
			var lastSize = 30;
			var lastResults = [];
			var timeoutID = null;

			this.getLastQuery = function() {
				return lastQuery;
			};

			/**
			 * Do a search query and display the results
			 * @param {string} query the search query
			 */
			this.search = function(query, inApps, page, size) {
				if (query) {
					OC.addStyle('core/search','results');
					if (typeof page !== 'number') {
						page = 1;
					}
					if (typeof size !== 'number') {
						size = 30;
					}
					if (typeof inApps !== 'object') {
						var currentApp = getCurrentApp();
						if(currentApp) {
							inApps = [currentApp];
						} else {
							inApps = [];
						}
					}
					// prevent double pages
					if ($searchResults && query === lastQuery && page === lastPage && size === lastSize) {
						return;
					}
					window.clearTimeout(timeoutID);
					timeoutID = window.setTimeout(function() {
						lastQuery = query;
						lastInApps = inApps;
						lastPage = page;
						lastSize = size;

						//show spinner
						$searchResults.removeClass('hidden');
						$status.addClass('status');
						$status.html(t('core', 'Searching other places')+'');

						// do the actual search query
						$.getJSON(OC.generateUrl('core/search'), {query:query, inApps:inApps, page:page, size:size }, function(results) {
							lastResults = results;
							if (page === 1) {
								showResults(results);
							} else {
								addResults(results);
							}
						});
					}, 500);
				}
			};

			//TODO should be a core method, see https://github.com/owncloud/core/issues/12557
			function getCurrentApp() {
				var content = document.getElementById('content');
				if (content) {
					var classList = document.getElementById('content').className.split(/\s+/);
					for (var i = 0; i < classList.length; i++) {
						if (classList[i].indexOf('app-') === 0) {
							return classList[i].substr(4);
						}
					}
				}
				return false;
			}

			var $status = $searchResults.find('#status');
			// summaryAndStatusHeight is a constant
			var summaryAndStatusHeight = 118;

			function isStatusOffScreen() {
				return $searchResults.position() && ($searchResults.position().top + summaryAndStatusHeight > window.innerHeight);
			}

			function placeStatus() {
				if (isStatusOffScreen()) {
					$status.addClass('fixed');
				} else {
					$status.removeClass('fixed');
				}
			}
			function showResults(results) {
				lastResults = results;
				$searchResults.find('tr.result').remove();
				$searchResults.removeClass('hidden');
				addResults(results);
			}
			function addResults(results) {
				var $template = $searchResults.find('tr.template');
				jQuery.each(results, function (i, result) {
					var $row = $template.clone();
					$row.removeClass('template');
					$row.addClass('result');

					$row.data('result', result);

					// generic results only have four attributes
					$row.find('td.info div.name').text(result.name);
					$row.find('td.info a').attr('href', result.link);

					/**
					 * Give plugins the ability to customize the search results. see result.js for examples
					 */
					if (self.hasRenderer(result.type)) {
						$row = self.getRenderer(result.type)($row, result);
					} else {
						// for backward compatibility add text div
						$row.find('td.info div.name').addClass('result');
						$row.find('td.result div.name').after('

');
						$row.find('td.result div.text').text(result.name);
						if (OC.search.customResults && OC.search.customResults[result.type]) {
							OC.search.customResults[result.type]($row, result);
						}
					}
					if ($row) {
						$searchResults.find('tbody').append($row);
					}
				});
				var count = $searchResults.find('tr.result').length;
				$status.data('count', count);
				if (count === 0) {
					$status.addClass('emptycontent').removeClass('status');
					$status.html('');
					$status.append('

');
					$status.append('
' + t('core', 'No search results in other places') + '
');
				} else {
					$status.removeClass('emptycontent').addClass('status');
					$status.text(n('core', '{count} search result in other places', '{count} search results in other places', count, {count:count}));
				}
			}
			function renderCurrent() {
				var result = $searchResults.find('tr.result')[currentResult];
				if (result) {
					var $result = $(result);
					var currentOffset = $('#app-content').scrollTop();
					$('#app-content').animate({
						// Scrolling to the top of the new result
						scrollTop: currentOffset + $result.offset().top - $result.height() * 2
					}, {
						duration: 100
					});
					$searchResults.find('tr.result.current').removeClass('current');
					$result.addClass('current');
				}
			}
			this.hideResults = function() {
				$searchResults.addClass('hidden');
				$searchResults.find('tr.result').remove();
				lastQuery = false;
			};
			this.clear = function() {
				self.hideResults();
				if(self.hasFilter(getCurrentApp())) {
					self.getFilter(getCurrentApp())('');
				}
				$searchBox.val('');
				$searchBox.blur();
			};

			/**
			 * Event handler for when scrolling the list container.
			 * This appends/renders the next page of entries when reaching the bottom.
			 */
			function onScroll(e) {
				if ($searchResults && lastQuery !== false && lastResults.length > 0) {
					var resultsBottom = $searchResults.offset().top + $searchResults.height();
					var containerBottom = $searchResults.offsetParent().offset().top + $searchResults.offsetParent().height();
					if ( resultsBottom < containerBottom * 1.2 ) {
						self.search(lastQuery, lastInApps, lastPage + 1);
					}
					placeStatus();
				}
			}

			$('#app-content').on('scroll', _.bind(onScroll, this));

			/**
			 * scrolls the search results to the top
			 */
			function scrollToResults() {
				setTimeout(function() {
					if (isStatusOffScreen()) {
						var newScrollTop = $('#app-content').prop('scrollHeight') - $searchResults.height();
						console.log('scrolling to ' + newScrollTop);
						$('#app-content').animate({
							scrollTop: newScrollTop
						}, {
							duration: 100,
							complete: function () {
								scrollToResults();
							}
						});
					}
				}, 150);
			}

			$('form.searchbox').submit(function(event) {
				event.preventDefault();
			});

			$searchBox.on('search', function (event) {
				if($searchBox.val() === '') {
					if(self.hasFilter(getCurrentApp())) {
						self.getFilter(getCurrentApp())('');
					}
					self.hideResults();
				}
			});
			$searchBox.keyup(function(event) {
				if (event.keyCode === 13) { //enter
					if(currentResult > -1) {
						var result = $searchResults.find('tr.result a')[currentResult];
						window.location = $(result).attr('href');
					}
				} else if(event.keyCode === 38) { //up
					if(currentResult > 0) {
						currentResult--;
						renderCurrent();
					}
				} else if(event.keyCode === 40) { //down
					if(lastResults.length > currentResult + 1){
						currentResult++;
						renderCurrent();
					}
				} else {
					var query = $searchBox.val();
					if (lastQuery !== query) {
						currentResult = -1;
						if (query.length > 2) {
							self.search(query);
						} else {
							self.hideResults();
						}
						if(self.hasFilter(getCurrentApp())) {
							self.getFilter(getCurrentApp())(query);
						}
					}
				}
			});
			$(document).keyup(function(event) {
				if(event.keyCode === 27) { //esc
					$searchBox.val('');
					if(self.hasFilter(getCurrentApp())) {
						self.getFilter(getCurrentApp())('');
					}
					self.hideResults();
				}
			});

			$searchResults.on('click', 'tr.result', function (event) {
				var $row = $(this);
				var item = $row.data('result');
				if(self.hasHandler(item.type)){
					var result = self.getHandler(item.type)($row, item, event);
					$searchBox.val('');
					if(self.hasFilter(getCurrentApp())) {
						self.getFilter(getCurrentApp())('');
					}
					self.hideResults();
					return result;
				}
			});
			$searchResults.on('click', '#status', function (event) {
				event.preventDefault();
				scrollToResults();
				return false;
			});
			placeStatus();

			OC.Plugins.attach('OCA.Search', this);
		}
	};
	OCA.Search = Search;
})();

$(document).ready(function() {
	var $searchResults = $('#searchresults');
	if ($searchResults.length) {
		$searchResults.addClass('hidden');
		$('#app-content')
			.find('.viewcontainer').css('min-height', 'initial');
	} else {
		$searchResults = $('
');
		$('#app-content')
			.append($searchResults)
			.find('.viewcontainer').css('min-height', 'initial');
	}
	$searchResults.load(OC.webroot + '/core/search/templates/part.results.html', function () {
		OC.Search = new OCA.Search($('#searchbox'), $('#searchresults'));
	});
});

/**
 * @deprecated use get/setRenderer() instead
 */
OC.search.customResults = {};
/**
 * @deprecated use get/setRenderer() instead
 */
OC.search.resultTypes = {};
<|endoftext|>


<commit_before>define([
    "./deferred"
], function (mjp) {
    function buildParams(prefix, obj, traditional, add ) { // Borrowed from jQuery
        var name;

        if ( Array.isArray( obj ) ) {
            // Serialize array item.
            mjp(obj).each(function( i, v ) {
                if ( traditional || /\[\]$/.test( prefix ) ) {
                    // Treat each array item as a scalar.
                    add( prefix, v );
                } else {
                    // Item is non-scalar (array or object), encode its numeric index.
                    buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
                }
            });
        } else if ( !traditional && typeof obj === "object" ) {
            // Serialize object item.
            for ( name in obj ) {
                buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
            }
        } else {
            // Serialize scalar item.
            add( prefix, obj );
        }
    }

    mjp.extend(mjp, {
        // Defaults
        ajaxSettings : {
            // accepts?
            async: true,
            cache: true,
            contentType: "application/x-www-form-urlencoded; charset=UTF-8",
            crossDomain: false,
            // data
            dataType: "json",
            // headers
            jsonp: "callback",
            // jsonpCallback
            // mimeType
            // password
            processData: true,
            statusCode: {},
            type: "GET",
            // username
            // url
            // xhr
            xhrFields: {}
        },

        // Helpers
        param : function (obj, traditional) { // Borrowed from jQuery
            var prefix,
                s = [],
                add = function( key, value ) {
                    // If value is a function, invoke it and return its value
                    value = typeof value === "function" ? value() : ( value == null ? "" : value );
                    s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
                };

            // Set traditional to true for jQuery <= 1.3.2 behavior.
            if ( traditional === undefined ) {
                traditional = mjp.ajaxSettings && mjp.ajaxSettings.traditional;
            }

            // If an array was passed in, assume that it is an array of form elements.
            if (Array.isArray(obj)) {
                // Serialize the form elements
                mjp(obj).each(function(i, el) {
                    add( el.name, el.value );
                });
            } else {
                // If traditional, encode the "old" way (the way 1.3.2 or older
                // did it), otherwise encode params recursively.
                for ( prefix in obj ) {
                    buildParams( prefix, obj[ prefix ], traditional, add );
                }
            }

            // Return the resulting serialization
            return s.join( "&" ).replace( /%20/g, "+" );
        }
    });
    //cors = ("withCredentials" in getXHR() || XDomainRequest);

    mjp.ajaxSetup = function(options) {
        mjp.extend(mjp.ajaxSettings, options);
    };

    mjp.ajax = function (url, settings) {
        var opts = {},
            deferred = mjp.Deferred(),
            xhr;

        if (!url) { settings = url; }

        opts = mjp.extend(opts, mjp.ajaxSettings, settings);

        opts.success && deferred.done(opts.success);
        opts.error && deferred.fail(opts.error);
        opts.complete && deferred.always(opts.complete);

        // Build XHR and execute it
        xhr = opts.xhr || (new XMLHttpRequest());
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    try {
                        deferred.resolve(
                            JSON.parse(xhr.responseText), xhr.statusText, xhr
                        );
                    } catch(e) {
                        deferred.reject(xhr, "parsererror");
                    }
                } else { // Failed
                    deferred.reject(xhr, "error");
                }
            }
        };
        xhr.open(opts.type, url, opts.async, opts.username, opts.password);

        if (opts.timeout) {
            setTimeout(function () {
                if (deferred.state() === "pending") {
                    xhr.abort();
                    deferred.reject(xhr, "timeout");
                }
            }, opts.timeout);
        }
        xhr.send(opts.data || null);

        return deferred.promise(xhr);
    };

    mjp.get = function (url, data, success, dataType) {
        return mjp.ajax(url, {
            data: data,
            url: url,
            success: success,
            dataType: dataType
        });
    };

    // Call methods .get, .getJSON and .post
    function createCall(settings) {
        return function (url, data, success, dataType) {
            var opts = {
                data: data,
                url: url,
                success: success,
                dataType: dataType
            };
            return mjp.ajax(mjp.extend(opts, settings));
        };
    }

    mjp.get = createCall({});
    mjp.getJSON = createCall({dataType: "json"});
    mjp.post = createCall({type: "POST"});

    // Methods that belong on mjp objects
    mjp.extend(mjp.fn, {
        load: function (url, data, complete) {
            var self = this;
            if (self.length) {
                mjp.get(url, data, complete || function (value) {
                    self.each(function (i, el) {
                        mjp(el).html(value);
                    });
                });
            }
            return self;
        },

        serialize: function() {
            return mjp.param( this.serializeArray() );
        },

        // Simplified jQuery version
        serializeArray: function() {
            var rCRLF = /\r?\n/g,
                rcheckableType = /^(?:checkbox|radio)$/i,
                rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
                rsubmittable = /^(?:input|select|textarea|keygen)/i;

            return this.map(function(i, el) {
                // If passed form element, then fetch its elements (form fields)
                return el.elements ? [].slice.call(el.elements) : this;
            })
            // Filter out disabled, unchecked and unselected
            .filter(function(i, el) {
                return el.name && !el.disabled &&
                    rsubmittable.test( el.nodeName ) &&
                    !rsubmitterTypes.test( el.type ) &&
                    ( el.checked || !rcheckableType.test( el.type ) );
            })
            // Grab values of the rest
            .map(function( i, elem ) {
                var val = elem.value;

                return val == null ?
                    null :
                    Array.isArray( val ) ?
                        mjp.map( val, function( val ) {
                            return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                        }) :
                        { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
            });
        }
    });

    return mjp;
});
<commit_msg>Mostly refactor of ajax + a bit of XHR<commit_after>define([
    "./deferred"
], function (mjp) {
    function buildParams(prefix, obj, traditional, add ) { // Borrowed from jQuery
        var name;

        if ( Array.isArray( obj ) ) {
            // Serialize array item.
            mjp(obj).each(function( i, v ) {
                if ( traditional || /\[\]$/.test( prefix ) ) {
                    // Treat each array item as a scalar.
                    add( prefix, v );
                } else {
                    // Item is non-scalar (array or object), encode its numeric index.
                    buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
                }
            });
        } else if ( !traditional && typeof obj === "object" ) {
            // Serialize object item.
            for ( name in obj ) {
                buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
            }
        } else {
            // Serialize scalar item.
            add( prefix, obj );
        }
    }

    mjp.extend(mjp, {
        // Defaults
        ajaxSettings : {
            // accepts?
            async: true,
            cache: true,
            contentType: "application/x-www-form-urlencoded; charset=UTF-8",
            crossDomain: false,
            // data
            dataType: "json",
            // headers
            jsonp: "callback",
            // jsonpCallback
            // mimeType
            // password
            processData: true,
            statusCode: {},
            type: "GET",
            // username
            // url
            // xhr
            xhrFields: {}
        }
    });
    //cors = ("withCredentials" in getXHR() || XDomainRequest);

    mjp.ajaxSetup = function(options) {
        mjp.extend(mjp.ajaxSettings, options);
    };

    mjp.ajax = function (url, settings) {
        var opts = {},
            deferred = mjp.Deferred(),
            xhr,
            data;

        if (!url) { settings = url; }

        opts = mjp.extend(opts, mjp.ajaxSettings, settings);

        opts.success && deferred.done(opts.success);
        opts.error && deferred.fail(opts.error);
        opts.complete && deferred.always(opts.complete);

        // Build XHR and execute it
        xhr = opts.xhr || (new XMLHttpRequest());
        xhr.onreadystatechange = function () {
            var data;
            if (xhr.readyState === 4) {
                if (xhr.status === 200) {
                    data = xhr.responseText;
                    try {
                        switch (opts.dataType) {
                            case "json":
                                data = JSON.parse(data);
                                break;
                            default:
                        }
                        deferred.resolve(data, xhr.statusText, xhr);
                    } catch(e) {
                        deferred.reject(xhr, "parsererror");
                    }
                } else { // Failed
                    deferred.reject(xhr, "error");
                }
            }
        };
        data = opts.data || null;
        if (data) {
            if (typeof data !== "string") {
                data = mjp.param(data, opts.traditional);
            }
            // Attach to URL if GET or HEAD, otherwise add to send
            if (/^(?:GET|HEAD)$/.test(opts.type)) {
                url += (/\?/.type(url) ? "&" : "?") + data;
                data = null;
            } else {
                // Set headers
                xhr.setRequestHeader("Content-Type", opts.contentType);
            }
        }

        if (opts.timeout) {
            setTimeout(function () {
                if (deferred.state() === "pending") {
                    xhr.abort();
                    deferred.reject(xhr, "timeout");
                }
            }, opts.timeout);
        }
        xhr.open(opts.type, url, opts.async, opts.username, opts.password);
        xhr.send(data);  // data can be null which is fine

        return deferred.promise(xhr);
    };

    // Call methods .get, .getJSON and .post
    function createCall(settings) {
        return function (url, data, success, dataType) {
            var opts = {
                data: data,
                url: url,
                success: success,
                dataType: dataType
            };
            return mjp.ajax(mjp.extend(opts, settings));
        };
    }

    // Attach methods that go on mjp itself
    mjp.extend(mjp, {
        get: createCall({}),
        getJSON: createCall({dataType: "json"}),
        post: createCall({type: "POST"}),
        getScript: createCall({dataType: "script"}),

        // Helpers
        param : function (obj, traditional) { // Borrowed from jQuery
            var prefix,
                s = [],
                add = function( key, value ) {
                    // If value is a function, invoke it and return its value
                    value = typeof value === "function" ? value() : ( value == null ? "" : value );
                    s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
                };

            // Set traditional to true for jQuery <= 1.3.2 behavior.
            if ( traditional === undefined ) {
                traditional = mjp.ajaxSettings && mjp.ajaxSettings.traditional;
            }

            // If an array was passed in, assume that it is an array of form elements.
            if (Array.isArray(obj)) {
                // Serialize the form elements
                mjp(obj).each(function(i, el) {
                    add( el.name, el.value );
                });
            } else {
                // If traditional, encode the "old" way (the way 1.3.2 or older
                // did it), otherwise encode params recursively.
                for ( prefix in obj ) {
                    buildParams( prefix, obj[ prefix ], traditional, add );
                }
            }

            // Return the resulting serialization
            return s.join( "&" ).replace( /%20/g, "+" );
        }
    });

    // Methods that belong on mjp objects
    mjp.extend(mjp.fn, {
        load: function (url, data, complete) {
            var self = this;
            if (self.length) {
                mjp.get(url, data, complete || function (value) {
                    self.each(function (i, el) {
                        mjp(el).html(value);
                    });
                });
            }
            return self;
        },

        // TODO: serialize & serializeArray add ~200 bytes to gzipped lib
        serialize: function() {
            return mjp.param( this.serializeArray() );
        },

        // Simplified jQuery version
        serializeArray: function() {
            var rCRLF = /\r?\n/g,
                rcheckableType = /^(?:checkbox|radio)$/i,
                rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
                rsubmittable = /^(?:input|select|textarea|keygen)/i;

            return this.map(function(i, el) {
                // If passed form element, then fetch its elements (form fields)
                return el.elements ? [].slice.call(el.elements) : this;
            })
            // Filter out disabled, unchecked and unselected
            .filter(function(i, el) {
                return el.name && !el.disabled &&
                    rsubmittable.test( el.nodeName ) &&
                    !rsubmitterTypes.test( el.type ) &&
                    ( el.checked || !rcheckableType.test( el.type ) );
            })
            // Grab values of the rest
            .map(function( i, elem ) {
                var val = elem.value;

                return val == null ?
                    null :
                    Array.isArray( val ) ?
                        mjp.map( val, function( val ) {
                            return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
                        }) :
                        { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
            });
        }
    });

    return mjp;
});
<|endoftext|>


<commit_before>/*global extendOptions, extendViewMember */

extendOptions('_setModelOptions', function() {
  return {
    populate: true
  };
});

extendViewMember('_onModelChange', function(model) {
  // TODO : What can we do to remove this duplication?
  var modelOptions = model && this._modelOptionsByCid[model.cid];
  if (modelOptions && modelOptions.populate) {
    this.populate(model.attributes, modelOptions.populate === true ? {} : modelOptions.populate);
  }
});

_.extend(Thorax.View.prototype, {
  //serializes a form present in the view, returning the serialized data
  //as an object
  //pass {set:false} to not update this.model if present
  //can pass options, callback or event in any order
  serialize: function() {
    var callback, options, event;
    //ignore undefined arguments in case event was null
    for (var i = 0; i < arguments.length; ++i) {
      if (typeof arguments[i] === 'function') {
        callback = arguments[i];
      } else if (typeof arguments[i] === 'object') {
        if ('stopPropagation' in arguments[i] && 'preventDefault' in arguments[i]) {
          event = arguments[i];
        } else {
          options = arguments[i];
        }
      }
    }

    if (event && !this._preventDuplicateSubmission(event)) {
      return;
    }

    options = _.extend({
      set: true,
      validate: true,
      children: true,
      silent: true
    }, options || {});

    var attributes = options.attributes || {};

    //callback has context of element
    var view = this;
    var errors = [];
    eachNamedInput.call(this, options, function() {
      var value = view._getInputValue(this, options, errors);
      if (typeof value !== 'undefined') {
        objectAndKeyFromAttributesAndName.call(this, attributes, this.name, {mode: 'serialize'}, function(object, key) {
          if (!object[key]) {
            object[key] = value;
          } else if (_.isArray(object[key])) {
            object[key].push(value);
          } else {
            object[key] = [object[key], value];
          }
        });
      }
    });

    this.trigger('serialize', attributes, options);

    if (options.validate) {
      var validateInputErrors = this.validateInput(attributes);
      if (validateInputErrors && validateInputErrors.length) {
        errors = errors.concat(validateInputErrors);
      }
      this.trigger('validate', attributes, errors, options);
      if (errors.length) {
        this.trigger('error', errors);
        return;
      }
    }

    if (options.set && this.model) {
      if (!this.model.set(attributes, {silent: options.silent})) {
        return false;
      }
    }

    callback && callback.call(this, attributes, _.bind(resetSubmitState, this));
    return attributes;
  },

  _preventDuplicateSubmission: function(event, callback) {
    event.preventDefault();

    var form = $(event.target);
    if ((event.target.tagName || '').toLowerCase() !== 'form') {
      // Handle non-submit events by gating on the form
      form = $(event.target).closest('form');
    }

    if (!form.attr('data-submit-wait')) {
      form.attr('data-submit-wait', 'true');
      if (callback) {
        callback.call(this, event);
      }
      return true;
    } else {
      return false;
    }
  },

  //populate a form from the passed attributes or this.model if present
  populate: function(attributes, options) {
    options = _.extend({
      children: true
    }, options || {});
    var value, attributes = attributes || this._getContext(this.model);
    //callback has context of element
    eachNamedInput.call(this, options, function() {
      objectAndKeyFromAttributesAndName.call(this, attributes, this.name, {mode: 'populate'}, function(object, key) {
        if (object && typeof (value = object[key]) !== 'undefined') {
          //will only execute if we have a name that matches the structure in attributes
          if (this.type === 'checkbox' && _.isBoolean(value)) {
            this.checked = value;
          } else if (this.type === 'checkbox' || this.type === 'radio') {
            this.checked = value == this.value;
          } else {
            this.value = value;
          }
        }
      });
    });

    this.trigger('populate', attributes);
  },

  //perform form validation, implemented by child class
  validateInput: function(/* attributes, options, errors */) {},

  _getInputValue: function(input /* , options, errors */) {
    if (input.type === 'checkbox' || input.type === 'radio') {
      if (input.checked) {
        return input.value;
      }
    } else if (input.multiple === true) {
      var values = [];
      $('option', input).each(function() {
        if (this.selected) {
          values.push(this.value);
        }
      });
      return values;
    } else {
      return input.value;
    }
  }
});

Thorax.View.on({
  error: function() {
    resetSubmitState.call(this);

    // If we errored with a model we want to reset the content but leave the UI
    // intact. If the user updates the data and serializes any overwritten data
    // will be restored.
    if (this.model && this.model.previousAttributes) {
      this.model.set(this.model.previousAttributes(), {
        silent: true
      });
    }
  },
  deactivated: function() {
    resetSubmitState.call(this);
  }
});

function eachNamedInput(options, iterator, context) {
  var i = 0,
      self = this;

  this.$('select,input,textarea', options.root || this.el).each(function() {
    if (!options.children) {
      if (self !== $(this).view({helper: false})) {
        return;
      }
    }
    if (this.type !== 'button' && this.type !== 'cancel' && this.type !== 'submit' && this.name && this.name !== '') {
      iterator.call(context || this, i, this);
      ++i;
    }
  });
}

//calls a callback with the correct object fragment and key from a compound name
function objectAndKeyFromAttributesAndName(attributes, name, options, callback) {
  var key,
      object = attributes,
      keys = name.split('['),
      mode = options.mode;

  for (var i = 0; i < keys.length - 1; ++i) {
    key = keys[i].replace(']', '');
    if (!object[key]) {
      if (mode === 'serialize') {
        object[key] = {};
      } else {
        return callback.call(this, false, key);
      }
    }
    object = object[key];
  }
  key = keys[keys.length - 1].replace(']', '');
  callback.call(this, object, key);
}

function resetSubmitState() {
  this.$('form').removeAttr('data-submit-wait');
}
<commit_msg>Remove unneded this.model argument<commit_after>/*global extendOptions, extendViewMember */

extendOptions('_setModelOptions', function() {
  return {
    populate: true
  };
});

extendViewMember('_onModelChange', function(model) {
  // TODO : What can we do to remove this duplication?
  var modelOptions = model && this._modelOptionsByCid[model.cid];
  if (modelOptions && modelOptions.populate) {
    this.populate(model.attributes, modelOptions.populate === true ? {} : modelOptions.populate);
  }
});

_.extend(Thorax.View.prototype, {
  //serializes a form present in the view, returning the serialized data
  //as an object
  //pass {set:false} to not update this.model if present
  //can pass options, callback or event in any order
  serialize: function() {
    var callback, options, event;
    //ignore undefined arguments in case event was null
    for (var i = 0; i < arguments.length; ++i) {
      if (typeof arguments[i] === 'function') {
        callback = arguments[i];
      } else if (typeof arguments[i] === 'object') {
        if ('stopPropagation' in arguments[i] && 'preventDefault' in arguments[i]) {
          event = arguments[i];
        } else {
          options = arguments[i];
        }
      }
    }

    if (event && !this._preventDuplicateSubmission(event)) {
      return;
    }

    options = _.extend({
      set: true,
      validate: true,
      children: true,
      silent: true
    }, options || {});

    var attributes = options.attributes || {};

    //callback has context of element
    var view = this;
    var errors = [];
    eachNamedInput.call(this, options, function() {
      var value = view._getInputValue(this, options, errors);
      if (typeof value !== 'undefined') {
        objectAndKeyFromAttributesAndName.call(this, attributes, this.name, {mode: 'serialize'}, function(object, key) {
          if (!object[key]) {
            object[key] = value;
          } else if (_.isArray(object[key])) {
            object[key].push(value);
          } else {
            object[key] = [object[key], value];
          }
        });
      }
    });

    this.trigger('serialize', attributes, options);

    if (options.validate) {
      var validateInputErrors = this.validateInput(attributes);
      if (validateInputErrors && validateInputErrors.length) {
        errors = errors.concat(validateInputErrors);
      }
      this.trigger('validate', attributes, errors, options);
      if (errors.length) {
        this.trigger('error', errors);
        return;
      }
    }

    if (options.set && this.model) {
      if (!this.model.set(attributes, {silent: options.silent})) {
        return false;
      }
    }

    callback && callback.call(this, attributes, _.bind(resetSubmitState, this));
    return attributes;
  },

  _preventDuplicateSubmission: function(event, callback) {
    event.preventDefault();

    var form = $(event.target);
    if ((event.target.tagName || '').toLowerCase() !== 'form') {
      // Handle non-submit events by gating on the form
      form = $(event.target).closest('form');
    }

    if (!form.attr('data-submit-wait')) {
      form.attr('data-submit-wait', 'true');
      if (callback) {
        callback.call(this, event);
      }
      return true;
    } else {
      return false;
    }
  },

  //populate a form from the passed attributes or this.model if present
  populate: function(attributes, options) {
    options = _.extend({
      children: true
    }, options || {});
    var value, attributes = attributes || this._getContext();
    //callback has context of element
    eachNamedInput.call(this, options, function() {
      objectAndKeyFromAttributesAndName.call(this, attributes, this.name, {mode: 'populate'}, function(object, key) {
        if (object && typeof (value = object[key]) !== 'undefined') {
          //will only execute if we have a name that matches the structure in attributes
          if (this.type === 'checkbox' && _.isBoolean(value)) {
            this.checked = value;
          } else if (this.type === 'checkbox' || this.type === 'radio') {
            this.checked = value == this.value;
          } else {
            this.value = value;
          }
        }
      });
    });

    this.trigger('populate', attributes);
  },

  //perform form validation, implemented by child class
  validateInput: function(/* attributes, options, errors */) {},

  _getInputValue: function(input /* , options, errors */) {
    if (input.type === 'checkbox' || input.type === 'radio') {
      if (input.checked) {
        return input.value;
      }
    } else if (input.multiple === true) {
      var values = [];
      $('option', input).each(function() {
        if (this.selected) {
          values.push(this.value);
        }
      });
      return values;
    } else {
      return input.value;
    }
  }
});

Thorax.View.on({
  error: function() {
    resetSubmitState.call(this);

    // If we errored with a model we want to reset the content but leave the UI
    // intact. If the user updates the data and serializes any overwritten data
    // will be restored.
    if (this.model && this.model.previousAttributes) {
      this.model.set(this.model.previousAttributes(), {
        silent: true
      });
    }
  },
  deactivated: function() {
    resetSubmitState.call(this);
  }
});

function eachNamedInput(options, iterator, context) {
  var i = 0,
      self = this;

  this.$('select,input,textarea', options.root || this.el).each(function() {
    if (!options.children) {
      if (self !== $(this).view({helper: false})) {
        return;
      }
    }
    if (this.type !== 'button' && this.type !== 'cancel' && this.type !== 'submit' && this.name && this.name !== '') {
      iterator.call(context || this, i, this);
      ++i;
    }
  });
}

//calls a callback with the correct object fragment and key from a compound name
function objectAndKeyFromAttributesAndName(attributes, name, options, callback) {
  var key,
      object = attributes,
      keys = name.split('['),
      mode = options.mode;

  for (var i = 0; i < keys.length - 1; ++i) {
    key = keys[i].replace(']', '');
    if (!object[key]) {
      if (mode === 'serialize') {
        object[key] = {};
      } else {
        return callback.call(this, false, key);
      }
    }
    object = object[key];
  }
  key = keys[keys.length - 1].replace(']', '');
  callback.call(this, object, key);
}

function resetSubmitState() {
  this.$('form').removeAttr('data-submit-wait');
}
<|endoftext|>


<commit_before>class PictureForm extends React.Component {

  componentWillMount() {
    // set the initial photographer and this month
    let p = 'andrew';
    let m = Object.keys(this.months())[new Date().getMonth()];

    // set the state and create initial filename
    this.setState({ photographer: p, month: m });
    this.updateFilename(p, m);
  }

  handleChange(e) {
    let o = {};
    o[e.target.id] = e.target.value;

    // replace all " with \" in caption and description
    if (e.target.id == 'caption' || e.target.id == 'description') {
      // first unescape so we don't multi-escape
      let str = e.target.value.replace(/\\"/g, '"');
      o[e.target.id] = str.replace(/"/g, '\\"');
    }

    // make it so
    this.setState(o);

    if (e.target.id == 'photographer' || e.target.id == 'month') {
      let p = (e.target.id == 'photographer' ? e.target.value : this.state.photographer);
      let m = (e.target.id == 'month' ? e.target.value : this.state.month);
      this.updateFilename(p, m);
    }
  }

  updateFilename(p, m) {
    this.setState({ filename: this.months()[m] + "-" + p + ".jpg" });
  }

  months() {
    return {
             "january": "01",
             "february": "02",
             "march": "03",
             "april": "04",
             "may": "05",
             "june": "06",
             "july": "07",
             "august": "08",
             "september": "09",
             "october": "10",
             "november": "11",
             "december": "12"
           };
  }

  photographers() {
    return [
             "andrew",
             "dad",
             "dan",
             "ed",
             "gareth",
             "hon",
             "kirsty",
             "lyns",
             "michael",
             "sheena",
             "shelley",
             "tom"
           ];
  }

  selectOption(string, index) {
    return <option key={ index } value={ string }>
             { string.charAt(0).toUpperCase() + string.slice(1) }
           </option>;
  }

  clear() {
    this.setState({
                    caption: '',
                    title: '',
                    description: '',
                    alt: ''
                  });
  }

  render() {
    return (

          <form action="get" post="#">

              Photographer:
              <select name="photographer" id="photographer" className="form-control" onChange={ (event) => this.handleChange(event) }>
                { this.photographers().map(this.selectOption) }
              </select>

              Main page caption:
              <input type="text"
                     name="caption"
                     id="caption"
                     className="form-control"
                     value={ this.state.caption }
                     onChange={ (event) => this.handleChange(event) } />

              Image title:
              <input type="text"
                     name="title"
                     id="title"
                     className="form-control"
                     value={ this.state.title }
                     onChange={ (event) => this.handleChange(event) } />

              Image description:
              <textarea name="description"
                        id="description"
                        className="form-control"
                        rows="10"
                        value={ this.state.description }
                        onChange={ (event) => this.handleChange(event) } ></textarea>

              Image alt:
              <input type="text"
                     name="alt"
                     id="alt"
                     className="form-control"
                     value={ this.state.alt }
                     onChange={ (event) => this.handleChange(event) } />

              Month:
              <select name="month" id="month" className="form-control" onChange={ (event) => this.handleChange(event) } value={ this.state.month }>
                { Object.keys(this.months()).map(this.selectOption) }
              </select>

            <input type="button"
                   value="Clear"
                   className="btn btn-default"
                   onClick={ () => this.clear() } />

          </form>

        <PictureYaml filename={ this.state.filename }
                     title={ this.state.title }
                     caption={ this.state.caption }
                     description={ this.state.description }
                     alt={ this.state.alt }
                     month={ this.state.month } />

    );
  }
}

class PictureYaml extends React.Component {
  render() {
    return (

              -

                image: { this.props.filename }

                image_title: { this.props.title }

                caption: "{ this.props.caption }"

                description: "{ this.props.description }"

                alt: { this.props.alt }

                month: { this.props.month }

    );
  }
}

ReactDOM.render(
  <PictureForm />,
  document.getElementById('app')
);
<commit_msg>Added keyboard shortcut for clearing form<commit_after>class PictureForm extends React.Component {

  componentWillMount() {
    // set the initial photographer and this month
    let p = 'andrew';
    let m = Object.keys(this.months())[new Date().getMonth()];

    // set the state and create initial filename
    this.setState({ photographer: p, month: m });
    this.updateFilename(p, m);
  }

  handleChange(e) {
    let o = {};
    o[e.target.id] = e.target.value;

    // replace all " with \" in caption and description
    if (e.target.id == 'caption' || e.target.id == 'description') {
      // first unescape so we don't multi-escape
      let str = e.target.value.replace(/\\"/g, '"');
      o[e.target.id] = str.replace(/"/g, '\\"');
    }

    // make it so
    this.setState(o);

    if (e.target.id == 'photographer' || e.target.id == 'month') {
      let p = (e.target.id == 'photographer' ? e.target.value : this.state.photographer);
      let m = (e.target.id == 'month' ? e.target.value : this.state.month);
      this.updateFilename(p, m);
    }
  }

  updateFilename(p, m) {
    this.setState({ filename: this.months()[m] + "-" + p + ".jpg" });
  }

  months() {
    return {
             "january": "01",
             "february": "02",
             "march": "03",
             "april": "04",
             "may": "05",
             "june": "06",
             "july": "07",
             "august": "08",
             "september": "09",
             "october": "10",
             "november": "11",
             "december": "12"
           };
  }

  photographers() {
    return [
             "andrew",
             "dad",
             "dan",
             "ed",
             "gareth",
             "hon",
             "kirsty",
             "lyns",
             "michael",
             "sheena",
             "shelley",
             "tom"
           ];
  }

  selectOption(string, index) {
    return <option key={ index } value={ string }>
             { string.charAt(0).toUpperCase() + string.slice(1) }
           </option>;
  }

  clear() {
    this.setState({
                    caption: '',
                    title: '',
                    description: '',
                    alt: ''
                  });
  }

  render() {
    return (

          <form action="get" post="#">

              Photographer:
              <select name="photographer" id="photographer" className="form-control" onChange={ (event) => this.handleChange(event) }>
                { this.photographers().map(this.selectOption) }
              </select>

              Main page caption:
              <input type="text"
                     name="caption"
                     id="caption"
                     className="form-control"
                     value={ this.state.caption }
                     onChange={ (event) => this.handleChange(event) } />

              Image title:
              <input type="text"
                     name="title"
                     id="title"
                     className="form-control"
                     value={ this.state.title }
                     onChange={ (event) => this.handleChange(event) } />

              Image description:
              <textarea name="description"
                        id="description"
                        className="form-control"
                        rows="10"
                        value={ this.state.description }
                        onChange={ (event) => this.handleChange(event) } ></textarea>

              Image alt:
              <input type="text"
                     name="alt"
                     id="alt"
                     className="form-control"
                     value={ this.state.alt }
                     onChange={ (event) => this.handleChange(event) } />

              Month:
              <select name="month" id="month" className="form-control" onChange={ (event) => this.handleChange(event) } value={ this.state.month }>
                { Object.keys(this.months()).map(this.selectOption) }
              </select>

            <input type="button"
                   value="Clear"
                   className="btn btn-default"
                   accessKey="c"
                   onClick={ () => this.clear() } />

          </form>

        <PictureYaml filename={ this.state.filename }
                     title={ this.state.title }
                     caption={ this.state.caption }
                     description={ this.state.description }
                     alt={ this.state.alt }
                     month={ this.state.month } />

    );
  }
}

class PictureYaml extends React.Component {
  render() {
    return (

              -

                image: { this.props.filename }

                image_title: { this.props.title }

                caption: "{ this.props.caption }"

                description: "{ this.props.description }"

                alt: { this.props.alt }

                month: { this.props.month }

    );
  }
}

ReactDOM.render(
  <PictureForm />,
  document.getElementById('app')
);
<|endoftext|>


<commit_before>import React from 'react';

import Utils from './utils';

import Button from './button';

export default class Form extends React.Component{

    static propTypes = {
        config: React.PropTypes.object.isRequired
    };

	componentDidMount(){
        // 页面new时，表单验证初始化
        this.initFormValidation();
	}

    componentDidUpdate(){
        // 页面重构时，表单验证初始化
        this.initFormValidation();
    }

    // ========================================= 自定义方法 =================================================
	/**
	 * [initForm 表单数据初始化]
	 * @return        {[type]}                 [description]
	 */
	initForm(){
 		let datas = {};
        let checkValue = [];
        let pname;
        let len = this.refs.form && this.refs.form.length || 0;
        for (let i = 0; i < len; i++) {
        	let input = this.refs.form[i];
        	let name = input.name;
        	let val = input.value;
        	let data;
        	if(name){
        		if(input.type == 'checkbox'){
        			if(pname != name){
        				checkValue = [];
        				pname = name;
        			}
        			if(input.checked){
        				checkValue.push(val);
        				data = {
		        			[name]: checkValue.toString()
			        	};
        			}else{
        				continue;
        			}
        		}else if (input.type == 'radio') {
        			checkValue = [];
        			if(input.checked){
        				data = {
		        			[name]: val
			        	};
        			}else{
        				continue;
        			}
        		}else{
        			checkValue = [];
	        	    data = {
	        			[name]: val
		        	};
        		}

	        	Object.assign(datas, data);

                // 绑定回车事件
                this.keyEnter(input);
        	}
        }
        return datas;
	}

    /**
     * [initFormValidation 表单验证初始化]
     * @return        {[type]}                 [description]
     */
    initFormValidation(){
        // 实体 和 操作
        if(this.props.entityOper){
            // 判断能不能直接发布事件
            this.ifUseConfig = true;
            let entityOper = this.props.entityOper;
            // 获取实体
            this.entity = entityOper.split('-')[0];
            // 获取操作
            this.operation = entityOper.split('-')[1];
        }else{
            // 判断能不能直接发布事件
            this.ifUseConfig = false;
        }

        const config = this.props.config;
        // 初始化验证规则
        // 获取实体的 所有 验证规则
        let rulesAll = config.rules;
        // 获取实体的 所有 报错信息
        let msgAll = config.messages;

        // 表单初始数据
        let datas = this.initForm();

        // 获得表单的数据key
        let filter = Object.keys(datas);
        // 根据表单数据key获取 所需 验证
        let rules = Utils.getKeyObj(rulesAll, filter);
        // 根据表单数据key获取 所需 报错信息
        let messages = Utils.getKeyObj(msgAll, filter);

        // 验证实体
        let validator = {
            // 获得焦点报错信息清除
            focusCleanup: true,
            // 提交表单验证时不自动聚焦错误表单组件
            focusInvalid: false,
            // 验证规则
            rules,
            // 验证报错信息
            messages,
            // 失去焦点自动验证
            onfocusout: (element) => {
                element.value = element.value.trim();
                $(element).valid();
            },
            // 验证通过
            submitHandler: (form, event) => {
                console.log("Submitted!");
                this.formSubmit();
            },
            // 验证错误
            errorPlacement: (error, element) => {
                // 指定错误信息位置
                // 如果是radio或checkbox或select
                if (element.is(':radio') || element.is(':checkbox') || element.is('select')) {
                    // 将错误信息添加当前元素的父结点后面
                    error.appendTo(element.parent());
                } else if(element.is(':input') && element.parent().hasClass('input-group')){
                    // 将错误信息添加当前元素的父结点后面
                    error.insertAfter(element.parent());
                }else {
                    // 将错误信息直接添加在后面
                    error.insertAfter(element);
                }
            }
        };

        // 初始化验证
        this.validator = $(this.refs.form).validate(validator);
    }

	/**
	 * [formSubmit 表单验证成功之后的操作]
	 * @return        {[type]}                 [description]
	 */
	async formSubmit(){
        // 如果按钮 disabled装填，点击无效果
        if(this.refs.button.state.disabled){
            return false;
        }
        // 获取表单数据
		let datas = this.initForm();
        // 按钮 disabled
        console.log('disabled');
        this.refs.button.setState({disabled: true});

        if(this.ifUseConfig){
            // 表单数据操作
            await this.formOperation(datas);
        }else{
            await this.props.callback(datas);
        }

        // 按钮 enabled
        console.log('enabled');
        this.refs.button.setState({disabled: false});
        // 表单重置
        this.setState({});

	}

    /**
     * [formOperation 表单数据接口操作]
     * @return        {[type]}                 [description]
     */
    formOperation(datas){
        return new Promise((resolve, reject) => {
            setTimeout(() => {
                console.log('ajax ajax...');
                resolve();
            }, 5000);
        });

        // debugger;
        // return window.db[entity][this.operation](datas);
    }

    /**
     * [keyEnter 表单对事件的处理-回车自动定位下一输入框]
     * @param         {[type]}                 element [description]
     * @return        {[type]}                         [description]
     */
    keyEnter(element){
        // 绑定 keyup 事件
        $(element).keyup((e) => {
            if (13 == e.keyCode) {
                // 回车事件
                // 下个表单元素
                let next = $(e.target.parentElement).next();
                if(next.hasClass('form-group')){
                    // 表单元素
                    let ele;
                    // 拿到 表单输入元素
                    do{
                        ele = next.find('input[type!=checkbox][type!=radio][type!=search]');
                        ele = ele.length ? ele : next.find('textarea');
                        next = next.next();
                    }while(!ele.length && next.hasClass('form-group'));

                    if(ele.length){
                        // 有表单输入元素
                        ele.focus();
                    }else {
                        // 没有表单输入元素，进行验证
                        this.validator.form();
                    }
                }else{
                    // 按钮元素，进行验证
                    this.validator.form();
                }
                return false;
            }
        });
    }

	render(){
        const {disabled, ...others} = this.props;
		return(
			<form ref="form" key={Utils.getKey('form')}>
                {React.Children.map(this.props.children, (element, i) => {
                    // 设置 disabled 属性
                    if(element){
                        let props = Object.assign({}, element.props);
                        props.disabled = props.enabled ? '' : (props.disabled || disabled);
                        if(props.type == 'submit'){
                            const {...othersb} = props;
                            return (<Button ref="button" {...othersb} />);
                        }else{
                            let ele = Object.assign({}, element);
                            ele.props = props;
                            return ele;
                        }
                    }
                })}
			</form>
		)
	}
}<commit_msg>Delete form.js<commit_after><|endoftext|>


<commit_before>import React from 'react';
import {Board} from "./board";
import {Status} from "./status";

class Game extends React.Component {
    constructor() {
        super();
        this.state = {
            history: [{
                squares: new Array(9).fill(null)
            }],
            stepNumber: 0,
            xIsNext: true
        };
    }

    handleClick(i) {
        const history = this.state.history.slice(0, this.state.stepNumber + 1);
        const current = history[history.length - 1];
        const squares = current.squares.slice();
        if (this.calculateWinner(squares) || squares[i]) {
            return;
        }
        squares[i] = this.state.xIsNext ? "X" : "O";
        this.setState({
            history: history.concat([
                {
                    squares: squares
                }
            ]),
            stepNumber: history.length,
            xIsNext: !this.state.xIsNext
        });
    }

    jumpTo(step) {
        this.setState({
            stepNumber: step,
            xIsNext: (step % 2) === 0
        });
    }

    calculateWinner(squares) {
        const lines = [
            [0, 1, 2],
            [3, 4, 5],
            [6, 7, 8],
            [0, 3, 6],
            [1, 4, 7],
            [2, 5, 8],
            [0, 4, 8],
            [2, 4, 6],
        ];

        for (let i = 0; i < lines.length; i++) {
            const [a, b, c] = lines[i];
            if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
                return squares[a];
            }
        }

        return null;
    }

    render() {
        const history = this.state.history;
        const current = history[this.state.stepNumber];
        const winner = this.calculateWinner(current.squares);
        const nextPlayer = (this.state.xIsNext) ? "X" : "O";

        const moves = history.map((step, move) => {
            const desc = move ? "Move #" + move : "Game start";
            return (

                     this.jumpTo(move)}>{desc}

            );
        });

        return (

                    <Board
                        squares={current.squares}
                        onClick={(i) => this.handleClick(i)}
                    />

                    <Status
                        winner={winner}
                        nextPlayer={nextPlayer}
                    />

{moves}

        );
    }
}

export {Game};<commit_msg>Added the column and the row to each step<commit_after>import React from 'react';
import {Board} from "./board";
import {Status} from "./status";

class Game extends React.Component {
    constructor() {
        super();
        this.state = {
            history: [{
                squares: new Array(9).fill(null),
            }],
            stepNumber: 0,
            gameSteps: new Array(9).fill({column: null, row: null}),
            xIsNext: true
        };
    }

    handleClick(i) {
        const history = this.state.history.slice(0, this.state.stepNumber + 1);
        const current = history[history.length - 1];
        const squares = current.squares.slice();
        const gameSteps = this.state.gameSteps.slice();
        if (this.calculateWinner(squares) || squares[i]) {
            return;
        }

        squares[i] = this.state.xIsNext ? "X" : "O";
        gameSteps[history.length] = {
            column: ((i % 3) + 1),
            row: (Math.floor(i / 3) + 1)
        };

        this.setState({
            history: history.concat([
                {
                    squares,
                }
            ]),
            gameSteps,
            stepNumber: history.length,
            xIsNext: !this.state.xIsNext
        });
    }

    jumpTo(step) {
        this.setState({
            stepNumber: step,
            xIsNext: (step % 2) === 0
        });
    }

    calculateWinner(squares) {
        const lines = [
            [0, 1, 2],
            [3, 4, 5],
            [6, 7, 8],
            [0, 3, 6],
            [1, 4, 7],
            [2, 5, 8],
            [0, 4, 8],
            [2, 4, 6],
        ];

        for (let i = 0; i < lines.length; i++) {
            const [a, b, c] = lines[i];
            if (squares[a] && squares[a] === squares[b] && squares[a] === squares[c]) {
                return squares[a];
            }
        }

        return null;
    }

    render() {
        const history = this.state.history;
        const current = history[this.state.stepNumber];
        const winner = this.calculateWinner(current.squares);
        const nextPlayer = (this.state.xIsNext) ? "X" : "O";

        const moves = history.map((step, move) => {
            const gameStep = this.state.gameSteps[move];
            const desc = move ? "Move #" + move + " (R" + gameStep.row + ", C" + gameStep.column + ")" : "Game start";
            return (

                     this.jumpTo(move)}>{desc}

            );
        });

        return (

                    <Board
                        squares={current.squares}
                        onClick={(i) => this.handleClick(i)}
                    />

                    <Status
                        winner={winner}
                        nextPlayer={nextPlayer}
                    />

{moves}

        );
    }
}

export {Game};<|endoftext|>


<commit_before>const base = require('./karma.base.conf.js');

if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) {
	console.log('Make sure the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are set.');
	process.exit(1);
}

const {
	TRAVIS_BRANCH,
	TRAVIS_BUILD_NUMBER,
	TRAVIS_JOB_NUMBER,
} = process.env;

// Available browsers and platforms: https://saucelabs.com/rest/v1/info/platforms/webdriver
const platforms = {
	'Windows 10': {
		'chrome': ['latest'],
		'firefox': ['latest'],
		'microsoftEdge': ['latest', '13'],
	},
	'Windows 8.1': {
		'internet explorer': ['11'],
	},
	'Windows 7': {
		'internet explorer': ['10'],
		'chrome': ['26'],
		'firefox': ['16'],
	},
	'Android 6': {
		'android': ['latest'],
	},
	'Android 4': {
		'android': ['4.4'],
	},
	'Mac 10.10': {
		'iphone': ['10.0'],
		'safari': ['8'],
	},
	'Mac 10.13': {
		'iphone': ['latest'],
		'safari': ['latest'],
	},
};

const sl_launchers = {};
for (const platform in platforms) {
	for (const browserName in platforms[platform]) if (platforms[platform].hasOwnProperty(browserName)) {
		const versions = platforms[platform][browserName];
		versions.forEach(version => {
			const key = ['sl', browserName, version, platform.replace(/\s+/g, '')].join('_');
			sl_launchers[key] = {
				base: 'SauceLabs',
				browserName,
				version,
				platform,
			};
		})
	}
}

module.exports = function (config) {

	// turn off coverage for sauce
	base.karmaTypescriptConfig.coverageOptions.instrumentation = false;

	config.set(Object.assign(base, {
		sauceLabs: {
			testName: 'TsDate tests for Sauce Labs',
			build: TRAVIS_JOB_NUMBER || `test-${new Date().toISOString()}`,
			tags: [TRAVIS_BRANCH || 'local'],
			public: 'public',
		},

		// hostname: '127.0.0.1',

		captureTimeout: 5 * 60000,
		browserNoActivityTimeout: 5 * 60000,

		customLaunchers: sl_launchers,
		browsers: Object.keys(sl_launchers),

		reporters: base.reporters.concat([
			'dots',
			'saucelabs',
		]),

		concurrency: 3,
	}));
};
<commit_msg>test: fixed mac version<commit_after>const base = require('./karma.base.conf.js');

if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) {
	console.log('Make sure the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are set.');
	process.exit(1);
}

const {
	TRAVIS_BRANCH,
	TRAVIS_BUILD_NUMBER,
	TRAVIS_JOB_NUMBER,
} = process.env;

// Available browsers and platforms: https://saucelabs.com/rest/v1/info/platforms/webdriver
const platforms = {
	'Windows 10': {
		'chrome': ['latest'],
		'firefox': ['latest'],
		'microsoftEdge': ['latest', '13'],
	},
	'Windows 8.1': {
		'internet explorer': ['11'],
	},
	'Windows 7': {
		'internet explorer': ['10'],
		'chrome': ['26'],
		'firefox': ['16'],
	},
	'Android 6': {
		'android': ['latest'],
	},
	'Android 4': {
		'android': ['4.4'],
	},
	'Mac 10.10': {
		'iphone': ['10.0'],
		'safari': ['8'],
	},
	'Mac 10.12': {
		'iphone': ['latest'],
		'safari': ['latest'],
	},
};

const sl_launchers = {};
for (const platform in platforms) {
	for (const browserName in platforms[platform]) if (platforms[platform].hasOwnProperty(browserName)) {
		const versions = platforms[platform][browserName];
		versions.forEach(version => {
			const key = ['sl', browserName, version, platform.replace(/\s+/g, '')].join('_');
			sl_launchers[key] = {
				base: 'SauceLabs',
				browserName,
				version,
				platform,
			};
		})
	}
}

module.exports = function (config) {

	// turn off coverage for sauce
	base.karmaTypescriptConfig.coverageOptions.instrumentation = false;

	config.set(Object.assign(base, {
		sauceLabs: {
			testName: 'TsDate tests for Sauce Labs',
			build: TRAVIS_JOB_NUMBER || `test-${new Date().toISOString()}`,
			tags: [TRAVIS_BRANCH || 'local'],
			public: 'public',
		},

		// hostname: '127.0.0.1',

		captureTimeout: 5 * 60000,
		browserNoActivityTimeout: 5 * 60000,

		customLaunchers: sl_launchers,
		browsers: Object.keys(sl_launchers),

		reporters: base.reporters.concat([
			'dots',
			'saucelabs',
		]),

		concurrency: 3,
	}));
};
<|endoftext|>


<commit_before>javascript:
(function() {

	if (mw.config.get('wgPageName') !== 'Category:快速删除候选') return;

	var api = new mw.Api();
	var now = new Date();

	var titlelist = [];
	$('.mw-category-generated a').each(function(i, e) {
		var title = decodeURIComponent(e.href.replace(/^.*?\/wiki\/(.+?)(?:\?.+)?$/, '$1'));
		$('<span>').attr('id', 'cricc-item-' + title).insertAfter(e);
		titlelist.push(title);
	});
	var addText = function(title, type, text) {
		$('<span>').text('（' + text + '）').addClass('cricc-' + type).appendTo('#cricc-item-' + title.replace(/([():/])/g, '\\$1'));
	}

	for (var i = 0; i < titlelist.length; i += 50) {
		var titlestr = titlelist.slice(i, i + 50).join('|');
		api.get({
			'action': 'query',
			'format': 'json',
			'prop': 'revisions',
			'titles': titlestr,
			'rvprop': 'content',
		}).done(data => {
			for (i in data.query.pages) {
				var page = data.query.pages[i];
				if (page.missing === undefined) {
					var title = page.title.replace(/ /g, '_');
					var m = page.revisions[0]['*'].match(/{{\s*(?:d|delete|csd|速删|速刪)\s*\|\s*(.+?)\s*}}/i);
					if (m !== null) {
						var m2 = m[1].match(/bot=Jimmy-bot\|([^|}]+)/);
						if (m2 !== null) {
							addText(title, 'reason', m2[1]);
						} else {
							addText(title, 'reason', m[1]);
						}
					}
					if (page.revisions[0]['*'].match(/{{\s*Notmandarin\s*\|/i)) {
						addText(title, 'reason', 'G14');
					}
					if (page.revisions[0]['*'].match(/{{\s*AFC submission\s*\|/i)) {
						addText(title, 'reason', 'AFC');
					}
					if ((m = page.revisions[0]['*'].match(/{{\s*(?:hang ?on|有爭議|有争议)\s*\|\s*(.+?)\s*}}/i)) !== null) {
						addText(title, 'hangon', 'Hangon：' + m[1]);
					}
				}
			}
		}).fail(() => {
			alert('Error!');
		})
	}

	var usedTitle = [];
	api.get({
		'action': 'query',
		'list': 'recentchanges',
		'rcprop': 'timestamp|comment',
		'rclimit': '100',
		'rctype': 'categorize',
		'rctitle': 'Category:快速删除候选',
	}).done(data => {
		data.query.recentchanges.forEach(row => {
			var m = row.comment.match(/\[\[:([^\]]+)\]\]/);
			if (m) {
				var title = m[1].replace(/ /g, '_');
				if (usedTitle.indexOf(title) === -1 && row.comment.indexOf('已添加至分类') > -1) {
					var current = new Date(row.timestamp);
					var period = (now.getTime() - current.getTime()) / 1000 / 3600;
					if (period > 1) {
						addText(title, 'period', Math.round(period) + ' hr');
					} else {
						addText(title, 'period', Math.round(period * 60) + ' min')
					}
				}
				usedTitle.push(title);
			}
		});
	})

})();
<commit_msg>Update csd-reason-in-csd-cat.js<commit_after>javascript:
(function() {

	if (mw.config.get('wgPageName') !== 'Category:快速删除候选') return;

	var api = new mw.Api();
	var now = new Date();

	var titlelist = [];
	$('.mw-category-generated a').each(function(i, e) {
		var title = decodeURIComponent(e.href.replace(/^.*?\/wiki\/(.+?)(?:\?.+)?$/, '$1'));
		$('<span>').attr('id', 'cricc-item-' + title).insertAfter(e);
		titlelist.push(title);
	});
	var addText = function(title, type, text) {
		$('<span>').text('（' + text + '）').addClass('cricc-' + type).appendTo('#cricc-item-' + title.replace(/([():/.])/g, '\\$1'));
	}

	for (var i = 0; i < titlelist.length; i += 50) {
		var titlestr = titlelist.slice(i, i + 50).join('|');
		api.get({
			'action': 'query',
			'format': 'json',
			'prop': 'revisions',
			'titles': titlestr,
			'rvprop': 'content',
		}).done(data => {
			for (i in data.query.pages) {
				var page = data.query.pages[i];
				if (page.missing === undefined) {
					var title = page.title.replace(/ /g, '_');
					var m = page.revisions[0]['*'].match(/{{\s*(?:d|delete|csd|速删|速刪)\s*\|\s*(.+?)\s*}}/i);
					if (m !== null) {
						var m2 = m[1].match(/bot=Jimmy-bot\|([^|}]+)/);
						if (m2 !== null) {
							addText(title, 'reason', m2[1]);
						} else {
							addText(title, 'reason', m[1]);
						}
					}
					if (page.revisions[0]['*'].match(/{{\s*Notmandarin\s*\|/i)) {
						addText(title, 'reason', 'G14');
					}
					if (page.revisions[0]['*'].match(/{{\s*AFC submission\s*\|/i)) {
						addText(title, 'reason', 'AFC');
					}
					if ((m = page.revisions[0]['*'].match(/{{\s*(?:hang ?on|有爭議|有争议)\s*\|\s*(.+?)\s*}}/i)) !== null) {
						addText(title, 'hangon', 'Hangon：' + m[1]);
					}
				}
			}
		}).fail(() => {
			alert('Error!');
		})
	}

	var usedTitle = [];
	api.get({
		'action': 'query',
		'list': 'recentchanges',
		'rcprop': 'timestamp|comment',
		'rclimit': '500',
		'rctype': 'categorize',
		'rctitle': 'Category:快速删除候选',
	}).done(data => {
		data.query.recentchanges.forEach(row => {
			var m = row.comment.match(/\[\[:([^\]]+)\]\]/);
			if (m) {
				var title = m[1].replace(/ /g, '_');
				if (usedTitle.indexOf(title) === -1 && row.comment.indexOf('已添加至分类') > -1) {
					var current = new Date(row.timestamp);
					var period = (now.getTime() - current.getTime()) / 1000 / 3600;
					if (period > 1) {
						addText(title, 'period', Math.round(period) + ' hr');
					} else {
						addText(title, 'period', Math.round(period * 60) + ' min')
					}
				}
				usedTitle.push(title);
			}
		});
	})

})();
<|endoftext|>


<commit_before>process.env.BABEL_ENV = 'production'

const
  path = require('path'),
  fs = require('fs'),
  rollup = require('rollup'),
  uglify = require('uglify-es'),
  buble = require('rollup-plugin-buble'),
  json = require('rollup-plugin-json'),
  vue = require('rollup-plugin-vue'),
  replace = require('rollup-plugin-replace'),
  nodeResolve = require('rollup-plugin-node-resolve'),
  buildConf = require('./build.conf'),
  buildUtils = require('./build.utils'),
  vueConfig = {
    compileTemplate: true,
    htmlMinifier: {collapseBooleanAttributes: false}
  }

const builds = [
  {
    rollup: {
      input: {
        input: resolve(`src/index.esm.js`)
      },
      output: {
        file: resolve(`dist/quasar.${buildConf.themeToken}.esm.js`),
        format: 'es'
      }
    },
    build: { unminified: true }
  },
  {
    rollup: {
      input: {
        input: resolve('src/ie-compat/ie.js')
      },
      output: {
        file: resolve('dist/quasar.ie.polyfills.js'),
        format: 'es'
      }
    },
    build: { unminified: true }
  },
  {
    rollup: {
      input: {
        input: resolve('src/ie-compat/ie.js')
      },
      output: {
        file: resolve('dist/umd/quasar.ie.polyfills.umd.js'),
        format: 'umd'
      }
    },
    build: { minified: true }
  },
  {
    rollup: {
      input: {
        input: resolve(`src/index.umd.js`)
      },
      output: {
        file: resolve(`dist/umd/quasar.${buildConf.themeToken}.umd.js`),
        format: 'umd'
      }
    },
    build: {
      unminified: true,
      minified: true
    }
  }
]

addAssets(builds, 'i18n')
addAssets(builds, 'icons')

build(builds)

/**
 * Helpers
 */

function resolve (_path) {
  return path.resolve(__dirname, '..', _path)
}

function addAssets (builds, type) {
  const
    files = fs.readdirSync(resolve(type)),
    plugins = [ buble() ]

  files.forEach(file => {
    const name = file.substr(0, file.length - 3).replace(/-([a-z])/g, g => g[1].toUpperCase())
    builds.push({
      rollup: {
        input: {
          input: resolve(`${type}/${file}`),
          plugins
        },
        output: {
          file: addExtension(resolve(`dist/umd/${type}.${file}`), 'umd'),
          format: 'umd',
          name: `Quasar.${type}.${name}`
        }
      },
      build: {
        minified: true
      }
    })
  })
}

function processEntries (entries) {
  const builds = []

  entries.forEach(entry => {
    if (entry.rollup.output.file.indexOf(buildConf.themeToken) === -1) {
      builds.push(entry)
      return
    }

    buildConf.themes.forEach(theme => {
      const clone = JSON.parse(JSON.stringify(entry))
      clone.rollup.output.file = entry.rollup.output.file.replace(buildConf.themeToken, theme)
      clone.build.theme = theme
      builds.push(clone)
    })
  })

  return builds
}

function build (builds) {
  Promise
    .all(processEntries(builds).map(genConfig).map(buildEntry))
    .catch(buildUtils.logError)
}

function genConfig (opts) {
  const theme = opts.build && opts.build.theme
    ? opts.build.theme
    : null

  const plugins = opts.rollup.input.plugins || [
    nodeResolve({
      extensions: theme
        ? [`.${theme}.js`, '.js', `.${theme}.vue`, '.vue']
        : ['.js', '.vue'],
      preferBuiltins: false
    }),
    json(),
    vue(vueConfig),
    buble()
  ]

  if (theme) {
    plugins.push(
      replace({
        '__THEME__': JSON.stringify(theme)
      })
    )
  }

  opts.rollup.input.plugins = plugins
  opts.rollup.output.banner = buildConf.banner
  opts.rollup.output.name = opts.rollup.output.name || 'Quasar'

  if (opts.rollup.output.format === 'umd') {
    opts.rollup.input.external = opts.rollup.input.external || []
    opts.rollup.input.external.push('vue')

    opts.rollup.output.globals = opts.rollup.output.globals || {}
    opts.rollup.output.globals.vue = 'Vue'
  }

  return opts
}

function addExtension (filename, ext = 'min') {
  const insertionPoint = filename.lastIndexOf('.')
  return `${filename.slice(0, insertionPoint)}.${ext}${filename.slice(insertionPoint)}`
}

function buildEntry (config) {
  return rollup
    .rollup(config.rollup.input)
    .then(bundle => bundle.generate(config.rollup.output))
    .then(({ code }) => {
      return config.build.unminified
        ? buildUtils.writeFile(config.rollup.output.file, code)
        : code
    })
    .then(code => {
      if (!config.build.minified) {
        return code
      }

      const minified = uglify.minify(code, {
        compress: {
          pure_funcs: ['makeMap']
        }
      })

      if (minified.error) {
        return new Promise((resolve, reject) => reject(minified.error))
      }

      return buildUtils.writeFile(
        addExtension(config.rollup.output.file),
        buildConf.banner + minified.code,
        true
      )
    })
}
<commit_msg>chore: Update rollup-plugin-vue config<commit_after>process.env.BABEL_ENV = 'production'

const
  path = require('path'),
  fs = require('fs'),
  rollup = require('rollup'),
  uglify = require('uglify-es'),
  buble = require('rollup-plugin-buble'),
  json = require('rollup-plugin-json'),
  vue = require('rollup-plugin-vue'),
  replace = require('rollup-plugin-replace'),
  nodeResolve = require('rollup-plugin-node-resolve'),
  buildConf = require('./build.conf'),
  buildUtils = require('./build.utils'),
  vueConfig = {
    css: false,
    compileTemplate: true,
    htmlMinifier: {
      collapseBooleanAttributes: false,
      collapseWhitespace: true,
      removeRedundantAttributes: true,
      removeComments: true
    }
  }

const builds = [
  {
    rollup: {
      input: {
        input: resolve(`src/index.esm.js`)
      },
      output: {
        file: resolve(`dist/quasar.${buildConf.themeToken}.esm.js`),
        format: 'es'
      }
    },
    build: { unminified: true }
  },
  {
    rollup: {
      input: {
        input: resolve('src/ie-compat/ie.js')
      },
      output: {
        file: resolve('dist/quasar.ie.polyfills.js'),
        format: 'es'
      }
    },
    build: { unminified: true }
  },
  {
    rollup: {
      input: {
        input: resolve('src/ie-compat/ie.js')
      },
      output: {
        file: resolve('dist/umd/quasar.ie.polyfills.umd.js'),
        format: 'umd'
      }
    },
    build: { minified: true }
  },
  {
    rollup: {
      input: {
        input: resolve(`src/index.umd.js`)
      },
      output: {
        file: resolve(`dist/umd/quasar.${buildConf.themeToken}.umd.js`),
        format: 'umd'
      }
    },
    build: {
      unminified: true,
      minified: true
    }
  }
]

addAssets(builds, 'i18n')
addAssets(builds, 'icons')

build(builds)

/**
 * Helpers
 */

function resolve (_path) {
  return path.resolve(__dirname, '..', _path)
}

function addAssets (builds, type) {
  const
    files = fs.readdirSync(resolve(type)),
    plugins = [ buble() ]

  files.forEach(file => {
    const name = file.substr(0, file.length - 3).replace(/-([a-z])/g, g => g[1].toUpperCase())
    builds.push({
      rollup: {
        input: {
          input: resolve(`${type}/${file}`),
          plugins
        },
        output: {
          file: addExtension(resolve(`dist/umd/${type}.${file}`), 'umd'),
          format: 'umd',
          name: `Quasar.${type}.${name}`
        }
      },
      build: {
        minified: true
      }
    })
  })
}

function processEntries (entries) {
  const builds = []

  entries.forEach(entry => {
    if (entry.rollup.output.file.indexOf(buildConf.themeToken) === -1) {
      builds.push(entry)
      return
    }

    buildConf.themes.forEach(theme => {
      const clone = JSON.parse(JSON.stringify(entry))
      clone.rollup.output.file = entry.rollup.output.file.replace(buildConf.themeToken, theme)
      clone.build.theme = theme
      builds.push(clone)
    })
  })

  return builds
}

function build (builds) {
  Promise
    .all(processEntries(builds).map(genConfig).map(buildEntry))
    .catch(buildUtils.logError)
}

function genConfig (opts) {
  const theme = opts.build && opts.build.theme
    ? opts.build.theme
    : null

  const plugins = opts.rollup.input.plugins || [
    nodeResolve({
      extensions: theme
        ? [`.${theme}.js`, '.js', `.${theme}.vue`, '.vue']
        : ['.js', '.vue'],
      preferBuiltins: false
    }),
    json(),
    vue(vueConfig),
    buble()
  ]

  if (theme) {
    plugins.push(
      replace({
        '__THEME__': JSON.stringify(theme)
      })
    )
  }

  opts.rollup.input.plugins = plugins
  opts.rollup.output.banner = buildConf.banner
  opts.rollup.output.name = opts.rollup.output.name || 'Quasar'

  if (opts.rollup.output.format === 'umd') {
    opts.rollup.input.external = opts.rollup.input.external || []
    opts.rollup.input.external.push('vue')

    opts.rollup.output.globals = opts.rollup.output.globals || {}
    opts.rollup.output.globals.vue = 'Vue'
  }

  return opts
}

function addExtension (filename, ext = 'min') {
  const insertionPoint = filename.lastIndexOf('.')
  return `${filename.slice(0, insertionPoint)}.${ext}${filename.slice(insertionPoint)}`
}

function buildEntry (config) {
  return rollup
    .rollup(config.rollup.input)
    .then(bundle => bundle.generate(config.rollup.output))
    .then(({ code }) => {
      return config.build.unminified
        ? buildUtils.writeFile(config.rollup.output.file, code)
        : code
    })
    .then(code => {
      if (!config.build.minified) {
        return code
      }

      const minified = uglify.minify(code, {
        compress: {
          pure_funcs: ['makeMap']
        }
      })

      if (minified.error) {
        return new Promise((resolve, reject) => reject(minified.error))
      }

      return buildUtils.writeFile(
        addExtension(config.rollup.output.file),
        buildConf.banner + minified.code,
        true
      )
    })
}
<|endoftext|>