From 84a5d9f32a253f52f8bdcb54476fe00b6356afc1 Mon Sep 17 00:00:00 2001 From: Ben Keen Date: Mon, 1 Jul 2019 21:02:23 -0700 Subject: [PATCH] dependency updates --- gruntfile.js | 4 +- resources/classes/Minification.class.php | 1 - resources/scripts/generator.js | 4502 +++++++++++----------- resources/scripts/requireConfig.js | 28 +- yarn.lock | 389 ++ 5 files changed, 2658 insertions(+), 2266 deletions(-) create mode 100644 yarn.lock diff --git a/gruntfile.js b/gruntfile.js index aa74db76b..4267d4952 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -106,7 +106,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-search'); /** - * The default - and only - Grunt task simply recreates all the bundled resources. It doesn't alter the actual + * The default - and only - Grunt task recreates all the bundled resources. It doesn't alter the actual * script at all. To use these bundled resources you need to add a $useMinifiedResources = true; var to your * settings.php file. */ @@ -117,7 +117,7 @@ module.exports = function(grunt) { 'cssmin', // generate the requireJS bundle containing all modules + core. This relies on there being a - // /cache/appStartStatic file having been auto-generated by the script first. That file is generated every + // /cache/appStartGenerated file having been auto-generated by the script first. That file is generated every // time you click the "Reset Plugins" link 'requirejs', diff --git a/resources/classes/Minification.class.php b/resources/classes/Minification.class.php index 7794e9c53..f21addbbc 100644 --- a/resources/classes/Minification.class.php +++ b/resources/classes/Minification.class.php @@ -32,7 +32,6 @@ public static function getMinifiedResourcePaths() { public static function createAppStartFile() { $exportTypes = Core::$exportTypePlugins; $exportTypeJSModules = ExportTypePluginHelper::getExportTypeJSResources($exportTypes, "string"); - $dataTypes = DataTypePluginHelper::getDataTypeList(Core::$dataTypePlugins); $dataTypeJSModules = DataTypePluginHelper::getDataTypeJSResources($dataTypes, "string"); diff --git a/resources/scripts/generator.js b/resources/scripts/generator.js index c1ff73855..ba6075558 100755 --- a/resources/scripts/generator.js +++ b/resources/scripts/generator.js @@ -1,2254 +1,2258 @@ /*global $:false,CodeMirror:false,console:false,define:false,require:false*/ define([ - "manager", - "pluginManager", - "utils", - "history", - "queue", - "constants", - "lang", - "moment" + "manager", + "pluginManager", + "utils", + "history", + "queue", + "constants", + "lang", + "moment" ], function (manager, pluginManager, utils, history, Queue, C, L, moment) { - "use strict"; - - /** - * @name Generator - * @see Core - * @description This file contains the core client-side code for the Data Generator. It initializes the default - * functionality of the generator page, subscribes to and publishes all the appropriate Core events and - * offers a few public functions for use by any plugins running custom JS code. - * @author Ben Keen - * @return {Object} - * @namespace - */ - - var MODULE_ID = "core-generator"; - - var _numRows = 0; - var _numRowsToShowOnStart = 4; - var _countries = []; - var _currExportType = null; // populated onload - var _showExportTypeSettings = true; - var _codeMirror = null; - var _lastSelectedDataType = null; - var _isHighlightingDeleteRow; - - // the number of results being generated - var _numRowsToGenerate; - - // for storing data during in-page data generation - var _generateInPageRunningCount; - var _generateInPageBatchNum; - var _generateInPageData; - var _generateInPageContent = ""; - var _isGenerating = false; - var _generationCancelled = false; - var _currHelpDialogTab = 1; - var _currLoginDialogTab = 1; - var _currDataTypeHelp = null; - - // accounts - var _isLoggedIn = null; - var _isLoaded = false; - var _accountInfo = null; - var _dataSets = []; - var _currConfigurationID = null; - var _loginModalID = "gdLoginDialog"; - - - /** - * Called when everything is loaded. This binds the appropriate event handlers and sets up the - * page. - */ - var _run = function () { - - // retrieve the data sets for the current user - if ($("body").data("loggedIn")) { - utils.startProcessing(); - _isLoggedIn = true; - _getAccount(); - } else { - _isLoggedIn = false; - $("#gdMainDialogTabs ul").hide(); - $("#gdDataSetStatusLine,#gdProcessingIcon").hide(); - } - - $("#gdDataSetName").focus(); - $("#gdCountries").chosen().change(_updateCountryChoice); - $("#gdRegenerateButton").on("click", _generateData); - $("#gdBackButton").on("click", _onClickBackButton); - - $("#gdShowSettingsLink").bind("click", function () { - if (_showExportTypeSettings) { - _hideExportTypeSettingsSection(); - } else { - _showExportTypeSettingsSection(_currExportType); - } - return false; - }); - - // each event is handled separately to ensure that the Change Data Type event isn't unnecessarily - // republished. Only really an issue on Firefox, which publishes keyup and change events when - // changing the selected option via the keyboard (up and down). It also allows us to tab off the field - // into whatever field is displayed next. - var $tableRows = $("#gdTableRows"); - $tableRows.on("change keyup", ".gdDataType", _onChangeDataType); - $tableRows.on("focus", ".gdDataType", _onFocusDataType); - $tableRows.on("change", ".gdDeleteRows", _markRowToDelete); - $tableRows.on("change", ".gdColExamples select", _publishExampleChange); - - $tableRows.sortable({ - handle: ".gdColOrder", - axis: "y", - update: function (event, ui) { - _updateVisibleRowNums(); - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.DATA_TABLE.ROW.RE_SORT, - row: ui.item - }); - } - }); - - $(document).on("click", ".gdMessageClose", function (e) { - $(e.target).closest(".gdMessage").hide("blind", null, 500); - return false; - }); - - $("#gdData").bind("submit", _generateData); - $("#gdExportTypeTabs>ul>li").bind("click", function (e) { - _selectExportTypeTab($(e.target).data("exportType")); - }); - - $(".gdAddRowsBtn").bind("click", function () { - _addRows($("#gdNumRowsToAdd").val()); - }); - $(".gdDeleteRowsBtn").bind("click", _deleteRows); - $("#gdTextSize").on("click", "li", _changeTextSize); - $("#gdGenerationPanelCancel").on("click", _cancelGeneration); - $("#gdDataSetPublic").on("click", _toggleDataSetVisibilityStatus); - $("#gdSettingsForm").on("submit", function (e) { e.preventDefault(); }); - $("#updateSettingsBtn").on("click", _submitSettingsForm); - $("#gdResetPluginsBtn").on("click", _onClickResetPlugins); - $("#gdNumRowsToGenerate").on("click", _onClickNumRowsField); - $("input[name=gdExportTarget]").on("change", _onChangeExportTarget); - - // icon actions - $("#gdSaveBtn").on("click", _onClickSaveButton); - $("#gdSaveDataSet").on("click", _saveDataSet); - $("#gdEmptyForm").on("click", _emptyForm); - $("#gdDataSetLink").on("click", _openDataSetLinkDialog); - - // main dialog - $("#gdLogout").on("click", function () { - return _logout(); - }); - $("#gdUserAccount").on("click", _onClickUserAccountLink); - $("#gdLogin").on("click", _onClickLoginLink); - $("#gdLoadLink").on("click", _onClickLoadDataSetIcon); - - var $accountDataSets = $(".gdDialogTable"); - $accountDataSets.on("click", "a.loadDataSet", _onClickLoadDataSet); - $accountDataSets.on("click", "a.loadConfigurationHistory", _onClickLoadConfigurationHistory); - $accountDataSets.on("change", ".gdSelectDataSets", _onChangeCheckDataSet); - $accountDataSets.on("click", _onClickToggleDeleteRow); - $(".gdDeleteDataSetsBtn").bind("click", _confirmDeleteDataSets); - $("#gdDataSetHelpNav").on("click", "a", _onclickDataTypeHelpNav); - $tableRows.on("click", ".ui-icon-help", _onClickDataSetRowHelp); - $("#gdSelectAllDataSets").on("click", _onToggleSelectAllDataSets); - $("#gdDataSetStatusLine").html(L.not_saved); - $("#gdMainDialog").on("click", ".gdDataSetStatus", _onToggleSaveDataSetVisibilityStatus); - $("#gdUpdateAccountInfo").on("click", _updateAccountInfo); - - _initMainDialog(); - _initLoginDialog(); - _initExportTypeTab(); - _updateCountryChoice(); - _addRows(_numRowsToShowOnStart); - _initInPageCodeMirror(); - _initTooltips(); - - history.init({ - loadDataSet: _loadDataSet - }); - - // finally, if the URL contains a Data Set ID, request it from the server - var loadDataSetID = utils.getParamByName("load"); - if (loadDataSetID !== null && /^\d+$/.test(loadDataSetID)) { - _getPublicDataSet(loadDataSetID); - } - }; - - - var _onClickNumRowsField = function () { - if (!_isLoggedIn) { - _showPermissionDeniedDialog(L.cannot_change_num_rows); - } - }; - - var _getPublicDataSet = function (dataSetID) { - utils.startProcessing(); - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "json", - data: { - action: "getPublicDataSet", - dataSetID: dataSetID - } - }).then( - function (response) { - if (response.success) { - _loadDataSet(response.content); - } else { - // TODO - } - }, - - function () { - utils.stopProcessing(); - } - ); - }; - - /** - * Called when the user clicks the "LOAD" link for a particular data set. This queries the account Manager - * to retrieve the data set, and then displays the information in the page. - */ - var _onClickLoadDataSet = function (e) { - e.preventDefault(); - var configurationID = $(e.target).closest("tr").data("id"); - var dataSet = _getConfiguration(configurationID); - _loadDataSet(dataSet); - }; - - var _onClickLoadDataSetIcon = function (e) { - e.preventDefault(); - if (_isLoggedIn) { - _openMainDialog({tab: 2}); - } else { - _showPermissionDeniedDialog(); - } - }; - - var _onChangeExportTarget = function (e) { - _handleZipOption(); - }; - - var _onClickUserAccountLink = function (e) { - e.preventDefault(); - _openMainDialog({tab: 1}); - }; - - var _onClickLoginLink = function (e) { - e.preventDefault(); - _openLoginDialog(); - }; - - var _loadDataSet = function (configuration) { - utils.startProcessing(); - - var json = $.evalJSON(configuration.content); - var numRows = json.hasOwnProperty("dataTypes") ? json.dataTypes.length : _numRowsToShowOnStart; - - // clear the form - _clearForm(numRows); - - // now the form's been cleared, store the new configuration ID. We stash it in the page so that it's - // sent along for the - _currConfigurationID = configuration.configuration_id; - $("#configurationID").val(_currConfigurationID); - - // now start populating the page - $("#gdDataSetName").val(configuration.configuration_name); - $("#gdNumRowsToGenerate").val(json.numResults); - $("input[name=gdExportTarget]").each(function () { - if (this.value === json.exportTarget) { - this.checked = true; - } - }); - - _updateCountries(json.countries); - - // update the Export Types section - _selectExportTypeTab(json.selectedExportType, true); - manager.loadExportType(json.selectedExportType, json.exportTypes); - - - // now populate the rows. Do everything that we can: create the rows, populate the titles & select - // the data type. The remaining fields are custom to the data type, so we leave them to their - // .loadData function (if defined) - if (json.hasOwnProperty("dataTypes")) { - var numDataTypeRows = json.dataTypes.length; - var orderedRowIDs = _getRowOrder(); - - var data = []; - for (var i = 0; i < numDataTypeRows; i++) { - var currDataType = json.dataTypes[i]; - var currRowID = orderedRowIDs[i]; - $("#gdTitle_" + currRowID).val(currDataType.title); - $("#gdDataType_" + currRowID).val(currDataType.dataType); - _publishDataTypeChange($("#gdDataType_" + currRowID)[0]); - - currDataType.rowID = currRowID; - data.push(currDataType); - } - - manager.loadDataTypeRows(data); - } - - // update the status line - var lastUpdated = moment.unix(configuration.last_updated_unix).format("h:mm A, MMM Do YYYY"); - $("#gdDataSetStatusLine").html(L.last_edited + " " + lastUpdated); - - utils.stopProcessing(); - _closeMainDialog(); - - _handleZipOption(); - - // publish the IO LOAD event - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.IO.LOAD - }); - }; - - - var _updateCountries = function (countries) { - $("#gdCountries option").each(function () { - if ($.inArray(this.value, countries) != -1) { - this.selected = true; - } else { - this.selected = false; - } - }); - $("#gdCountries").trigger("liszt:updated"); - _updateCountryChoice(); - }; - - - var _showSubtab = function (tab) { - if (tab == 1) { - $("#gdGenerateSubtab1").show(); - $("#gdGenerateSubtab2").hide(); - $("#gdEmptyForm,#gdLoadLink").show(); - } else { - $("#gdGenerateSubtab1").hide(); - $("#gdGenerateSubtab2").show(); - $("#gdEmptyForm,#gdLoadLink").hide(); - } - return false; - }; - - - /** - * Called when the user clicks the save icon. This intelligently decides how to save the information, - * based on whether it's a totally new data set, or if the user had loaded one already. - */ - var _onClickSaveButton = function () { - if (!_isLoggedIn) { - _showPermissionDeniedDialog(); - } else if (C.DEMO_MODE) { - _showDemoOnlyDialog(); - } else { - if (_currConfigurationID === null) { - _saveDataSet(); - } else { - // confirmation...? - _saveDataSet(); - } - } - - return false; - }; - - - /** - * Serializes the current data set and passes it over to the Account Manager to actually save. - */ - var _saveDataSet = function () { - var newDataSetName = $("#gdDataSetName").val(); - - // if there's no Data Set name provided, briefly highlight the field to draw attention to it - // and halt the process - if (!newDataSetName) { - $("#gdDataSetName").css({ - backgroundColor: "#770000", - borderTopColor: "#550000", - borderLeftColor: "#550000", - borderBottomColor: "#550000" - }).animate({ - backgroundColor: "#ffffff", - borderTopColor: "#cccccc", - borderLeftColor: "#cccccc", - borderBottomColor: "#cccccc" - }, 1500); - return false; - } - - var rowData = []; - var orderedRowIDs = _getRowOrder(); - for (var i = 0; i < orderedRowIDs.length; i++) { - var rowNum = orderedRowIDs[i]; - var rowDataType = $("#gdDataType_" + rowNum).val(); - if (rowDataType === "") { - continue; - } - - rowData.push({ - title: $("#gdTitle_" + rowNum).val(), - dataType: rowDataType, - data: manager.serializeDataTypeRow(rowDataType, rowNum) - }); - } - - var configuration = { - action: "saveConfiguration", - dataSetName: newDataSetName, - exportTarget: _getExportTarget(), - numResults: _getNumRowsToGenerate(), - countries: _countries, - dataTypes: rowData, - exportTypes: manager.serializeExportTypes(), - selectedExportType: _currExportType - }; - - if (_currConfigurationID !== null) { - configuration.configurationID = _currConfigurationID; - } - - utils.startProcessing(); - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "json", - data: configuration - }).then( - function (response) { - if (response.success) { - _currConfigurationID = response.content; - var lastUpdated = moment.unix(response.lastUpdated).format("h:mm A, MMM Do YYYY"); - $("#gdDataSetStatusLine").removeClass("hidden").html(L.last_saved + " " + lastUpdated).css("color", "#7fbcf8").animate({color: "#666666"}, 1400); - $("#gdDataSetHistoryNav").addClass("hidden"); - _getAccount(); - } else { - // TODO - } - }, - function () { - // alert(L.fatal_error); - // gd.stopProcessing(); - } - ); - }; - - var _addRows = function (rows) { - rows = rows.toString(); - if (rows.match(/\D/) || rows === 0 || rows === "") { - utils.clearValidationErrors($("#gdMainTab1Content")); - utils.addValidationErrors({els: [$("#gdNumRowsToAdd")], error: L.no_num_rows}); - utils.displayValidationErrors("#gdMessages"); - return false; - } - - var rowIDs = []; - var numRowsToAdd = parseInt(rows, 10); - for (var i = 1; i <= numRowsToAdd; i++) { - var currRow = ++_numRows; - rowIDs.push(currRow); - var newRowHTML = $("#gdTableRowTemplate").html().replace(/%ROW%/g, currRow); - $("#gdTableRows").append('
  • ' + newRowHTML + '
  • '); - } - - _updateVisibleRowNums(); - - // curious! This is done to force Chrome to do a redraw/repaint after adding rows - $("body").addClass("forceRedraw").removeClass("forceRedraw"); - - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.DATA_TABLE.ROW.ADD, - numRows: rows, - rowIDs: rowIDs - }); - }; - - - /** - * This is called when the user actually clicks one of the DEL buttons, deleting those rows marked - * as deleted. - * - * @function - * @private - */ - var _deleteRows = function () { - var rowIDs = []; - $(".gdDeleteRows:checked").each(function () { - var row = $(this).closest(".gdTableRow"); - var parentRowID = row.attr("id"); - if (parentRowID !== null) { - var rowID = parseInt(parentRowID.replace(/row_/g, ""), 10); - row.remove(); - rowIDs.push(rowID); - } - }); - - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.DATA_TABLE.ROW.DELETE, - rowIDs: rowIDs - }); - - _updateVisibleRowNums(); - }; - - var _updateVisibleRowNums = function () { - $("#gdTableRows>li .gdColOrder").each(function (i) { - $(this).html(i + 1); - }); - }; - - var _markRowToDelete = function (e) { - var el = e.target; - var event = null; - var tr = $(el).closest(".gdTableRow"); - - if (el.checked) { - _isHighlightingDeleteRow = true; - tr.addClass("gdDeletedRow").effect("highlight", {color: "#cc0000"}, 1000); - event = C.EVENT.DATA_TABLE.ROW.CHECK_TO_DELETE; - } else { - if (_isHighlightingDeleteRow) { - tr.stop(true, true); - } - tr.removeClass("gdDeletedRow"); - event = C.EVENT.DATA_TABLE.ROW.UNCHECK_TO_DELETE; - _isHighlightingDeleteRow = false; - } - manager.publish({ - sender: MODULE_ID, - type: event, - row: el - }); - }; - - /** - * Resets the entire page back to its defaults: default countries, a blank table and the default data - * format. The optional object param lets you optionally display a confirmation modal and reset the - * the table to the default num of rows. - * @function - * @private - * @name#Generator - */ - var _emptyForm = function (settings) { - var opts = $.extend({ - requireConfirmation: true, - numRows: _numRowsToShowOnStart - }, settings); - - if (opts.requireConfirmation) { - $("#gdEmptyFormDialog").html("

    " + L.confirm_empty_form + "

    ").dialog({ - title: L.please_confirm, - modal: true, - width: 400, - buttons: [ - { - text: L.yes, - click: function () { - _clearForm(opts.numRows); - $(this).dialog("close"); - } - }, - { - text: L.no, - click: function () { - $(this).dialog("close"); - } - } - ] - }); - } else { - _clearForm(opts.numRows); - } - - }; - - var _clearForm = function (numDefaultRows) { - $("#gdDataSetName").val(""); - - // reset the countries - _updateCountries([]); - - // remove the rows - $("#gdTableRows .gdDeleteRows").attr("checked", "checked"); - _deleteRows(); - _addRows(numDefaultRows); - - _currConfigurationID = null; - - // set the default Export Type - _selectExportTypeTab($(".gdDefaultExportType").data("exportType"), true); - manager.resetExportTypes(); - - $("#gdDataSetStatusLine").html(L.not_saved); - - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.DATA_TABLE.CLEAR - }); - }; - - /** - * Called whenever the user selects or deselects a Country. If any modules need to do - * anything special, they can subscribe to the appropriate event. - */ - var _updateCountryChoice = function () { - _countries.length = 0; - $("#gdCountries option").each(function () { - if (this.selected) { - _countries.push(this.value); - } - }); - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.COUNTRIES.CHANGE, - countries: _countries - }); - }; - - var _initExportTypeTab = function () { - var newExportType = $("#gdExportTypeTabs li.gdSelected").data("exportType"); - _selectExportTypeTab(newExportType); - }; - - - /** - * Called whenever the user changes the result type (XML, HTML, CSV etc). This function publishes - * the appropriate event in case a plugin needs to be aware of the event, but it handles the - * hiding/showing and changing of the title column label "out-the-box" rather than force - * the Export Type modules to have to do the work. - */ - var _selectExportTypeTab = function (newExportType, showImmediately) { - if (newExportType === _currExportType) { - return; - } - - if (_currExportType !== null) { - $("#gdExportTypeTabs>ul>li").removeClass("gdSelected"); - $("#gdExportType_" + newExportType).addClass("gdSelected"); - } - - // always reset the column heading to the default "Column Title". Export Types have the option - // to overwrite it through the publish event below - $("#gdColTitleTop,#gdColTitleBottom").html(L.row_label); - - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.RESULT_TYPE.CHANGE, - newExportType: newExportType, - oldExportType: _currExportType - }); - - // hide and show the appropriate Export Type additional settings section (if the + showdata format options link - // has been clicked) - if (_showExportTypeSettings) { - _showExportTypeSettingsSection(newExportType, showImmediately); - } - - // now enable/disable the available export targets - var exportTargets = $("#gdExportType_" + newExportType).data("compatibleExportTargets").split(","); - $("input[name=gdExportTarget]").attr("disabled", "disabled"); - $("#gdGenerateSection label").addClass("gdDisabled"); - var firstNonDisabledExportTarget = null; - for (var i = 0; i < exportTargets.length; i++) { - $("#gdExportTarget_" + exportTargets[i]).removeAttr("disabled"); - $("#gdExportTarget_" + exportTargets[i] + "_label").removeClass("gdDisabled"); - if (firstNonDisabledExportTarget === null) { - firstNonDisabledExportTarget = exportTargets[i]; - } - } - - // now, if the current selected export target is disabled, reset it to the first available non-disabled option - var sel = $("input[name=gdExportTarget]:checked:disabled"); - if (sel.length) { - $("#gdExportTarget_" + firstNonDisabledExportTarget).attr("checked", "checked"); - } - _handleZipOption(); - - _currExportType = newExportType; - }; - - // only enable the Zip checkbox if the Prompt to Download export target is selected - var _handleZipOption = function () { - var selectedTarget = $("input[name=gdExportTarget]:checked").val(); - if (selectedTarget === "promptDownload") { - $("#gdExportTarget_promptDownload_zip").removeAttr("disabled"); - $("#gdExportTarget_promptDownload_zip_label").removeClass("gdDisabled"); - } else { - $("#gdExportTarget_promptDownload_zip").attr("disabled", "disabled"); - $("#gdExportTarget_promptDownload_zip_label").addClass("gdDisabled"); - } - }; - - var _showExportTypeSettingsSection = function (newExportType, showImmediately) { - if ($("#gdExportTypeAdditionalSettings_" + _currExportType).length > 0 && _showExportTypeSettings) { - if (showImmediately === true) { - $("#gdExportTypeAdditionalSettings_" + _currExportType).hide(); - } else { - $("#gdExportTypeAdditionalSettings_" + _currExportType).hide("blind", C.EXPORT_TYPE_SETTINGS_BLIND_SPEED); - } - } - if (_currExportType === null || showImmediately === true) { - $("#gdExportTypeAdditionalSettings_" + newExportType).show(); - } else { - $("#gdExportTypeAdditionalSettings_" + newExportType).show( - "blind", - C.EXPORT_TYPE_SETTINGS_BLIND_SPEED, - function () { - _showExportTypeSettings = true; - $("#gdShowSettingsLink span").html("-"); - $("#gdShowSettingsLink a").html(L.hide_data_format_options); - } - ); - } - }; - - var _hideExportTypeSettingsSection = function () { - $("#gdExportTypeAdditionalSettings_" + _currExportType).hide( - "blind", - C.EXPORT_TYPE_SETTINGS_BLIND_SPEED, - function () { - _showExportTypeSettings = false; - $("#gdShowSettingsLink span").html("+"); - $("#gdShowSettingsLink a").html(L.show_data_format_options); - } - ); - }; - - var _publishExampleChange = function (e) { - var select = e.target; - var rowElement = $(select).closest(".gdTableRow"); - var rowID = parseInt($(rowElement).attr("id").replace(/^row_/, ""), 10); - var dataTypeFolder = $(rowElement).find(".gdDataType").val(); - - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.DATA_TABLE.ROW.EXAMPLE_CHANGE + "__" + dataTypeFolder, - rowID: rowID, - value: select.value - }); - }; - - /** - * Called whenever the user focuses on a Row Type. This makes a note of the last selected - * Data Type, to prevent unnecessary re-publishing of (non-)changed data types. - */ - var _onFocusDataType = function (e) { - _lastSelectedDataType = e.target.value; - }; - - /** - * Called when the user changes the Data Type for a particular row. - */ - var _onChangeDataType = function (e) { - if (e.target.value != _lastSelectedDataType) { - _publishDataTypeChange(e.target); - } - }; - - var _publishDataTypeChange = function (el) { - var rowID = parseInt($(el).attr("id").replace(/^gdDataType_/, ""), 10); - var dataTypeModuleID = el.value; - - // make a note of the last value - _lastSelectedDataType = dataTypeModuleID; - - // if the user just selected the empty value ("Please Select"), clear everything - if (dataTypeModuleID === "") { - $('#gdColExamples_' + rowID + ',#gdColOptions_' + rowID + ',#gdColHelp_' + rowID).html(""); - return; - } - - // update the example + options divs - var exampleHTML = null; - var optionsHTML = null; - var dataTypeExampleHTML = $("#gdDataTypeExamples_" + dataTypeModuleID).html(); - if (dataTypeExampleHTML !== "") { - exampleHTML = dataTypeExampleHTML.replace(/%ROW%/g, rowID); - } else { - exampleHTML = " " + L.no_examples_available; - } - $("#gdColExamples_" + rowID).html(exampleHTML); - - var dataTypeOptionHTML = $("#gdDataTypeOptions_" + dataTypeModuleID).html(); - if (dataTypeOptionHTML !== "") { - optionsHTML = dataTypeOptionHTML.replace(/%ROW%/g, rowID); - } else { - optionsHTML = L.no_options_available; - } - $("#gdColOptions_" + rowID).html(optionsHTML); - - if ($("#gdDataTypeHelp_" + dataTypeModuleID).html() !== "") { - $('#gdColHelp_' + rowID).html($("#gdHelpIcon").html().replace(/%ROW%/g, rowID)); - } else { - $('#gdColHelp_' + rowID).html(" "); - } - - // now publish the row change - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.DATA_TABLE.ROW.TYPE_CHANGE, - rowID: rowID, - dataTypeModuleID: dataTypeModuleID - }); - }; - - - var _getRowOrder = function () { - var orderedRowIDs = $("#gdTableRows").sortable("toArray"); - var sortedOrder = []; - for (var i = 0; i < orderedRowIDs.length; i++) { - var row = orderedRowIDs[i].replace(/row_/g, ""); - sortedOrder.push(row); - } - return sortedOrder; - }; - - - /** - * Called when the user submits the main Generate tab. It performs all necessary validation - * and starts the data generation process. - */ - var _generateData = function (e) { - - // TODO pretty poor. Validation should be performed on this var prior to setting it in the private var - _numRowsToGenerate = _getNumRowsToGenerate() - utils.clearValidationErrors($("#gdMainTab1Content")); - - // check the users specified a numeric value for the number of results - if (_numRowsToGenerate.match(/\D/) || _numRowsToGenerate === 0 || _numRowsToGenerate === "") { - utils.addValidationErrors({els: $("#gdNumRowsToGenerate"), error: L.invalid_num_results}); - } - - var orderedRowIDs = _getRowOrder(); - var validRowIDs = []; - - // look through the form and construct an object of data-type-folder => [row IDs] to - // pass to the manager. The manager uses that to farm out the actual validation work - // to the appropriate module - var rowValidationNeededGroupByDataType = {}; - for (var i = 0; i < orderedRowIDs.length; i++) { - var rowID = orderedRowIDs[i]; - var currRowType = $("#gdDataType_" + rowID).val(); - - // ignore empty rows, they don't need validating - if (currRowType === "") { - continue; - } - if (!rowValidationNeededGroupByDataType.hasOwnProperty(currRowType)) { - rowValidationNeededGroupByDataType[currRowType] = []; - } - rowValidationNeededGroupByDataType[currRowType].push(rowID); - validRowIDs.push(rowID); - } - - // if none of the data columns had a selected data type, display an error about that, too - if (!validRowIDs.length) { - utils.addValidationErrors({els: null, error: L.no_data}); - } else { - // check all filled-in rows contained something in the first column - var rowsMissingTitleEls = []; - for (var j = 0; j < validRowIDs.length; j++) { - var currRowID = validRowIDs[j]; - var currTitle = $("#gdTitle_" + currRowID); - if ($.trim(currTitle.val()) === "") { - rowsMissingTitleEls.push(currTitle[0]); - } - } - - if (rowsMissingTitleEls.length) { - var label = L.row_label_plural; - if (L.exportTypePlugins[_currExportType].hasOwnProperty("row_label_plural")) { - label = L.exportTypePlugins[_currExportType].row_label_plural; - } - var message = L.please_enter_all + " " + label + "."; - utils.addValidationErrors({els: rowsMissingTitleEls, error: message}); - } - } - - utils.addValidationErrors(manager.validateDataTypes(rowValidationNeededGroupByDataType)); - - var exportTypeValidationErrors = manager.validateExportType({exportType: _currExportType, rows: validRowIDs}); - if (!$.isArray(exportTypeValidationErrors)) { - utils.addValidationErrors({els: null, error: L.export_type_validate_error}); - } else { - utils.addValidationErrors(exportTypeValidationErrors); - } - - // ensure the number of rows is an acceptable number - _numRowsToGenerate = parseInt(_numRowsToGenerate, 10); - if (_numRowsToGenerate > C.MAX_GENERATED_ROWS) { - var msg = L.num_rows_too_large.replace(/%1/, C.MAX_GENERATED_ROWS); - utils.addValidationErrors({els: null, error: msg}); - $("#gdNumRowsToGenerate").val(C.MAX_GENERATED_ROWS); - } - - var errors = utils.getValidationErrors(); - if (errors.length) { - utils.displayValidationErrors("#gdMessages"); - return false; - } - - var exportTarget = _getExportTarget(); - var rowOrder = _getRowOrder().toString(); - $("#gdRowOrder").val(rowOrder); - $("#gdExportType").val(_currExportType); - $("#gdNumCols").val(_numRows); - - // reset CodeMirror (scrollTo not working ...) - _codeMirror.setOption("lineWrapping", false); - _codeMirror.scrollTo(0, 0); - _codeMirror.setValue(""); - - // now pass off the work to the appropriate generation function. Each works slightly differently. - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.GENERATE, - exportTarget: exportTarget, - exportType: _currExportType, - editor: _codeMirror - }); - - // if the messages section is displayed, hide it - whatever old errors are no longer pertinent - if ($("#gdMessages").css("display") == "block") { - $("#gdMessages .gdMessageClose").trigger("click"); - } - - // only the inPage choice prevents the default form submit event - if (exportTarget == "inPage") { - _generateInPage(); - e.preventDefault(); - } else if (exportTarget == "newTab") { - _generateNewWindow(); - } else if (exportTarget == "promptDownload") { - _generatePromptDownload(); - } - }; - - - /** - * Generate the results in-page. This option hides the generator table and displays the results in a large, - * CodeMirror-enhanced textarea. This is the only generation format that makes use of *batches*: since generation - * can take a long time, this passes off work to the server in batches of (say) 100, so the user can see the - * generation process take place. - */ - var _generateInPage = function () { - var formData = $("#gdData").serialize(); - - // "action" added for AjaxRequest only - var data = formData + "&action=generateInPage&gdBatchSize=" + C.GENERATE_IN_PAGE_BATCH_SIZE; - if (_currConfigurationID !== null) { - data += "&configurationID=" + _currConfigurationID; - } - _showSubtab(2); - utils.startProcessing(); - - _generateInPageRunningCount = 0; - _isGenerating = true; - _generationCancelled = false; - - $("#gdGenerateCount").html(utils.formatNumWithCommas(_generateInPageRunningCount)); - $("#gdGenerateTotal").html(utils.formatNumWithCommas(_numRowsToGenerate)); - $("#gdProgressMeter").attr("max", _numRowsToGenerate); - $("#gdProgressMeter").attr("value", 0); - - _codeMirror.setValue(""); - - _generateInPageBatchNum = 1; - _generateInPageData = data; - _generateInPageContent = ""; - _generateInPageBatch(); - }; - - var _generateInPageBatch = function () { - $("#gdGenerationPanelCancel").removeClass("gdDisabledLink"); - var data = _generateInPageData + "&gdCurrentBatchNum=" + _generateInPageBatchNum; - if (_currConfigurationID !== null) { - data += "&configurationID=" + _currConfigurationID; - } - $.ajax({ - url: "ajax.php", - type: "POST", - data: data, - dataType: "json", - contentType: "application/x-www-form-urlencoded;charset=utf-8", - success: _generateInPageBatchResponse, - error: function () { - _isGenerating = false; - utils.stopProcessing(); - } - }); - }; - - var _generateInPageBatchResponse = function (response) { - if (response.success) { - // 1. Update the running count ("Generated X of Y rows") - _generateInPageRunningCount = (_generateInPageRunningCount + C.GENERATE_IN_PAGE_BATCH_SIZE) > _numRowsToGenerate ? - _numRowsToGenerate : _generateInPageRunningCount + C.GENERATE_IN_PAGE_BATCH_SIZE; - $("#gdGenerateCount").html(utils.formatNumWithCommas(_generateInPageRunningCount)); - $("#gdProgressMeter").attr("value", _generateInPageRunningCount); - - // 2. Update the actual content - _generateInPageContent += decodeURIComponent(escape(response.content)); - _codeMirror.setValue(_generateInPageContent); - - // check the process hasn't been interrupted - if (_generationCancelled) { - _isGenerating = false; - utils.stopProcessing(); - $("#gdGenerationPanelCancel").addClass("gdDisabledLink"); - $("#gdProgressMeter").attr("value", _numRowsToGenerate); - return; - } - - // now either continue processing, or indicate we're done - if (response.isComplete) { - _isGenerating = false; - utils.stopProcessing(); - $("#gdGenerationPanelCancel").addClass("gdDisabledLink"); - - // update the data in _dataSets - if (_currConfigurationID !== null) { - _incrementConfigurationRowGenerationCount(_currConfigurationID, _numRowsToGenerate); - } - } else { - _generateInPageBatchNum++; - _generateInPageBatch(); - } - } else { - _isGenerating = false; - utils.stopProcessing(); - console.warn("response.success fail"); - } - }; - - var _incrementConfigurationRowGenerationCount = function (configurationID, numRows) { - - // first, update the actual data set - var newNum = ""; - for (var i = 0; i < _dataSets.length; i++) { - if (_dataSets[i].configuration_id == configurationID) { - var currCount = parseInt(_dataSets[i].num_rows_generated, 10); - newNum = currCount + numRows; - _dataSets[i].num_rows_generated = newNum; - } - } - - // second, update the displayed data. This does surgery on the Data Sets tab to just update the one DOM - // element rather than redraw everything - var rows = $("#gdAccountDataSets tbody tr"); - for (var j = 0; j < rows.length; j++) { - if ($(rows[j]).data("id") == configurationID) { - $(rows[j]).find(".gdDataSetNumRowsGenerated").html(utils.formatNumWithCommas(newNum)); - } - } - - // this updates the account info tab "total" count - _updateAccountInfoTab(); - }; - - var _generateNewWindow = function () { - $("#gdData").attr({ - "target": "blank", - "action": "generate.php" - }); - _updateConfigurationRowGenerationCount(); - }; - - var _generatePromptDownload = function () { - $("#gdData").attr({ - "target": "blank", - "action": "generate.php" - }); - _updateConfigurationRowGenerationCount(); - }; - - var _updateConfigurationRowGenerationCount = function () { - var numRows = parseInt(_getNumRowsToGenerate(), 10); - if (_currConfigurationID !== null) { - _incrementConfigurationRowGenerationCount(_currConfigurationID, numRows); - } - }; - - var _onClickResetPlugins = function (e) { - var useMinified = $(e.target).data("useMinified"); - if (useMinified) { - $("#gdResetPluginsDialog").dialog({ - modal: true, - resizable: true, - title: "Reset Plugins", - width: 480, - height: 200, - buttons: [ - { - text: L.close, - click: function () { - $(this).dialog("close"); - } - }, - { - text: L.reset_plugins, - click: function () { - $(this).dialog("close"); - _resetPlugins(); - } - } - ] - }); - return; - } - - _resetPlugins(); - }; - - var _resetPlugins = function () { - - - pluginManager.installPlugins({ - context: "update", - errorHandler: null, - prefill: { - dataTypes: pluginManager.getSelectedDataTypes(), - exportTypes: pluginManager.getSelectedExportTypes(), - countries: pluginManager.getSelectedCountries() - }, - onCompleteHandler: function () { - window.scrollTo(0, 0); - - // boy this is awful. Wrap it in a helper! - $("#settingsTabMessage").removeClass("gdErrors").addClass("gdNotify").css({ display: 'block' }) - .find("p").html("The plugin list has been updated. Click the Save button below to save any changes."); - } - }); - }; - - var _changeTextSize = function (e) { - $("#gdTextSize li").removeClass("gdSelected"); - var size = $(e.target).attr("class"); - $(e.target).addClass("gdSelected"); - $("#gdGenerateSubtab2 .CodeMirror").removeClass("CodeMirror_small CodeMirror_medium CodeMirror_large").addClass("CodeMirror_" + size); - _codeMirror.refresh(); - }; - - /** - * Called on page load. We always instantiate the codemirror object on the generate in-page. This object is - * passed in the C.EVENT.GENERATE message for export types to mess with (i.e. change the mode). - */ - var _initInPageCodeMirror = function () { - _codeMirror = CodeMirror.fromTextArea($("#gdGeneratedData")[0], { - mode: "xml", - readOnly: true, - lineNumbers: true - }); - $(".CodeMirror").addClass("CodeMirror_medium"); - }; - - var _getExportTarget = function () { - return $("input[name=gdExportTarget]:checked").val(); - }; - - var _getNumRowsToGenerate = function () { - return $("#gdNumRowsToGenerate").val(); - }; - - var _getVisibleRowOrderByRowNum = function (rowNum) { - var rowOrder = _getRowOrder(); - var visibleRowNum = 1; - for (var i = 0; i < rowOrder.length; i++) { - if (rowOrder[i] == rowNum) { - return visibleRowNum; - } - visibleRowNum++; - } - return false; - }; - - - // main dialog - - var _initMainDialog = function () { - $("#gdMainDialogTabs ul li").each(function () { - var newTab = parseInt($(this).attr("id").replace(/^gdMainDialogTab/, ""), 10); - - $(this).bind("click", function (e) { - utils.selectTab({ - tabGroup: "dialogTabs", - tabIDPrefix: "gdMainDialogTab", - newTab: newTab, - oldTab: _currHelpDialogTab - }); - _currHelpDialogTab = newTab; - var $dialog = $("#gdMainDialog"); - - if ($dialog.css("display") == "block") { - if (newTab == 1) { - $dialog.dialog("option", "buttons", [{ - text: L.close, click: function () { - $(this).dialog("close"); - } - }]); - } else if (newTab == 2) { - _updateMainDialogDataSetButtons(); - - // yuck! We want to hide the history section when a user clicks on a tab but NOT when the history content - // was opened and they close then re-open the main dialog. isTrigger is a native jQuery event property - if (!e.isTrigger) { - history.hideDataSetHistorySection(); - } - - } else if (newTab == 3) { - $dialog.dialog("option", "buttons", [{ - text: L.close, click: function () { - $(this).dialog("close"); - } - }]); - if (_currDataTypeHelp === null) { - var dataTypeItems = $("#gdDataSetHelpNav li").not(".gdDataTypeHeader"); - _showDataTypeHelp(dataTypeItems[0]); - } - } - } - }); - }); - }; - - var _onclickDataTypeHelpNav = function (e) { - e.preventDefault(); - var dataTypeNavItem = $(e.target).closest("li"); - _showDataTypeHelp(dataTypeNavItem); - }; - - var _showDataTypeHelp = function (el) { - var dataType = $(el).data("module"); - var link = $(el).find("a"); - - $("#gdDataSetHelpNav a").removeClass("gdSelected"); - $(link).addClass("gdSelected").focus(); - - var linkPosition = $(link).position().top; - var dialogHeight = $("#gdMainDialogTab3Content").height(); - - // if the selected Data Type > the total dialog height, scroll to that point; otherwise - // scroll the top - var scrollPosition = 0; - if (linkPosition + 50 > dialogHeight) { // 50 is to handle the extra space by the tabs - scrollPosition = linkPosition; - } - $("#gdDataSetHelpNav").scrollTop(scrollPosition); - - // set the header to the name of the Data Type - $("#gdFocusedDataTypeHeader").html($(link).html()); - - if (_currDataTypeHelp !== null) { - $("#gdDataTypeHelp_" + _currDataTypeHelp).addClass("hidden"); - } - $("#gdDataTypeHelp_" + dataType).removeClass("hidden"); - _currDataTypeHelp = dataType; - }; - - - var _onClickDataSetRowHelp = function (e) { - var row = $(e.target).closest(".gdTableRow"); - var dataTypeDropdown = row.find(".gdDataType"); - var choice = dataTypeDropdown.val(); - - _openMainDialog({tab: 3, dataType: choice}); - - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.DATA_TABLE.ROW.HELP_DIALOG_OPEN, - rowElement: row - }); - }; - - - var _openMainDialog = function (settings) { - var opts = $.extend({ - tab: 1, - dataType: null - }, settings); - - // hide/show the appropriate tab - $("#gdMainDialogTab" + opts.tab).trigger("click"); - - // remove any custom styles - $(".gdHelpSection").removeAttr("style"); - - // close any messages that are open - $("#gdMainDialogTab1Message").hide(); - - // open the dialog - $("#gdMainDialog").dialog({ - title: 'generatedata.com', - dialogClass: "gdMainDialog", - width: 860, - minHeight: 420, - modal: true, - resizable: false, - buttons: [ - { - text: L.close, - click: function () { - $(this).dialog("close"); - } - } - ] - }); - - // if required, ensure the appropriate Data Type item is selected. This is done after - // the dialog is opened because it needs to set the appropriate offset height of the - // left sidebar to focus on the appropriate Data Type's help link. This can only be computed - // after it's been opened - if (opts.dataType !== null) { - var helpNavEl = ($("#gdDataSetHelpNav li[data-module='" + opts.dataType + "']"))[0]; - _showDataTypeHelp(helpNavEl); - } - - utils.insertModalSpinner({modalID: "gdMainDialog"}); - - if (opts.tab == 2) { - _updateMainDialogDataSetButtons(); - } - - return false; - }; - - var _closeMainDialog = function () { - $("#gdMainDialog").dialog("close"); - }; - - var _onChangeCheckDataSet = function (e) { - var el = e.target; - _markDataSetRowToDelete(el); - }; - - var _onClickToggleDeleteRow = function (e) { - if ($.inArray(e.target.nodeName.toUpperCase(), ["INPUT", "A"]) !== -1) { - return; - } - - // reverse the checked-ness of the row - var el = $(e.target).closest("tr").find(".gdSelectDataSets"); - var isChecked = $(e.target).closest("tr").find(".gdSelectDataSets").attr("checked"); - if (isChecked) { - $(el).removeAttr("checked"); - } else { - $(e.target).closest("tr").find(".gdSelectDataSets").attr("checked", "checked"); - } - - _markDataSetRowToDelete(el[0]); - }; - - var _markDataSetRowToDelete = function (el) { - if (!el) { - return; - } - - if (el.checked) { - $(el).closest("tr").addClass("gdSelectedDataSetRow"); - } else { - $(el).closest("tr").removeClass("gdSelectedDataSetRow"); - } - _updateMainDialogDataSetButtons(); - }; - - var _onToggleSelectAllDataSets = function (e) { - var isChecked = e.target.checked; - var cbs = $(".gdSelectDataSets"); - for (var i = 0; i < cbs.length; i++) { - cbs[i].checked = isChecked; - _markDataSetRowToDelete(cbs[i]); - } - _updateMainDialogDataSetButtons(); - }; - - var _confirmDeleteDataSets = function () { - }; - - /** - * Called whenever one or more rows is selected / unselected. This checks to see how - * many rows are selected, and hides/shows the delete/copy buttons. - */ - var _updateMainDialogDataSetButtons = function () { - var cbs = $(".gdSelectDataSets:checked"); - if (cbs.length) { - - var buttons = []; - - // if there's only one row selected, show the Copy Data Set button - if (cbs.length === 1) { - buttons.push({ - text: "Copy Data Set", - click: function () { - var existingDataSet = $(cbs[0]).closest("tr"); - var existingDataSetId = parseInt(existingDataSet.data("id"), 10); - var existingDataSetName = existingDataSet.find(".dataSetName")[0].innerHTML; - - var result = window.prompt(L.please_enter_data_set_name, existingDataSetName); - if (result !== null) { - _copyDataSet(existingDataSetId, result); - } - } - }); - } - - var deleteButtonLabel = L.delete_1_data_set; - if (cbs.length > 1) { - deleteButtonLabel = L.delete_N_data_sets.replace(/%1/, cbs.length); - } - - buttons.push({ - text: deleteButtonLabel, - "class": "gdDeleteDataSetsBtn", - click: function () { - _onClickDeleteDataSets(); - } - }); - - buttons.push({ - text: L.close, - click: function () { - $(this).dialog("close"); - } - }); - - $("#gdMainDialog").dialog("option", "buttons", buttons); - } else { - $("#gdMainDialog").dialog("option", "buttons", [{ - text: L.close, - click: function () { - $(this).dialog("close"); - } - }]); - } - }; - - /** - * Makes a copy of an existing Data Set. - * @param dataSetId - * @param newDataSetName - * @private - */ - var _copyDataSet = function (dataSetId, newDataSetName) { - utils.startProcessing(); - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "JSON", - data: { - action: "copyDataSet", - dataSetId: dataSetId, - newDataSetName: newDataSetName - }, - success: function (response) { - _getAccount(); - }, - error: _onError - }); - }; - - - var _onClickDeleteDataSets = function () { - if (C.DEMO_MODE) { - _closeMainDialog(); - _showDemoOnlyDialog(); - return false; - } - - // get the configuration IDs of the selected rows - var configurationIDs = []; - var cbs = $("#gdAccountDataSets tbody input:checked"); - for (var i = 0; i < cbs.length; i++) { - configurationIDs.push($(cbs[i]).closest("tr").data("id")); - } - - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "JSON", - data: { - action: "deleteDataSets", - configurationIDs: configurationIDs - }, - success: _onSuccessDeleteDataSets, - error: _onError - }); - }; - - - var _onSuccessDeleteDataSets = function (response) { - if (response.success) { - var remainingDataSets = []; - for (var i = 0; i < _dataSets.length; i++) { - if ($.inArray(_dataSets[i].configuration_id, response.content) == -1) { - remainingDataSets.push(_dataSets[i]); - } - } - _dataSets = remainingDataSets; - $("#gdAccount_NumSavedDataSets").html(_dataSets.length); - - _displayDataSets(); - _updateMainDialogDataSetButtons(); - } else { - // TODO - } - }; - - - // account-related - - var _getAccount = function (params) { - var settings = $.extend({ - onComplete: function () { - } - }, params); - - utils.startProcessing(); - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "JSON", - data: { - action: "getAccount" - }, - success: function (data, textStatus, jqXHR) { - var params = { - data: data, - textStatus: textStatus, - jqXHR: jqXHR, - settings: settings - }; - _onRetrievingAccountInfo(params); - }, - error: _onError - }); - }; - - var _onRetrievingAccountInfo = function (params) { - var response = params.data; - utils.stopProcessing(); - - // enable the save, load and link icons - $("#gdActionIcons .loading").removeClass("loading"); - - _isLoaded = true; - _dataSets = response.content.configurations; - - _accountInfo = response.content; - - // remove configurations from the account Info object. This is just to prevent someone (like me) accidentally using - // that data in that object, and not in _dataSets. - delete _accountInfo.configurations; - - _updateAccountInfoTab(); - _displayDataSets(); - - params.settings.onComplete(); - - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.ACCOUNT.AVAILABLE, - accountInfo: _accountInfo - }); - }; - - - /** - * Called when the user clicks the Update Account button on the first tab of the main dialog. - */ - var _updateAccountInfo = function (e) { - e.preventDefault(); - - if (C.DEMO_MODE) { - _showDemoOnlyDialog(); - return; - } - - // check all fields have been entered - var firstNameField = $("#gdUserAccount_firstName"); - var firstNameFieldVal = $.trim(firstNameField.val()); - var lastNameField = $("#gdUserAccount_lastName"); - var lastNameFieldVal = $.trim(lastNameField.val()); - var emailField = $("#gdUserAccount_email"); - var emailFieldVal = $.trim(emailField.val()); - var passwordField = $("#gdUserAccount_password"); - var passwordFieldVal = $.trim(passwordField.val()); - var passwordField2 = $("#gdUserAccount_password2"); - var passwordField2Val = $.trim(passwordField2.val()); - - var hasErrors = false; - if (firstNameFieldVal === "") { - firstNameField.addClass("gdProblemField"); - hasErrors = true; - } else { - firstNameField.removeClass("gdProblemField"); - } - - if (lastNameFieldVal === "") { - lastNameField.addClass("gdProblemField"); - hasErrors = true; - } else { - lastNameField.removeClass("gdProblemField"); - } - - if (emailFieldVal === "") { - emailField.addClass("gdProblemField"); - hasErrors = true; - } else { - emailField.removeClass("gdProblemField"); - } - - // if the users's entered a password, validate them too - if (passwordFieldVal !== "" || passwordField2Val !== "") { - if (passwordFieldVal === "") { - passwordField.addClass("gdProblemField"); - hasErrors = true; - } else { - passwordField.removeClass("gdProblemField"); - } - if (passwordField2Val === "") { - passwordField2.addClass("gdProblemField"); - hasErrors = true; - } else { - passwordField2.removeClass("gdProblemField"); - } - if (passwordFieldVal !== passwordField2Val) { - passwordField.addClass("gdProblemField"); - passwordField2.addClass("gdProblemField"); - hasErrors = true; - } else { - passwordField.removeClass("gdProblemField"); - passwordField2.removeClass("gdProblemField"); - } - } - - if (!hasErrors) { - utils.playSpinner("gdMainDialog"); - var data = { - action: "updateAccount", - accountID: _accountInfo.accountID, - firstName: firstNameFieldVal, - lastName: lastNameFieldVal, - email: emailFieldVal - }; - if (passwordFieldVal !== "") { - data.password = passwordFieldVal; - } - - $.ajax({ - url: "ajax.php", - type: "POST", - data: data, - dataType: "json", - success: function (response) { - utils.pauseSpinner("gdMainDialog"); - if (response.success) { - $("#gdMainDialogTab1Message").show("blind"); - $(passwordField).val(""); - $(passwordField2).val(""); - } else { - // TODO - } - }, - error: function (response) { - utils.pauseSpinner("gdMainDialog"); - console.log("error response: ", response); - } - }); - } - }; - - var _updateAccountInfoTab = function () { - if (_accountInfo.isAnonymous) { - $("#gdAccount_AccountType").html(L.anonymous_admin_account); - } else { - var accountTypeStr = ""; - if (_accountInfo.accountType == "admin") { - accountTypeStr = L.admin; - } else { - accountTypeStr = L.user; - } - $("#gdAccount_AccountType").html(accountTypeStr); - } - - $("#gdUserAccount_firstName").val(_accountInfo.firstName); - $("#gdUserAccount_lastName").val(_accountInfo.lastName); - $("#gdUserAccount_email").val(_accountInfo.email); - $("#gdAccount_NumSavedDataSets").html(_dataSets.length); - $("#gdAccount_DateAccountCreated").html(moment.unix(_accountInfo.dateCreated).format("MMM Do, YYYY")); - - var totalRowsGenerated = 0; - for (var i = 0; i < _dataSets.length; i++) { - totalRowsGenerated += parseInt(_dataSets[i].num_rows_generated, 10); - } - $("#gdAccount_TotalRowsGenerated").html(utils.formatNumWithCommas(totalRowsGenerated)); - }; - - var _displayDataSets = function () { - if (_dataSets.length) { - $("#gdNoAccountDataSets").addClass("hidden"); - var html = ""; - var row = ""; - var currDataSet; - for (var i = 0; i < _dataSets.length; i++) { - currDataSet = _dataSets[i]; - var dateCreated = moment.unix(currDataSet.date_created_unix).format("MMM Do, YYYY"); - var lastUpdated = moment.unix(currDataSet.last_updated_unix).format("MMM Do, YYYY"); - var isPublic = (currDataSet.status === "public") ? 'checked="checked"' : ""; - - row = '' + - '' + utils.decodeUTF8(currDataSet.configuration_name) + '' + - '' + dateCreated + '' + - '' + lastUpdated + '' + - '' + - '' + utils.formatNumWithCommas(currDataSet.num_rows_generated) + '' + - 'history' + - 'load' + - '' + - ''; - html += row; - } - $("#gdAccountDataSets tbody").html(html); - $("#gdAccountDataSets").removeClass("hidden"); - $("#gdConfigurationHistory").addClass("hidden"); - } else { - $("#gdAccountDataSets tbody").html(""); - $("#gdNoAccountDataSets").removeClass("hidden"); - $("#gdAccountDataSets").addClass("hidden"); - } - }; - - var _onError = function (response) { - console.log("on error"); - console.log(response); - }; - - var _getConfiguration = function (configurationID) { - var dataSet = {}; - for (var i = 0; i < _dataSets.length; i++) { - if (_dataSets[i].configuration_id != configurationID) { - continue; - } - dataSet = _dataSets[i]; - } - return dataSet; - }; - - - var _initTooltips = function () { - $(document).tooltip({ - position: { - my: "center bottom-6", - at: "center top" - } - }); - }; - - var _cancelGeneration = function (e) { - e.preventDefault(); - if ($(e.target).hasClass("gdDisabledLink")) { - return; - } - if (!_isGenerating) { - return; - } - _generationCancelled = true; - }; - - var _onClickBackButton = function (e) { - e.preventDefault(); - _showSubtab(1); - - // if the user was in the process of generating a data set, cancel it - if (_isGenerating) { - _generationCancelled = true; - } - }; - - var _openDataSetLinkDialog = function (e) { - if (_currConfigurationID === null) { - $("#gdLinkToDataSet_incomplete").removeClass("hidden"); - $("#gdLinkToDataSet_complete").addClass("hidden"); - } else { - $("#gdLinkToDataSet_incomplete").addClass("hidden"); - $("#gdLinkToDataSet_complete").removeClass("hidden"); - } - - $("#gdLinkToDataSetDialog").dialog({ - title: L.link_to_data_set, - dialogClass: "gdMainDialog", - width: 500, - modal: true, - resizable: false, - open: function () { - if (_currConfigurationID !== null) { - var url = window.location.href.replace(/(#.*)/, ""); - url = url.replace(/(\?.*)/, ""); - $("#gdLinkURL").data("url", url + "?load=" + _currConfigurationID); - - var config = _getConfiguration(_currConfigurationID); - if (config.status == "public") { - $("#gdDataSetPublic").attr("checked", "checked"); - $("#gdLinkURL").val(url).removeClass("gdDisabled").removeAttr("disabled").select(); - } else { - $("#gdDataSetPublic").removeAttr("checked"); - $("#gdLinkURL").val("-").attr("disabled", "disabled").addClass("gdDisabled"); - } - } - }, - buttons: [ - { - text: L.close, - click: function () { - $(this).dialog("close"); - } - } - ] - }); - - return false; - }; - - - var _toggleDataSetVisibilityStatus = function (e) { - var status = null; - if (e.target.checked) { - status = "public"; - var url = $("#gdLinkURL").data("url"); - $("#gdLinkURL").removeClass("gdDisabled").removeAttr("disabled").val(url).select(); - } else { - status = "private"; - $("#gdLinkURL").addClass("gdDisabled").attr("disabled", "disabled").val(""); - } - - _saveVisibilityStatus(_currConfigurationID, status); - }; - - /** - * Called when the user checks/unchecks the "Public?" checkbox for a Data Set. This sends an Ajax request - * to the server to store the value. - */ - var _onToggleSaveDataSetVisibilityStatus = function (e) { - utils.startProcessing(); - var configurationID = $(e.target).closest("tr").data("id"); - var status = (e.target.checked) ? "public" : "private"; - _saveVisibilityStatus(configurationID, status); - }; - - var _saveVisibilityStatus = function (configurationID, status) { - var data = { - action: "saveDataSetVisibilityStatus", - configurationID: configurationID, - status: status, - time: Date.now() - }; - - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "json", - data: data, - success: function (response) { - if (response.success) { - // update the status in memory for the appropriate Data Set - var configurationID = parseInt(response.content, 10); - var newStatus = response.newStatus; - for (var i = 0; i < _dataSets.length; i++) { - if (_dataSets[i].configuration_id != configurationID) { - continue; - } - _dataSets[i].status = newStatus; - break; - } - - // ensure the Data Set table reflects this value - if ($("#gdDataSetStatus_" + configurationID).length) { - if (newStatus === "public") { - $("#gdDataSetStatus_" + configurationID).attr("checked", "checked"); - } else { - $("#gdDataSetStatus_" + configurationID).removeAttr("checked"); - } - } - } - utils.stopProcessing(); - }, - error: function () { - utils.stopProcessing(); - } - }); - }; - - var _showDemoOnlyDialog = function () { - var content = '

    This is a demo only. Please download the script to save your Data Sets.
    ' + - 'https://github.com/benkeen/generatedata

    '; - $('
    ' + content + '
    ').dialog({ - title: "Sorry!", - width: 500, - modal: true, - buttons: [{ - text: L.close, - click: function () { - $(this).dialog("close"); - } - }] - }); - }; - - var _showPermissionDeniedDialog = function (extraMsg) { - var content = C.SETTINGS.ANON_USER_PERMISSION_DENIED_MSG; - if (extraMsg) { - content = extraMsg + " " + content; - } - $('
    ' + content + '
    ').dialog({ - title: "Sorry!", - width: 500, - modal: true, - buttons: [{ - text: L.close, - click: function () { - $(this).dialog("close"); - } - }] - }); - }; - - var _initLoginDialog = function () { - $("#gdLoginDialogTabs>ul>li").each(function () { - var newTab = parseInt($(this).attr("id").replace(/^gdLoginDialogTab/, ""), 10); - $(this).bind("click", function () { - utils.selectTab({ - tabGroup: "dialogTabs", - tabIDPrefix: "gdLoginDialogTab", - newTab: newTab, - oldTab: _currLoginDialogTab - }); - _currLoginDialogTab = newTab; - - if (newTab === 1) { - $("#" + _loginModalID).dialog("option", "buttons", [{text: L.login, click: _login}]); - $("#gdLogin_email").focus(); - } else { - $("#" + _loginModalID).dialog("option", "buttons", [{text: L.email, click: _resetPassword}]); - $("#gdEmailReminder").focus(); - } - }); - }); - $("#" + _loginModalID).on("keyup", "#gdLogin_password", function (e) { - if (e.keyCode === $.ui.keyCode.ENTER) { - _login(); - } - }); - }; - - var _openLoginDialog = function () { - var buttons = []; - if (_currLoginDialogTab === 1) { - buttons = [{text: L.login, click: _login}]; - } else { - buttons = [{text: L.email, click: _resetPassword}]; - } - - $("#" + _loginModalID).dialog({ - title: L.please_login, - width: 500, - modal: true, - open: function () { - utils.insertModalSpinner({modalID: _loginModalID}); - }, - buttons: buttons - }); - $("#gdLoginError").hide(); - $("#gdLoginDialogContent .gdProblemField").removeClass("gdProblemField"); - }; - - var _submitSettingsForm = function () { - if (C.DEMO_MODE) { - _showDemoOnlyDialog(); - return; - } - - utils.startProcessing(); - pluginManager.savePlugins({ - success: _maybeSaveGlobalSettings, - error: function (resp) { - window.scrollTo(0, 0); - $("#settingsTabMessage").addClass("gdErrors").removeClass("gdNotify").css({ display: 'block' }) - .find("p").html("There was an error saving the plugins. Please report this on github."); - utils.stopProcessing(); - }, - - onValidationError: function (error) { - window.scrollTo(0, 0); - $("#settingsTabMessage").addClass("gdErrors").removeClass("gdNotify").css({ display: 'block' }) - .find("p").html(error); - utils.stopProcessing(); - } - }); - }; - - - // called after the user-specific settings are updated. If it's an admin, it updates the global settings as well - var _maybeSaveGlobalSettings = function (resp) { - - function complete () { - window.scrollTo(0, 0); - var refreshButton = ''; // TODO translate - $("#settingsTabMessage").removeClass("gdErrors").addClass("gdNotify").css({ display: 'block' }).find("p").html(resp.content + ' ' + refreshButton); - utils.stopProcessing(); - } - - if (!_accountInfo.isAnonymous && _accountInfo.accountType != "admin") { - return complete(); - } - - var data = { - action: "saveGlobalSettings", - consoleWarnings: $("#gdSettingsConsoleWarnings").prop("checked"), - consoleEventsPublish: $("#gdSettingsConsoleEventsPublish").prop("checked"), - consoleEventsSubscribe: $("#gdSettingsConsoleEventsSubscribe").prop("checked"), - consoleCoreEvents: $("#gdSettingsConsoleCoreEvents").prop("checked"), - consoleEventsDataTypePlugins: $("#consoleEventsDataTypePlugins").val(), - consoleEventsExportTypePlugins: $("#consoleEventsExportTypePlugins").val() - }; - - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "json", - data: data, - success: function (response) { - if (response.success) { - complete(); - } - }, - error: function () { - utils.stopProcessing(); - } - }); - }; - - - var _login = function () { - var email = $.trim($("#gdLogin_email").val()); - var password = $.trim($("#gdLogin_password").val()); - - // validate - utils.clearValidationErrors($("#gdLoginDialog")); - if (email === "") { - utils.addValidationErrors({els: [$("#gdLogin_email")], error: L.validation_no_email}); - } - if (password === "") { - utils.addValidationErrors({els: [$("#gdLogin_password")], error: L.validation_no_password}); - } - - var errors = utils.getValidationErrors(); - if (errors.length) { - utils.displayValidationErrors("#gdLoginError"); - return false; - } - - utils.playSpinner(_loginModalID); - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "json", - data: { - action: "login", - email: email, - password: password - }, - success: function (response) { - if (response.success) { - // we explicitly reload the page because the user may have custom plugins selected (added in 3.2.2) - window.location = "./"; - } else { - utils.clearValidationErrors($("#" + _loginModalID)); - utils.addValidationErrors({els: [], error: response.content}); - utils.displayValidationErrors("#gdLoginError"); - utils.pauseSpinner(_loginModalID); - } - - }, - error: function () { - utils.pauseSpinner(_loginModalID); - } - }); - }; - - var _onLoginComplete = function () { - if (_accountInfo.accountType === "admin") { - $("#gdMainTab2,#gdMainTab3").show(); - require(["accountManager"], function (am) { - am.run(); - }); - } - $("#gdMainDialogTabs ul").show(); - - _isLoggedIn = true; - $("#gdNumRowsToGenerate").removeAttr("readonly"); - $("#gdDataSetStatusLine,#gdProcessingIcon").show(); - - // now show the links in the header - $("#gdUserAccount,#gdLogout").show(); - $("#gdLogin").hide(); - - utils.pauseSpinner(_loginModalID); - - $("#" + _loginModalID).dialog("close"); - - manager.publish({ - sender: MODULE_ID, - type: C.EVENT.ACCOUNT.LOGGED_IN, - accountInfo: _accountInfo - }); - }; - - var _logout = function () { - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "json", - data: { - action: "logout" - }, - success: function (response) { - if (response.success) { - // always redirect to login.php. If the system is set up to allow anonymous - // logins for multi-users it'll automatically redirect - window.location = "login.php"; - } - }, - error: function () { - } - }); - }; - - var _resetPassword = function () { - var email = $.trim($("#gdEmailReminder").val()); - - utils.clearValidationErrors($("#" + _loginModalID)); - if (email === "") { - utils.addValidationErrors({els: [$("#gdEmailReminder")], error: L.validation_no_email}); - } else if (!utils.isValidEmail(email)) { - utils.addValidationErrors({els: [$("#gdEmailReminder")], error: L.validation_invalid_email}); - } - - var errors = utils.getValidationErrors(); - if (errors.length) { - utils.displayValidationErrors("#gdResetPasswordMessage"); - return false; - } - - utils.startProcessing(); - $.ajax({ - url: "ajax.php", - type: "POST", - dataType: "json", - data: { - action: "resetPassword", - email: email - }, - success: function (json) { - var resetPasswordMessage = $("#gdResetPasswordMessage"); - if (json.success) { - resetPasswordMessage.removeClass("gdErrors").addClass("gdNotify").find("div").html("

    " + json.content + "

    "); - } else { - resetPasswordMessage.removeClass("gdNotify").addClass("gdErrors").find("div").html(""); - } - - if (resetPasswordMessage.css("display") !== "block") { - resetPasswordMessage.show("blind", null, 500); - } else { - resetPasswordMessage.effect("highlight", {color: "#ffc9c9"}, 1500); - } - - utils.stopProcessing(); - }, - error: function (json) { - utils.stopProcessing(); - } - }); - }; - - var _onClickLoadConfigurationHistory = function (e) { - e.preventDefault(); - var configurationID = $(e.target).closest("tr").data("id"); - history.getHistory(configurationID); - }; - - - // register our module - manager.registerCoreModule(MODULE_ID, { - run: _run - }); - - - /* - * The public API for this module. These are the only revealed functions for use by other modules - * that choose to include generator.js as a dependency. Even though the bulk of the functions are private, - * it still contains a couple of handy methods. - */ - return { - - /** - * Returns an ordered array of row IDs. Row IDs are unique and may be in any order with possible gaps. Each - * row is added dynamically, and may be sorted or deleted. - * @function - * @name Generator#getRowOrder - */ - getRowOrder: _getRowOrder, - - /** - * When a user re-orders or deletes some rows, the table gives the appearance of being numbered - * numerically 1-N, however the actual markup retains the original number scheme according to how it - * was first generated. This function returns the visible number of the row number, used for generating - * helpful error messages. - * @function - * @param {Number} rowNum a row number. Returns false if there's no corresponding visible row number. - * @name Generator#getVisibleRowOrderByRowNum - */ - getVisibleRowOrderByRowNum: _getVisibleRowOrderByRowNum, - - /** - * Returns an array of selected countries. - * @function - * @name Generator#getCountries - */ - getCountries: function () { - return _countries; - }, - - /** - * Returns the current export target (new window, prompt, in-page). - * @function - * @name Generator#getExportTarget - */ - getExportTarget: _getExportTarget, - - /** - * Returns the number of rows to generate currently entered. Note: this returns a STRING. - * @function - * @name Generator#getNumRowsToGenerate - */ - getNumRowsToGenerate: _getNumRowsToGenerate, - - /** - * Returns the account information for the current logged in user (or null if they're not logged in). - * @returns {null} - */ - getAccount: function () { - return _accountInfo; - } - }; + "use strict"; + + /** + * @name Generator + * @see Core + * @description This file contains the core client-side code for the Data Generator. It initializes the default + * functionality of the generator page, subscribes to and publishes all the appropriate Core events and + * offers a few public functions for use by any plugins running custom JS code. + * @author Ben Keen + * @return {Object} + * @namespace + */ + + var MODULE_ID = "core-generator"; + + var _numRows = 0; + var _numRowsToShowOnStart = 4; + var _countries = []; + var _currExportType = null; // populated onload + var _showExportTypeSettings = true; + var _codeMirror = null; + var _lastSelectedDataType = null; + var _isHighlightingDeleteRow; + + // the number of results being generated + var _numRowsToGenerate; + + // for storing data during in-page data generation + var _generateInPageRunningCount; + var _generateInPageBatchNum; + var _generateInPageData; + var _generateInPageContent = ""; + var _isGenerating = false; + var _generationCancelled = false; + var _currHelpDialogTab = 1; + var _currLoginDialogTab = 1; + var _currDataTypeHelp = null; + + // accounts + var _isLoggedIn = null; + var _isLoaded = false; + var _accountInfo = null; + var _dataSets = []; + var _currConfigurationID = null; + var _loginModalID = "gdLoginDialog"; + + + /** + * Called when everything is loaded. This binds the appropriate event handlers and sets up the + * page. + */ + var _run = function () { + + // retrieve the data sets for the current user + if ($("body").data("loggedIn")) { + utils.startProcessing(); + _isLoggedIn = true; + _getAccount(); + } else { + _isLoggedIn = false; + $("#gdMainDialogTabs ul").hide(); + $("#gdDataSetStatusLine,#gdProcessingIcon").hide(); + } + + $("#gdDataSetName").focus(); + $("#gdCountries").chosen().change(_updateCountryChoice); + $("#gdRegenerateButton").on("click", _generateData); + $("#gdBackButton").on("click", _onClickBackButton); + + $("#gdShowSettingsLink").bind("click", function () { + if (_showExportTypeSettings) { + _hideExportTypeSettingsSection(); + } else { + _showExportTypeSettingsSection(_currExportType); + } + return false; + }); + + // each event is handled separately to ensure that the Change Data Type event isn't unnecessarily + // republished. Only really an issue on Firefox, which publishes keyup and change events when + // changing the selected option via the keyboard (up and down). It also allows us to tab off the field + // into whatever field is displayed next. + var $tableRows = $("#gdTableRows"); + $tableRows.on("change keyup", ".gdDataType", _onChangeDataType); + $tableRows.on("focus", ".gdDataType", _onFocusDataType); + $tableRows.on("change", ".gdDeleteRows", _markRowToDelete); + $tableRows.on("change", ".gdColExamples select", _publishExampleChange); + + $tableRows.sortable({ + handle: ".gdColOrder", + axis: "y", + update: function (event, ui) { + _updateVisibleRowNums(); + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.DATA_TABLE.ROW.RE_SORT, + row: ui.item + }); + } + }); + + $(document).on("click", ".gdMessageClose", function (e) { + $(e.target).closest(".gdMessage").hide("blind", null, 500); + return false; + }); + + $("#gdData").bind("submit", _generateData); + $("#gdExportTypeTabs>ul>li").bind("click", function (e) { + _selectExportTypeTab($(e.target).data("exportType")); + }); + + $(".gdAddRowsBtn").bind("click", function () { + _addRows($("#gdNumRowsToAdd").val()); + }); + $(".gdDeleteRowsBtn").bind("click", _deleteRows); + $("#gdTextSize").on("click", "li", _changeTextSize); + $("#gdGenerationPanelCancel").on("click", _cancelGeneration); + $("#gdDataSetPublic").on("click", _toggleDataSetVisibilityStatus); + $("#gdSettingsForm").on("submit", function (e) { + e.preventDefault(); + }); + $("#updateSettingsBtn").on("click", _submitSettingsForm); + $("#gdResetPluginsBtn").on("click", _onClickResetPlugins); + $("#gdNumRowsToGenerate").on("click", _onClickNumRowsField); + $("input[name=gdExportTarget]").on("change", _onChangeExportTarget); + + // icon actions + $("#gdSaveBtn").on("click", _onClickSaveButton); + $("#gdSaveDataSet").on("click", _saveDataSet); + $("#gdEmptyForm").on("click", _emptyForm); + $("#gdDataSetLink").on("click", _openDataSetLinkDialog); + + // main dialog + $("#gdLogout").on("click", function () { + return _logout(); + }); + $("#gdUserAccount").on("click", _onClickUserAccountLink); + $("#gdLogin").on("click", _onClickLoginLink); + $("#gdLoadLink").on("click", _onClickLoadDataSetIcon); + + var $accountDataSets = $(".gdDialogTable"); + $accountDataSets.on("click", "a.loadDataSet", _onClickLoadDataSet); + $accountDataSets.on("click", "a.loadConfigurationHistory", _onClickLoadConfigurationHistory); + $accountDataSets.on("change", ".gdSelectDataSets", _onChangeCheckDataSet); + $accountDataSets.on("click", _onClickToggleDeleteRow); + $(".gdDeleteDataSetsBtn").bind("click", _confirmDeleteDataSets); + $("#gdDataSetHelpNav").on("click", "a", _onclickDataTypeHelpNav); + $tableRows.on("click", ".ui-icon-help", _onClickDataSetRowHelp); + $("#gdSelectAllDataSets").on("click", _onToggleSelectAllDataSets); + $("#gdDataSetStatusLine").html(L.not_saved); + $("#gdMainDialog").on("click", ".gdDataSetStatus", _onToggleSaveDataSetVisibilityStatus); + $("#gdUpdateAccountInfo").on("click", _updateAccountInfo); + + _initMainDialog(); + _initLoginDialog(); + _initExportTypeTab(); + _updateCountryChoice(); + _addRows(_numRowsToShowOnStart); + _initInPageCodeMirror(); + _initTooltips(); + + history.init({ + loadDataSet: _loadDataSet + }); + + // finally, if the URL contains a Data Set ID, request it from the server + var loadDataSetID = utils.getParamByName("load"); + if (loadDataSetID !== null && /^\d+$/.test(loadDataSetID)) { + _getPublicDataSet(loadDataSetID); + } + }; + + + var _onClickNumRowsField = function () { + if (!_isLoggedIn) { + _showPermissionDeniedDialog(L.cannot_change_num_rows); + } + }; + + var _getPublicDataSet = function (dataSetID) { + utils.startProcessing(); + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "json", + data: { + action: "getPublicDataSet", + dataSetID: dataSetID + } + }).then( + function (response) { + if (response.success) { + _loadDataSet(response.content); + } else { + // TODO + } + }, + + function () { + utils.stopProcessing(); + } + ); + }; + + /** + * Called when the user clicks the "LOAD" link for a particular data set. This queries the account Manager + * to retrieve the data set, and then displays the information in the page. + */ + var _onClickLoadDataSet = function (e) { + e.preventDefault(); + var configurationID = $(e.target).closest("tr").data("id"); + var dataSet = _getConfiguration(configurationID); + _loadDataSet(dataSet); + }; + + var _onClickLoadDataSetIcon = function (e) { + e.preventDefault(); + if (_isLoggedIn) { + _openMainDialog({ tab: 2 }); + } else { + _showPermissionDeniedDialog(); + } + }; + + var _onChangeExportTarget = function (e) { + _handleZipOption(); + }; + + var _onClickUserAccountLink = function (e) { + e.preventDefault(); + _openMainDialog({ tab: 1 }); + }; + + var _onClickLoginLink = function (e) { + e.preventDefault(); + _openLoginDialog(); + }; + + var _loadDataSet = function (configuration) { + utils.startProcessing(); + + var json = $.evalJSON(configuration.content); + var numRows = json.hasOwnProperty("dataTypes") ? json.dataTypes.length : _numRowsToShowOnStart; + + // clear the form + _clearForm(numRows); + + // now the form's been cleared, store the new configuration ID. We stash it in the page so that it's + // sent along for the + _currConfigurationID = configuration.configuration_id; + $("#configurationID").val(_currConfigurationID); + + // now start populating the page + $("#gdDataSetName").val(configuration.configuration_name); + $("#gdNumRowsToGenerate").val(json.numResults); + $("input[name=gdExportTarget]").each(function () { + if (this.value === json.exportTarget) { + this.checked = true; + } + }); + + _updateCountries(json.countries); + + // update the Export Types section + _selectExportTypeTab(json.selectedExportType, true); + manager.loadExportType(json.selectedExportType, json.exportTypes); + + + // now populate the rows. Do everything that we can: create the rows, populate the titles & select + // the data type. The remaining fields are custom to the data type, so we leave them to their + // .loadData function (if defined) + if (json.hasOwnProperty("dataTypes")) { + var numDataTypeRows = json.dataTypes.length; + var orderedRowIDs = _getRowOrder(); + + var data = []; + for (var i = 0; i < numDataTypeRows; i++) { + var currDataType = json.dataTypes[i]; + var currRowID = orderedRowIDs[i]; + $("#gdTitle_" + currRowID).val(currDataType.title); + $("#gdDataType_" + currRowID).val(currDataType.dataType); + _publishDataTypeChange($("#gdDataType_" + currRowID)[0]); + + currDataType.rowID = currRowID; + data.push(currDataType); + } + + manager.loadDataTypeRows(data); + } + + // update the status line + var lastUpdated = moment.unix(configuration.last_updated_unix).format("h:mm A, MMM Do YYYY"); + $("#gdDataSetStatusLine").html(L.last_edited + " " + lastUpdated); + + utils.stopProcessing(); + _closeMainDialog(); + + _handleZipOption(); + + // publish the IO LOAD event + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.IO.LOAD + }); + }; + + + var _updateCountries = function (countries) { + $("#gdCountries option").each(function () { + if ($.inArray(this.value, countries) != -1) { + this.selected = true; + } else { + this.selected = false; + } + }); + $("#gdCountries").trigger("liszt:updated"); + _updateCountryChoice(); + }; + + + var _showSubtab = function (tab) { + if (tab == 1) { + $("#gdGenerateSubtab1").show(); + $("#gdGenerateSubtab2").hide(); + $("#gdEmptyForm,#gdLoadLink").show(); + } else { + $("#gdGenerateSubtab1").hide(); + $("#gdGenerateSubtab2").show(); + $("#gdEmptyForm,#gdLoadLink").hide(); + } + return false; + }; + + + /** + * Called when the user clicks the save icon. This intelligently decides how to save the information, + * based on whether it's a totally new data set, or if the user had loaded one already. + */ + var _onClickSaveButton = function () { + if (!_isLoggedIn) { + _showPermissionDeniedDialog(); + } else if (C.DEMO_MODE) { + _showDemoOnlyDialog(); + } else { + if (_currConfigurationID === null) { + _saveDataSet(); + } else { + // confirmation...? + _saveDataSet(); + } + } + + return false; + }; + + + /** + * Serializes the current data set and passes it over to the Account Manager to actually save. + */ + var _saveDataSet = function () { + var newDataSetName = $("#gdDataSetName").val(); + + // if there's no Data Set name provided, briefly highlight the field to draw attention to it + // and halt the process + if (!newDataSetName) { + $("#gdDataSetName").css({ + backgroundColor: "#770000", + borderTopColor: "#550000", + borderLeftColor: "#550000", + borderBottomColor: "#550000" + }).animate({ + backgroundColor: "#ffffff", + borderTopColor: "#cccccc", + borderLeftColor: "#cccccc", + borderBottomColor: "#cccccc" + }, 1500); + return false; + } + + var rowData = []; + var orderedRowIDs = _getRowOrder(); + for (var i = 0; i < orderedRowIDs.length; i++) { + var rowNum = orderedRowIDs[i]; + var rowDataType = $("#gdDataType_" + rowNum).val(); + if (rowDataType === "") { + continue; + } + + rowData.push({ + title: $("#gdTitle_" + rowNum).val(), + dataType: rowDataType, + data: manager.serializeDataTypeRow(rowDataType, rowNum) + }); + } + + var configuration = { + action: "saveConfiguration", + dataSetName: newDataSetName, + exportTarget: _getExportTarget(), + numResults: _getNumRowsToGenerate(), + countries: _countries, + dataTypes: rowData, + exportTypes: manager.serializeExportTypes(), + selectedExportType: _currExportType + }; + + if (_currConfigurationID !== null) { + configuration.configurationID = _currConfigurationID; + } + + utils.startProcessing(); + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "json", + data: configuration + }).then( + function (response) { + if (response.success) { + _currConfigurationID = response.content; + var lastUpdated = moment.unix(response.lastUpdated).format("h:mm A, MMM Do YYYY"); + $("#gdDataSetStatusLine").removeClass("hidden").html(L.last_saved + " " + lastUpdated).css("color", "#7fbcf8").animate({ color: "#666666" }, 1400); + $("#gdDataSetHistoryNav").addClass("hidden"); + _getAccount(); + } else { + // TODO + } + }, + function () { + // alert(L.fatal_error); + // gd.stopProcessing(); + } + ); + }; + + var _addRows = function (rows) { + rows = rows.toString(); + if (rows.match(/\D/) || rows === 0 || rows === "") { + utils.clearValidationErrors($("#gdMainTab1Content")); + utils.addValidationErrors({ els: [$("#gdNumRowsToAdd")], error: L.no_num_rows }); + utils.displayValidationErrors("#gdMessages"); + return false; + } + + var rowIDs = []; + var numRowsToAdd = parseInt(rows, 10); + for (var i = 1; i <= numRowsToAdd; i++) { + var currRow = ++_numRows; + rowIDs.push(currRow); + var newRowHTML = $("#gdTableRowTemplate").html().replace(/%ROW%/g, currRow); + $("#gdTableRows").append('
  • ' + newRowHTML + '
  • '); + } + + _updateVisibleRowNums(); + + // curious! This is done to force Chrome to do a redraw/repaint after adding rows + $("body").addClass("forceRedraw").removeClass("forceRedraw"); + + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.DATA_TABLE.ROW.ADD, + numRows: rows, + rowIDs: rowIDs + }); + }; + + + /** + * This is called when the user actually clicks one of the DEL buttons, deleting those rows marked + * as deleted. + * + * @function + * @private + */ + var _deleteRows = function () { + var rowIDs = []; + $(".gdDeleteRows:checked").each(function () { + var row = $(this).closest(".gdTableRow"); + var parentRowID = row.attr("id"); + if (parentRowID !== null) { + var rowID = parseInt(parentRowID.replace(/row_/g, ""), 10); + row.remove(); + rowIDs.push(rowID); + } + }); + + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.DATA_TABLE.ROW.DELETE, + rowIDs: rowIDs + }); + + _updateVisibleRowNums(); + }; + + var _updateVisibleRowNums = function () { + $("#gdTableRows>li .gdColOrder").each(function (i) { + $(this).html(i + 1); + }); + }; + + var _markRowToDelete = function (e) { + var el = e.target; + var event = null; + var tr = $(el).closest(".gdTableRow"); + + if (el.checked) { + _isHighlightingDeleteRow = true; + tr.addClass("gdDeletedRow").effect("highlight", { color: "#cc0000" }, 1000); + event = C.EVENT.DATA_TABLE.ROW.CHECK_TO_DELETE; + } else { + if (_isHighlightingDeleteRow) { + tr.stop(true, true); + } + tr.removeClass("gdDeletedRow"); + event = C.EVENT.DATA_TABLE.ROW.UNCHECK_TO_DELETE; + _isHighlightingDeleteRow = false; + } + manager.publish({ + sender: MODULE_ID, + type: event, + row: el + }); + }; + + /** + * Resets the entire page back to its defaults: default countries, a blank table and the default data + * format. The optional object param lets you optionally display a confirmation modal and reset the + * the table to the default num of rows. + * @function + * @private + * @name#Generator + */ + var _emptyForm = function (settings) { + var opts = $.extend({ + requireConfirmation: true, + numRows: _numRowsToShowOnStart + }, settings); + + if (opts.requireConfirmation) { + $("#gdEmptyFormDialog").html("

    " + L.confirm_empty_form + "

    ").dialog({ + title: L.please_confirm, + modal: true, + width: 400, + buttons: [ + { + text: L.yes, + click: function () { + _clearForm(opts.numRows); + $(this).dialog("close"); + } + }, + { + text: L.no, + click: function () { + $(this).dialog("close"); + } + } + ] + }); + } else { + _clearForm(opts.numRows); + } + + }; + + var _clearForm = function (numDefaultRows) { + $("#gdDataSetName").val(""); + + // reset the countries + _updateCountries([]); + + // remove the rows + $("#gdTableRows .gdDeleteRows").attr("checked", "checked"); + _deleteRows(); + _addRows(numDefaultRows); + + _currConfigurationID = null; + + // set the default Export Type + _selectExportTypeTab($(".gdDefaultExportType").data("exportType"), true); + manager.resetExportTypes(); + + $("#gdDataSetStatusLine").html(L.not_saved); + + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.DATA_TABLE.CLEAR + }); + }; + + /** + * Called whenever the user selects or deselects a Country. If any modules need to do + * anything special, they can subscribe to the appropriate event. + */ + var _updateCountryChoice = function () { + _countries.length = 0; + $("#gdCountries option").each(function () { + if (this.selected) { + _countries.push(this.value); + } + }); + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.COUNTRIES.CHANGE, + countries: _countries + }); + }; + + var _initExportTypeTab = function () { + var newExportType = $("#gdExportTypeTabs li.gdSelected").data("exportType"); + _selectExportTypeTab(newExportType); + }; + + + /** + * Called whenever the user changes the result type (XML, HTML, CSV etc). This function publishes + * the appropriate event in case a plugin needs to be aware of the event, but it handles the + * hiding/showing and changing of the title column label "out-the-box" rather than force + * the Export Type modules to have to do the work. + */ + var _selectExportTypeTab = function (newExportType, showImmediately) { + if (newExportType === _currExportType) { + return; + } + + if (_currExportType !== null) { + $("#gdExportTypeTabs>ul>li").removeClass("gdSelected"); + $("#gdExportType_" + newExportType).addClass("gdSelected"); + } + + // always reset the column heading to the default "Column Title". Export Types have the option + // to overwrite it through the publish event below + $("#gdColTitleTop,#gdColTitleBottom").html(L.row_label); + + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.RESULT_TYPE.CHANGE, + newExportType: newExportType, + oldExportType: _currExportType + }); + + // hide and show the appropriate Export Type additional settings section (if the + showdata format options link + // has been clicked) + if (_showExportTypeSettings) { + _showExportTypeSettingsSection(newExportType, showImmediately); + } + + // now enable/disable the available export targets + var exportTargets = $("#gdExportType_" + newExportType).data("compatibleExportTargets").split(","); + $("input[name=gdExportTarget]").attr("disabled", "disabled"); + $("#gdGenerateSection label").addClass("gdDisabled"); + var firstNonDisabledExportTarget = null; + for (var i = 0; i < exportTargets.length; i++) { + $("#gdExportTarget_" + exportTargets[i]).removeAttr("disabled"); + $("#gdExportTarget_" + exportTargets[i] + "_label").removeClass("gdDisabled"); + if (firstNonDisabledExportTarget === null) { + firstNonDisabledExportTarget = exportTargets[i]; + } + } + + // now, if the current selected export target is disabled, reset it to the first available non-disabled option + var sel = $("input[name=gdExportTarget]:checked:disabled"); + if (sel.length) { + $("#gdExportTarget_" + firstNonDisabledExportTarget).attr("checked", "checked"); + } + _handleZipOption(); + + _currExportType = newExportType; + }; + + // only enable the Zip checkbox if the Prompt to Download export target is selected + var _handleZipOption = function () { + var selectedTarget = $("input[name=gdExportTarget]:checked").val(); + if (selectedTarget === "promptDownload") { + $("#gdExportTarget_promptDownload_zip").removeAttr("disabled"); + $("#gdExportTarget_promptDownload_zip_label").removeClass("gdDisabled"); + } else { + $("#gdExportTarget_promptDownload_zip").attr("disabled", "disabled"); + $("#gdExportTarget_promptDownload_zip_label").addClass("gdDisabled"); + } + }; + + var _showExportTypeSettingsSection = function (newExportType, showImmediately) { + if ($("#gdExportTypeAdditionalSettings_" + _currExportType).length > 0 && _showExportTypeSettings) { + if (showImmediately === true) { + $("#gdExportTypeAdditionalSettings_" + _currExportType).hide(); + } else { + $("#gdExportTypeAdditionalSettings_" + _currExportType).hide("blind", C.EXPORT_TYPE_SETTINGS_BLIND_SPEED); + } + } + if (_currExportType === null || showImmediately === true) { + $("#gdExportTypeAdditionalSettings_" + newExportType).show(); + } else { + $("#gdExportTypeAdditionalSettings_" + newExportType).show( + "blind", + C.EXPORT_TYPE_SETTINGS_BLIND_SPEED, + function () { + _showExportTypeSettings = true; + $("#gdShowSettingsLink span").html("-"); + $("#gdShowSettingsLink a").html(L.hide_data_format_options); + } + ); + } + }; + + var _hideExportTypeSettingsSection = function () { + $("#gdExportTypeAdditionalSettings_" + _currExportType).hide( + "blind", + C.EXPORT_TYPE_SETTINGS_BLIND_SPEED, + function () { + _showExportTypeSettings = false; + $("#gdShowSettingsLink span").html("+"); + $("#gdShowSettingsLink a").html(L.show_data_format_options); + } + ); + }; + + var _publishExampleChange = function (e) { + var select = e.target; + var rowElement = $(select).closest(".gdTableRow"); + var rowID = parseInt($(rowElement).attr("id").replace(/^row_/, ""), 10); + var dataTypeFolder = $(rowElement).find(".gdDataType").val(); + + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.DATA_TABLE.ROW.EXAMPLE_CHANGE + "__" + dataTypeFolder, + rowID: rowID, + value: select.value + }); + }; + + /** + * Called whenever the user focuses on a Row Type. This makes a note of the last selected + * Data Type, to prevent unnecessary re-publishing of (non-)changed data types. + */ + var _onFocusDataType = function (e) { + _lastSelectedDataType = e.target.value; + }; + + /** + * Called when the user changes the Data Type for a particular row. + */ + var _onChangeDataType = function (e) { + if (e.target.value != _lastSelectedDataType) { + _publishDataTypeChange(e.target); + } + }; + + var _publishDataTypeChange = function (el) { + var rowID = parseInt($(el).attr("id").replace(/^gdDataType_/, ""), 10); + var dataTypeModuleID = el.value; + + // make a note of the last value + _lastSelectedDataType = dataTypeModuleID; + + // if the user just selected the empty value ("Please Select"), clear everything + if (dataTypeModuleID === "") { + $('#gdColExamples_' + rowID + ',#gdColOptions_' + rowID + ',#gdColHelp_' + rowID).html(""); + return; + } + + // update the example + options divs + var exampleHTML = null; + var optionsHTML = null; + var dataTypeExampleHTML = $("#gdDataTypeExamples_" + dataTypeModuleID).html(); + if (dataTypeExampleHTML !== "") { + exampleHTML = dataTypeExampleHTML.replace(/%ROW%/g, rowID); + } else { + exampleHTML = " " + L.no_examples_available; + } + $("#gdColExamples_" + rowID).html(exampleHTML); + + var dataTypeOptionHTML = $("#gdDataTypeOptions_" + dataTypeModuleID).html(); + if (dataTypeOptionHTML !== "") { + optionsHTML = dataTypeOptionHTML.replace(/%ROW%/g, rowID); + } else { + optionsHTML = L.no_options_available; + } + $("#gdColOptions_" + rowID).html(optionsHTML); + + if ($("#gdDataTypeHelp_" + dataTypeModuleID).html() !== "") { + $('#gdColHelp_' + rowID).html($("#gdHelpIcon").html().replace(/%ROW%/g, rowID)); + } else { + $('#gdColHelp_' + rowID).html(" "); + } + + // now publish the row change + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.DATA_TABLE.ROW.TYPE_CHANGE, + rowID: rowID, + dataTypeModuleID: dataTypeModuleID + }); + }; + + + var _getRowOrder = function () { + var orderedRowIDs = $("#gdTableRows").sortable("toArray"); + var sortedOrder = []; + for (var i = 0; i < orderedRowIDs.length; i++) { + var row = orderedRowIDs[i].replace(/row_/g, ""); + sortedOrder.push(row); + } + return sortedOrder; + }; + + + /** + * Called when the user submits the main Generate tab. It performs all necessary validation + * and starts the data generation process. + */ + var _generateData = function (e) { + + // TODO pretty poor. Validation should be performed on this var prior to setting it in the private var + _numRowsToGenerate = _getNumRowsToGenerate() + utils.clearValidationErrors($("#gdMainTab1Content")); + + // check the users specified a numeric value for the number of results + if (_numRowsToGenerate.match(/\D/) || _numRowsToGenerate === 0 || _numRowsToGenerate === "") { + utils.addValidationErrors({ els: $("#gdNumRowsToGenerate"), error: L.invalid_num_results }); + } + + var orderedRowIDs = _getRowOrder(); + var validRowIDs = []; + + // look through the form and construct an object of data-type-folder => [row IDs] to + // pass to the manager. The manager uses that to farm out the actual validation work + // to the appropriate module + var rowValidationNeededGroupByDataType = {}; + for (var i = 0; i < orderedRowIDs.length; i++) { + var rowID = orderedRowIDs[i]; + var currRowType = $("#gdDataType_" + rowID).val(); + + // ignore empty rows, they don't need validating + if (currRowType === "") { + continue; + } + if (!rowValidationNeededGroupByDataType.hasOwnProperty(currRowType)) { + rowValidationNeededGroupByDataType[currRowType] = []; + } + rowValidationNeededGroupByDataType[currRowType].push(rowID); + validRowIDs.push(rowID); + } + + // if none of the data columns had a selected data type, display an error about that, too + if (!validRowIDs.length) { + utils.addValidationErrors({ els: null, error: L.no_data }); + } else { + // check all filled-in rows contained something in the first column + var rowsMissingTitleEls = []; + for (var j = 0; j < validRowIDs.length; j++) { + var currRowID = validRowIDs[j]; + var currTitle = $("#gdTitle_" + currRowID); + if ($.trim(currTitle.val()) === "") { + rowsMissingTitleEls.push(currTitle[0]); + } + } + + if (rowsMissingTitleEls.length) { + var label = L.row_label_plural; + if (L.exportTypePlugins[_currExportType].hasOwnProperty("row_label_plural")) { + label = L.exportTypePlugins[_currExportType].row_label_plural; + } + var message = L.please_enter_all + " " + label + "."; + utils.addValidationErrors({ els: rowsMissingTitleEls, error: message }); + } + } + + utils.addValidationErrors(manager.validateDataTypes(rowValidationNeededGroupByDataType)); + + var exportTypeValidationErrors = manager.validateExportType({ exportType: _currExportType, rows: validRowIDs }); + if (!$.isArray(exportTypeValidationErrors)) { + utils.addValidationErrors({ els: null, error: L.export_type_validate_error }); + } else { + utils.addValidationErrors(exportTypeValidationErrors); + } + + // ensure the number of rows is an acceptable number + _numRowsToGenerate = parseInt(_numRowsToGenerate, 10); + if (_numRowsToGenerate > C.MAX_GENERATED_ROWS) { + var msg = L.num_rows_too_large.replace(/%1/, C.MAX_GENERATED_ROWS); + utils.addValidationErrors({ els: null, error: msg }); + $("#gdNumRowsToGenerate").val(C.MAX_GENERATED_ROWS); + } + + var errors = utils.getValidationErrors(); + if (errors.length) { + utils.displayValidationErrors("#gdMessages"); + return false; + } + + var exportTarget = _getExportTarget(); + var rowOrder = _getRowOrder().toString(); + $("#gdRowOrder").val(rowOrder); + $("#gdExportType").val(_currExportType); + $("#gdNumCols").val(_numRows); + + // reset CodeMirror (scrollTo not working ...) + _codeMirror.setOption("lineWrapping", false); + _codeMirror.scrollTo(0, 0); + _codeMirror.setValue(""); + + // now pass off the work to the appropriate generation function. Each works slightly differently. + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.GENERATE, + exportTarget: exportTarget, + exportType: _currExportType, + editor: _codeMirror + }); + + // if the messages section is displayed, hide it - whatever old errors are no longer pertinent + if ($("#gdMessages").css("display") == "block") { + $("#gdMessages .gdMessageClose").trigger("click"); + } + + // only the inPage choice prevents the default form submit event + if (exportTarget == "inPage") { + _generateInPage(); + e.preventDefault(); + } else if (exportTarget == "newTab") { + _generateNewWindow(); + } else if (exportTarget == "promptDownload") { + _generatePromptDownload(); + } + }; + + + /** + * Generate the results in-page. This option hides the generator table and displays the results in a large, + * CodeMirror-enhanced textarea. This is the only generation format that makes use of *batches*: since generation + * can take a long time, this passes off work to the server in batches of (say) 100, so the user can see the + * generation process take place. + */ + var _generateInPage = function () { + var formData = $("#gdData").serialize(); + + // "action" added for AjaxRequest only + var data = formData + "&action=generateInPage&gdBatchSize=" + C.GENERATE_IN_PAGE_BATCH_SIZE; + if (_currConfigurationID !== null) { + data += "&configurationID=" + _currConfigurationID; + } + _showSubtab(2); + utils.startProcessing(); + + _generateInPageRunningCount = 0; + _isGenerating = true; + _generationCancelled = false; + + $("#gdGenerateCount").html(utils.formatNumWithCommas(_generateInPageRunningCount)); + $("#gdGenerateTotal").html(utils.formatNumWithCommas(_numRowsToGenerate)); + $("#gdProgressMeter").attr("max", _numRowsToGenerate); + $("#gdProgressMeter").attr("value", 0); + + _codeMirror.setValue(""); + + _generateInPageBatchNum = 1; + _generateInPageData = data; + _generateInPageContent = ""; + _generateInPageBatch(); + }; + + var _generateInPageBatch = function () { + $("#gdGenerationPanelCancel").removeClass("gdDisabledLink"); + var data = _generateInPageData + "&gdCurrentBatchNum=" + _generateInPageBatchNum; + if (_currConfigurationID !== null) { + data += "&configurationID=" + _currConfigurationID; + } + $.ajax({ + url: "ajax.php", + type: "POST", + data: data, + dataType: "json", + contentType: "application/x-www-form-urlencoded;charset=utf-8", + success: _generateInPageBatchResponse, + error: function () { + _isGenerating = false; + utils.stopProcessing(); + } + }); + }; + + var _generateInPageBatchResponse = function (response) { + if (response.success) { + // 1. Update the running count ("Generated X of Y rows") + _generateInPageRunningCount = (_generateInPageRunningCount + C.GENERATE_IN_PAGE_BATCH_SIZE) > _numRowsToGenerate ? + _numRowsToGenerate : _generateInPageRunningCount + C.GENERATE_IN_PAGE_BATCH_SIZE; + $("#gdGenerateCount").html(utils.formatNumWithCommas(_generateInPageRunningCount)); + $("#gdProgressMeter").attr("value", _generateInPageRunningCount); + + // 2. Update the actual content + _generateInPageContent += decodeURIComponent(escape(response.content)); + _codeMirror.setValue(_generateInPageContent); + + // check the process hasn't been interrupted + if (_generationCancelled) { + _isGenerating = false; + utils.stopProcessing(); + $("#gdGenerationPanelCancel").addClass("gdDisabledLink"); + $("#gdProgressMeter").attr("value", _numRowsToGenerate); + return; + } + + // now either continue processing, or indicate we're done + if (response.isComplete) { + _isGenerating = false; + utils.stopProcessing(); + $("#gdGenerationPanelCancel").addClass("gdDisabledLink"); + + // update the data in _dataSets + if (_currConfigurationID !== null) { + _incrementConfigurationRowGenerationCount(_currConfigurationID, _numRowsToGenerate); + } + } else { + _generateInPageBatchNum++; + _generateInPageBatch(); + } + } else { + _isGenerating = false; + utils.stopProcessing(); + console.warn("response.success fail"); + } + }; + + var _incrementConfigurationRowGenerationCount = function (configurationID, numRows) { + + // first, update the actual data set + var newNum = ""; + for (var i = 0; i < _dataSets.length; i++) { + if (_dataSets[i].configuration_id == configurationID) { + var currCount = parseInt(_dataSets[i].num_rows_generated, 10); + newNum = currCount + numRows; + _dataSets[i].num_rows_generated = newNum; + } + } + + // second, update the displayed data. This does surgery on the Data Sets tab to just update the one DOM + // element rather than redraw everything + var rows = $("#gdAccountDataSets tbody tr"); + for (var j = 0; j < rows.length; j++) { + if ($(rows[j]).data("id") == configurationID) { + $(rows[j]).find(".gdDataSetNumRowsGenerated").html(utils.formatNumWithCommas(newNum)); + } + } + + // this updates the account info tab "total" count + _updateAccountInfoTab(); + }; + + var _generateNewWindow = function () { + $("#gdData").attr({ + "target": "blank", + "action": "generate.php" + }); + _updateConfigurationRowGenerationCount(); + }; + + var _generatePromptDownload = function () { + $("#gdData").attr({ + "target": "blank", + "action": "generate.php" + }); + _updateConfigurationRowGenerationCount(); + }; + + var _updateConfigurationRowGenerationCount = function () { + var numRows = parseInt(_getNumRowsToGenerate(), 10); + if (_currConfigurationID !== null) { + _incrementConfigurationRowGenerationCount(_currConfigurationID, numRows); + } + }; + + var _onClickResetPlugins = function (e) { + var useMinified = $(e.target).data("useMinified"); + if (useMinified) { + $("#gdResetPluginsDialog").dialog({ + modal: true, + resizable: true, + title: "Reset Plugins", + width: 480, + height: 200, + buttons: [ + { + text: L.close, + click: function () { + $(this).dialog("close"); + } + }, + { + text: L.reset_plugins, + click: function () { + $(this).dialog("close"); + _resetPlugins(); + } + } + ] + }); + return; + } + + _resetPlugins(); + }; + + var _resetPlugins = function () { + + + pluginManager.installPlugins({ + context: "update", + errorHandler: null, + prefill: { + dataTypes: pluginManager.getSelectedDataTypes(), + exportTypes: pluginManager.getSelectedExportTypes(), + countries: pluginManager.getSelectedCountries() + }, + onCompleteHandler: function () { + window.scrollTo(0, 0); + + // boy this is awful. Wrap it in a helper! + $("#settingsTabMessage").removeClass("gdErrors").addClass("gdNotify").css({ display: 'block' }) + .find("p").html("The plugin list has been updated. Click the Save button below to save any changes."); + } + }); + }; + + var _changeTextSize = function (e) { + $("#gdTextSize li").removeClass("gdSelected"); + var size = $(e.target).attr("class"); + $(e.target).addClass("gdSelected"); + $("#gdGenerateSubtab2 .CodeMirror").removeClass("CodeMirror_small CodeMirror_medium CodeMirror_large").addClass("CodeMirror_" + size); + _codeMirror.refresh(); + }; + + /** + * Called on page load. We always instantiate the codemirror object on the generate in-page. This object is + * passed in the C.EVENT.GENERATE message for export types to mess with (i.e. change the mode). + */ + var _initInPageCodeMirror = function () { + _codeMirror = CodeMirror.fromTextArea($("#gdGeneratedData")[0], { + mode: "xml", + readOnly: true, + lineNumbers: true + }); + $(".CodeMirror").addClass("CodeMirror_medium"); + }; + + var _getExportTarget = function () { + return $("input[name=gdExportTarget]:checked").val(); + }; + + var _getNumRowsToGenerate = function () { + return $("#gdNumRowsToGenerate").val(); + }; + + var _getVisibleRowOrderByRowNum = function (rowNum) { + var rowOrder = _getRowOrder(); + var visibleRowNum = 1; + for (var i = 0; i < rowOrder.length; i++) { + if (rowOrder[i] == rowNum) { + return visibleRowNum; + } + visibleRowNum++; + } + return false; + }; + + + // main dialog + + var _initMainDialog = function () { + $("#gdMainDialogTabs ul li").each(function () { + var newTab = parseInt($(this).attr("id").replace(/^gdMainDialogTab/, ""), 10); + + $(this).bind("click", function (e) { + utils.selectTab({ + tabGroup: "dialogTabs", + tabIDPrefix: "gdMainDialogTab", + newTab: newTab, + oldTab: _currHelpDialogTab + }); + _currHelpDialogTab = newTab; + var $dialog = $("#gdMainDialog"); + + if ($dialog.css("display") == "block") { + if (newTab == 1) { + $dialog.dialog("option", "buttons", [{ + text: L.close, click: function () { + $(this).dialog("close"); + } + }]); + } else if (newTab == 2) { + _updateMainDialogDataSetButtons(); + + // yuck! We want to hide the history section when a user clicks on a tab but NOT when the history content + // was opened and they close then re-open the main dialog. isTrigger is a native jQuery event property + if (!e.isTrigger) { + history.hideDataSetHistorySection(); + } + + } else if (newTab == 3) { + $dialog.dialog("option", "buttons", [{ + text: L.close, click: function () { + $(this).dialog("close"); + } + }]); + if (_currDataTypeHelp === null) { + var dataTypeItems = $("#gdDataSetHelpNav li").not(".gdDataTypeHeader"); + _showDataTypeHelp(dataTypeItems[0]); + } + } + } + }); + }); + }; + + var _onclickDataTypeHelpNav = function (e) { + e.preventDefault(); + var dataTypeNavItem = $(e.target).closest("li"); + _showDataTypeHelp(dataTypeNavItem); + }; + + var _showDataTypeHelp = function (el) { + var dataType = $(el).data("module"); + var link = $(el).find("a"); + + $("#gdDataSetHelpNav a").removeClass("gdSelected"); + $(link).addClass("gdSelected").focus(); + + var linkPosition = $(link).position().top; + var dialogHeight = $("#gdMainDialogTab3Content").height(); + + // if the selected Data Type > the total dialog height, scroll to that point; otherwise + // scroll the top + var scrollPosition = 0; + if (linkPosition + 50 > dialogHeight) { // 50 is to handle the extra space by the tabs + scrollPosition = linkPosition; + } + $("#gdDataSetHelpNav").scrollTop(scrollPosition); + + // set the header to the name of the Data Type + $("#gdFocusedDataTypeHeader").html($(link).html()); + + if (_currDataTypeHelp !== null) { + $("#gdDataTypeHelp_" + _currDataTypeHelp).addClass("hidden"); + } + $("#gdDataTypeHelp_" + dataType).removeClass("hidden"); + _currDataTypeHelp = dataType; + }; + + + var _onClickDataSetRowHelp = function (e) { + var row = $(e.target).closest(".gdTableRow"); + var dataTypeDropdown = row.find(".gdDataType"); + var choice = dataTypeDropdown.val(); + + _openMainDialog({ tab: 3, dataType: choice }); + + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.DATA_TABLE.ROW.HELP_DIALOG_OPEN, + rowElement: row + }); + }; + + + var _openMainDialog = function (settings) { + var opts = $.extend({ + tab: 1, + dataType: null + }, settings); + + // hide/show the appropriate tab + $("#gdMainDialogTab" + opts.tab).trigger("click"); + + // remove any custom styles + $(".gdHelpSection").removeAttr("style"); + + // close any messages that are open + $("#gdMainDialogTab1Message").hide(); + + // open the dialog + $("#gdMainDialog").dialog({ + title: 'generatedata.com', + dialogClass: "gdMainDialog", + width: 860, + minHeight: 420, + modal: true, + resizable: false, + buttons: [ + { + text: L.close, + click: function () { + $(this).dialog("close"); + } + } + ] + }); + + // if required, ensure the appropriate Data Type item is selected. This is done after + // the dialog is opened because it needs to set the appropriate offset height of the + // left sidebar to focus on the appropriate Data Type's help link. This can only be computed + // after it's been opened + if (opts.dataType !== null) { + var helpNavEl = ($("#gdDataSetHelpNav li[data-module='" + opts.dataType + "']"))[0]; + _showDataTypeHelp(helpNavEl); + } + + utils.insertModalSpinner({ modalID: "gdMainDialog" }); + + if (opts.tab == 2) { + _updateMainDialogDataSetButtons(); + } + + return false; + }; + + var _closeMainDialog = function () { + $("#gdMainDialog").dialog("close"); + }; + + var _onChangeCheckDataSet = function (e) { + var el = e.target; + _markDataSetRowToDelete(el); + }; + + var _onClickToggleDeleteRow = function (e) { + if ($.inArray(e.target.nodeName.toUpperCase(), ["INPUT", "A"]) !== -1) { + return; + } + + // reverse the checked-ness of the row + var el = $(e.target).closest("tr").find(".gdSelectDataSets"); + var isChecked = $(e.target).closest("tr").find(".gdSelectDataSets").attr("checked"); + if (isChecked) { + $(el).removeAttr("checked"); + } else { + $(e.target).closest("tr").find(".gdSelectDataSets").attr("checked", "checked"); + } + + _markDataSetRowToDelete(el[0]); + }; + + var _markDataSetRowToDelete = function (el) { + if (!el) { + return; + } + + if (el.checked) { + $(el).closest("tr").addClass("gdSelectedDataSetRow"); + } else { + $(el).closest("tr").removeClass("gdSelectedDataSetRow"); + } + _updateMainDialogDataSetButtons(); + }; + + var _onToggleSelectAllDataSets = function (e) { + var isChecked = e.target.checked; + var cbs = $(".gdSelectDataSets"); + for (var i = 0; i < cbs.length; i++) { + cbs[i].checked = isChecked; + _markDataSetRowToDelete(cbs[i]); + } + _updateMainDialogDataSetButtons(); + }; + + var _confirmDeleteDataSets = function () { + }; + + /** + * Called whenever one or more rows is selected / unselected. This checks to see how + * many rows are selected, and hides/shows the delete/copy buttons. + */ + var _updateMainDialogDataSetButtons = function () { + var cbs = $(".gdSelectDataSets:checked"); + if (cbs.length) { + + var buttons = []; + + // if there's only one row selected, show the Copy Data Set button + if (cbs.length === 1) { + buttons.push({ + text: "Copy Data Set", + click: function () { + var existingDataSet = $(cbs[0]).closest("tr"); + var existingDataSetId = parseInt(existingDataSet.data("id"), 10); + var existingDataSetName = existingDataSet.find(".dataSetName")[0].innerHTML; + + var result = window.prompt(L.please_enter_data_set_name, existingDataSetName); + if (result !== null) { + _copyDataSet(existingDataSetId, result); + } + } + }); + } + + var deleteButtonLabel = L.delete_1_data_set; + if (cbs.length > 1) { + deleteButtonLabel = L.delete_N_data_sets.replace(/%1/, cbs.length); + } + + buttons.push({ + text: deleteButtonLabel, + "class": "gdDeleteDataSetsBtn", + click: function () { + _onClickDeleteDataSets(); + } + }); + + buttons.push({ + text: L.close, + click: function () { + $(this).dialog("close"); + } + }); + + $("#gdMainDialog").dialog("option", "buttons", buttons); + } else { + $("#gdMainDialog").dialog("option", "buttons", [{ + text: L.close, + click: function () { + $(this).dialog("close"); + } + }]); + } + }; + + /** + * Makes a copy of an existing Data Set. + * @param dataSetId + * @param newDataSetName + * @private + */ + var _copyDataSet = function (dataSetId, newDataSetName) { + utils.startProcessing(); + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "JSON", + data: { + action: "copyDataSet", + dataSetId: dataSetId, + newDataSetName: newDataSetName + }, + success: function (response) { + _getAccount(); + }, + error: _onError + }); + }; + + + var _onClickDeleteDataSets = function () { + if (C.DEMO_MODE) { + _closeMainDialog(); + _showDemoOnlyDialog(); + return false; + } + + // get the configuration IDs of the selected rows + var configurationIDs = []; + var cbs = $("#gdAccountDataSets tbody input:checked"); + for (var i = 0; i < cbs.length; i++) { + configurationIDs.push($(cbs[i]).closest("tr").data("id")); + } + + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "JSON", + data: { + action: "deleteDataSets", + configurationIDs: configurationIDs + }, + success: _onSuccessDeleteDataSets, + error: _onError + }); + }; + + + var _onSuccessDeleteDataSets = function (response) { + if (response.success) { + var remainingDataSets = []; + for (var i = 0; i < _dataSets.length; i++) { + if ($.inArray(_dataSets[i].configuration_id, response.content) == -1) { + remainingDataSets.push(_dataSets[i]); + } + } + _dataSets = remainingDataSets; + $("#gdAccount_NumSavedDataSets").html(_dataSets.length); + + _displayDataSets(); + _updateMainDialogDataSetButtons(); + } else { + // TODO + } + }; + + + // account-related + + var _getAccount = function (params) { + var settings = $.extend({ + onComplete: function () { + } + }, params); + + utils.startProcessing(); + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "JSON", + data: { + action: "getAccount" + }, + success: function (data, textStatus, jqXHR) { + var params = { + data: data, + textStatus: textStatus, + jqXHR: jqXHR, + settings: settings + }; + _onRetrievingAccountInfo(params); + }, + error: _onError + }); + }; + + var _onRetrievingAccountInfo = function (params) { + var response = params.data; + utils.stopProcessing(); + + // enable the save, load and link icons + $("#gdActionIcons .loading").removeClass("loading"); + + _isLoaded = true; + _dataSets = response.content.configurations; + + _accountInfo = response.content; + + // remove configurations from the account Info object. This is just to prevent someone (like me) accidentally using + // that data in that object, and not in _dataSets. + delete _accountInfo.configurations; + + _updateAccountInfoTab(); + _displayDataSets(); + + params.settings.onComplete(); + + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.ACCOUNT.AVAILABLE, + accountInfo: _accountInfo + }); + }; + + + /** + * Called when the user clicks the Update Account button on the first tab of the main dialog. + */ + var _updateAccountInfo = function (e) { + e.preventDefault(); + + if (C.DEMO_MODE) { + _showDemoOnlyDialog(); + return; + } + + // check all fields have been entered + var firstNameField = $("#gdUserAccount_firstName"); + var firstNameFieldVal = $.trim(firstNameField.val()); + var lastNameField = $("#gdUserAccount_lastName"); + var lastNameFieldVal = $.trim(lastNameField.val()); + var emailField = $("#gdUserAccount_email"); + var emailFieldVal = $.trim(emailField.val()); + var passwordField = $("#gdUserAccount_password"); + var passwordFieldVal = $.trim(passwordField.val()); + var passwordField2 = $("#gdUserAccount_password2"); + var passwordField2Val = $.trim(passwordField2.val()); + + var hasErrors = false; + if (firstNameFieldVal === "") { + firstNameField.addClass("gdProblemField"); + hasErrors = true; + } else { + firstNameField.removeClass("gdProblemField"); + } + + if (lastNameFieldVal === "") { + lastNameField.addClass("gdProblemField"); + hasErrors = true; + } else { + lastNameField.removeClass("gdProblemField"); + } + + if (emailFieldVal === "") { + emailField.addClass("gdProblemField"); + hasErrors = true; + } else { + emailField.removeClass("gdProblemField"); + } + + // if the users's entered a password, validate them too + if (passwordFieldVal !== "" || passwordField2Val !== "") { + if (passwordFieldVal === "") { + passwordField.addClass("gdProblemField"); + hasErrors = true; + } else { + passwordField.removeClass("gdProblemField"); + } + if (passwordField2Val === "") { + passwordField2.addClass("gdProblemField"); + hasErrors = true; + } else { + passwordField2.removeClass("gdProblemField"); + } + if (passwordFieldVal !== passwordField2Val) { + passwordField.addClass("gdProblemField"); + passwordField2.addClass("gdProblemField"); + hasErrors = true; + } else { + passwordField.removeClass("gdProblemField"); + passwordField2.removeClass("gdProblemField"); + } + } + + if (!hasErrors) { + utils.playSpinner("gdMainDialog"); + var data = { + action: "updateAccount", + accountID: _accountInfo.accountID, + firstName: firstNameFieldVal, + lastName: lastNameFieldVal, + email: emailFieldVal + }; + if (passwordFieldVal !== "") { + data.password = passwordFieldVal; + } + + $.ajax({ + url: "ajax.php", + type: "POST", + data: data, + dataType: "json", + success: function (response) { + utils.pauseSpinner("gdMainDialog"); + if (response.success) { + $("#gdMainDialogTab1Message").show("blind"); + $(passwordField).val(""); + $(passwordField2).val(""); + } else { + // TODO + } + }, + error: function (response) { + utils.pauseSpinner("gdMainDialog"); + console.log("error response: ", response); + } + }); + } + }; + + var _updateAccountInfoTab = function () { + if (_accountInfo.isAnonymous) { + $("#gdAccount_AccountType").html(L.anonymous_admin_account); + } else { + var accountTypeStr = ""; + if (_accountInfo.accountType == "admin") { + accountTypeStr = L.admin; + } else { + accountTypeStr = L.user; + } + $("#gdAccount_AccountType").html(accountTypeStr); + } + + $("#gdUserAccount_firstName").val(_accountInfo.firstName); + $("#gdUserAccount_lastName").val(_accountInfo.lastName); + $("#gdUserAccount_email").val(_accountInfo.email); + $("#gdAccount_NumSavedDataSets").html(_dataSets.length); + $("#gdAccount_DateAccountCreated").html(moment.unix(_accountInfo.dateCreated).format("MMM Do, YYYY")); + + var totalRowsGenerated = 0; + for (var i = 0; i < _dataSets.length; i++) { + totalRowsGenerated += parseInt(_dataSets[i].num_rows_generated, 10); + } + $("#gdAccount_TotalRowsGenerated").html(utils.formatNumWithCommas(totalRowsGenerated)); + }; + + var _displayDataSets = function () { + if (_dataSets.length) { + $("#gdNoAccountDataSets").addClass("hidden"); + var html = ""; + var row = ""; + var currDataSet; + for (var i = 0; i < _dataSets.length; i++) { + currDataSet = _dataSets[i]; + var dateCreated = moment.unix(currDataSet.date_created_unix).format("MMM Do, YYYY"); + var lastUpdated = moment.unix(currDataSet.last_updated_unix).format("MMM Do, YYYY"); + var isPublic = (currDataSet.status === "public") ? 'checked="checked"' : ""; + + row = '' + + '' + utils.decodeUTF8(currDataSet.configuration_name) + '' + + '' + dateCreated + '' + + '' + lastUpdated + '' + + '' + + '' + utils.formatNumWithCommas(currDataSet.num_rows_generated) + '' + + 'history' + + 'load' + + '' + + ''; + html += row; + } + $("#gdAccountDataSets tbody").html(html); + $("#gdAccountDataSets").removeClass("hidden"); + $("#gdConfigurationHistory").addClass("hidden"); + } else { + $("#gdAccountDataSets tbody").html(""); + $("#gdNoAccountDataSets").removeClass("hidden"); + $("#gdAccountDataSets").addClass("hidden"); + } + }; + + var _onError = function (response) { + console.log("on error"); + console.log(response); + }; + + var _getConfiguration = function (configurationID) { + var dataSet = {}; + for (var i = 0; i < _dataSets.length; i++) { + if (_dataSets[i].configuration_id != configurationID) { + continue; + } + dataSet = _dataSets[i]; + } + return dataSet; + }; + + + var _initTooltips = function () { + $(document).tooltip({ + position: { + my: "center bottom-6", + at: "center top" + } + }); + }; + + var _cancelGeneration = function (e) { + e.preventDefault(); + if ($(e.target).hasClass("gdDisabledLink")) { + return; + } + if (!_isGenerating) { + return; + } + _generationCancelled = true; + }; + + var _onClickBackButton = function (e) { + e.preventDefault(); + _showSubtab(1); + + // if the user was in the process of generating a data set, cancel it + if (_isGenerating) { + _generationCancelled = true; + } + }; + + var _openDataSetLinkDialog = function (e) { + if (_currConfigurationID === null) { + $("#gdLinkToDataSet_incomplete").removeClass("hidden"); + $("#gdLinkToDataSet_complete").addClass("hidden"); + } else { + $("#gdLinkToDataSet_incomplete").addClass("hidden"); + $("#gdLinkToDataSet_complete").removeClass("hidden"); + } + + $("#gdLinkToDataSetDialog").dialog({ + title: L.link_to_data_set, + dialogClass: "gdMainDialog", + width: 500, + modal: true, + resizable: false, + open: function () { + if (_currConfigurationID !== null) { + var url = window.location.href.replace(/(#.*)/, ""); + url = url.replace(/(\?.*)/, ""); + $("#gdLinkURL").data("url", url + "?load=" + _currConfigurationID); + + var config = _getConfiguration(_currConfigurationID); + if (config.status == "public") { + $("#gdDataSetPublic").attr("checked", "checked"); + $("#gdLinkURL").val(url).removeClass("gdDisabled").removeAttr("disabled").select(); + } else { + $("#gdDataSetPublic").removeAttr("checked"); + $("#gdLinkURL").val("-").attr("disabled", "disabled").addClass("gdDisabled"); + } + } + }, + buttons: [ + { + text: L.close, + click: function () { + $(this).dialog("close"); + } + } + ] + }); + + return false; + }; + + + var _toggleDataSetVisibilityStatus = function (e) { + var status = null; + if (e.target.checked) { + status = "public"; + var url = $("#gdLinkURL").data("url"); + $("#gdLinkURL").removeClass("gdDisabled").removeAttr("disabled").val(url).select(); + } else { + status = "private"; + $("#gdLinkURL").addClass("gdDisabled").attr("disabled", "disabled").val(""); + } + + _saveVisibilityStatus(_currConfigurationID, status); + }; + + /** + * Called when the user checks/unchecks the "Public?" checkbox for a Data Set. This sends an Ajax request + * to the server to store the value. + */ + var _onToggleSaveDataSetVisibilityStatus = function (e) { + utils.startProcessing(); + var configurationID = $(e.target).closest("tr").data("id"); + var status = (e.target.checked) ? "public" : "private"; + _saveVisibilityStatus(configurationID, status); + }; + + var _saveVisibilityStatus = function (configurationID, status) { + var data = { + action: "saveDataSetVisibilityStatus", + configurationID: configurationID, + status: status, + time: Date.now() + }; + + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "json", + data: data, + success: function (response) { + if (response.success) { + // update the status in memory for the appropriate Data Set + var configurationID = parseInt(response.content, 10); + var newStatus = response.newStatus; + for (var i = 0; i < _dataSets.length; i++) { + if (_dataSets[i].configuration_id != configurationID) { + continue; + } + _dataSets[i].status = newStatus; + break; + } + + // ensure the Data Set table reflects this value + if ($("#gdDataSetStatus_" + configurationID).length) { + if (newStatus === "public") { + $("#gdDataSetStatus_" + configurationID).attr("checked", "checked"); + } else { + $("#gdDataSetStatus_" + configurationID).removeAttr("checked"); + } + } + } + utils.stopProcessing(); + }, + error: function () { + utils.stopProcessing(); + } + }); + }; + + var _showDemoOnlyDialog = function () { + var content = '

    This is a demo only. Please download the script to save your Data Sets.
    ' + + 'https://github.com/benkeen/generatedata

    '; + $('
    ' + content + '
    ').dialog({ + title: "Sorry!", + width: 500, + modal: true, + buttons: [{ + text: L.close, + click: function () { + $(this).dialog("close"); + } + }] + }); + }; + + var _showPermissionDeniedDialog = function (extraMsg) { + var content = C.SETTINGS.ANON_USER_PERMISSION_DENIED_MSG; + if (extraMsg) { + content = extraMsg + " " + content; + } + $('
    ' + content + '
    ').dialog({ + title: "Sorry!", + width: 500, + modal: true, + buttons: [{ + text: L.close, + click: function () { + $(this).dialog("close"); + } + }] + }); + }; + + var _initLoginDialog = function () { + $("#gdLoginDialogTabs>ul>li").each(function () { + var newTab = parseInt($(this).attr("id").replace(/^gdLoginDialogTab/, ""), 10); + $(this).bind("click", function () { + utils.selectTab({ + tabGroup: "dialogTabs", + tabIDPrefix: "gdLoginDialogTab", + newTab: newTab, + oldTab: _currLoginDialogTab + }); + _currLoginDialogTab = newTab; + + if (newTab === 1) { + $("#" + _loginModalID).dialog("option", "buttons", [{ text: L.login, click: _login }]); + $("#gdLogin_email").focus(); + } else { + $("#" + _loginModalID).dialog("option", "buttons", [{ text: L.email, click: _resetPassword }]); + $("#gdEmailReminder").focus(); + } + }); + }); + $("#" + _loginModalID).on("keyup", "#gdLogin_password", function (e) { + if (e.keyCode === $.ui.keyCode.ENTER) { + _login(); + } + }); + }; + + var _openLoginDialog = function () { + var buttons = []; + if (_currLoginDialogTab === 1) { + buttons = [{ text: L.login, click: _login }]; + } else { + buttons = [{ text: L.email, click: _resetPassword }]; + } + + $("#" + _loginModalID).dialog({ + title: L.please_login, + width: 500, + modal: true, + open: function () { + utils.insertModalSpinner({ modalID: _loginModalID }); + }, + buttons: buttons + }); + $("#gdLoginError").hide(); + $("#gdLoginDialogContent .gdProblemField").removeClass("gdProblemField"); + }; + + var _submitSettingsForm = function () { + if (C.DEMO_MODE && !C.EVENT.ACCOUNT.LOGGED_IN) { + _showDemoOnlyDialog(); + return; + } + + utils.startProcessing(); + pluginManager.savePlugins({ + success: _maybeSaveGlobalSettings, + error: function (resp) { + window.scrollTo(0, 0); + $("#settingsTabMessage").addClass("gdErrors").removeClass("gdNotify").css({ display: 'block' }) + .find("p").html("There was an error saving the plugins. Please report this on github."); + utils.stopProcessing(); + }, + + onValidationError: function (error) { + window.scrollTo(0, 0); + $("#settingsTabMessage").addClass("gdErrors").removeClass("gdNotify").css({ display: 'block' }) + .find("p").html(error); + utils.stopProcessing(); + } + }); + }; + + + // called after the user-specific settings are updated. If it's an admin, it updates the global settings as well + var _maybeSaveGlobalSettings = function (resp) { + + debugger; + + function complete () { + window.scrollTo(0, 0); + var refreshButton = ''; // TODO translate + $("#settingsTabMessage").removeClass("gdErrors").addClass("gdNotify").css({ display: 'block' }).find("p").html(resp.content + ' ' + refreshButton); + utils.stopProcessing(); + } + + if (!_accountInfo.isAnonymous && _accountInfo.accountType != "admin") { + return complete(); + } + + var data = { + action: "saveGlobalSettings", + consoleWarnings: $("#gdSettingsConsoleWarnings").prop("checked"), + consoleEventsPublish: $("#gdSettingsConsoleEventsPublish").prop("checked"), + consoleEventsSubscribe: $("#gdSettingsConsoleEventsSubscribe").prop("checked"), + consoleCoreEvents: $("#gdSettingsConsoleCoreEvents").prop("checked"), + consoleEventsDataTypePlugins: $("#consoleEventsDataTypePlugins").val(), + consoleEventsExportTypePlugins: $("#consoleEventsExportTypePlugins").val() + }; + + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "json", + data: data, + success: function (response) { + if (response.success) { + complete(); + } + }, + error: function () { + utils.stopProcessing(); + } + }); + }; + + + var _login = function () { + var email = $.trim($("#gdLogin_email").val()); + var password = $.trim($("#gdLogin_password").val()); + + // validate + utils.clearValidationErrors($("#gdLoginDialog")); + if (email === "") { + utils.addValidationErrors({ els: [$("#gdLogin_email")], error: L.validation_no_email }); + } + if (password === "") { + utils.addValidationErrors({ els: [$("#gdLogin_password")], error: L.validation_no_password }); + } + + var errors = utils.getValidationErrors(); + if (errors.length) { + utils.displayValidationErrors("#gdLoginError"); + return false; + } + + utils.playSpinner(_loginModalID); + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "json", + data: { + action: "login", + email: email, + password: password + }, + success: function (response) { + if (response.success) { + // we explicitly reload the page because the user may have custom plugins selected (added in 3.2.2) + window.location = "./"; + } else { + utils.clearValidationErrors($("#" + _loginModalID)); + utils.addValidationErrors({ els: [], error: response.content }); + utils.displayValidationErrors("#gdLoginError"); + utils.pauseSpinner(_loginModalID); + } + + }, + error: function () { + utils.pauseSpinner(_loginModalID); + } + }); + }; + + var _onLoginComplete = function () { + if (_accountInfo.accountType === "admin") { + $("#gdMainTab2,#gdMainTab3").show(); + require(["accountManager"], function (am) { + am.run(); + }); + } + $("#gdMainDialogTabs ul").show(); + + _isLoggedIn = true; + $("#gdNumRowsToGenerate").removeAttr("readonly"); + $("#gdDataSetStatusLine,#gdProcessingIcon").show(); + + // now show the links in the header + $("#gdUserAccount,#gdLogout").show(); + $("#gdLogin").hide(); + + utils.pauseSpinner(_loginModalID); + + $("#" + _loginModalID).dialog("close"); + + manager.publish({ + sender: MODULE_ID, + type: C.EVENT.ACCOUNT.LOGGED_IN, + accountInfo: _accountInfo + }); + }; + + var _logout = function () { + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "json", + data: { + action: "logout" + }, + success: function (response) { + if (response.success) { + // always redirect to login.php. If the system is set up to allow anonymous + // logins for multi-users it'll automatically redirect + window.location = "login.php"; + } + }, + error: function () { + } + }); + }; + + var _resetPassword = function () { + var email = $.trim($("#gdEmailReminder").val()); + + utils.clearValidationErrors($("#" + _loginModalID)); + if (email === "") { + utils.addValidationErrors({ els: [$("#gdEmailReminder")], error: L.validation_no_email }); + } else if (!utils.isValidEmail(email)) { + utils.addValidationErrors({ els: [$("#gdEmailReminder")], error: L.validation_invalid_email }); + } + + var errors = utils.getValidationErrors(); + if (errors.length) { + utils.displayValidationErrors("#gdResetPasswordMessage"); + return false; + } + + utils.startProcessing(); + $.ajax({ + url: "ajax.php", + type: "POST", + dataType: "json", + data: { + action: "resetPassword", + email: email + }, + success: function (json) { + var resetPasswordMessage = $("#gdResetPasswordMessage"); + if (json.success) { + resetPasswordMessage.removeClass("gdErrors").addClass("gdNotify").find("div").html("

    " + json.content + "

    "); + } else { + resetPasswordMessage.removeClass("gdNotify").addClass("gdErrors").find("div").html(""); + } + + if (resetPasswordMessage.css("display") !== "block") { + resetPasswordMessage.show("blind", null, 500); + } else { + resetPasswordMessage.effect("highlight", { color: "#ffc9c9" }, 1500); + } + + utils.stopProcessing(); + }, + error: function (json) { + utils.stopProcessing(); + } + }); + }; + + var _onClickLoadConfigurationHistory = function (e) { + e.preventDefault(); + var configurationID = $(e.target).closest("tr").data("id"); + history.getHistory(configurationID); + }; + + + // register our module + manager.registerCoreModule(MODULE_ID, { + run: _run + }); + + + /* + * The public API for this module. These are the only revealed functions for use by other modules + * that choose to include generator.js as a dependency. Even though the bulk of the functions are private, + * it still contains a couple of handy methods. + */ + return { + + /** + * Returns an ordered array of row IDs. Row IDs are unique and may be in any order with possible gaps. Each + * row is added dynamically, and may be sorted or deleted. + * @function + * @name Generator#getRowOrder + */ + getRowOrder: _getRowOrder, + + /** + * When a user re-orders or deletes some rows, the table gives the appearance of being numbered + * numerically 1-N, however the actual markup retains the original number scheme according to how it + * was first generated. This function returns the visible number of the row number, used for generating + * helpful error messages. + * @function + * @param {Number} rowNum a row number. Returns false if there's no corresponding visible row number. + * @name Generator#getVisibleRowOrderByRowNum + */ + getVisibleRowOrderByRowNum: _getVisibleRowOrderByRowNum, + + /** + * Returns an array of selected countries. + * @function + * @name Generator#getCountries + */ + getCountries: function () { + return _countries; + }, + + /** + * Returns the current export target (new window, prompt, in-page). + * @function + * @name Generator#getExportTarget + */ + getExportTarget: _getExportTarget, + + /** + * Returns the number of rows to generate currently entered. Note: this returns a STRING. + * @function + * @name Generator#getNumRowsToGenerate + */ + getNumRowsToGenerate: _getNumRowsToGenerate, + + /** + * Returns the account information for the current logged in user (or null if they're not logged in). + * @returns {null} + */ + getAccount: function () { + return _accountInfo; + } + }; }); diff --git a/resources/scripts/requireConfig.js b/resources/scripts/requireConfig.js index 7ba4879fd..52f7b7af7 100644 --- a/resources/scripts/requireConfig.js +++ b/resources/scripts/requireConfig.js @@ -2,21 +2,21 @@ require.config({ baseUrl: "", paths: { - manager: "resources/scripts/manager", + manager: "resources/scripts/manager", appStartGenerated: "cache/appStartGenerated", - pluginManager: "resources/scripts/pluginManager", - accountManager: "resources/scripts/accountManager", - settings: "resources/scripts/settings", - history: "resources/scripts/history", - constants: "resources/scripts/constants.php?", - generator: "resources/scripts/generator", - io: "resources/scripts/io", - utils: "resources/scripts/utils", - moment: "resources/scripts/libs/moment.min", - lang: "resources/scripts/lang.php?", - queue: "resources/scripts/queue", - pageInit: "resources/scripts/pageInit", - tablesorter: "resources/scripts/libs/jquery.tablesorter.widgets.min" + pluginManager: "resources/scripts/pluginManager", + accountManager: "resources/scripts/accountManager", + settings: "resources/scripts/settings", + history: "resources/scripts/history", + constants: "resources/scripts/constants.php?", + generator: "resources/scripts/generator", + io: "resources/scripts/io", + utils: "resources/scripts/utils", + moment: "resources/scripts/libs/moment.min", + lang: "resources/scripts/lang.php?", + queue: "resources/scripts/queue", + pageInit: "resources/scripts/pageInit", + tablesorter: "resources/scripts/libs/jquery.tablesorter.widgets.min" }, shim: { "tablesorter": { diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 000000000..1cbf5567d --- /dev/null +++ b/yarn.lock @@ -0,0 +1,389 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= + +"argparse@~ 0.1.11": + version "0.1.16" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-0.1.16.tgz#cfd01e0fbba3d6caed049fbd758d40f65196f57c" + integrity sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw= + dependencies: + underscore "~1.7.0" + underscore.string "~2.4.0" + +async@~0.1.22: + version "0.1.22" + resolved "https://registry.yarnpkg.com/async/-/async-0.1.22.tgz#0fc1aaa088a0e3ef0ebe2d8831bab0dcf8845061" + integrity sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE= + +async@~0.2.6: + version "0.2.10" + resolved "https://registry.yarnpkg.com/async/-/async-0.2.10.tgz#b6bbe0b0674b9d719708ca38de8c237cb526c3d1" + integrity sha1-trvgsGdLnXGXCMo43owjfLUmw9E= + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= + +clean-css@~1.1.1: + version "1.1.7" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-1.1.7.tgz#601ef9cf7642b982cb33efc9488a6444c986686e" + integrity sha1-YB75z3ZCuYLLM+/JSIpkRMmGaG4= + dependencies: + commander "2.0.x" + +coffee-script@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.3.3.tgz#150d6b4cb522894369efed6a2101c20bc7f4a4f4" + integrity sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ= + +colors@~0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-0.6.2.tgz#2423fe6678ac0c5dae8852e5d0e5be08c997abcc" + integrity sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w= + +commander@2.0.x: + version "2.0.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.0.0.tgz#d1b86f901f8b64bd941bdeadaf924530393be928" + integrity sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg= + +dateformat@1.0.2-1.2.3: + version "1.0.2-1.2.3" + resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-1.0.2-1.2.3.tgz#b0220c02de98617433b72851cf47de3df2cdbee9" + integrity sha1-sCIMAt6YYXQztyhRz0fePfLNvuk= + +decamelize@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= + +"esprima@~ 1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.0.4.tgz#9f557e08fc3b4d26ece9dd34f8fbf476b62585ad" + integrity sha1-n1V+CPw7TSbs6d00+Pv0drYlha0= + +eventemitter2@~0.4.13: + version "0.4.14" + resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" + integrity sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas= + +exit@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= + +findup-sync@~0.1.0, findup-sync@~0.1.2: + version "0.1.3" + resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.1.3.tgz#7f3e7a97b82392c653bf06589bd85190e93c3683" + integrity sha1-fz56l7gjksZTvwZYm9hRkOk8NoM= + dependencies: + glob "~3.2.9" + lodash "~2.4.1" + +getobject@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/getobject/-/getobject-0.1.0.tgz#047a449789fa160d018f5486ed91320b6ec7885c" + integrity sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw= + +glob@~3.1.21: + version "3.1.21" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.1.21.tgz#d29e0a055dea5138f4d07ed40e8982e83c2066cd" + integrity sha1-0p4KBV3qUTj00H7UDomC6DwgZs0= + dependencies: + graceful-fs "~1.2.0" + inherits "1" + minimatch "~0.2.11" + +glob@~3.2.9: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + integrity sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0= + dependencies: + inherits "2" + minimatch "0.3" + +graceful-fs@~1.2.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-1.2.3.tgz#15a4806a57547cb2d2dbf27f42e89a8c3451b364" + integrity sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q= + +grunt-cli@~0.1.9: + version "0.1.13" + resolved "https://registry.yarnpkg.com/grunt-cli/-/grunt-cli-0.1.13.tgz#e9ebc4047631f5012d922770c39378133cad10f4" + integrity sha1-6evEBHYx9QEtkidww5N4EzytEPQ= + dependencies: + findup-sync "~0.1.0" + nopt "~1.0.10" + resolve "~0.3.1" + +grunt-contrib-cssmin@~0.6.1: + version "0.6.2" + resolved "https://registry.yarnpkg.com/grunt-contrib-cssmin/-/grunt-contrib-cssmin-0.6.2.tgz#2804dc0e81f98e8a54d61eee84a1d3fe1a3af8e2" + integrity sha1-KATcDoH5jopU1h7uhKHT/ho6+OI= + dependencies: + clean-css "~1.1.1" + grunt-lib-contrib "~0.6.0" + +grunt-contrib-requirejs@~0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/grunt-contrib-requirejs/-/grunt-contrib-requirejs-0.4.4.tgz#87f2165a981e48a45d22f8cc5299d0934031b972" + integrity sha1-h/IWWpgeSKRdIvjMUpnQk0AxuXI= + dependencies: + requirejs "~2.1.0" + +grunt-contrib-uglify@~0.2.4: + version "0.2.7" + resolved "https://registry.yarnpkg.com/grunt-contrib-uglify/-/grunt-contrib-uglify-0.2.7.tgz#e6bda51e0c40a1459f6cead423c65efd725a1bf7" + integrity sha1-5r2lHgxAoUWfbOrUI8Ze/XJaG/c= + dependencies: + grunt-lib-contrib "~0.6.1" + uglify-js "~2.4.0" + +grunt-legacy-log-utils@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz#c0706b9dd9064e116f36f23fe4e6b048672c0f7e" + integrity sha1-wHBrndkGThFvNvI/5OawSGcsD34= + dependencies: + colors "~0.6.2" + lodash "~2.4.1" + underscore.string "~2.3.3" + +grunt-legacy-log@~0.1.0: + version "0.1.3" + resolved "https://registry.yarnpkg.com/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz#ec29426e803021af59029f87d2f9cd7335a05531" + integrity sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE= + dependencies: + colors "~0.6.2" + grunt-legacy-log-utils "~0.1.1" + hooker "~0.2.3" + lodash "~2.4.1" + underscore.string "~2.3.3" + +grunt-legacy-util@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz#93324884dbf7e37a9ff7c026dff451d94a9e554b" + integrity sha1-kzJIhNv343qf98Am3/RR2UqeVUs= + dependencies: + async "~0.1.22" + exit "~0.1.1" + getobject "~0.1.0" + hooker "~0.2.3" + lodash "~0.9.2" + underscore.string "~2.2.1" + which "~1.0.5" + +grunt-lib-contrib@~0.6.0, grunt-lib-contrib@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/grunt-lib-contrib/-/grunt-lib-contrib-0.6.1.tgz#3f56adb7da06e814795ee2415b0ebe5fb8903ebb" + integrity sha1-P1att9oG6BR5XuJBWw6+X7iQPrs= + dependencies: + zlib-browserify "0.0.1" + +grunt-md5@~0.1.11: + version "0.1.12" + resolved "https://registry.yarnpkg.com/grunt-md5/-/grunt-md5-0.1.12.tgz#d51399cca0fa0483bf1035d330221c9aac99ad14" + integrity sha1-1ROZzKD6BIO/EDXTMCIcmqyZrRQ= + +grunt-search@~0.1.2: + version "0.1.8" + resolved "https://registry.yarnpkg.com/grunt-search/-/grunt-search-0.1.8.tgz#f4ad6440baf8f5dfb5db6a7f2927940cd27d55e7" + integrity sha1-9K1kQLr49d+122p/KSeUDNJ9Vec= + +grunt-template@~0.2.0: + version "0.2.3" + resolved "https://registry.yarnpkg.com/grunt-template/-/grunt-template-0.2.3.tgz#24155afea78984be6730c1c773bb92de8747a370" + integrity sha1-JBVa/qeJhL5nMMHHc7uS3odHo3A= + +grunt@~0.4.1: + version "0.4.5" + resolved "https://registry.yarnpkg.com/grunt/-/grunt-0.4.5.tgz#56937cd5194324adff6d207631832a9d6ba4e7f0" + integrity sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A= + dependencies: + async "~0.1.22" + coffee-script "~1.3.3" + colors "~0.6.2" + dateformat "1.0.2-1.2.3" + eventemitter2 "~0.4.13" + exit "~0.1.1" + findup-sync "~0.1.2" + getobject "~0.1.0" + glob "~3.1.21" + grunt-legacy-log "~0.1.0" + grunt-legacy-util "~0.2.0" + hooker "~0.2.3" + iconv-lite "~0.2.11" + js-yaml "~2.0.5" + lodash "~0.9.2" + minimatch "~0.2.12" + nopt "~1.0.10" + rimraf "~2.2.8" + underscore.string "~2.2.1" + which "~1.0.5" + +hooker@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/hooker/-/hooker-0.2.3.tgz#b834f723cc4a242aa65963459df6d984c5d3d959" + integrity sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk= + +iconv-lite@~0.2.11: + version "0.2.11" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.2.11.tgz#1ce60a3a57864a292d1321ff4609ca4bb965adc8" + integrity sha1-HOYKOleGSiktEyH/RgnKS7llrcg= + +inherits@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" + integrity sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js= + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +js-yaml@~2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-2.0.5.tgz#a25ae6509999e97df278c6719da11bd0687743a8" + integrity sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g= + dependencies: + argparse "~ 0.1.11" + esprima "~ 1.0.2" + +lodash@~0.9.2: + version "0.9.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-0.9.2.tgz#8f3499c5245d346d682e5b0d3b40767e09f1a92c" + integrity sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw= + +lodash@~2.4.1: + version "2.4.2" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-2.4.2.tgz#fadd834b9683073da179b3eae6d9c0d15053f73e" + integrity sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4= + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + integrity sha1-bUUk6LlV+V1PW1iFHOId1y+06VI= + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + integrity sha1-J12O2qxPG7MyZHIInnlJyDlGmd0= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimatch@~0.2.11, minimatch@~0.2.12: + version "0.2.14" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" + integrity sha1-x054BXT2PG+aCQ6Q775u9TpqdWo= + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +nopt@~1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" + integrity sha1-bd0hvSoxQXuScn3Vhfim83YI6+4= + dependencies: + abbrev "1" + +requirejs@~2.1.0: + version "2.1.22" + resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.1.22.tgz#dd78fd2d34180c0d62c724b5b8aebc0664e0366f" + integrity sha1-3Xj9LTQYDA1ixyS1uK68BmTgNm8= + +resolve@~0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-0.3.1.tgz#34c63447c664c70598d1c9b126fc43b2a24310a4" + integrity sha1-NMY0R8ZkxwWY0cmxJvxDsqJDEKQ= + +rimraf@~2.2.8: + version "2.2.8" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.2.8.tgz#e439be2aaee327321952730f99a8929e4fc50582" + integrity sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI= + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= + +source-map@0.1.34: + version "0.1.34" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.34.tgz#a7cfe89aec7b1682c3b198d0acfb47d7d090566b" + integrity sha1-p8/omux7FoLDsZjQrPtH19CQVms= + dependencies: + amdefine ">=0.0.4" + +uglify-js@~2.4.0: + version "2.4.24" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.4.24.tgz#fad5755c1e1577658bb06ff9ab6e548c95bebd6e" + integrity sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4= + dependencies: + async "~0.2.6" + source-map "0.1.34" + uglify-to-browserify "~1.0.0" + yargs "~3.5.4" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + integrity sha1-bgkk1r2mta/jSeOabWMoUKD4grc= + +underscore.string@~2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.2.1.tgz#d7c0fa2af5d5a1a67f4253daee98132e733f0f19" + integrity sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk= + +underscore.string@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.3.3.tgz#71c08bf6b428b1133f37e78fa3a21c82f7329b0d" + integrity sha1-ccCL9rQosRM/N+ePo6Icgvcymw0= + +underscore.string@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-2.4.0.tgz#8cdd8fbac4e2d2ea1e7e2e8097c42f442280f85b" + integrity sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs= + +underscore@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.7.0.tgz#6bbaf0877500d36be34ecaa584e0db9fef035209" + integrity sha1-a7rwh3UA02vjTsqlhODbn+8DUgk= + +which@~1.0.5: + version "1.0.9" + resolved "https://registry.yarnpkg.com/which/-/which-1.0.9.tgz#460c1da0f810103d0321a9b633af9e575e64486f" + integrity sha1-RgwdoPgQED0DIam2M6+eV15kSG8= + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + integrity sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0= + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= + +yargs@~3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.5.4.tgz#d8aff8f665e94c34bd259bdebd1bfaf0ddd35361" + integrity sha1-2K/49mXpTDS9JZvevRv68N3TU2E= + dependencies: + camelcase "^1.0.2" + decamelize "^1.0.0" + window-size "0.1.0" + wordwrap "0.0.2" + +zlib-browserify@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/zlib-browserify/-/zlib-browserify-0.0.1.tgz#4fa6a45d00dbc15f318a4afa1d9afc0258e176cc" + integrity sha1-T6akXQDbwV8xikr6HZr8Aljhdsw=