<commit_before>/*global define*/
define([
	'jQuery-2.1.4.min',
	'underscore_1.3.3',
	'backbone_0.9.2',
  'text!../../../js/app/templates/fileLibrary.html'
], function ($, _, Backbone, FileLibraryTemplate) {
	'use strict';

	var FileLibraryView = Backbone.View.extend({

		tagName:  'div',

    el: '#fileLibraryView',

		template: _.template(FileLibraryTemplate),

		// The DOM events specific to an item.
		events: {
			//'click .toggle':	'toggleCompleted',
			//'dblclick label':	'edit',
			//'click .destroy':	'clear',
			//'keypress .edit':	'updateOnEnter',
			//'keydown .edit':	'revertOnEscape',
			//'blur .edit':		'close'
      'hidden.bs.modal #fileLibraryModal': 'refreshView'
		},

		// The TodoView listens for changes to its model, re-rendering. Since there's
		// a one-to-one correspondence between a **Todo** and a **TodoView** in this
		// app, we set a direct reference on the model for convenience.
		initialize: function () {
			//this.listenTo(this.model, 'change', this.render);
			//this.listenTo(this.model, 'destroy', this.remove);
			//this.listenTo(this.model, 'visible', this.toggleVisible);

		},

		// Re-render the titles of the todo item.

    render: function () {

      //debugger;
      this.$el.html(this.template);
      //global.pagesView.populateTable();
      this.populateTable();

      //debugger;

      //$('#dashboardView').hide();
      //$('#pagesView').show();

			//this.toggleVisible();
			//this.$input = this.$('.edit');
			return this;
		},

    populateTable: function() {
      //debugger;

      //Loop through each model in the collection.
      //global.postsCollection.forEach( function(model) {
      for( var i = 0; i < global.fileUploadCollection.length; i++ ) {

        try {
          //debugger;

          var model = global.fileUploadCollection.models[i];

          //Clone the example row provided in the template.
          var tempRow = global.fileLibraryView.$el.find('#fileRow').clone();

          //Clear the ID copied from the example row.
          tempRow.attr('id', '');

          //Populate the new row with data from the model.
          var fileName = model.get('fileName');
          tempRow.find('th').html(''+fileName+'');
          tempRow.find('th').find('a').attr('onclick', 'global.fileLibraryView.editPost('+i+')');

          //Add the on-click function to the Delete button.
          tempRow.find('.postCol4').find('button').attr('onclick', 'global.fileLibraryView.deleteFile(global.fileUploadCollection.models['+i+'].id)');

          //tempRow.find('.postAuthor').text(model.get('author'));
          //tempRow.find('.postCategories').text(model.get('categories').join(','));

          //var publishedDate = new Date(model.get('publishedDate'));
          //var datestr = (publishedDate.getMonth()+1)+'/'+publishedDate.getDate()+'/'+publishedDate.getFullYear();
          //tempRow.find('.postDate').text(datestr);

          //Remove the 'hidden' attribute copied from the example row.
          tempRow.show();

          //Append the new row to the DOM.
          global.fileLibraryView.$el.find('#filesTable').append(tempRow);
        } catch(err) {
          console.error('Error encountered in fileLibraryView.populateTable(). Error message:');
          console.error(err.message);

          log.push('Error encountered in fileLibraryView.populateTable(). Error message:')
          log.push(err.message)
          sendLog();
        }

      }
      //});

    },

    editPost: function(model_index) {
      debugger;

      //$('#pagesView').hide();
      //$('#pagesAddNewView').show();

      //$('#app-location').text('Pages : Edit Post');

      //Load the currently selected model into the TinyMCE state variable so that once
      //the TinyMCE editor has been loaded, it knows which model to load.
      //global.tinymce.currentModelIndex = model_index;

      //Render the Add New pages View view.
      //global.pagesAddNewView.render();

      //global.pagesAddNewView.loadPost(model_index);
    },

    uploadFile: function() {
      //debugger;

      var fileName = this.$el.find('#fileNameUpload').val();

      if( fileName == "" ) {
        this.$el.find('#successMsgUpload').text('Please enter a file name.');
        return;
      }

      var selectedFile = this.$el.find('#file_upload').get(0).files[0];

      //Create the FormData data object and append the file to it.
      var newFile = new FormData();
      newFile.append('file_upload', selectedFile); //This is the raw file that was selected

      var opts = {
        url: 'http://'+global.serverIp+':'+global.serverPort+'/api/fileupload/create',
        data: newFile,
        cache: false,
        contentType: false,
        processData: false,
        type: 'POST',
        success: function(data){
          //Dev Note: KeystoneAPI only allows file and image uploads with the file itself. Any extra metadata will have to
          //be uploaded/updated with a second call.

          //debugger;
          console.log('File upload succeeded! ID: ' + data.file_upload._id);
          log.push('File upload succeeded! ID: ' + data.file_upload._id);

          //Fill out the file information
          data.file_upload.name = global.fileLibraryView.$el.find('#fileNameUpload').val();
          data.file_upload.fileName = global.fileLibraryView.$el.find('#fileNameUpload').val();
          data.file_upload.url = 'http://'+global.serverIp+'/uploads/files/'+data.file_upload.file.filename;
          data.file_upload.fileType = data.file_upload.file.type;

          //Update the file with the information above.
          $.get('http://'+global.serverIp+':'+global.serverPort+'/api/fileupload/'+data.file_upload._id+'/update', data.file_upload, function(data) {
            //debugger;
            log.push('File information updated.');

            //Refresh the Collection.
            //global.fileUploadCollection.refreshView = true; //Set flag so view is refreshed after collection is updated.
            global.fileUploadCollection.fetch();

            //Notify successful upload via modal.
            global.fileLibraryView.$el.find('.modal-sm').find('#waitingGif').hide();
            global.fileLibraryView.$el.find('.modal-sm').find('#successMsg').show();
            //global.fileLibraryView.render();
          })
          //If the metadata update fails:
          .fail(function(data) {
            debugger;
          });
        },

        //This error function is called if the POST fails for submitting the file itself.
        error: function(err) {
          //debugger;

          global.fileLibraryView.$el.find('.modal-sm').find('#waitingGif').hide();
          global.fileLibraryView.$el.find('.modal-sm').find('#errorMsg').show();
          global.fileLibraryView.$el.find('.modal-sm').find('#errorMsg').html(
            '
The file was not uploaded to the server. This is most likely because the server does not accept the selected file TYPE.

'+
            'Here is the error message from the server:
'+
            'Server status: '+err.status+'
'+
            'Server message: '+err.statusText+'

'
          );

        }
      };

      //Execute the AJAX operation.
      jQuery.ajax(opts);
    },

    fileSelected: function() {
      //debugger;

      this.$el.find('#fileNameUploadDiv').show();
    },

    //This function is called when the modal has completed closing. It refreshes the View to make sure
    //any new uploaded files appear in the file table.
    refreshView: function() {
      //debugger;

      this.render();
    },

    deleteFile: function(id) {
      //debugger;
      $.get('http://'+global.serverIp+':'+global.serverPort+'/api/fileupload/'+id+'/remove', '', function(data) {
        //debugger;

        if( data.success == true ) {
          log.push('FileUpload object deleted successfully. ID: '+id);
          global.fileUploadCollection.refreshView = true; //Set flag so view is refreshed after collection is updated.
          global.fileUploadCollection.fetch();
          //global.fileLibraryView.render();
        } else {
          console.error('FileUpload object no deleted! ID: '+id);
          log.push('FileUpload object no deleted! ID: '+id);
          sendLog();
        }
      })
    }

	});

  //debugger;
	return FileLibraryView;
});
<commit_msg>fixing bug that populates first row in File Upload View when no entries are in the DB.<commit_after>/*global define*/
define([
	'jQuery-2.1.4.min',
	'underscore_1.3.3',
	'backbone_0.9.2',
  'text!../../../js/app/templates/fileLibrary.html'
], function ($, _, Backbone, FileLibraryTemplate) {
	'use strict';

	var FileLibraryView = Backbone.View.extend({

		tagName:  'div',

    el: '#fileLibraryView',

		template: _.template(FileLibraryTemplate),

		// The DOM events specific to an item.
		events: {
			//'click .toggle':	'toggleCompleted',
			//'dblclick label':	'edit',
			//'click .destroy':	'clear',
			//'keypress .edit':	'updateOnEnter',
			//'keydown .edit':	'revertOnEscape',
			//'blur .edit':		'close'
      'hidden.bs.modal #fileLibraryModal': 'refreshView'
		},

		// The TodoView listens for changes to its model, re-rendering. Since there's
		// a one-to-one correspondence between a **Todo** and a **TodoView** in this
		// app, we set a direct reference on the model for convenience.
		initialize: function () {
			//this.listenTo(this.model, 'change', this.render);
			//this.listenTo(this.model, 'destroy', this.remove);
			//this.listenTo(this.model, 'visible', this.toggleVisible);

		},

		// Re-render the titles of the todo item.

    render: function () {

      //debugger;
      this.$el.html(this.template);
      //global.pagesView.populateTable();
      this.populateTable();

      //debugger;

      //$('#dashboardView').hide();
      //$('#pagesView').show();

			//this.toggleVisible();
			//this.$input = this.$('.edit');
			return this;
		},

    populateTable: function() {
      //debugger;

      //Loop through each model in the collection.
      //global.postsCollection.forEach( function(model) {
      for( var i = 0; i < global.fileUploadCollection.length; i++ ) {

        try {
          //debugger;

          var model = global.fileUploadCollection.models[i];

          //Handle corner case of new install with empty DB
          if( (global.fileUploadCollection.models.length == 1) && (model.id == "") ) {
            return;
          }

          //Clone the example row provided in the template.
          var tempRow = global.fileLibraryView.$el.find('#fileRow').clone();

          //Clear the ID copied from the example row.
          tempRow.attr('id', '');

          //Populate the new row with data from the model.
          var fileName = model.get('fileName');
          tempRow.find('th').html(''+fileName+'');
          tempRow.find('th').find('a').attr('onclick', 'global.fileLibraryView.editPost('+i+')');

          //Add the on-click function to the Delete button.
          tempRow.find('.postCol4').find('button').attr('onclick', 'global.fileLibraryView.deleteFile(global.fileUploadCollection.models['+i+'].id)');

          //tempRow.find('.postAuthor').text(model.get('author'));
          //tempRow.find('.postCategories').text(model.get('categories').join(','));

          //var publishedDate = new Date(model.get('publishedDate'));
          //var datestr = (publishedDate.getMonth()+1)+'/'+publishedDate.getDate()+'/'+publishedDate.getFullYear();
          //tempRow.find('.postDate').text(datestr);

          //Remove the 'hidden' attribute copied from the example row.
          tempRow.show();

          //Append the new row to the DOM.
          global.fileLibraryView.$el.find('#filesTable').append(tempRow);
        } catch(err) {
          console.error('Error encountered in fileLibraryView.populateTable(). Error message:');
          console.error(err.message);

          log.push('Error encountered in fileLibraryView.populateTable(). Error message:')
          log.push(err.message)
          sendLog();
        }

      }
      //});

    },

    editPost: function(model_index) {
      debugger;

      //$('#pagesView').hide();
      //$('#pagesAddNewView').show();

      //$('#app-location').text('Pages : Edit Post');

      //Load the currently selected model into the TinyMCE state variable so that once
      //the TinyMCE editor has been loaded, it knows which model to load.
      //global.tinymce.currentModelIndex = model_index;

      //Render the Add New pages View view.
      //global.pagesAddNewView.render();

      //global.pagesAddNewView.loadPost(model_index);
    },

    uploadFile: function() {
      //debugger;

      var fileName = this.$el.find('#fileNameUpload').val();

      if( fileName == "" ) {
        this.$el.find('#successMsgUpload').text('Please enter a file name.');
        return;
      }

      var selectedFile = this.$el.find('#file_upload').get(0).files[0];

      //Create the FormData data object and append the file to it.
      var newFile = new FormData();
      newFile.append('file_upload', selectedFile); //This is the raw file that was selected

      var opts = {
        url: 'http://'+global.serverIp+':'+global.serverPort+'/api/fileupload/create',
        data: newFile,
        cache: false,
        contentType: false,
        processData: false,
        type: 'POST',
        success: function(data){
          //Dev Note: KeystoneAPI only allows file and image uploads with the file itself. Any extra metadata will have to
          //be uploaded/updated with a second call.

          //debugger;
          console.log('File upload succeeded! ID: ' + data.file_upload._id);
          log.push('File upload succeeded! ID: ' + data.file_upload._id);

          //Fill out the file information
          data.file_upload.name = global.fileLibraryView.$el.find('#fileNameUpload').val();
          data.file_upload.fileName = global.fileLibraryView.$el.find('#fileNameUpload').val();
          data.file_upload.url = 'http://'+global.serverIp+'/uploads/files/'+data.file_upload.file.filename;
          data.file_upload.fileType = data.file_upload.file.type;

          //Update the file with the information above.
          $.get('http://'+global.serverIp+':'+global.serverPort+'/api/fileupload/'+data.file_upload._id+'/update', data.file_upload, function(data) {
            //debugger;
            log.push('File information updated.');

            //Refresh the Collection.
            //global.fileUploadCollection.refreshView = true; //Set flag so view is refreshed after collection is updated.
            global.fileUploadCollection.fetch();

            //Notify successful upload via modal.
            global.fileLibraryView.$el.find('.modal-sm').find('#waitingGif').hide();
            global.fileLibraryView.$el.find('.modal-sm').find('#successMsg').show();
            //global.fileLibraryView.render();
          })
          //If the metadata update fails:
          .fail(function(data) {
            debugger;
          });
        },

        //This error function is called if the POST fails for submitting the file itself.
        error: function(err) {
          //debugger;

          global.fileLibraryView.$el.find('.modal-sm').find('#waitingGif').hide();
          global.fileLibraryView.$el.find('.modal-sm').find('#errorMsg').show();
          global.fileLibraryView.$el.find('.modal-sm').find('#errorMsg').html(
            '
The file was not uploaded to the server. This is most likely because the server does not accept the selected file TYPE.

'+
            'Here is the error message from the server:
'+
            'Server status: '+err.status+'
'+
            'Server message: '+err.statusText+'

'
          );

        }
      };

      //Execute the AJAX operation.
      jQuery.ajax(opts);
    },

    fileSelected: function() {
      //debugger;

      this.$el.find('#fileNameUploadDiv').show();
    },

    //This function is called when the modal has completed closing. It refreshes the View to make sure
    //any new uploaded files appear in the file table.
    refreshView: function() {
      //debugger;

      this.render();
    },

    deleteFile: function(id) {
      //debugger;
      $.get('http://'+global.serverIp+':'+global.serverPort+'/api/fileupload/'+id+'/remove', '', function(data) {
        //debugger;

        if( data.success == true ) {
          log.push('FileUpload object deleted successfully. ID: '+id);
          global.fileUploadCollection.refreshView = true; //Set flag so view is refreshed after collection is updated.
          global.fileUploadCollection.fetch();
          //global.fileLibraryView.render();
        } else {
          console.error('FileUpload object no deleted! ID: '+id);
          log.push('FileUpload object no deleted! ID: '+id);
          sendLog();
        }
      })
    }

	});

  //debugger;
	return FileLibraryView;
});
<|endoftext|>


<commit_before>/**
 * Created by maddoxw on 7/18/16.
 */

require.config({
    // baseUrl: '../app',  // development
    // baseUrl: '/',  // production
    // urlArgs: 'bust=' + (new Date()).getTime(),
    deps: ['main'],
    // waitSeconds: 10,
    paths: {
        'serversettings': 'settings.dev',  // development
        // serversettings': 'settings.prod',  // production
        'jquery': '../bower_components/jquery/dist/jquery',
        'jquery-ui': '../bower_components/jquery-ui/jquery-ui',
        'jsonix': '../bower_components/jsonix/dist/Jsonix-all',
        'WFS_2_0': '../bower_components/ogc-schemas/scripts/lib/WFS_2_0',
        'WFS_1_1_0': '../bower_components/ogc-schemas/scripts/lib/WFS_1_1_0',
        'XLink_1_0': '../bower_components/w3c-schemas/scripts/lib/XLink_1_0',
        'GML_3_1_1': '../bower_components/ogc-schemas/scripts/lib/GML_3_1_1',
        'OWS_1_0_0': '../bower_components/ogc-schemas/scripts/lib/OWS_1_0_0',
        'OWS_1_1_0': '../bower_components/ogc-schemas/scripts/lib/OWS_1_1_0',
        'Filter_2_0': '../bower_components/ogc-schemas/scripts/lib/Filter_2_0',
        'Filter_1_1_0': '../bower_components/ogc-schemas/scripts/lib/Filter_1_1_0',
        'SMIL_2_0': '../bower_components/ogc-schemas/scripts/lib/SMIL_2_0',
        'SMIL_2_0_Language': '../bower_components/ogc-schemas/scripts/lib/SMIL_2_0_Language',
        // 'ol': '../js/libs/ol3-3.19.1/ol',
        'ol': '../js/libs/ol3-3.19.1/ol-debug',
        'jsts': '../js/libs/jsts-1.2.1/jsts.min',
        'layerswitcher': '../js/libs/ol3-layerswitcher/ol3-layerswitcher',
        'shp': '../js/libs/gitmodules/shapefile-js/dist/shp',
        'domReady': '../js/libs/domReady',
        'wfs110context': 'WFS110Context',
        'wfs200context': 'WFS200Context',
        'ispointinpoly': 'geometry/ispointinpoly',
        'ispolyvalid': 'geometry/ispolyvalid',
        'doespolycoverhole': 'geometry/doespolycoverhole',
        'deg2tile': 'tools/deg2tile',
        'utils': 'utils/utils',
        'exists': 'utils/exists',
        'defaultsensors': 'utils/defaultsensors',
        'mouseprojection': 'utils/mouseprojection',
        'mouseunits': 'utils/mouseunits',
        'map': 'map',
        'messagebar': 'messagebar',
        'layertree': 'layertree',
        // 'layertree': 'layertree_old',
        'layertoolbar': 'layertoolbar',
        'toolbar': 'toolbar',
        'layerinteractor': 'layerinteractor',
        'featureeditor': 'featureeditor',
        'cameraeditor': 'cameraeditor',
        'ttemplate': 'tobjects/template',
        'tstylefunction': 'tobjects/stylefunction',
        'stemplate': 'sensors/template',
        'sstylefunction': 'sensors/stylefunction',
        'bingKey': ['bingkey', 'bingkey-sample']
    },
    shim: {
        'layerswitcher': {
            deps: ['ol'],
            exports: 'layerswitcher'
        },
        'WFS_1_1_0': {
            deps: ['OWS_1_0_0', 'Filter_1_1_0', 'GML_3_1_1']
        },
        'WFS_2_0': {
            deps: ['OWS_1_1_0', 'Filter_2_0', 'GML_3_1_1']
        }
    }
});

// require(['main'], function(main) {});
// require(['main'], function(main) {
//     main.init();
// });
// require(['domReady!'], function (doc) {
//     //This function is called once the DOM is ready,
//     //notice the value for 'domReady!' is the current
//     //document.
// });
<commit_msg>refactor<commit_after>/**
 * Created by maddoxw on 7/18/16.
 */

require.config({
    // baseUrl: '../app',  // development
    // baseUrl: '/',  // production
    // urlArgs: 'bust=' + (new Date()).getTime(),
    deps: ['main'],
    // waitSeconds: 10,
    paths: {
        'serversettings': 'settings.dev',  // development
        // serversettings': 'settings.prod',  // production
        'jquery': '../bower_components/jquery/dist/jquery',
        'jquery-ui': '../bower_components/jquery-ui/jquery-ui',
        'jsonix': '../bower_components/jsonix/dist/Jsonix-all',
        'WFS_2_0': '../bower_components/ogc-schemas/scripts/lib/WFS_2_0',
        'WFS_1_1_0': '../bower_components/ogc-schemas/scripts/lib/WFS_1_1_0',
        'XLink_1_0': '../bower_components/w3c-schemas/scripts/lib/XLink_1_0',
        'GML_3_1_1': '../bower_components/ogc-schemas/scripts/lib/GML_3_1_1',
        'OWS_1_0_0': '../bower_components/ogc-schemas/scripts/lib/OWS_1_0_0',
        'OWS_1_1_0': '../bower_components/ogc-schemas/scripts/lib/OWS_1_1_0',
        'Filter_2_0': '../bower_components/ogc-schemas/scripts/lib/Filter_2_0',
        'Filter_1_1_0': '../bower_components/ogc-schemas/scripts/lib/Filter_1_1_0',
        'SMIL_2_0': '../bower_components/ogc-schemas/scripts/lib/SMIL_2_0',
        'SMIL_2_0_Language': '../bower_components/ogc-schemas/scripts/lib/SMIL_2_0_Language',
        'ol': '../js/libs/ol3-3.19.1/ol-debug',
        'jsts': '../js/libs/jsts-1.2.1/jsts.min',
        'layerswitcher': '../js/libs/ol3-layerswitcher/ol3-layerswitcher',
        'shp': '../js/libs/gitmodules/shapefile-js/dist/shp',
        'domReady': '../js/libs/domReady',
        'wfs110context': 'WFS110Context',
        'wfs200context': 'WFS200Context',
        'ispointinpoly': 'geometry/ispointinpoly',
        'ispolyvalid': 'geometry/ispolyvalid',
        'doespolycoverhole': 'geometry/doespolycoverhole',
        'deg2tile': 'tools/deg2tile',
        'utils': 'utils/utils',
        'exists': 'utils/exists',
        'defaultsensors': 'utils/defaultsensors',
        'mouseprojection': 'utils/mouseprojection',
        'mouseunits': 'utils/mouseunits',
        'map': 'map',
        'messagebar': 'messagebar',
        'layertree': 'layertree',
        // 'layertree': 'layertree_old',
        'layertoolbar': 'layertoolbar',
        'toolbar': 'toolbar',
        'layerinteractor': 'layerinteractor',
        'featureeditor': 'featureeditor',
        'cameraeditor': 'cameraeditor',
        'ttemplate': 'tobjects/template',
        'tstylefunction': 'tobjects/stylefunction',
        'stemplate': 'sensors/template',
        'sstylefunction': 'sensors/stylefunction',
        'bingKey': ['bingkey', 'bingkey-sample']
    },
    shim: {
        'layerswitcher': {
            deps: ['ol'],
            exports: 'layerswitcher'
        },
        'WFS_1_1_0': {
            deps: ['OWS_1_0_0', 'Filter_1_1_0', 'GML_3_1_1']
        },
        'WFS_2_0': {
            deps: ['OWS_1_1_0', 'Filter_2_0', 'GML_3_1_1']
        }
    }
});

// require(['main'], function(main) {});
// require(['main'], function(main) {
//     main.init();
// });
// require(['domReady!'], function (doc) {
//     //This function is called once the DOM is ready,
//     //notice the value for 'domReady!' is the current
//     //document.
// });
<|endoftext|>


<commit_before>/**
 * Created by KIMSEONHO on 2016-08-16.
 */
const passport = require('passport'),
  express = require('express'),
  multer = require('multer'),
  bodyParser = require('body-parser'),

  multerConfig = require('./config/multer'),
  PublicController = require('./controllers/public'),
  AuthController = require('./controllers/authentication'),
  UserController = require('./controllers/user'),
  BuildCaseController = require('./controllers/build-case'),
  BizStoreController = require('./controllers/biz-store'),
  ConsultController = require('./controllers/consult')

  // ChatController = require('./controllers/chat'),
  // CommunicationController = require('./controllers/communication'),
  // StripeController = require('./controllers/stripe'),
  passportService = require('./config/passport'),   // 설정값 로딩때문에 필요함

  quoter  = require('./tests/quoter');    // test route

// Middleware to require login/auth
const requireAuth = passport.authenticate('jwt', { session: false });
const requireLogin = passport.authenticate('local', { session: false });

// 'name=file'로 들어옴(X)
// const buildCaseImageUpload = multer({ storage: multerConfig.buildCaseInfoStorage }).array('file', 11);
const buildCaseImageUpload = multer({ storage: multerConfig.buildCaseInfoStorage }).fields([
  { name: 'previewImage', maxCount: 1 }, { name: 'vrImage', maxCount: 10 }]);
const editorImageUpload = multer({ storage: multerConfig.editorImageStorage })
  .array('editorImage', 12);
var testImageUpload = multer({ dest: 'uploads/images' }).any();

module.exports = function(app) {
  // Initializing route groups
  var apiRoutes = express.Router(),
    publicRoutes = express.Router(),
    authRoutes = express.Router(),
    userRoutes = express.Router();
    buildCaseRoutes = express.Router();
    bizStoreRoutes = express.Router();
    consultRoutes = express.Router();

  // chatRoutes = express.Router(),
  // payRoutes = express.Router(),
  // communicationRoutes = express.Router();

  //=========================
  // Test Routes
  //=========================

  // Test normal route
  apiRoutes.get('/random', function(req, res) {
    res.status(200).json({ quote: quoter.getRandomOne() });
  });

  // Test protected route, 회원 id를 포함한 정보는 jwt값으로 인코딩해서 보내야 함.
  apiRoutes.get('/protected', requireAuth, function(req, res) {
    res.status(200).json({ content: 'The protected test route is functional!'});
  });

  //=========================
  // public Routes
  //=========================

  // Set public routes as subgroup/middleware to apiRoutes
  apiRoutes.use('/public', publicRoutes);

  // upload Image and return path when try to attaching device image
  publicRoutes.post('/image', PublicController.uploadEditorImage);

  // test - upload Image and return path when try to attaching device image
  publicRoutes.post('/image/test', testImageUpload, PublicController.uploadTestImage);

  //=========================
  // Auth Routes
  //=========================

  // Set auth routes as subgroup/middleware to apiRoutes
  apiRoutes.use('/auth', authRoutes);

  // Registration route
  authRoutes.post('/register', AuthController.register);

  // Login route
  authRoutes.post('/login', requireLogin, AuthController.login);

  // Password reset request route (generate/send token)
  authRoutes.post('/forgot-password', AuthController.forgotPassword);

  authRoutes.post('/reset-password/:token', AuthController.verifyToken);

  //=========================
  // Member Routes
  //=========================

  // Set user routes as a subgroup/middleware to apiRoutes
  apiRoutes.use('/user', userRoutes);

  // View public user profile route
  userRoutes.get('/:memberIdx', requireAuth, UserController.viewProfile);

  // Update user profile route
  userRoutes.put('/:memberIdx', requireAuth, UserController.updateProfile, requireLogin, AuthController.login);

  // View business user profile route
  userRoutes.get('/biz/:memberIdx', requireAuth, UserController.viewBizProfile);

  // update business user profile route
  userRoutes.put('/biz/:memberIdx', requireAuth, UserController.updateBizProfile);

  //=========================
  // Build Case Routes
  //=========================

  // Set BuildCase routes as a subgroup/middleware to apiRoutes
  apiRoutes.use('/build-case', buildCaseRoutes);

  // View Build Case List from authenticated user(must get query(?pageSize={}&pageStartIndex={}) param)
  buildCaseRoutes.get('/', BuildCaseController.viewBuildCaseList);

  // create new Build Case Info from authenticated user
  // buildCaseRoutes.post('/', requireAuth,  buildCaseImageUpload, BuildCaseController.createBuildCase);

  // create new Build Case Info from authenticated user
  buildCaseRoutes.post('/', requireAuth,  buildCaseImageUpload, BuildCaseController.createBuildCaseAndVRPano);

  // update Build Case Info from authenticated user
  buildCaseRoutes.put('/:buildCaseIdx', requireAuth, buildCaseImageUpload, BuildCaseController.updateBuildCase);

  // delete Build Case Info from authenticated user
  buildCaseRoutes.delete('/:buildCaseIdx', requireAuth, BuildCaseController.deleteBuildCase);

  // search Build Case Info (must get query(?query={}) param)
  buildCaseRoutes.get('/search', BuildCaseController.searchBuildCase);

  //=========================
  // Biz Store Route - 업체 목록 조회
  //=========================

  // Set chat routes as a subgroup/middleware to apiRoutes
  apiRoutes.use('/biz-store', bizStoreRoutes);

  // View business user profile list route(must get query(?pageSize={}&pageStartIndex={}) param)
  bizStoreRoutes.get('/', BizStoreController.viewBizProfileList);

  // View business user profile to customer route
  bizStoreRoutes.get('/:memberIdx', BizStoreController.viewBizProfile);

  //=========================
  // Payment Routes
  //=========================
  // apiRoutes.use('/pay', payRoutes);

  // Webhook endpoint for Stripe
  // payRoutes.post('/webhook-notify', StripeController.webhook);

  // Create customer and subscription
  // payRoutes.post('/customer', requireAuth, StripeController.createSubscription);

  // Update customer object and billing information
  // payRoutes.put('/customer', requireAuth, StripeController.updateCustomerBillingInfo);

  // Delete subscription from customer
  // payRoutes.delete('/subscription', requireAuth, StripeController.deleteSubscription);

  // Upgrade or downgrade subscription
  // payRoutes.put('/subscription', requireAuth, StripeController.changeSubscription);

  // Fetch customer information
  // payRoutes.get('/customer', requireAuth, StripeController.getCustomer);

  //=========================
  // Communication Routes
  //=========================
  // apiRoutes.use('/communication', communicationRoutes);

  // Send email from contact form
  // communicationRoutes.post('/contact', CommunicationController.sendContactForm);
  //=========================
  // Consult Routes
  //=========================
  apiRoutes.use('/consult', consultRoutes);

  // insert consulting information
  consultRoutes.post('/', requireAuth, ConsultController.consultingCounsel);

  // consulting information list
  consultRoutes.get('/', ConsultController.consultingList);

  // consulting information list
  consultRoutes.get('/my/', requireAuth, ConsultController.consultingMyList);

  // consulting information detail
  consultRoutes.get('/:consultDataIdx', requireAuth, ConsultController.consultingDetail);

  // modify consulting information
  consultRoutes.put('/:consultDataIdx', requireAuth, ConsultController.consultingModify);

  // Set url for API group routes
  app.use('/api', apiRoutes);
};
<commit_msg>server<commit_after>/**
 * Created by KIMSEONHO on 2016-08-16.
 */
const passport = require('passport'),
  express = require('express'),
  multer = require('multer'),
  bodyParser = require('body-parser'),

  multerConfig = require('./config/multer'),
  PublicController = require('./controllers/public'),
  AuthController = require('./controllers/authentication'),
  UserController = require('./controllers/user'),
  ConsultController = require('./controllers/consult'),
  BuildCaseController = require('./controllers/build-case'),
  BizStoreController = require('./controllers/biz-store'),

  // ChatController = require('./controllers/chat'),
  // CommunicationController = require('./controllers/communication'),
  // StripeController = require('./controllers/stripe'),
  passportService = require('./config/passport'),   // 설정값 로딩때문에 필요함

  quoter  = require('./tests/quoter');    // test route

// Middleware to require login/auth
const requireAuth = passport.authenticate('jwt', { session: false });
const requireLogin = passport.authenticate('local', { session: false });

const buildCaseImageUpload = multer({ storage: multerConfig.buildCaseInfoStorage }).fields([
  { name: 'previewImage', maxCount: 1 }, { name: 'vrImage', maxCount: 15 }]);
const editorImageUpload = multer({ storage: multerConfig.editorImageStorage })
  .array('editorImage', 12);
var testImageUpload = multer({ dest: 'uploads/images' }).any();

module.exports = function(app) {
  // Initializing route groups
  var apiRoutes = express.Router(),
    publicRoutes = express.Router(),
    authRoutes = express.Router(),
    userRoutes = express.Router();
  consultRoutes = express.Router(),
    buildCaseRoutes = express.Router();
  bizStoreRoutes = express.Router();

  // chatRoutes = express.Router(),
  // payRoutes = express.Router(),
  // communicationRoutes = express.Router();

  //=========================
  // Test Routes
  //=========================

  // Test normal route
  apiRoutes.get('/random', function(req, res) {
    res.status(200).json({ quote: quoter.getRandomOne() });
  });

  // Test protected route, 회원 id를 포함한 정보는 jwt값으로 인코딩해서 보내야 함.
  apiRoutes.get('/protected', requireAuth, function(req, res) {
    res.status(200).json({ content: 'The protected test route is functional!'});
  });

  //=========================
  // public Routes
  //=========================

  // Set public routes as subgroup/middleware to apiRoutes
  apiRoutes.use('/public', publicRoutes);

  // upload Image and return path when try to attaching device image
  publicRoutes.post('/image', PublicController.uploadEditorImage);

  // test - upload Image and return path when try to attaching device image
  publicRoutes.post('/image/test', testImageUpload, PublicController.uploadTestImage);

  //=========================
  // Auth Routes
  //=========================

  // Set auth routes as subgroup/middleware to apiRoutes
  apiRoutes.use('/auth', authRoutes);

  // Registration route
  authRoutes.post('/register', AuthController.register);

  // Login route
  authRoutes.post('/login', requireLogin, AuthController.login);

  // Password reset request route (generate/send token)
  authRoutes.post('/forgot-password', AuthController.forgotPassword);

  authRoutes.post('/reset-password/:token', AuthController.verifyToken);

  //=========================
  // Member Routes
  //=========================

  // Set user routes as a subgroup/middleware to apiRoutes
  apiRoutes.use('/user', userRoutes);

  // View public user profile route
  userRoutes.get('/:memberIdx', requireAuth, UserController.viewProfile);

  // Update user profile route
  userRoutes.put('/:memberIdx', requireAuth, UserController.updateProfile, requireLogin, AuthController.login);

  // View business user profile route
  userRoutes.get('/biz/:memberIdx', requireAuth, UserController.viewBizProfile);

  // update business user profile route
  userRoutes.put('/biz/:memberIdx', requireAuth, UserController.updateBizProfile);

  //=========================
  // Build Case Routes
  //=========================

  // Set BuildCase routes as a subgroup/middleware to apiRoutes
  apiRoutes.use('/build-case', buildCaseRoutes);

  // View Build Case List from authenticated user(must get query(?pageSize={}&pageStartIndex={}) param)
  buildCaseRoutes.get('/', BuildCaseController.viewBuildCaseList);

  // create new Build Case Info from authenticated user
  buildCaseRoutes.post('/', requireAuth,  buildCaseImageUpload, BuildCaseController.createBuildCase);

  // update Build Case Info from authenticated user
  buildCaseRoutes.put('/:buildCaseIdx', requireAuth, buildCaseImageUpload, BuildCaseController.updateBuildCase);

  // delete Build Case Info from authenticated user
  buildCaseRoutes.delete('/:buildCaseIdx', requireAuth, BuildCaseController.deleteBuildCase);

  // search Build Case Info (must get query(?query={}) param)
  buildCaseRoutes.get('/search', BuildCaseController.searchBuildCase);

  //=========================
  // Biz Store Route - 업체 목록 조회
  //=========================

  // Set chat routes as a subgroup/middleware to apiRoutes
  apiRoutes.use('/biz-store', bizStoreRoutes);

  // View business user profile list route(must get query(?pageSize={}&pageStartIndex={}) param)
  bizStoreRoutes.get('/', BizStoreController.viewBizProfileList);

  // View business user profile to customer route
  bizStoreRoutes.get('/:memberIdx', BizStoreController.viewBizProfile);

  //=========================
  // Payment Routes
  //=========================
  // apiRoutes.use('/pay', payRoutes);

  // Webhook endpoint for Stripe
  // payRoutes.post('/webhook-notify', StripeController.webhook);

  // Create customer and subscription
  // payRoutes.post('/customer', requireAuth, StripeController.createSubscription);

  // Update customer object and billing information
  // payRoutes.put('/customer', requireAuth, StripeController.updateCustomerBillingInfo);

  // Delete subscription from customer
  // payRoutes.delete('/subscription', requireAuth, StripeController.deleteSubscription);

  // Upgrade or downgrade subscription
  // payRoutes.put('/subscription', requireAuth, StripeController.changeSubscription);

  // Fetch customer information
  // payRoutes.get('/customer', requireAuth, StripeController.getCustomer);

  //=========================
  // Communication Routes
  //=========================
  // apiRoutes.use('/communication', communicationRoutes);

  // Send email from contact form
  // communicationRoutes.post('/contact', CommunicationController.sendContactForm);

  //=========================
  // Consult Routes
  //=========================
  apiRoutes.use('/consult', consultRoutes);

  // insert consulting information
  consultRoutes.post('/', requireAuth, ConsultController.consultingCounsel);

  // consulting information list
  consultRoutes.get('/', ConsultController.consultingList);

  // consulting information list
  consultRoutes.get('/my/', requireAuth, ConsultController.consultingMyList);

  // consulting information detail
  consultRoutes.get('/:consultDataIdx', requireAuth, ConsultController.consultingDetail);

  // modify consulting information
  consultRoutes.put('/:consultDataIdx', requireAuth, ConsultController.consultingModify);

  // Set url for API group routes
  app.use('/api', apiRoutes);
};
<|endoftext|>


<commit_before>import $ from 'jquery'
import DateAlignedGraph from '../../../app/jsx/graphs/DateAlignedGraph'
import 'translations/_core_en'

QUnit.module('DateAlignedGraph', {
  setup() {
    this.$el = $('<div/>')
  }
})

const tolerantEqual = function(actual, expected, tolerance, message) {
  if (tolerance == null) {
    tolerance = 0.001
  }
  if (message == null) {
    message = `expected ${actual} to be within ${tolerance} of ${expected}`
  }
  ok(Math.abs(actual - expected) < tolerance, message)
}

const width = 200
const expectedX = function(index, days) {
  const indent = width / (days + 1) / 1.2 / 2
  const spacing = (width - 2 * indent) / days
  return indent + index * spacing
}

test('dateX', function() {
  const startDate = new Date(2012, 0, 1)
  const endDate = startDate.clone().addDays(10)
  const examples = [
    {date: startDate, expected: expectedX(0, 10)},
    {date: startDate.clone().addDays(3), expected: expectedX(3, 10)},
    {date: startDate.clone().addDays(7), expected: expectedX(7, 10)},
    {date: endDate, expected: expectedX(10, 10)}
  ]

  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  for (const example of examples) {
    tolerantEqual(graph.dateX(example.date), example.expected)
  }
})

test('dateX: values out of range', function() {
  const startDate = new Date(2012, 0, 1) // Sunday
  const endDate = startDate.clone().addDays(5) // Monday
  const graph = new DateAlignedGraph(this.$el, {
    margin: 10,
    padding: 5,
    width,
    height: 100,
    startDate,
    endDate
  })

  const leftOutOfBoundsDate = startDate.clone().addDays(-1)
  const rightOutOfBoundsDate = endDate.clone().addDays(1)
  equal(10, graph.dateX(leftOutOfBoundsDate))
  equal(true, graph.clippedDate)
  graph.clippedDate = false

  equal(210, graph.dateX(rightOutOfBoundsDate))
  equal(true, graph.clippedDate)
})

test('drawDateAxis', function() {
  const startDate = new Date(2016, 1, 28) // Sunday, Feb 28, 2016 (0-based month)
  const endDate = startDate.clone().addDays(4)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })

  // draw the axis
  const labelSpy = sinon.spy(graph, 'dateLabel')
  graph.drawDateAxis()

  // should draw day labels on each day, but month labels only
  // if it doesn't match the previous month
  const labels = [
    [expectedX(0, 4), 100, 'Feb 28'],
    [expectedX(1, 4), 100, 29],
    [expectedX(2, 4), 100, 'Mar 1'],
    [expectedX(3, 4), 100, 2],
    [expectedX(4, 4), 100, 3]
  ]
  equal(labelSpy.callCount, labels.length)
  __range__(0, labels.length, false).forEach(
    i => deepEqual(labelSpy.args[i], labels[i])
  )
})

test('drawDateAxis: spanning months', function() {
  const startDate = new Date(2016, 1, 28)
  const endDate = startDate.clone().addDays(45)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })

  // draw the axis
  const labelSpy = sinon.spy(graph, 'dateLabel')
  graph.drawDateAxis()

  // both months should have labels, but the second should not include the year
  const labels = [
    [expectedX(2, 7), 100, 'Mar 2016'],
    [expectedX(6, 7), 100, 'Apr']
  ]
  equal(labelSpy.callCount, labels.length)
  __range__(0, labels.length, false).forEach(
    i => deepEqual(labelSpy.args[i], labels[i])
  )
})

test('drawDateAxis: spanning years', function() {
  const startDate = new Date(2015, 10, 25)
  const endDate = startDate.clone().addDays(45)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })

  // draw the axis
  const labelSpy = sinon.spy(graph, 'dateLabel')
  graph.drawDateAxis()

  // both months should have month labels with years
  const labels = [[expectedX(2, 6), 100, 'Dec 2015'], [expectedX(6, 6), 100, 'Jan 2016']]
  equal(labelSpy.callCount, labels.length)
  __range__(0, labels.length, false).map(i => deepEqual(labelSpy.args[i], labels[i]))
})

test('binDateText, day binner', function() {
  const startDate = new Date(2015, 0, 1)
  const endDate = startDate.clone().addDays(5)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  equal(graph.binDateText({date: startDate}), 'Jan 1, 2015')
  equal(graph.binDateText({date: graph.binner.nextTick(startDate)}), 'Jan 2, 2015')
})

test('binDateText, week binner', function() {
  const startDate = new Date(2015, 0, 1)
  const endDate = startDate.clone().addDays(30)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  equal(graph.binDateText({date: startDate}), 'Jan 1 - Jan 7, 2015')
  equal(graph.binDateText({date: graph.binner.nextTick(startDate)}), 'Jan 8 - Jan 14, 2015')
})

test('binDateText, week binner, spanning years', function() {
  const startDate = new Date(2015, 11, 28)
  const endDate = startDate.clone().addDays(30)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  equal(graph.binDateText({date: startDate}), 'Dec 28, 2015 - Jan 3, 2016')
  equal(graph.binDateText({date: graph.binner.nextTick(startDate)}), 'Jan 4 - Jan 10, 2016')
})

test('binDateText, month binner', function() {
  const startDate = new Date(2015, 3, 15)
  const endDate = startDate.clone().addDays(500)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  equal(graph.binDateText({date: startDate}), 'Apr 2015')
  equal(graph.binDateText({date: graph.binner.nextTick(startDate)}), 'May 2015')
})

function __range__(left, right, inclusive) {
  const range = []
  const ascending = left < right
  const end = !inclusive ? right : ascending ? right + 1 : right - 1
  for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
    range.push(i)
  }
  return range
}
<commit_msg>use core translations in JSON format in test<commit_after>import $ from 'jquery'
import DateAlignedGraph from '../../../app/jsx/graphs/DateAlignedGraph'
import coreTranslations from 'translations/en.json'
import {useTranslations} from '@canvas/i18n'

QUnit.module('DateAlignedGraph', {
  setup() {
    useTranslations('en', coreTranslations)
    this.$el = $('<div/>')
  }
})

const tolerantEqual = function(actual, expected, tolerance, message) {
  if (tolerance == null) {
    tolerance = 0.001
  }
  if (message == null) {
    message = `expected ${actual} to be within ${tolerance} of ${expected}`
  }
  ok(Math.abs(actual - expected) < tolerance, message)
}

const width = 200
const expectedX = function(index, days) {
  const indent = width / (days + 1) / 1.2 / 2
  const spacing = (width - 2 * indent) / days
  return indent + index * spacing
}

test('dateX', function() {
  const startDate = new Date(2012, 0, 1)
  const endDate = startDate.clone().addDays(10)
  const examples = [
    {date: startDate, expected: expectedX(0, 10)},
    {date: startDate.clone().addDays(3), expected: expectedX(3, 10)},
    {date: startDate.clone().addDays(7), expected: expectedX(7, 10)},
    {date: endDate, expected: expectedX(10, 10)}
  ]

  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  for (const example of examples) {
    tolerantEqual(graph.dateX(example.date), example.expected)
  }
})

test('dateX: values out of range', function() {
  const startDate = new Date(2012, 0, 1) // Sunday
  const endDate = startDate.clone().addDays(5) // Monday
  const graph = new DateAlignedGraph(this.$el, {
    margin: 10,
    padding: 5,
    width,
    height: 100,
    startDate,
    endDate
  })

  const leftOutOfBoundsDate = startDate.clone().addDays(-1)
  const rightOutOfBoundsDate = endDate.clone().addDays(1)
  equal(10, graph.dateX(leftOutOfBoundsDate))
  equal(true, graph.clippedDate)
  graph.clippedDate = false

  equal(210, graph.dateX(rightOutOfBoundsDate))
  equal(true, graph.clippedDate)
})

test('drawDateAxis', function() {
  const startDate = new Date(2016, 1, 28) // Sunday, Feb 28, 2016 (0-based month)
  const endDate = startDate.clone().addDays(4)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })

  // draw the axis
  const labelSpy = sinon.spy(graph, 'dateLabel')
  graph.drawDateAxis()

  // should draw day labels on each day, but month labels only
  // if it doesn't match the previous month
  const labels = [
    [expectedX(0, 4), 100, 'Feb 28'],
    [expectedX(1, 4), 100, 29],
    [expectedX(2, 4), 100, 'Mar 1'],
    [expectedX(3, 4), 100, 2],
    [expectedX(4, 4), 100, 3]
  ]
  equal(labelSpy.callCount, labels.length)
  __range__(0, labels.length, false).forEach(
    i => deepEqual(labelSpy.args[i], labels[i])
  )
})

test('drawDateAxis: spanning months', function() {
  const startDate = new Date(2016, 1, 28)
  const endDate = startDate.clone().addDays(45)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })

  // draw the axis
  const labelSpy = sinon.spy(graph, 'dateLabel')
  graph.drawDateAxis()

  // both months should have labels, but the second should not include the year
  const labels = [
    [expectedX(2, 7), 100, 'Mar 2016'],
    [expectedX(6, 7), 100, 'Apr']
  ]
  equal(labelSpy.callCount, labels.length)
  __range__(0, labels.length, false).forEach(
    i => deepEqual(labelSpy.args[i], labels[i])
  )
})

test('drawDateAxis: spanning years', function() {
  const startDate = new Date(2015, 10, 25)
  const endDate = startDate.clone().addDays(45)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })

  // draw the axis
  const labelSpy = sinon.spy(graph, 'dateLabel')
  graph.drawDateAxis()

  // both months should have month labels with years
  const labels = [[expectedX(2, 6), 100, 'Dec 2015'], [expectedX(6, 6), 100, 'Jan 2016']]
  equal(labelSpy.callCount, labels.length)
  __range__(0, labels.length, false).map(i => deepEqual(labelSpy.args[i], labels[i]))
})

test('binDateText, day binner', function() {
  const startDate = new Date(2015, 0, 1)
  const endDate = startDate.clone().addDays(5)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  equal(graph.binDateText({date: startDate}), 'Jan 1, 2015')
  equal(graph.binDateText({date: graph.binner.nextTick(startDate)}), 'Jan 2, 2015')
})

test('binDateText, week binner', function() {
  const startDate = new Date(2015, 0, 1)
  const endDate = startDate.clone().addDays(30)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  equal(graph.binDateText({date: startDate}), 'Jan 1 - Jan 7, 2015')
  equal(graph.binDateText({date: graph.binner.nextTick(startDate)}), 'Jan 8 - Jan 14, 2015')
})

test('binDateText, week binner, spanning years', function() {
  const startDate = new Date(2015, 11, 28)
  const endDate = startDate.clone().addDays(30)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  equal(graph.binDateText({date: startDate}), 'Dec 28, 2015 - Jan 3, 2016')
  equal(graph.binDateText({date: graph.binner.nextTick(startDate)}), 'Jan 4 - Jan 10, 2016')
})

test('binDateText, month binner', function() {
  const startDate = new Date(2015, 3, 15)
  const endDate = startDate.clone().addDays(500)
  const graph = new DateAlignedGraph(this.$el, {
    margin: 0,
    padding: 0,
    width,
    height: 100,
    startDate,
    endDate
  })
  equal(graph.binDateText({date: startDate}), 'Apr 2015')
  equal(graph.binDateText({date: graph.binner.nextTick(startDate)}), 'May 2015')
})

function __range__(left, right, inclusive) {
  const range = []
  const ascending = left < right
  const end = !inclusive ? right : ascending ? right + 1 : right - 1
  for (let i = left; ascending ? i < end : i > end; ascending ? i++ : i--) {
    range.push(i)
  }
  return range
}
<|endoftext|>


<commit_before>/*
 * This will write to an actual instance of signalfuse. It responds to
 * the 'stats' and 'flush' commands.
 *
 * On stats it will return the following stats:
 *
 *  - TODO
 *
 * On flush it will write out messages to signalfuse with multidimensional tagging.
 * Configuration includes::
 *
 *  {
 *    ...
 *    debug: true,
 *    signalfuse: {
 *      host: "",
 *      token: "",
 *      globalPrefix: "",
 *      useMultiKey: true
 *    }
 *  }
 *
 */

var http = require('https');
var util = require('util');

var l; // the logger
var debug;

// ---------------------------------------------------------------------------
// HELPER functions
// ---------------------------------------------------------------------------
function buildStat(name, value, tags) {
  return {
    metric: name,
    value: value,
    dimensions: tags
  }
}

// ---------------------------------------------------------------------------
// The actual BACKEND
// ---------------------------------------------------------------------------
function SignalFuseBackend(startup_time, config, emitter) {
  var c = config.signalfuse || {};

  this.sfxConfig = c;
  this.sfxConfig.host = this.sfxConfig.host || "";
  this.sfxConfig.token = this.sfxConfig.token || "";

  if(this.sfxConfig.dryrun == undefined)
    this.sfxConfig.dryrun = false;

  if(this.sfxConfig.useMultiKey == undefined)
    this.sfxConfig.useMultiKey = true;

  // add the '.' so that we can just jam keys onto the end
  if(this.sfxConfig.globalPrefix == "" || this.sfxConfig.globalPrefix === undefined) {
    this.sfxConfig.globalPrefix = "";
  } else {
    this.sfxConfig.globalPrefix += '.';
  }

  this.sfxConfig.http = http;
  this.sfxConfig.onComplete = this.onComplete;
  this.sfxConfig.post = this.post;

  var self = this;
  emitter.on('flush', function(timestamp, metrics) {
    self.flush(timestamp, metrics, self.post);
  });
  emitter.on('status', function(callback) {
    self.status(callback);
  });
}

SignalFuseBackend.prototype.getConfig = function() {
  return this.sfxConfig;
}

SignalFuseBackend.prototype.onComplete = function(rsp) {
  if(rsp.statusCode != 200) {
    rsp.setEncoding('utf8');
    l.log('Post to signalfx FAILED ' + rsp.statusCode + " " + rsp.statusMessage);
    rsp.on('data', function(chunk) {
      l.log(chunk);
    });
  } else {
    l.debug("Finished Flush to signalfx: " + rsp.statusCode + " " + rsp.statusMessage);
  }
}

//
// Parses the key into its metric name and actual tags
// The expected format is a JSON list of tuples: (key, value)
// For example::
//
//    [
//        [
//            "metric_name",
//            "myname"
//        ],
//        [
//            "sometag",
//            "tagsval"
//        ]
//    ]
//
// The name 'metric_name' is reserved to mean exactly what it says
SignalFuseBackend.prototype.parseMultiKey = function(stringKey) {
  var parsed = undefined
  try {
    parsed = JSON.parse(stringKey);
  } catch(err) {
    l.debug("Failed to parse '" + stringKey + "' into valid JSON");
    return undefined
  }

  var mapVersion = {};
  parsed.forEach(function(item) {
    mapVersion[item[0]] = item[1];
  });
  mname = mapVersion['metric_name'];
  delete mapVersion['metric_name'];
  return {metric_name: mname, tags: mapVersion};
}

//
// This is intended to split the key itself into its tags and name
// some rules::
//
//    - '/' are replaced with '-'
//    - ' ' spaces (incl mutlitple) are replaced with '_'
//    - characters not in: [a-zA-Z], [0-9], [-_=.] are removed
//
// then the key is split on its '.' and the first is start
// of the metric's name. The rest are evaluated as such:
//
//    - if they contain an '=' they are a tag
//    - otherwise they are part of the name
//
// UNLESS the 'useMultiKey' config value is enabled. Then it is parsed
// according to those rules. See 'parseMultiKey'
//
SignalFuseBackend.prototype.parseKey = function(rawkey) {
  var metricName = "";
  var tags = {}

  if(this.sfxConfig.useMultiKey == true) {
    var parsed = this.parseMultiKey(rawkey);
    if(parsed != undefined) {
      metricName = parsed['metric_name'];
      tags = parsed['tags'];
    }
  }

  if(metricName == "") {
    var cleanerKey = rawkey.replace(/\s+/g, '_')
                           .replace(/\//g, '-')
                           .replace(/[^a-zA-Z_\-0-9=\.]/g, '');

    parts = cleanerKey.split('.');
    metricParts = [parts[0]];
    if(metricParts[0].indexOf('=') >= 0) {
      return {}; // can't start with an '=' in it
    }

    for(i = 1; i < parts.length; i++) {
      if(parts[i].indexOf('=') >= 0) {
        var tagParts = parts[i].split('=');
        tags[tagParts[0]] = tagParts[1];
      } else {
        metricParts.push(parts[i]);
      }
    }

    metricName = metricParts.join('.');
  }

  return {metricName: metricName, tags: tags};
}

//
// handles transforming metrics of shape::
//
//  { metricname: { subkey: #, subkey: # ..}, .. }
//
// into discrete metrics, that are metricname.subkey = #
//
SignalFuseBackend.prototype.transformTimerData = function(timerData) {
  l.debug('Starting to process ' + Object.keys(timerData).length + ' timers');

  var globalPrefix = this.sfxConfig.globalPrefix;
  var resultingStats = [];

  for(rawKey in timerData) {
    if(timerData.hasOwnProperty(rawKey)) {
      var metricParts = this.parseKey(rawKey);
      var keyName = metricParts['metricName'];
      var tags = metricParts['tags'];
      tags['type'] = 'timerdata';

      for(subKey in timerData[rawKey]) {
        if(timerData[rawKey].hasOwnProperty(subKey)) {
          var val = timerData[rawKey][subKey];
          var fqMetricName = globalPrefix + [keyName, subKey].join('.');
          resultingStats.push(buildStat(fqMetricName, val, tags));
        }
      }
    }
  }

  return resultingStats;
}

//
// handles transforming metrics of this shape::
//
//   { 'metricname': [ #, #, #..], ... }
//
// turns them into discrete events
//
SignalFuseBackend.prototype.transformTimers = function(timers) {
  l.debug('Starting to process ' + Object.keys(timers).length + ' timers');

  var globalPrefix = this.sfxConfig.globalPrefix;
  var resultingStats = [];

  for(rawKey in timers) {
    if(timers.hasOwnProperty(rawKey)) {
      var metricParts = this.parseKey(rawKey);
      var keyName = metricParts['metricName'];
      var tags = metricParts['tags'];
      tags['type'] = 'timer';

      var fqMetricName = globalPrefix + keyName;
      var events = timers[rawKey];
      for(var i = 0; i < events.length; i++) {
        resultingStats.push(buildStat(fqMetricName, events[i], tags));
      }
    }
  }

  l.debug("Finished transforming metrics into " +
          resultingStats.length + " signalfuse metrics");

  return resultingStats;
}

//
// This will transform gauges, counters, and sets
// naming goes: globalPrefix.[metricname]
// also the tags will get a 'type: type' added
//
SignalFuseBackend.prototype.transformMetrics = function(metrics, type) {
  l.debug('Starting to process ' + Object.keys(metrics).length + ' ' + type);

  var globalPrefix = this.sfxConfig.globalPrefix;
  var resultingStats = [];

  for(rawKey in metrics) {
    if(metrics.hasOwnProperty(rawKey)) {
      var value = metrics[rawKey];

      var metricParts = this.parseKey(rawKey);
      var keyName = metricParts['metricName'];
      var tags = metricParts['tags'];
      tags['type'] = type;

      var fqMetricName = globalPrefix + keyName;
      resultingStats.push(buildStat(fqMetricName, value, tags));
    }
  }

  l.debug("Finished transforming " + type + " into " +
          resultingStats.length + " signalfuse metrics");

  return resultingStats;
};

//
// Takes each of the different components, transformts them, then ships
// ships them off
//
SignalFuseBackend.prototype.flush = function(timestamp, metric, postcb) {
  l.debug(timestamp + ' starting a flush');

  var resultingMetrics = [];
  var partial = [];

  partial = this.transformMetrics(metric.counters, 'counter');
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformMetrics(metric.sets, 'set');
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformMetrics(metric.gauges, 'gauge');
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformMetrics(metric.counter_rates, 'rate');
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformTimers(metric.timers);
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformTimerData(metric.timer_data);
  Array.prototype.push.apply(resultingMetrics, partial);

  postcb(resultingMetrics, this.sfxConfig);
}

SignalFuseBackend.prototype.post = function(metricList, sfxConfig) {
  l.debug('Sending ' + metricList.length + ' metrics to signal fuse');

  var postOptions = {
    host: sfxConfig.host,
    path: '/v2/datapoint',
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-SF-Token': sfxConfig.token
    }
  };

  var out = {gauge: metricList};
  var postData = JSON.stringify(out);

  l.debug('Payload will be: ' + util.inspect(out, {depth:5, colors:true}));

  if(!sfxConfig.dryrun) {
    var req = sfxConfig.http.request(postOptions, sfxConfig.onComplete);
    req.write(postData);
    req.end();
  } else {
    l.log('Not sending because of dryrun flag. request:');
    l.log(postData);
  }

  l.debug('Finished sending data to signalfx');
}

SignalFuseBackend.prototype.status = function(callback) {
  callback('not yet implemented', 'signalfuse');
}

// ---------------------------------------------------------------------------
// export a build method
// ---------------------------------------------------------------------------
exports.init = function(startup_time, config, events, logger) {
  debug = config.debug;
  l = logger;

  var instance = new SignalFuseBackend(startup_time, config, events);
  return instance;
};

<commit_msg>handle errors explicitly in the signalfuse backend<commit_after>/*
 * This will write to an actual instance of signalfuse. It responds to
 * the 'stats' and 'flush' commands.
 *
 * On stats it will return the following stats:
 *
 *  - TODO
 *
 * On flush it will write out messages to signalfuse with multidimensional tagging.
 * Configuration includes::
 *
 *  {
 *    ...
 *    debug: true,
 *    signalfuse: {
 *      host: "",
 *      token: "",
 *      globalPrefix: "",
 *      useMultiKey: true
 *    }
 *  }
 *
 */

var http = require('https');
var util = require('util');

var l; // the logger
var debug;

// ---------------------------------------------------------------------------
// HELPER functions
// ---------------------------------------------------------------------------
function buildStat(name, value, tags) {
  return {
    metric: name,
    value: value,
    dimensions: tags
  }
}

// ---------------------------------------------------------------------------
// The actual BACKEND
// ---------------------------------------------------------------------------
function SignalFuseBackend(startup_time, config, emitter) {
  var c = config.signalfuse || {};

  this.sfxConfig = c;
  this.sfxConfig.host = this.sfxConfig.host || "";
  this.sfxConfig.token = this.sfxConfig.token || "";

  if(this.sfxConfig.dryrun == undefined)
    this.sfxConfig.dryrun = false;

  if(this.sfxConfig.useMultiKey == undefined)
    this.sfxConfig.useMultiKey = true;

  // add the '.' so that we can just jam keys onto the end
  if(this.sfxConfig.globalPrefix == "" || this.sfxConfig.globalPrefix === undefined) {
    this.sfxConfig.globalPrefix = "";
  } else {
    this.sfxConfig.globalPrefix += '.';
  }

  this.sfxConfig.http = http;
  this.sfxConfig.onComplete = this.onComplete;
  this.sfxConfig.post = this.post;

  var self = this;
  emitter.on('flush', function(timestamp, metrics) {
    self.flush(timestamp, metrics, self.post);
  });
  emitter.on('status', function(callback) {
    self.status(callback);
  });
}

SignalFuseBackend.prototype.getConfig = function() {
  return this.sfxConfig;
}

SignalFuseBackend.prototype.onComplete = function(rsp) {
  if(rsp.statusCode != 200) {
    rsp.setEncoding('utf8');
    l.log('Post to signalfx FAILED ' + rsp.statusCode + " " + rsp.statusMessage);
    rsp.on('data', function(chunk) {
      l.log(chunk);
    });
  } else {
    l.debug("Finished Flush to signalfx: " + rsp.statusCode + " " + rsp.statusMessage);
  }
}

//
// Parses the key into its metric name and actual tags
// The expected format is a JSON list of tuples: (key, value)
// For example::
//
//    [
//        [
//            "metric_name",
//            "myname"
//        ],
//        [
//            "sometag",
//            "tagsval"
//        ]
//    ]
//
// The name 'metric_name' is reserved to mean exactly what it says
SignalFuseBackend.prototype.parseMultiKey = function(stringKey) {
  var parsed = undefined
  try {
    parsed = JSON.parse(stringKey);
  } catch(err) {
    l.debug("Failed to parse '" + stringKey + "' into valid JSON");
    return undefined
  }

  var mapVersion = {};
  parsed.forEach(function(item) {
    mapVersion[item[0]] = item[1];
  });
  mname = mapVersion['metric_name'];
  delete mapVersion['metric_name'];
  return {metric_name: mname, tags: mapVersion};
}

//
// This is intended to split the key itself into its tags and name
// some rules::
//
//    - '/' are replaced with '-'
//    - ' ' spaces (incl mutlitple) are replaced with '_'
//    - characters not in: [a-zA-Z], [0-9], [-_=.] are removed
//
// then the key is split on its '.' and the first is start
// of the metric's name. The rest are evaluated as such:
//
//    - if they contain an '=' they are a tag
//    - otherwise they are part of the name
//
// UNLESS the 'useMultiKey' config value is enabled. Then it is parsed
// according to those rules. See 'parseMultiKey'
//
SignalFuseBackend.prototype.parseKey = function(rawkey) {
  var metricName = "";
  var tags = {}

  if(this.sfxConfig.useMultiKey == true) {
    var parsed = this.parseMultiKey(rawkey);
    if(parsed != undefined) {
      metricName = parsed['metric_name'];
      tags = parsed['tags'];
    }
  }

  if(metricName == "") {
    var cleanerKey = rawkey.replace(/\s+/g, '_')
                           .replace(/\//g, '-')
                           .replace(/[^a-zA-Z_\-0-9=\.]/g, '');

    parts = cleanerKey.split('.');
    metricParts = [parts[0]];
    if(metricParts[0].indexOf('=') >= 0) {
      return {}; // can't start with an '=' in it
    }

    for(i = 1; i < parts.length; i++) {
      if(parts[i].indexOf('=') >= 0) {
        var tagParts = parts[i].split('=');
        tags[tagParts[0]] = tagParts[1];
      } else {
        metricParts.push(parts[i]);
      }
    }

    metricName = metricParts.join('.');
  }

  return {metricName: metricName, tags: tags};
}

//
// handles transforming metrics of shape::
//
//  { metricname: { subkey: #, subkey: # ..}, .. }
//
// into discrete metrics, that are metricname.subkey = #
//
SignalFuseBackend.prototype.transformTimerData = function(timerData) {
  l.debug('Starting to process ' + Object.keys(timerData).length + ' timers');

  var globalPrefix = this.sfxConfig.globalPrefix;
  var resultingStats = [];

  for(rawKey in timerData) {
    if(timerData.hasOwnProperty(rawKey)) {
      var metricParts = this.parseKey(rawKey);
      var keyName = metricParts['metricName'];
      var tags = metricParts['tags'];
      tags['type'] = 'timerdata';

      for(subKey in timerData[rawKey]) {
        if(timerData[rawKey].hasOwnProperty(subKey)) {
          var val = timerData[rawKey][subKey];
          var fqMetricName = globalPrefix + [keyName, subKey].join('.');
          resultingStats.push(buildStat(fqMetricName, val, tags));
        }
      }
    }
  }

  return resultingStats;
}

//
// handles transforming metrics of this shape::
//
//   { 'metricname': [ #, #, #..], ... }
//
// turns them into discrete events
//
SignalFuseBackend.prototype.transformTimers = function(timers) {
  l.debug('Starting to process ' + Object.keys(timers).length + ' timers');

  var globalPrefix = this.sfxConfig.globalPrefix;
  var resultingStats = [];

  for(rawKey in timers) {
    if(timers.hasOwnProperty(rawKey)) {
      var metricParts = this.parseKey(rawKey);
      var keyName = metricParts['metricName'];
      var tags = metricParts['tags'];
      tags['type'] = 'timer';

      var fqMetricName = globalPrefix + keyName;
      var events = timers[rawKey];
      for(var i = 0; i < events.length; i++) {
        resultingStats.push(buildStat(fqMetricName, events[i], tags));
      }
    }
  }

  l.debug("Finished transforming metrics into " +
          resultingStats.length + " signalfuse metrics");

  return resultingStats;
}

//
// This will transform gauges, counters, and sets
// naming goes: globalPrefix.[metricname]
// also the tags will get a 'type: type' added
//
SignalFuseBackend.prototype.transformMetrics = function(metrics, type) {
  l.debug('Starting to process ' + Object.keys(metrics).length + ' ' + type);

  var globalPrefix = this.sfxConfig.globalPrefix;
  var resultingStats = [];

  for(rawKey in metrics) {
    if(metrics.hasOwnProperty(rawKey)) {
      var value = metrics[rawKey];

      var metricParts = this.parseKey(rawKey);
      var keyName = metricParts['metricName'];
      var tags = metricParts['tags'];
      tags['type'] = type;

      var fqMetricName = globalPrefix + keyName;
      resultingStats.push(buildStat(fqMetricName, value, tags));
    }
  }

  l.debug("Finished transforming " + type + " into " +
          resultingStats.length + " signalfuse metrics");

  return resultingStats;
};

//
// Takes each of the different components, transformts them, then ships
// ships them off
//
SignalFuseBackend.prototype.flush = function(timestamp, metric, postcb) {
  l.debug(timestamp + ' starting a flush');

  var resultingMetrics = [];
  var partial = [];

  partial = this.transformMetrics(metric.counters, 'counter');
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformMetrics(metric.sets, 'set');
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformMetrics(metric.gauges, 'gauge');
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformMetrics(metric.counter_rates, 'rate');
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformTimers(metric.timers);
  Array.prototype.push.apply(resultingMetrics, partial);
  partial = this.transformTimerData(metric.timer_data);
  Array.prototype.push.apply(resultingMetrics, partial);

  postcb(resultingMetrics, this.sfxConfig);
}

SignalFuseBackend.prototype.post = function(metricList, sfxConfig) {
  l.debug('Sending ' + metricList.length + ' metrics to signal fuse');

  var postOptions = {
    host: sfxConfig.host,
    path: '/v2/datapoint',
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-SF-Token': sfxConfig.token
    }
  };

  var out = {gauge: metricList};
  var postData = JSON.stringify(out);

  l.debug('Payload will be:\n' + util.inspect(out, {depth:5, colors:true}));

  if(!sfxConfig.dryrun) {
    var req = sfxConfig.http.request(postOptions, sfxConfig.onComplete);
    req.on('error', function(res) {
      log.log("Somethign went terribly wrong trying to send data to signalfx");
      log.log("Payload:\n" + util.inspect(out, {depth:5, colors:true}));
      log.log("Error:" + util.inspect(res, {depth:5, colors:true}));
    });

    req.write(postData);
    req.end();
  } else {
    l.log('Not sending because of dryrun flag. request:');
    l.log(postData);
  }

  l.debug('Finished sending data to signalfx');
}

SignalFuseBackend.prototype.status = function(callback) {
  callback('not yet implemented', 'signalfuse');
}

// ---------------------------------------------------------------------------
// export a build method
// ---------------------------------------------------------------------------
exports.init = function(startup_time, config, events, logger) {
  debug = config.debug;
  l = logger;

  var instance = new SignalFuseBackend(startup_time, config, events);
  return instance;
};

<|endoftext|>


<commit_before>function smf_fileUpload(oOptions)
{
	// Default values in case oOptions isn't defined.
	var dOptions = {
		url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=add;' + smf_session_var + '=' + smf_session_id,
		dataType: 'json',
		dropZone: $('.drop_zone'),
		singleFileUploads:false,
		forceIframeTransport: false,
		autoUpload: false,
		paramName: 'attachment[]',
		acceptFileTypes: /(\.|\/)(doc|gif|jpg|mpg|pdf|png|txt|zip)$/i,
		maxFileSize: 128000, // 128 KB
		disableImageResize: /Android(?!.*Chrome)|Opera/
			.test(window.navigator.userAgent),
		previewMaxWidth: 100,
		previewMaxHeight: 100,
		previewCrop: false,
		maxNumberOfFiles: 10,
		limitMultiFileUploads: 10,
		limitMultiFileUploadSize: 1000000,
		smf_mainDiv: '#fileupload',
		smf_containerDiv: '#files',
		smf_text: {}
	};

	$.extend(true, dOptions, oOptions);

	var uploadButton = $('')
		.addClass('button_submit uploadButton')
		.prop('disabled', true)
		.text(dOptions.smf_text.upload)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data();

			fileUpload.track[data.i].submit().always(function () {
				$this.remove();
			});
		}),
	cancelButton = $('')
		.addClass('button_submit cancelButton')
		.prop('disabled', false)
		.text(dOptions.smf_text.cancel)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				fileIndex = data.fileIndex,
				currentFile = data.context.files[fileIndex],
				node = $(data.context.context);

			data.abort().always(function () {
				// @todo do stuff while aborting.
			});

			// Gotta remove this from the number of files.
			--numberOfFiles;

			// And remove this file's size from the total.
			totalSize = totalSize - currentFile.size;

			$this.remove();
			data.currentNode.fadeOut('slow', function() {
				data.currentNode.remove();
			});
		}),
	deleteButton = $('')
		.addClass('button_submit deleteButton you_sure')
		.prop('disabled', false)
		.text(dOptions.smf_text.deleteAttach)
		.on('click', function (e) {

			var $this = $(this),
				mainData = $this.data(),
				node = $(mainData.context);

			// Let the server know you want to delete the file you just recently uploaded...
			$.ajax({
				url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=delete;attach='+ mainData.currentFile['attachID'] +';' + smf_session_var + '=' + smf_session_id,
				type: 'GET',
				dataType: 'json',
				success: function (data, textStatus, xhr) {
					// Gotta remove this from the number of files.
					--numberOfFiles;

					// And remove this file's size from the total.
					totalSize = totalSize - mainData.currentFile['size'];

					// Replace the delete button with the "cancel" one.
					$this.replaceWith(cancelButton.clone(true).data(mainData));

					// Don't remove the entire node, just leave a message.
					node.find('.file_details').fadeOut('slow', function() {
						node.find('.file_details').text(dOptions.smf_text.attachDeleted);
						node.find('.file_details').fadeIn('slow', function() {
						});
					});

					// Remove the BBC tag.
					node.find('.file_info').empty();

					// Lastly, abort the whole thing.
					mainData.abort();
				},
				error: function (xhr, textStatus, errorThrown) {
					// Lastly, abort the whole thing.
					mainData.abort();

					node
						.find('.file_info')
						.append($('
').text((typeof file.error !== 'undefined' ? file.error : dOptions.smf_text.genericError)));

					node.removeClass('descbox').addClass('errorbox');
				}
			});
		}),
	insertBBC = $('')
		.addClass('button_submit insertBBC')
		.prop('disabled', false)
		.text(dOptions.smf_text.insertBBC)
		.on('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				node = $(data.context);

			// Get the text field value.
			oTag = node.find('input[name=attachBBC]').val();

			$('#' + oEditorID).data('sceditor').sourceEditorInsertText(oTag);
		}),
	uploadAll = $('')
		.addClass('button_submit uploadAllButton')
		.prop('disabled', true)
		.text(dOptions.smf_text.uploadAll)
		.one('click', function (e) {
			e.preventDefault();
			for (i=0; i < fileUpload.length; i++) {
				fileUpload[i].data.submit().always(function () {

				});
			}
		}),
	cancelAll = $('')
		.addClass('button_submit cancelAllButton')
		.prop('disabled', false)
		.text(dOptions.smf_text.cancelAll)
		.on('click', function (e) {
			e.preventDefault();
			// Gotta remove everything.
			numberOfFiles = 0;

			// And this stuff too!.
			totalSize = 0;

			fileIndicator = false;

			for (i=0; i < fileUpload.track.length; i++) {
				fileUpload.track[i].justCancel = true;
				fileUpload.track[i].abort().always(function () {
					// @todo do stuff while aborting.
				});
			}
		}),
	numberOfTimes = 0,
	numberOfFiles = 0,
	totalSize = 0,
	fileIndicator = false,

	fileUpload = $(dOptions.smf_mainDiv).fileupload(dOptions)
		.on('fileuploadadd', function (e, data) {

			// Track the number of times this event has been fired.
			++numberOfTimes;

			// Create a master and empty div.
			data.context = $('
').addClass('attach_container').appendTo(dOptions.smf_containerDiv);

			// Perhaps we just want to cancel...
			data.justCancel = false;

			// Show some general controls.
			if (!fileIndicator){
				fileIndicator = true;

				$('.attachControl')
					.append(cancelAll.clone(true))
					.append(uploadAll.clone(true));
					fileUpload.track = [];
			}

			// Append the file.
			$.each(data.files, function (index, file) {
				var node = $('
').addClass('attach_holder descbox')
				.attr('id', 'attach_holder_' + numberOfTimes)
				.html('

');

				// Hide the progress bar, we don't want to show it just yet!
				node.find('.progressBar').hide();

				node.find('.file_details')
						.append($('
').text(file.name));

				node.find('.file_buttons')
						.append(cancelButton.clone(true).data({i:index}))
						.append(uploadButton.clone(true).data({i:index}));

				node.appendTo(data.context);

				fileUpload.track.push(data);
			});
		})
		.on('fileuploadsend', function (e, data) {

			// Show the progress bar.
			data.context.find('.progressBar').fadeIn();

		})
		.on('fileuploadprocessalways', function (e, data) {
			var index = data.index,
				file = data.files[index],
				node = $(data.context.children()[index]);

			// Track the file size.
			totalSize = totalSize + file.size;

			// Show a nice preview.
			if (file.preview) {
				node
					.find('.file_details')
					.append($('
').prepend(file.preview));
			}

			if (file.error || numberOfFiles >= dOptions.maxNumberOfFiles || totalSize >= dOptions.limitMultiFileUploadSize) {
				// There isn't an error with the actual file, must be something else then!
				if (!file.error && numberOfFiles >= dOptions.maxNumberOfFiles)
					file.error = dOptions.messages.maxNumberOfFiles;

				// You reached the uploads total size.
				else if (totalSize >= dOptions.limitMultiFileUploadSize){
					file.error = dOptions.messages.maxTotalSize
						.replace('{currentTotal}', smf_fileUpload_bytesToSize(dOptions.limitMultiFileUploadSize))
						.replace('{currentRemain}', smf_fileUpload_bytesToSize(totalSize));
				}

				// Cancel the current upload.
				data.abort();
			}

			// The file was added.
			if (index + 1 === data.files.length) {
				// "un-disable" the upload button :P
				data.context.find('.uploadButton')
					.prop('disabled', !!data.files.error);

				// The file has been appended, lets keep track of it!
				++numberOfFiles;

				// append some text here to tell the user what to do, hit Upload or hit Cancel...
				// or add some other indication that the file passed the client tests.
			}
		})
		.on('fileuploaddone', function (e, data) {

			// Check out the general errors first...
			if (data.result.generalErrors){

				// Show the big fat generic error list!
				genericErrors = $('#attachGenericErrors ul');
				$.each(data.result.generalErrors, function (index, error) {
					genericErrors.append('
'+ error + '
');
				});

				genericErrors.fadeIn();
			}

			else{
				$.each(data.result.files, function (index, file) {

					var node = $(data.context.children()[index]);

					// Hide the progress bar.
					node.find('.progressBar').fadeOut();

					// Show the brand new attach ID bbc tag.
					if (file.attachID) {
						var bbcTag = $('
').append(dOptions.smf_text.insertAttach + '<input type="text" name="attachBBC" value="[attach]' + file.attachID + '[/attach]" readonly>')
							.append(insertBBC.clone(true).data(data));

						node
							.find('.file_info')
							.append(bbcTag);

						// Change the css to indicate everything went better than expected...
						node.removeClass('descbox').addClass('infobox');

						// Append the current node/file to make it easier to handle it.
						data.currentNode = node;
						data.currentFile = file;

						// Replace the cancel button with a lovely "Delete" one.
						node.find('.cancelButton').replaceWith(deleteButton.clone(true).data(data));
					}

					// Nope!
					else if (file.errors) {
						var errors = $('
').html('
');

						$.each(data.result.error, function (index, singleError) {
							errors.append('
' + singleError + '
');
						});

						// Close the ul
						errors.append('
');

						node
							.find('.file_info')
							.append(errors);

						node.removeClass('descbox').addClass('errorbox');
					}
				});
			}
		})
		.on('fileuploadprogress', function (e, data) {
			data.context.find('.uploadButton')
				.text(dOptions.smf_text.processing);

			var progress = parseInt(data.loaded / data.total * 100, 10);
			data.context.find('.progressBar').children().css(
				'width',
				progress + '%'
			);
		})
		.on('fileuploadfail', function (e, data) {
			$.each(data.files, function (index, file) {
				var node = $(data.context.children()[index]);

				// Hide the progress bar.
				node.find('.progressBar').fadeOut();

				if (!data.justCancel){
				node
					.find('.file_info')
					.append($('
').text((typeof file.error !== 'undefined' ? file.error : dOptions.smf_text.genericError)));
				}

				node.removeClass('descbox').addClass('errorbox');
				node.find('.uploadButton').remove();
			});
		});
}

function smf_fileUpload_bytesToSize(bytes) {

	if(typeof bytes == 'undefined' || bytes == 0){
		return 0;
	}

	var k = 1000; // change to 1024 for binary stuff
	var i = Math.floor(Math.log(bytes) / Math.log(k));
	return (bytes / Math.pow(k, i)).toPrecision(3);
}

$(function() {
	if (typeof oEditorID !== 'undefined'){
		$('.editeIinsertBBC').on('click', function() {
				// Get the text field value.
				var oValue = $(this).data('attach'),
					oTag = $('input[name=editedAttachBBC_'+ oValue +']').val();

				$('#' + oEditorID).data('sceditor').sourceEditorInsertText(oTag);
		});
	}

	// Since people will inevitably drop their files outside the drop area...
	window.addEventListener('dragover', function(e){
		e = e || event;
		e.preventDefault();
	},false);
	window.addEventListener('drop', function(e){
		e = e || event;
		e.preventDefault();
	},false);
});
<commit_msg>Back to the starting point to try to figure it out where the hell did I mess up things :P<commit_after>function smf_fileUpload(oOptions)
{
	// Default values in case oOptions isn't defined.
	var dOptions = {
		url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=add;' + smf_session_var + '=' + smf_session_id,
		dataType: 'json',
		dropZone: $('.drop_zone'),
		singleFileUploads:false,
		forceIframeTransport: false,
		autoUpload: false,
		paramName: 'attachment[]',
		acceptFileTypes: /(\.|\/)(doc|gif|jpg|mpg|pdf|png|txt|zip)$/i,
		maxFileSize: 128000, // 128 KB
		disableImageResize: /Android(?!.*Chrome)|Opera/
			.test(window.navigator.userAgent),
		previewMaxWidth: 100,
		previewMaxHeight: 100,
		previewCrop: false,
		maxNumberOfFiles: 10,
		limitMultiFileUploads: 10,
		limitMultiFileUploadSize: 1000000,
		smf_mainDiv: '#fileupload',
		smf_containerDiv: '#files',
		smf_text: {}
	};

	$.extend(true, dOptions, oOptions);

	var uploadButton = $('')
		.addClass('button_submit uploadButton')
		.prop('disabled', true)
		.text(dOptions.smf_text.upload)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data();

			data.submit().always(function () {
				$this.remove();
			});
		}),
	cancelButton = $('')
		.addClass('button_submit cancelButton')
		.prop('disabled', false)
		.text(dOptions.smf_text.cancel)
		.one('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				node = $(data.context);

			// Gotta remove this from the number of files.
			--numberOfFiles;

			// And remove this file's size from the total.
			totalSize = totalSize - data.currentFile.size;

			$this.remove();
			data.currentNode.fadeOut('slow', function() {
				data.currentNode.remove();
			});
		}),
	deleteButton = $('')
		.addClass('button_submit deleteButton you_sure')
		.prop('disabled', false)
		.text(dOptions.smf_text.deleteAttach)
		.on('click', function (e) {

			var $this = $(this),
				mainData = $this.data(),
				node = $(mainData.context);

			// Let the server know you want to delete the file you just recently uploaded...
			$.ajax({
				url: smf_prepareScriptUrl(smf_scripturl) + 'action=uploadAttach;sa=delete;attach='+ mainData.currentFile['attachID'] +';' + smf_session_var + '=' + smf_session_id,
				type: 'GET',
				dataType: 'json',
				success: function (data, textStatus, xhr) {
					// Gotta remove this from the number of files.
					--numberOfFiles;

					// And remove this file's size from the total.
					totalSize = totalSize - mainData.currentFile['size'];

					// Replace the delete button with the "cancel" one.
					$this.replaceWith(cancelButton.clone(true).data(mainData));

					// Don't remove the entire node, just leave a message.
					node.find('.file_details').fadeOut('slow', function() {
						node.find('.file_details').text(dOptions.smf_text.attachDeleted);
						node.find('.file_details').fadeIn('slow', function() {
						});
					});

					// Remove the BBC tag.
					node.find('.file_info').empty();

					// Lastly, abort the whole thing.
					mainData.abort();
				},
				error: function (xhr, textStatus, errorThrown) {
					// Lastly, abort the whole thing.
					mainData.abort();

					node
						.find('.file_info')
						.append($('
').text((typeof file.error !== 'undefined' ? file.error : dOptions.smf_text.genericError)));

					node.removeClass('descbox').addClass('errorbox');
				}
			});
		}),
	insertBBC = $('')
		.addClass('button_submit insertBBC')
		.prop('disabled', false)
		.text(dOptions.smf_text.insertBBC)
		.on('click', function (e) {
			e.preventDefault();
			var $this = $(this),
				data = $this.data(),
				node = $(data.context);

			// Get the text field value.
			oTag = node.find('input[name=attachBBC]').val();

			$('#' + oEditorID).data('sceditor').InsertText(oTag);
		}),
	numberOfTimes = 0,
	numberOfFiles = 0,
	totalSize = 0;

	$(dOptions.smf_mainDiv).fileupload(dOptions)
		.on('fileuploadadd', function (e, data) {

			// Track the number of times this event has been fired.
			++numberOfTimes;

			// Create a master and empty div.
			data.context = $('
').addClass('attach_container').appendTo(dOptions.smf_containerDiv);

			// Append the file.
			$.each(data.files, function (index, file) {
				var node = $('
').addClass('attach_holder descbox')
				.attr('id', 'attach_holder_' + numberOfTimes)
				.html('

');

				// Hide the progress bar, we don't want to show it just yet!
				node.find('.progressBar').hide();

				node.find('.file_details')
						.append($('
').text(file.name));

				// Append the current node info so it would be easier for the buttons to target it.
				data.currentNode = node;
				data.currentFile = file;

				node.find('.file_buttons')
						.append(cancelButton.clone(true).data(data))
						.append(uploadButton.clone(true).data(data));

				node.appendTo(data.context);
			});
		})
		.on('fileuploadsend', function (e, data) {

			// Show the progress bar.
			data.context.find('.progressBar').fadeIn();

		})
		.on('fileuploadprocessalways', function (e, data) {
			var index = data.index,
				file = data.files[index],
				node = $(data.context.children()[index]);

			// Track the file size.
			totalSize = totalSize + file.size;

			// Show a nice preview.
			if (file.preview) {
				node
					.find('.file_details')
					.append($('
').prepend(file.preview));
			}

			if (file.error || numberOfFiles >= dOptions.maxNumberOfFiles || totalSize >= dOptions.limitMultiFileUploadSize) {
				// There isn't an error with the actual file, must be something else then!
				if (!file.error && numberOfFiles >= dOptions.maxNumberOfFiles)
					file.error = dOptions.messages.maxNumberOfFiles;

				// You reached the uploads total size.
				else if (totalSize >= dOptions.limitMultiFileUploadSize){
					file.error = dOptions.messages.maxTotalSize
						.replace('{currentTotal}', smf_fileUpload_bytesToSize(dOptions.limitMultiFileUploadSize))
						.replace('{currentRemain}', smf_fileUpload_bytesToSize(totalSize));
				}

				// Cancel the current upload.
				data.abort();
			}

			// The file was added.
			if (index + 1 === data.files.length) {
				// "un-disable" the upload button :P
				data.context.find('.uploadButton')
					.prop('disabled', !!data.files.error);

				// The file has been appended, lets keep track of it!
				++numberOfFiles;

				// append some text here to tell the user what to do, hit Upload or hit Cancel...
				// or add some other indication that the file passed the client tests.
			}
		})
		.on('fileuploaddone', function (e, data) {

			// Check out the general errors first...
			if (data.result.generalErrors){

				// Show the big fat generic error list!
				genericErrors = $('#attachGenericErrors ul');
				$.each(data.result.generalErrors, function (index, error) {
					genericErrors.append('
'+ error + '
');
				});

				genericErrors.fadeIn();
			}

			else{
				$.each(data.result.files, function (index, file) {

					var node = $(data.context.children()[index]);

					// Hide the progress bar.
					node.find('.progressBar').fadeOut();

					// Show the brand new attach ID bbc tag.
					if (file.attachID) {
						var bbcTag = $('
').append(dOptions.smf_text.insertAttach + '<input type="text" name="attachBBC" value="[attach]' + file.attachID + '[/attach]" readonly>')
							.append(insertBBC.clone(true).data(data));

						node
							.find('.file_info')
							.append(bbcTag);

						// Change the css to indicate everything went better than expected...
						node.removeClass('descbox').addClass('infobox');

						// Append the current node/file to make it easier to handle it.
						data.currentNode = node;
						data.currentFile = file;

						// Replace the cancel button with a lovely "Delete" one.
						node.find('.cancelButton').replaceWith(deleteButton.clone(true).data(data));
					}

					// Nope!
					else if (file.errors) {
						var errors = $('
').html('
');

						$.each(data.result.error, function (index, singleError) {
							errors.append('
' + singleError + '
');
						});

						// Close the ul
						errors.append('
');

						node
							.find('.file_info')
							.append(errors);

						node.removeClass('descbox').addClass('errorbox');
					}
				});
			}
		})
		.on('fileuploadprogress', function (e, data) {
			data.context.find('.uploadButton')
				.text(dOptions.smf_text.processing);

			var progress = parseInt(data.loaded / data.total * 100, 10);
			data.context.find('.progressBar').children().css(
				'width',
				progress + '%'
			);
		})
		.on('fileuploadfail', function (e, data) {
			$.each(data.files, function (index, file) {
				var node = $(data.context.children()[index]);

				// Hide the progress bar.
				node.find('.progressBar').fadeOut();

				node
					.find('.file_info')
					.append($('
').text((typeof file.error !== 'undefined' ? file.error : dOptions.smf_text.genericError)));

				node.removeClass('descbox').addClass('errorbox');
				node.find('.uploadButton').remove();
			});
		});
}

function smf_fileUpload_bytesToSize(bytes) {

	if(typeof bytes == 'undefined' || bytes == 0){
		return 0;
	}

	var k = 1000; // change to 1024 for binary stuff
	var i = Math.floor(Math.log(bytes) / Math.log(k));
	return (bytes / Math.pow(k, i)).toPrecision(3);
}

$(function() {
	if (typeof oEditorID !== 'undefined'){
		$('.editeIinsertBBC').on('click', function() {
				// Get the text field value.
				var oValue = $(this).data('attach'),
					oTag = $('input[name=editedAttachBBC_'+ oValue +']').val();

				$('#' + oEditorID).data('sceditor').InsertText(oTag);
		});
	}

	// Since people will inevitably drop their files outside the drop area...
	window.addEventListener('dragover', function(e){
		e = e || event;
		e.preventDefault();
	},false);
	window.addEventListener('drop', function(e){
		e = e || event;
		e.preventDefault();
	},false);
});
<|endoftext|>