<commit_before>module.exports = function(grunt) {
    grunt.util.linefeed = '\n';

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: grunt.file.readYAML('../config/config.yml'),

        watch: {
            options: {
                spawn: false,
                livereload: true
            },
            scripts: {
                files: [
                    'sass/*.scss',
                    'sass/nav/*.scss',
                    'sass/modules/*.scss'
                ],
                tasks: [
                    'sass'
                ]
            },
            js: {
                files: [
                    'lib/bolt/*.js'
                ],
                tasks: [
                    'uglify:bolt'
                ]
            }
        },

        sass: {
            dist: {
                options: {
                    outputStyle: 'compressed',
                    includePaths: [
                        'node_modules/bootstrap-sass/assets/stylesheets/',
                        'node_modules/font-awesome/scss/'
                    ],
                    lineNumbers: false,
                    unixNewlines: true,
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED CSS files!\n" +
                            " * Do not edit these files, because all changes will be lost.\n" +
                            " * You can edit ../scss/app.scss & ../scss/app-old-ie.scss, and run 'grunt' to generate this file.\n" +
                            " */\n",
                    precision: 5
                },
                files: {
                    'css/bolt-old-ie.css': 'sass/app-old-ie.scss',
                    'css/bolt.css': 'sass/app.scss'
                }
            }

        },

        copy: {
            main: {
                // Includes files within path
                files: [{
                    expand: true,
                    flatten: true,
                    src: [
                        'node_modules/font-awesome/fonts/*'
                    ],
                    filter: 'isFile',
                    dest: 'fonts/'
                }]
            }
        },

        concat: {
            lib: {
                options: {
                    separator: '\n\n'
                },
                nonull: true,
                src: [
                    'lib/jquery-1.11.2/jquery.min.js',
                    'lib/jquery-ui-1.10.3/jquery-ui.custom.min.js',
                    'lib/bootstrap-file-input/bootstrap-file-input.min.js',
                    'lib/jquery-tagcloud/jquery-tagcloud.min.js',
                    'lib/jquery-hotkeys/jquery-hotkeys.min.js',
                    'lib/jquery-watchchanges/jquery-watchchanges.min.js',
                    'lib/jquery-cookie-1.4.0/jquery-cookie.min.js',
                    'lib/jquery-formatdatetime-1.1.4/jquery-formatdatetime.min.js',
                    'lib/jquery-fileupload-5.26/jquery-iframe-transport.min.js',
                    'lib/jquery-fileupload-5.26/jquery-fileupload.min.js',
                    'lib/underscore-1.7.0/underscore-min.js',
                    'lib/backbone/backbone-min.js',
                    'lib/bootstrap-sass.generated/bootstrap.min.js',
                    'lib/magnific-popup-0.9.9/magnific-popup.min.js',
                    'lib/select2-3.5.1/select2.min.js',
                    'lib/moment-2.8.3/moment.min.js',
                    'lib/bootbox-4.3.0/bootbox.min.js',
                    'lib/modernizr-2.8.3/modernizr.custom.min.js'
                ],
                dest: 'js/lib.min.js'
            }
        },

        cssmin: {
            lib: {
                options: {
                    compatibility: 'ie8',
                    relativeTo: './css/',
                    target: './css/'
                },
                files: {
                    'css/lib.css': [
                        'lib/jquery-ui-1.10.3/jquery-ui.custom.min.css',
                        'lib/magnific-popup-0.9.9/magnific-popup.css',
                        'lib/select2-3.5.1/select2.css',
                        'lib/jquery-fileupload-5.26/jquery-fileupload-ui.css'
                    ]
                }
            }
        },

        uglify: {
            lib: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    ext: '.min.js',
                    src: [
                        'lib/bootstrap-file-input/bootstrap-file-input.js',
                        'lib/jquery-catchpaste-1.0.0p/jquery-catchpaste.js',
                        'lib/jquery-cookie-1.4.0/jquery-cookie.js',
                        'lib/jquery-fileupload-5.26/jquery-fileupload.js',
                        'lib/jquery-fileupload-5.26/jquery-iframe-transport.js',
                        'lib/jquery-formatdatetime-1.1.4/jquery-formatdatetime.js',
                        'lib/jquery-hotkeys/jquery-hotkeys.js',
                        'lib/jquery-tagcloud/jquery-tagcloud.js',
                        'lib/jquery-watchchanges/jquery-watchchanges.js'
                    ]
                }]
            },
            bootstrap: {
                files: {
                    'lib/bootstrap-sass.generated/bootstrap.min.js': [
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/alert.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/button.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tab.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/transition.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/modal.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/popover.js'
                    ]
                }
            },
            bolt: {
                options: {
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED JS files!\n" +
                            " * You can edit files in <js/src/*.js> and run 'grunt' to generate this file.\n" +
                            " */",
                    sourceMap: true
                },
                files: {
                    'js/bolt.min.js': [
                        'lib/bolt/console.js',
                        'lib/bolt/fnc-helpers.js',
                        'lib/bolt/activity.js',
                        'lib/bolt/bind-fileupload.js',
                        'lib/bolt/make-uri-slug.js',
                        'lib/bolt/video-embed.js',
                        'lib/bolt/geolocation.js',
                        'lib/bolt/upload-files.js',
                        'lib/bolt/obj-sidebar.js',
                        'lib/bolt/obj-navpopups.js',
                        'lib/bolt/obj-moments.js',
                        'lib/bolt/obj-files.js',
                        'lib/bolt/obj-stack.js',
                        'lib/bolt/obj-folders.js',
                        'lib/bolt/obj-datetime.js',
                        'lib/bolt/extend.js',
                        'lib/bolt/init.js',
                        'lib/bolt/start.js'
                    ]
                }
            }
        },

        jshint: {
            options: {
                browser: true,          // Defines globals exposed by modern browsers
                curly: true,            // Always put curly braces around blocks
                devel: true,            // Defines globals that are usually used for logging/debugging
                immed: true,            // Prohibits the use of immediate function invocations without parentheses
                indent: 4,              // Tab width
                latedef: true,          // Prohibits the use of a variable before it was defined
                maxlen: 120,            // Maximum length of a line
                noarg: true,            // Prohibits the use of arguments.caller and arguments.callee
                nonbsp: true,           // Warns about "non-breaking whitespace" characters
                singleGroups: true,     // Prohibits the use of the grouping operator for single-expression statements
                undef: true,            // Prohibits the use of undeclared variables
                globals: {
                    // Bolt
                    bolt: true,                 // src/console.js
                    FilelistHolder: true,       // src/upload-files.js
                    Files: true,                // src/obj-files.js
                    Folders: true,              // src/obj-folders.js
                    init: true,                 // src/init.js
                    Moments: true,              // src/obj-moments.js
                    Navpopups: true,            // src/obj-navpopups.js
                    Sidebar: true,              // src/obj-sidebar.js
                    Stack: true,                // src/obj-stack.js
                    site: true,                 // src/extend.js/extend.twig
                    baseurl: true,              // src/extend.js/extend.twig
                    rootpath: true,             // src/extend.js/extend.twig
                    // Bolt global functions
                    bindFileUpload: true,       // src/bindfileuploads.js
                    bindGeolocation: true,      // src/geolocation.js
                    bindVideoEmbed: true,       // src/video-embed.js
                    getSelectedItems: true,     // src/fnc-helpers.js
                    makeUri: true,              // src/make-uri-slug.js
                    makeUriAjax: true,          // src/make-uri-slug.js
                    stopMakeUri: true,          // src/make-uri-slug.js
                    updateLatestActivity: true, // src/activity.js
                    validateContent: true,      // src/fnc-helpers.js
                    // Vendor
                    $: true,                    // jQuery
                    _: true,                    // underscore.js
                    Backbone: true,             // backbone.min.js
                    bootbox: true,              // bootbox.min.js
                    CKEDITOR: true,             // ckeditor.js
                    CodeMirror: true,           // ckeditor.js
                    google: true,               // Google
                    jQuery: true,               // jQuery
                    moment: true,               // moment.min.js
                    UAParser: true              // ua-parser.min.js
                }
            },
            src: ['lib/bolt/*.js']
        }

    });

    require('load-grunt-tasks')(grunt);

    grunt.registerTask('default', ['sass', 'jshint', 'uglify', 'cssmin', 'concat', 'watch']);

};
<commit_msg>Update Gruntfile, added filesizes, to see where there's most to gain.<commit_after>module.exports = function(grunt) {
    grunt.util.linefeed = '\n';

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: grunt.file.readYAML('../config/config.yml'),

        watch: {
            options: {
                spawn: false,
                livereload: true
            },
            scripts: {
                files: [
                    'sass/*.scss',
                    'sass/nav/*.scss',
                    'sass/modules/*.scss'
                ],
                tasks: [
                    'sass'
                ]
            },
            js: {
                files: [
                    'lib/bolt/*.js'
                ],
                tasks: [
                    'uglify:bolt'
                ]
            }
        },

        sass: {
            dist: {
                options: {
                    outputStyle: 'compressed',
                    includePaths: [
                        'node_modules/bootstrap-sass/assets/stylesheets/',
                        'node_modules/font-awesome/scss/'
                    ],
                    lineNumbers: false,
                    unixNewlines: true,
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED CSS files!\n" +
                            " * Do not edit these files, because all changes will be lost.\n" +
                            " * You can edit ../scss/app.scss & ../scss/app-old-ie.scss, and run 'grunt' to generate this file.\n" +
                            " */\n",
                    precision: 5
                },
                files: {
                    'css/bolt-old-ie.css': 'sass/app-old-ie.scss',
                    'css/bolt.css': 'sass/app.scss'
                }
            }

        },

        copy: {
            main: {
                // Includes files within path
                files: [{
                    expand: true,
                    flatten: true,
                    src: [
                        'node_modules/font-awesome/fonts/*'
                    ],
                    filter: 'isFile',
                    dest: 'fonts/'
                }]
            }
        },

        concat: {
            lib: {
                options: {
                    separator: '\n\n'
                },
                nonull: true,
                src: [
                    'lib/jquery-1.11.2/jquery.min.js',                              //  96 kb
                    'lib/jquery-ui-1.10.3/jquery-ui.custom.min.js',                 //  96 kb
                    'lib/bootstrap-file-input/bootstrap-file-input.min.js',         //   1 kb
                    'lib/jquery-tagcloud/jquery-tagcloud.min.js',                   //   1 kb
                    'lib/jquery-hotkeys/jquery-hotkeys.min.js',                     //   2 kb
                    'lib/jquery-watchchanges/jquery-watchchanges.min.js',           //   1 kb
                    'lib/jquery-cookie-1.4.0/jquery-cookie.min.js',                 //   1 kb
                    'lib/jquery-formatdatetime-1.1.4/jquery-formatdatetime.min.js', //   3 kb
                    'lib/jquery-fileupload-5.26/jquery-iframe-transport.min.js',    //   2 kb
                    'lib/jquery-fileupload-5.26/jquery-fileupload.min.js',          //  15 kb
                    'lib/underscore-1.7.0/underscore-min.js',                       //  16 kb
                    'lib/backbone/backbone-min.js',                                 //  20 kb
                    'lib/bootstrap-sass.generated/bootstrap.min.js',                //   2 kb
                    'lib/magnific-popup-0.9.9/magnific-popup.min.js',               //  21 kb
                    'lib/select2-3.5.1/select2.min.js',                             //  66 kb
                    'lib/moment-2.8.3/moment.min.js',                               // 149 kb
                    'lib/bootbox-4.3.0/bootbox.min.js',                             //   9 kb
                    'lib/modernizr-2.8.3/modernizr.custom.min.js'                   //   5 kb
                ],
                dest: 'js/lib.min.js'
            }
        },

        cssmin: {
            lib: {
                options: {
                    compatibility: 'ie8',
                    relativeTo: './css/',
                    target: './css/'
                },
                files: {
                    'css/lib.css': [
                        'lib/jquery-ui-1.10.3/jquery-ui.custom.min.css',      // 20 kb
                        'lib/magnific-popup-0.9.9/magnific-popup.css',        //  9 kb
                        'lib/select2-3.5.1/select2.css',                      // 19 kb
                        'lib/jquery-fileupload-5.26/jquery-fileupload-ui.css' //  2 kb
                    ]
                }
            }
        },

        uglify: {
            lib: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    ext: '.min.js',
                    src: [
                        'lib/bootstrap-file-input/bootstrap-file-input.js',
                        'lib/jquery-catchpaste-1.0.0p/jquery-catchpaste.js',
                        'lib/jquery-cookie-1.4.0/jquery-cookie.js',
                        'lib/jquery-fileupload-5.26/jquery-fileupload.js',
                        'lib/jquery-fileupload-5.26/jquery-iframe-transport.js',
                        'lib/jquery-formatdatetime-1.1.4/jquery-formatdatetime.js',
                        'lib/jquery-hotkeys/jquery-hotkeys.js',
                        'lib/jquery-tagcloud/jquery-tagcloud.js',
                        'lib/jquery-watchchanges/jquery-watchchanges.js'
                    ]
                }]
            },
            bootstrap: {
                files: {
                    'lib/bootstrap-sass.generated/bootstrap.min.js': [
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/alert.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/button.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tab.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/transition.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/modal.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/popover.js'
                    ]
                }
            },
            bolt: {
                options: {
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED JS files!\n" +
                            " * You can edit files in <js/src/*.js> and run 'grunt' to generate this file.\n" +
                            " */",
                    sourceMap: true
                },
                files: {
                    'js/bolt.min.js': [
                        'lib/bolt/console.js',
                        'lib/bolt/fnc-helpers.js',
                        'lib/bolt/activity.js',
                        'lib/bolt/bind-fileupload.js',
                        'lib/bolt/make-uri-slug.js',
                        'lib/bolt/video-embed.js',
                        'lib/bolt/geolocation.js',
                        'lib/bolt/upload-files.js',
                        'lib/bolt/obj-sidebar.js',
                        'lib/bolt/obj-navpopups.js',
                        'lib/bolt/obj-moments.js',
                        'lib/bolt/obj-files.js',
                        'lib/bolt/obj-stack.js',
                        'lib/bolt/obj-folders.js',
                        'lib/bolt/obj-datetime.js',
                        'lib/bolt/extend.js',
                        'lib/bolt/init.js',
                        'lib/bolt/start.js'
                    ]
                }
            }
        },

        jshint: {
            options: {
                browser: true,          // Defines globals exposed by modern browsers
                curly: true,            // Always put curly braces around blocks
                devel: true,            // Defines globals that are usually used for logging/debugging
                immed: true,            // Prohibits the use of immediate function invocations without parentheses
                indent: 4,              // Tab width
                latedef: true,          // Prohibits the use of a variable before it was defined
                maxlen: 120,            // Maximum length of a line
                noarg: true,            // Prohibits the use of arguments.caller and arguments.callee
                nonbsp: true,           // Warns about "non-breaking whitespace" characters
                singleGroups: true,     // Prohibits the use of the grouping operator for single-expression statements
                undef: true,            // Prohibits the use of undeclared variables
                globals: {
                    // Bolt
                    bolt: true,                 // src/console.js
                    FilelistHolder: true,       // src/upload-files.js
                    Files: true,                // src/obj-files.js
                    Folders: true,              // src/obj-folders.js
                    init: true,                 // src/init.js
                    Moments: true,              // src/obj-moments.js
                    Navpopups: true,            // src/obj-navpopups.js
                    Sidebar: true,              // src/obj-sidebar.js
                    Stack: true,                // src/obj-stack.js
                    site: true,                 // src/extend.js/extend.twig
                    baseurl: true,              // src/extend.js/extend.twig
                    rootpath: true,             // src/extend.js/extend.twig
                    // Bolt global functions
                    bindFileUpload: true,       // src/bindfileuploads.js
                    bindGeolocation: true,      // src/geolocation.js
                    bindVideoEmbed: true,       // src/video-embed.js
                    getSelectedItems: true,     // src/fnc-helpers.js
                    makeUri: true,              // src/make-uri-slug.js
                    makeUriAjax: true,          // src/make-uri-slug.js
                    stopMakeUri: true,          // src/make-uri-slug.js
                    updateLatestActivity: true, // src/activity.js
                    validateContent: true,      // src/fnc-helpers.js
                    // Vendor
                    $: true,                    // jQuery
                    _: true,                    // underscore.js
                    Backbone: true,             // backbone.min.js
                    bootbox: true,              // bootbox.min.js
                    CKEDITOR: true,             // ckeditor.js
                    CodeMirror: true,           // ckeditor.js
                    google: true,               // Google
                    jQuery: true,               // jQuery
                    moment: true,               // moment.min.js
                    UAParser: true              // ua-parser.min.js
                }
            },
            src: ['lib/bolt/*.js']
        }

    });

    require('load-grunt-tasks')(grunt);

    grunt.registerTask('default', ['sass', 'jshint', 'uglify', 'cssmin', 'concat', 'watch']);

};
<|endoftext|>


<commit_before>// Runs users through surveys

// Export
const survey = module.exports = {};

// Package Dependencies
const firebase = require('firebase-admin');
const database = firebase.database();
const logger = require('winston');

// Local Dependencies
const sender = require('../routes/object_sender');

/** Saves new users in Firebase
 * @param {string} userID - Facebook User ID
 * @param {string} firstName - User's First Name
 * FIXME: Placeholder
 */
survey.saveUser = function(userID, firstName) {
  logger.info('Saving User %d in the Database...', userID);
  return database.ref('users/' + userID).set({
    firstName,
    availableSurveys: {},
    currentSurvey: {},
  });
};

/** Checking if the user exists in the database
 * @param {string} userID - Facebook User ID
 */
survey.userFinder = function(userID) {
  logger.info('Checking if we\'ve met User %d before...', userID);

  // Check if the userID exists
  database.ref('users/' + userID).once('value', (snapshot) => {
    if (snapshot.exists()) {
      logger.info('We\'ve met User %d before!', userID);
      survey.surveyChecker(userID);

      // FIXME: Quick test of surveyAssigner loop; can assign any surveys in the UserFinder
      // TODO: Turn this into a test?
      // surveyer.surveyAssigner(userID, "survey_1")
    } else {
      logger.info('Have not met this user!', userID);
      survey.saveUser(userID, 'firstNameHolder')
        .then(() => {
          logger.info('Assigning User %d the starter survey: [survey_0]...', userID);
          survey.surveyAssigner(userID, 'survey_0', true)
            .then(() => {
              // TODO: Use promises to send the first survey question after assigning users a survey_0.
              logger.info('Sending User %d the Starting prompt message...', userID);
              sender.surveyChecker(userID);
            })
            .on((error) => {
              logger.error('Assigning [survey_0] to User %d failed.', userID);
            });
        });

      // FIXME: This means the user has to message us again to start the first survey; better chaining?
    }
  });
};

/** Assigns a user an available survey */
survey.surveyAssigner = function(userID, surveyID, current) {
  logger.info('Assiging User %d Survey %s...', userID, surveyID);

  // Lookup the survey
  database.ref('surveys/' + surveyID).once('value', (survey) => {
    const totalQuestions = survey.child('/questions/').numChildren(); // Array indices start at 0

    // Register it as an available survey for the user
    return database.ref('users/' + userID + '/availableSurveys/' + surveyID).set({
      postback: surveyID,
      completed: false,
      started: false,
      current: current || false,
      totalQuestions,
    });
  });
};

/** Starts user on a survey if he has a Current Survey, or sets him on one from Available if none exist */
survey.surveyChecker = function(userID) {
  logger.info('Checking if User %d has any available surveys...', userID);

  // If he has a current Survey that's not intialized, set it up and send him into the Looper
  database.ref('users/' + userID + '/availableSurveys').orderByChild('current').equalTo(true).once('value').then(
    // Promise is accepted
    (snapshot) => {
      // Get the current Survey key
      const currentSurvey = snapshot.val();
      const currentSurveyKey = Object.keys(currentSurvey)[0];

      // Get the current Survey state
      const startedCurrentSurvey = snapshot.child(currentSurveyKey).child('started').val();
      const completedCurrentSurvey = snapshot.child(currentSurveyKey).child('completed').val();

      if (currentSurvey && !startedCurrentSurvey) {
        // This means the survey was assigned as active, but not started (usually survey_0)
        logger.info('User %d has an "active" survey: %s! Setting it as the current survey...', userID, currentSurveyKey);

        // Initialize the current Survey State state
        database.ref('users/' + userID + '/currentSurvey/' + currentSurveyKey).set({
          finalQuestion: snapshot.child(currentSurveyKey + '/totalQuestions').val() - 1,
          currentQuestion: 0,
        })
          .then(() => {
            // After that state is updated, now start the user on that Survey
            survey.surveyLooper(userID, currentSurveyKey);

            // Update that survey state to started.
            logger.info('Setting User %d\'s Survey %s to "started" state...', userID, currentSurveyKey);
            database.ref('users/' + userID + '/availableSurveys/' + currentSurveyKey).update({
              started: true,
            });
          });
      } else if (currentSurvey && startedCurrentSurvey) {
        // This means he's already in a survey, and needs to continue looping
        logger.info('User %d is currently on Survey %s! Handing off to looper...', userID, currentSurveyKey);
        survey.surveyLooper(userID, currentSurveyKey);
      } else if (!currentSurvey) {
        // If he has no current surveys, let's assign him one
        database.ref('users/' + userID + '/availableSurveys').orderByChild('completed').equalTo(false)
          .once('value',

          // If the lookup works and the Promise is accepted, run here
          (snapshot) => {
            const availableSurveys = snapshot.val();

            logger.info('User %d has surveys available!', userID);

            // Get available Keys and number of available surveys
            const availableSurveyCount = snapshot.numChildren();
            const availableSurveyKeys = Object.keys(availableSurveys);

            // Grab a random available survey
            const randomSurveyKey = availableSurveyKeys[Math.floor(Math.random() * availableSurveyCount)];

            // Set that that survey as the current Survey
            logger.info('Setting User %d\'s current Survey to: %s...', randomSurveyKey);
            database.ref('users/' + userID + '/availableSurveys/' + randomSurveyKey).update({
              current: true,
            });

            // Inform the user
            logger.info('Sending User %d the Start Survey prompt...', userID);
            sender.sendTextMessage(userID, 'Looks like I do have a survey for you. Would you like to begin? ');
          }).catch(
          // Promise rejected (more likely to hit the higher level catch below than the one here)
          () => {
            logger.info('User %d has completed all available surveys...', userID);
            sender.sendTextMessage(userID, 'Good job, you\'ve answered all my questions!');
          });
      }
      // Promise rejected
    }).catch(() => {
      logger.info('User %d has no available surveys...', userID);
      sender.sendTextMessage(userID, 'I don\'t have any more questions for you. Come back later!');
    });
};

/** Sends a user a given survey question */
survey.surveyQuestionSender = function(userID, surveyID, questionNumber) {
  logger.info('Starting User %d on Survey: \'%s\'...', userID, surveyID);

  //  Get the Question
  database.ref('surveys/' + surveyID + '/questions').once('value', (snapshot) => {
    // Send it to the User
    sender.sendMessage(userID, snapshot.val()[questionNumber]);
  });
};

/** Loops users through their current survey until they are done */
survey.surveyLooper = function(userID, surveyID) {
  database.ref('users/' + userID + '/currentSurvey/' + surveyID).once('value', (snapshot) => {
    const finalQuestion = snapshot.child('finalQuestion').val();
    const currentQuestion = snapshot.child('currentQuestion').val();

    if (currentQuestion <= finalQuestion) {
      // Send them the current question for the current Survey
      logger.info('Sending User %d Question %d for Survey %s...', userID, currentQuestion, surveyID);
      survey.surveyQuestionSender(userID, surveyID, currentQuestion);
    } else {
      // If the current question is greater than the number of questions available, survey done!
      logger.info('User %d has completed Survey %s!', userID, surveyID);

      // Send user an ending Prompt
      sender.sendTextMessage(userID, 'Thanks for sharing your opinion with us!');

      // Update the available Survey state to complete, and make it inactive
      database.ref('users/' + userID + '/availableSurveys/' + surveyID).update({
        completed: true,
        current: false,
      });

      // Remove the current Survey entry
      logger.info('Removing User %d\'s Current Survey: %s. ', userID, surveyID);
      database.ref('users/' + userID + '/currentSurvey/' + surveyID).remove();
    }
  });
};

/** Saves question answers in Firebase, and increment user State */
survey.surveyAnswerSaver = function(userID, questionPayload, answer) {
  // Get the current Survey for the given user
  database.ref('users/' + userID + '/currentSurvey/').once('value', (snapshot) => {
    const surveyID = Object.keys(snapshot.val());

    // Save the users answer using payload text and the new survey ID
    logger.info('Saving User %d response to Question %d on Survey \'%s\': %s...', userID, questionPayload, surveyID, answer);
    database.ref('responses/' + surveyID + '/' + userID + '/' + questionPayload)
      .set({answer})
      .then(() => {
        logger.info('Increment User %d current Survey State to Question %d on Survey %s...', userID, questionPayload++, surveyID);
        database.ref('users/' + userID + '/currentSurvey/' + surveyID).update({
          currentQuestion: questionPayload++,
        });
      });
  });
};
<commit_msg>Evidently there is an ECMA2018.<commit_after>// Runs users through surveys

// Export
const survey = module.exports = {};

// Package Dependencies
const firebase = require('firebase-admin');
const database = firebase.database();
const logger = require('winston');

// Local Dependencies
const sender = require('../routes/object_sender');

/** Saves new users in Firebase
 * @param {string} userID - Facebook User ID
 * @param {string} firstName - User's First Name
 * FIXME: Placeholder
 */
survey.saveUser = async (userID, firstName) => {
  logger.info('Saving User %d in the Database...', userID);
  database.ref('users/' + userID).set({
    firstName,
    availableSurveys: {},
    currentSurvey: {},
  });
};

/** Checking if the user exists in the database
 * @param {string} userID - Facebook User ID
 */
survey.userFinder = async (userID) => {
  logger.info('Checking if we\'ve met User %d before...', userID);
  const userEntry = await database.ref('users/' + userID).once('value');
  const userExists = userEntry.exists();
  // Check if the userID exists
  if (userExists) {
    logger.info('We\'ve met User %d before!', userID);
    survey.surveyChecker(userID);
  } else {
    logger.info('Have not met this user!', userID);
    survey.saveUser(userID, 'firstNameHolder')
      .then(() => {
        logger.info('Assigning User %d the starter survey: [survey_0]...', userID);
        survey.surveyAssigner(userID, 'survey_0', true)
          .then(() => {
            logger.info('Sending User %d the Starting prompt message...', userID);
            survey.surveyChecker(userID);
          })
          .catch((error) => {
            logger.error('Prompting [survey_0] to User %d failed.', userID);
          });
      })
      .catch((error) => {
        logger.error('Assigning [survey_0] to User %d failed.', userID);
      });
  }
};

/** Assigns a user an available survey */
survey.surveyAssigner = async (userID, surveyID, current) => {
  logger.info('Assiging User %d Survey %s...', userID, surveyID);

  // Lookup surveys
  const survey = await database.ref('surveys/' + surveyID).once('value');

  // Register it as an available survey for the user
  return database.ref('users/' + userID + '/availableSurveys/' + surveyID)
    .set({
      postback: surveyID,
      completed: false,
      started: false,
      current: current || false,
      totalQuestions: survey.child('questions').numChildren(),
    });
};

/** Starts user on a survey if he has a Current Survey, or sets him on one from Available if none exist */
survey.surveyChecker = async (userID) => {
  logger.info('Checking if User %d has any available surveys...', userID);

  // If he has a current Survey that's not intialized, set it up and send him into the Looper
  database.ref('users/' + userID + '/availableSurveys').orderByChild('current').equalTo(true).once('value').then(
    // Promise is accepted
    (snapshot) => {
      // Get the current Survey key
      const currentSurvey = snapshot.val();
      const currentSurveyKey = Object.keys(currentSurvey)[0];

      // Get the current Survey state
      const startedCurrentSurvey = snapshot.child(currentSurveyKey).child('started').val();
      const completedCurrentSurvey = snapshot.child(currentSurveyKey).child('completed').val();

      if (currentSurvey && !startedCurrentSurvey) {
        // This means the survey was assigned as active, but not started (usually survey_0)
        logger.info('User %d has an "active" survey: %s! Setting it as the current survey...', userID, currentSurveyKey);

        // Initialize the current Survey State state
        database.ref('users/' + userID + '/currentSurvey/' + currentSurveyKey).set({
          finalQuestion: snapshot.child(currentSurveyKey + '/totalQuestions').val() - 1,
          currentQuestion: 0,
        })
          .then(() => {
            // After that state is updated, now start the user on that Survey
            survey.surveyLooper(userID, currentSurveyKey);

            // Update that survey state to started.
            logger.info('Setting User %d\'s Survey %s to "started" state...', userID, currentSurveyKey);
            database.ref('users/' + userID + '/availableSurveys/' + currentSurveyKey).update({
              started: true,
            });
          });
      } else if (currentSurvey && startedCurrentSurvey) {
        // This means he's already in a survey, and needs to continue looping
        logger.info('User %d is currently on Survey %s! Handing off to looper...', userID, currentSurveyKey);
        survey.surveyLooper(userID, currentSurveyKey);
      } else if (!currentSurvey) {
        // If he has no current surveys, let's assign him one
        database.ref('users/' + userID + '/availableSurveys').orderByChild('completed').equalTo(false)
          .once('value',

          // If the lookup works and the Promise is accepted, run here
          (snapshot) => {
            const availableSurveys = snapshot.val();

            logger.info('User %d has surveys available!', userID);

            // Get available Keys and number of available surveys
            const availableSurveyCount = snapshot.numChildren();
            const availableSurveyKeys = Object.keys(availableSurveys);

            // Grab a random available survey
            const randomSurveyKey = availableSurveyKeys[Math.floor(Math.random() * availableSurveyCount)];

            // Set that that survey as the current Survey
            logger.info('Setting User %d\'s current Survey to: %s...', randomSurveyKey);
            database.ref('users/' + userID + '/availableSurveys/' + randomSurveyKey).update({
              current: true,
            });

            // Inform the user
            logger.info('Sending User %d the Start Survey prompt...', userID);
            sender.sendTextMessage(userID, 'Looks like I do have a survey for you. Would you like to begin? ');
          }).catch(
          // Promise rejected (more likely to hit the higher level catch below than the one here)
          () => {
            logger.info('User %d has completed all available surveys...', userID);
            sender.sendTextMessage(userID, 'Good job, you\'ve answered all my questions!');
          });
      }
      // Promise rejected
    }).catch(() => {
      logger.info('User %d has no available surveys...', userID);
      sender.sendTextMessage(userID, 'I don\'t have any more questions for you. Come back later!');
    });
};

/** Sends a user a given survey question */
survey.surveyQuestionSender = function(userID, surveyID, questionNumber) {
  logger.info('Starting User %d on Survey: \'%s\'...', userID, surveyID);

  //  Get the Question
  database.ref('surveys/' + surveyID + '/questions').once('value', (snapshot) => {
    // Send it to the User
    sender.sendMessage(userID, snapshot.val()[questionNumber]);
  });
};

/** Loops users through their current survey until they are done */
survey.surveyLooper = function(userID, surveyID) {
  database.ref('users/' + userID + '/currentSurvey/' + surveyID).once('value', (snapshot) => {
    const finalQuestion = snapshot.child('finalQuestion').val();
    const currentQuestion = snapshot.child('currentQuestion').val();

    if (currentQuestion <= finalQuestion) {
      // Send them the current question for the current Survey
      logger.info('Sending User %d Question %d for Survey %s...', userID, currentQuestion, surveyID);
      survey.surveyQuestionSender(userID, surveyID, currentQuestion);
    } else {
      // If the current question is greater than the number of questions available, survey done!
      logger.info('User %d has completed Survey %s!', userID, surveyID);

      // Send user an ending Prompt
      sender.sendTextMessage(userID, 'Thanks for sharing your opinion with us!');

      // Update the available Survey state to complete, and make it inactive
      database.ref('users/' + userID + '/availableSurveys/' + surveyID).update({
        completed: true,
        current: false,
      });

      // Remove the current Survey entry
      logger.info('Removing User %d\'s Current Survey: %s. ', userID, surveyID);
      database.ref('users/' + userID + '/currentSurvey/' + surveyID).remove();
    }
  });
};

/** Saves question answers in Firebase, and increment user State */
survey.surveyAnswerSaver = function(userID, questionPayload, answer) {
  // Get the current Survey for the given user
  database.ref('users/' + userID + '/currentSurvey/').once('value', (snapshot) => {
    const surveyID = Object.keys(snapshot.val());

    // Save the users answer using payload text and the new survey ID
    logger.info('Saving User %d response to Question %d on Survey \'%s\': %s...', userID, questionPayload, surveyID, answer);
    database.ref('responses/' + surveyID + '/' + userID + '/' + questionPayload)
      .set({answer})
      .then(() => {
        logger.info('Increment User %d current Survey State to Question %d on Survey %s...', userID, questionPayload++, surveyID);
        database.ref('users/' + userID + '/currentSurvey/' + surveyID).update({
          currentQuestion: questionPayload++,
        });
      });
  });
};
<|endoftext|>


<commit_before>'use strict'
import {button, checkbox, div, h1, h2, h3, p, span, td, tr, select} from '../util/dom.js';
import * as dom from '../util/dom.js';

export function bindRenderUploadSettings(bindings) {
  /*
  {
		'uris': Array.from(document.querySelectorAll('input[name=uris]')),
		'collections': {
			'list': document.querySelector('table.collections > tbody'), // table
			'defaults': document.querySelector('input[name=collection-defaults]'),
			'batch': document.querySelector('input[name=collection-batch]')
		},
		'permissions': {
			'list': document.querySelector('table.permissions > tbody'), // table
			'defaults': document.querySelector('input[name=permission-defaults]')
		}
	}
  */
  return function(options, locale) {
    /*

Settings

URIs

<input type="radio" name="uris" value="filename" checked="checked"/>Filename

<input type="radio" name="uris" value="id"/>_id or id

<input type="radio" name="uris" value="uuid"/>UUID

Collections

staging	<input type="checkbox" name="collections" value="staging" checked="checked"/>

production	<input type="checkbox" name="collections" value="production" checked="checked"/>

<button name="collection-add">+ Add</button>

<input type="checkbox" name="collection-batch" value="true" checked="checked"/>Batch

<input type="checkbox" name="collection-defaults" value="true" checked="checked"/>Defaults

            <colgroup span="1"/>
            <colgroup span="4"/>

Permissions
	R	U	I	E

rest-reader
                <input type="checkbox" name="permission*rest-reader" value="read" checked="checked"/>
                <input type="checkbox" name="permission*rest-reader" value="update"/>
                <input type="checkbox" name="permission*rest-reader" value="insert"/>
                <input type="checkbox" name="permission*rest-reader" value="execute"/>

rest-writer
                <input type="checkbox" name="permission*rest-writer" value="read" checked="checked"/>
                <input type="checkbox" name="permission*rest-writer" value="update" checked="checked"/>
                <input type="checkbox" name="permission*rest-writer" value="insert" checked="checked"/>
                <input type="checkbox" name="permission*rest-writer" value="execute"/>

<button name="permission-add">+ Add</button>

<input type="checkbox" name="permission-defaults" value="true" checked="checked"/>Defaults

     */

    bindings.uris.forEach(uri => uri.checked = (uri.value === options.uri));

    const collections = dom.clear(bindings.collections.list);
    options.collections.user
      .map(coll => tr([
        td(coll.name),
        td(
          checkbox(coll.enabled, undefined, {name: 'collections', value: coll.name}),
          'check'
        )
      ])
      )
      .forEach(row => collections.appendChild(row));

    /*
      permissions: {
  			user: {
  				'some-role': ['read', 'update', 'insert', 'execute']
  			},
  			'default': true,
  			cachedRoles: null // Pre-load the roles from the server for auto-suggest
  		}
    */
    const permissions = dom.clear(bindings.permissions.list);
    const capabilities = ['read', 'update', 'insert', 'execute'];

    /*
    function makeKeyValue(obj) {
      if(obj) {
        const out = {};
        for(let k in obj) {
          if(out[obj[k]]) { throw new Error(`Key ${obj[k]} already exists`); }
          out[obj[k]] = obj[k];
        }
        return out;
      }
    }
    */

    // FIXME: Yuck! This is
    function firstValue(obj) { if(!obj) return; for(let k in obj) return obj[k]; }

    function buildRolesSelect(name, selectedValue) {
      let opts = [];
      if(options && options.permissions && options.permissions.cachedRoles) {
        opts = options.permissions.cachedRoles
          .sort((a, b) => firstValue(a) > firstValue(b) ? 1 : -1);
      }
      return select(
        opts,
        null,
        Object.assign({},
          name ? {name: name} : {},
          selectedValue ? {value: selectedValue} : {}
        )
      );
    }

    Object.keys(options.permissions.user)
      .map(role => tr([
        td(buildRolesSelect(role, role)),
        ...capabilities.map(
          cap => td(
            checkbox(
              options.permissions.user[role].indexOf(cap) > -1,
              undefined,
              {name: 'permission*' + role, value: cap}
            )
            , 'check'
          )
        ),
        td('X')
      ])
      )
      .forEach(row => permissions.appendChild(row));

    bindings.permissions.defaults.checked = options.permissions.default;
  }
}
<commit_msg>Adds hover text for checkboxes<commit_after>'use strict'
import {button, checkbox, div, h1, h2, h3, p, span, td, tr, select} from '../util/dom.js';
import * as dom from '../util/dom.js';

export function bindRenderUploadSettings(bindings) {
  /*
  {
		'uris': Array.from(document.querySelectorAll('input[name=uris]')),
		'collections': {
			'list': document.querySelector('table.collections > tbody'), // table
			'defaults': document.querySelector('input[name=collection-defaults]'),
			'batch': document.querySelector('input[name=collection-batch]')
		},
		'permissions': {
			'list': document.querySelector('table.permissions > tbody'), // table
			'defaults': document.querySelector('input[name=permission-defaults]')
		}
	}
  */
  return function(options, locale) {
    /*

Settings

URIs

<input type="radio" name="uris" value="filename" checked="checked"/>Filename

<input type="radio" name="uris" value="id"/>_id or id

<input type="radio" name="uris" value="uuid"/>UUID

Collections

staging	<input type="checkbox" name="collections" value="staging" checked="checked"/>

production	<input type="checkbox" name="collections" value="production" checked="checked"/>

<button name="collection-add">+ Add</button>

<input type="checkbox" name="collection-batch" value="true" checked="checked"/>Batch

<input type="checkbox" name="collection-defaults" value="true" checked="checked"/>Defaults

            <colgroup span="1"/>
            <colgroup span="4"/>

Permissions
	R	U	I	E

rest-reader
                <input type="checkbox" name="permission*rest-reader" value="read" checked="checked"/>
                <input type="checkbox" name="permission*rest-reader" value="update"/>
                <input type="checkbox" name="permission*rest-reader" value="insert"/>
                <input type="checkbox" name="permission*rest-reader" value="execute"/>

rest-writer
                <input type="checkbox" name="permission*rest-writer" value="read" checked="checked"/>
                <input type="checkbox" name="permission*rest-writer" value="update" checked="checked"/>
                <input type="checkbox" name="permission*rest-writer" value="insert" checked="checked"/>
                <input type="checkbox" name="permission*rest-writer" value="execute"/>

<button name="permission-add">+ Add</button>

<input type="checkbox" name="permission-defaults" value="true" checked="checked"/>Defaults

     */

    bindings.uris.forEach(uri => uri.checked = (uri.value === options.uri));

    const collections = dom.clear(bindings.collections.list);
    options.collections.user
      .map(coll => tr([
        td(coll.name),
        td(
          checkbox(coll.enabled, undefined, {name: 'collections', value: coll.name}),
          'check'
        )
      ])
      )
      .forEach(row => collections.appendChild(row));

    /*
      permissions: {
  			user: {
  				'some-role': ['read', 'update', 'insert', 'execute']
  			},
  			'default': true,
  			cachedRoles: null // Pre-load the roles from the server for auto-suggest
  		}
    */
    const permissions = dom.clear(bindings.permissions.list);
    const capabilities = ['read', 'update', 'insert', 'execute'];

    /*
    function makeKeyValue(obj) {
      if(obj) {
        const out = {};
        for(let k in obj) {
          if(out[obj[k]]) { throw new Error(`Key ${obj[k]} already exists`); }
          out[obj[k]] = obj[k];
        }
        return out;
      }
    }
    */

    // FIXME: Yuck! This is
    function firstValue(obj) { if(!obj) return; for(let k in obj) return obj[k]; }

    function buildRolesSelect(name, selectedValue) {
      let opts = [];
      if(options && options.permissions && options.permissions.cachedRoles) {
        opts = options.permissions.cachedRoles
          .sort((a, b) => firstValue(a) > firstValue(b) ? 1 : -1);
      }
      return select(
        opts,
        null,
        Object.assign({},
          name ? {name: name} : {},
          selectedValue ? {value: selectedValue} : {}
        )
      );
    }

    Object.keys(options.permissions.user)
      .map(role => tr([
        td(buildRolesSelect(role, role)),
        ...capabilities.map(
          cap => td(
            checkbox(
              options.permissions.user[role].indexOf(cap) > -1,
              undefined,
              {
                name: 'permission*' + role, value: cap,
                title: cap // TODO: l12n and capitalization
              }
            )
            , 'check'
          )
        ),
        td('X')
      ])
      )
      .forEach(row => permissions.appendChild(row));

    bindings.permissions.defaults.checked = options.permissions.default;
  }
}
<|endoftext|>


<commit_before>'use strict'

import React from 'react' //eslint-disable-line
import createRoutes from '../app/router'
import { match, RouterContext } from 'react-router'
import { renderToString } from 'react-dom/server'
import { Provider } from 'react-redux'
import createLocation from 'history/lib/createLocation'
import createMemoryHistory from 'history/lib/createMemoryHistory'
import configureStore from '../app/store/configureStore'

export default (url, cb) => {
  const memoryHistory = createMemoryHistory(url)
  let routes = createRoutes(memoryHistory)
  let location = createLocation(url)

  match({ routes, location }, (error, redirectLocation, renderProps) => {
    if (error) console.log('Error matching', error)

    let initialData = {entities: { cars: {}, account: {} }}

    const store = configureStore(initialData, memoryHistory)

    // Grab the initial state from our Redux store
    const finalState = store.getState()

    const rootComponent = renderToString(
      <Provider store={store}>
        <RouterContext {...renderProps} />
      </Provider>
    )

    return cb({
      appData: finalState,
      appMarkup: rootComponent
    })
  })
}
<commit_msg>Wrong routes import  (#12)<commit_after>'use strict'

import React from 'react' //eslint-disable-line
import createRoutes from '../app/routes'
import { match, RouterContext } from 'react-router'
import { renderToString } from 'react-dom/server'
import { Provider } from 'react-redux'
import createLocation from 'history/lib/createLocation'
import createMemoryHistory from 'history/lib/createMemoryHistory'
import configureStore from '../app/store/configureStore'

export default (url, cb) => {
  const memoryHistory = createMemoryHistory(url)
  let routes = createRoutes(memoryHistory)
  let location = createLocation(url)

  match({ routes, location }, (error, redirectLocation, renderProps) => {
    if (error) console.log('Error matching', error)

    let initialData = {entities: { cars: {}, account: {} }}

    const store = configureStore(initialData, memoryHistory)

    // Grab the initial state from our Redux store
    const finalState = store.getState()

    const rootComponent = renderToString(
      <Provider store={store}>
        <RouterContext {...renderProps} />
      </Provider>
    )

    return cb({
      appData: finalState,
      appMarkup: rootComponent
    })
  })
}
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import { Link } from 'react-router'
import Divider from 'material-ui/lib/divider'
import IconMenu from 'material-ui/lib/menus/icon-menu'
import MenuItem from 'material-ui/lib/menus/menu-item'
import List from 'material-ui/lib/lists/list'
import ListItem from 'material-ui/lib/lists/list-item'
import IconButton from 'material-ui/lib/icon-button'
import FontIcon from 'material-ui/lib/font-icon'
import AutoComplete from 'material-ui/lib/auto-complete'

import GMImportDialog from './GMImportDialog'

const GMProjectContent = React.createClass({
  getDataUrl: function() {
    const file = this.props.data
    if (file.filetype === 'resultFile' && file.info.resultType === 'matrix') {
      const markerLabelId = file.info.labels.marker
      const traitLabelId = file.info.labels.trait
      return '/visualization/matrix/' + markerLabelId + '/' + traitLabelId + '/' + file.id
    } else {
      return '/data/' + file.id
    }
  },
  iconButtonElement: function () {
    return (
      <IconButton touch={true}>
        <FontIcon className='material-icons'>more_vert</FontIcon>
      </IconButton>
    )
  },
  rightIconMenu: function () {
    const starMenuItem = (this.props.data.starred)
      ? <MenuItem
        primaryText={'Unstar ' + this.props.data.name}
        onTouchTap={this.handleStarFile}
        leftIcon={<FontIcon className='material-icons'>star</FontIcon>}
        disabled={true}
        />
      : <MenuItem
        primaryText={'Star ' + this.props.data.name}
        onTouchTap={this.handleStarFile}
        leftIcon={<FontIcon className='material-icons'>star_border</FontIcon>}
        disabled={true}
        />

    return (
      <IconMenu iconButtonElement={this.iconButtonElement()}>
        <MenuItem
          primaryText={'Delete ' + this.props.data.name}
          onTouchTap={this.handleDeleteFile}
          leftIcon={<FontIcon className='material-icons'>delete</FontIcon>}
        />
        <MenuItem
          primaryText={'Rename ' + this.props.data.name}
          onTouchTap={this.handleRenameFile}
          leftIcon={<FontIcon className='material-icons'>mode_edit</FontIcon>}
          disabled={true}
        />
        <Divider />
        {starMenuItem}
      </IconMenu>
    )
  },
  handleDeleteFile: function () {
    return this.props.actions.deleteFile(this.props.data.id)
  },
  handleRenameFile: function () {
    return this.props.actions.renameFile(this.props.data.id)
  },
  handleStarFile: function () {
    return
  },
  render: function () {
    return (
      <ListItem
        className='gm-project__file'
        leftIcon={<FontIcon className='material-icons'>assessment</FontIcon>}
        rightIconButton={this.rightIconMenu()}
        nestedLevel={3}>
        <Link to={this.getDataUrl()}>{this.props.data.name}</Link>
      </ListItem>
    )
  }
})

const GMProjectItem = React.createClass({
  render: function() {
    const fileList = this.props.files.map((file, i) =>
      <GMProjectContent
        key={i}
        data={file}
        actions={this.props.actions}
      />
    )

    return (
      <ListItem
        primaryText={this.props.name}
        leftIcon={<FontIcon className='material-icons'>folder</FontIcon>}
        initiallyOpen={false}
        primaryTogglesNestedList={true}
        nestedItems={fileList}
        nestedLevel={1}
      />
    )
  }
})

const GMProject = React.createClass({
  render: function () {
    const items = this.props.project.items

    const dataList = Object.keys(items).map((itemName, i) =>
      <GMProjectItem
        key={i}
        name={itemName}
        files={items[itemName].files}
        actions={this.props.actions}
      />
    )

    return (
      <ListItem
        className='gm-project__container'
        primaryText={this.props.project.name}
        leftIcon={<FontIcon className='material-icons'>folder</FontIcon>}
        initiallyOpen={true}
        primaryTogglesNestedList={true}
        nestedItems={dataList}
      />
    )
  }
})

var GMProjectList = React.createClass({
  render: function () {
    return (

        {
          this.props.projects.map((project, i) =>
            <GMProject
              key={i}
              project={project}
              actions={this.props.actions}
            />
          )
        }

    )
  }
})

var GMProjectSearch = React.createClass({
  getInitialState: function () {
    return {dataSource: []}
  },
  componentWillReceiveProps: function (nextProps) {
    const dataSource = nextProps.projects.reduce(function (a, b) {
      return a.concat(b.files.reduce(function (c, d) {
        return c.concat(d.name)
      }, []))
    }, [])
    this.setState({ dataSource })
  },
  render: function () {
    return (
      <ListItem
        primaryText=''
        disabled={true}
        leftIcon={<FontIcon className='material-icons'>search</FontIcon>}
        style={{display: 'inline'}}>
        <AutoComplete
          hintText='Search for files'
          dataSource={this.state.dataSource}
          filter={AutoComplete.caseInsensitiveFilter}
          style={{width: 'inherit'}} />
      </ListItem>
    )
  }
})

class GMProjectMenu extends Component {
  render () {
    return (
      <List>
        <GMProjectList projects={this.props.projects} actions={this.props.projectActions} />
        <Divider />
        <GMImportDialog
          submit={this.props.importDataSubmit}
          projects={this.props.projects}
        />
        <GMProjectSearch projects={this.props.projects} />
      </List>
    )
  }
}

GMProjectMenu.propTypes = {
  projects: PropTypes.array.isRequired,
  projectActions: PropTypes.object.isRequired,
  importDataSubmit: PropTypes.func.isRequired
}

export default GMProjectMenu
<commit_msg>readd file download<commit_after>import React, { Component, PropTypes } from 'react'
import { Link } from 'react-router'
import Divider from 'material-ui/lib/divider'
import IconMenu from 'material-ui/lib/menus/icon-menu'
import MenuItem from 'material-ui/lib/menus/menu-item'
import List from 'material-ui/lib/lists/list'
import ListItem from 'material-ui/lib/lists/list-item'
import IconButton from 'material-ui/lib/icon-button'
import FontIcon from 'material-ui/lib/font-icon'
import AutoComplete from 'material-ui/lib/auto-complete'

import GMImportDialog from './GMImportDialog'

const GMProjectContent = React.createClass({
  getDataUrl: function() {
    const file = this.props.data
    if (file.filetype === 'resultFile' && file.info.resultType === 'matrix') {
      const markerLabelId = file.info.labels.marker
      const traitLabelId = file.info.labels.trait
      return '/visualization/matrix/' + markerLabelId + '/' + traitLabelId + '/' + file.id
    } else {
      return '/data/' + file.id
    }
  },
  iconButtonElement: function () {
    return (
      <IconButton touch={true}>
        <FontIcon className='material-icons'>more_vert</FontIcon>
      </IconButton>
    )
  },
  rightIconMenu: function () {
    const starMenuItem = (this.props.data.starred)
      ? <MenuItem
        primaryText={'Unstar ' + this.props.data.name}
        onTouchTap={this.handleStarFile}
        leftIcon={<FontIcon className='material-icons'>star</FontIcon>}
        disabled={true}
        />
      : <MenuItem
        primaryText={'Star ' + this.props.data.name}
        onTouchTap={this.handleStarFile}
        leftIcon={<FontIcon className='material-icons'>star_border</FontIcon>}
        disabled={true}
        />

    return (
      <IconMenu iconButtonElement={this.iconButtonElement()}>
        <MenuItem
          primaryText={'Download ' + this.props.data.name}
          onTouchTap={this.handleDownloadFile}
          leftIcon={<FontIcon className='material-icons'>file_download</FontIcon>}
        />
        <MenuItem
          primaryText={'Delete ' + this.props.data.name}
          onTouchTap={this.handleDeleteFile}
          leftIcon={<FontIcon className='material-icons'>delete</FontIcon>}
        />
        <MenuItem
          primaryText={'Rename ' + this.props.data.name}
          onTouchTap={this.handleRenameFile}
          leftIcon={<FontIcon className='material-icons'>mode_edit</FontIcon>}
          disabled={true}
        />
        <Divider />
        {starMenuItem}
      </IconMenu>
    )
  },
  handleDeleteFile: function () {
    return this.props.actions.deleteFile(this.props.data.id)
  },
  handleDownloadFile: function() {
    this.props.actions.downloadFile(this.props.data.id)
    return
  },
  handleRenameFile: function () {
    return this.props.actions.renameFile(this.props.data.id)
  },
  handleStarFile: function () {
    return
  },
  render: function () {
    return (
      <ListItem
        className='gm-project__file'
        leftIcon={<FontIcon className='material-icons'>assessment</FontIcon>}
        rightIconButton={this.rightIconMenu()}
        nestedLevel={3}>
        <Link to={this.getDataUrl()}>{this.props.data.name}</Link>
      </ListItem>
    )
  }
})

const GMProjectItem = React.createClass({
  render: function() {
    const fileList = this.props.files.map((file, i) =>
      <GMProjectContent
        key={i}
        data={file}
        actions={this.props.actions}
      />
    )

    return (
      <ListItem
        primaryText={this.props.name}
        leftIcon={<FontIcon className='material-icons'>folder</FontIcon>}
        initiallyOpen={false}
        primaryTogglesNestedList={true}
        nestedItems={fileList}
        nestedLevel={1}
      />
    )
  }
})

const GMProject = React.createClass({
  render: function () {
    const items = this.props.project.items

    const dataList = Object.keys(items).map((itemName, i) =>
      <GMProjectItem
        key={i}
        name={itemName}
        files={items[itemName].files}
        actions={this.props.actions}
      />
    )

    return (
      <ListItem
        className='gm-project__container'
        primaryText={this.props.project.name}
        leftIcon={<FontIcon className='material-icons'>folder</FontIcon>}
        initiallyOpen={true}
        primaryTogglesNestedList={true}
        nestedItems={dataList}
      />
    )
  }
})

var GMProjectList = React.createClass({
  render: function () {
    return (

        {
          this.props.projects.map((project, i) =>
            <GMProject
              key={i}
              project={project}
              actions={this.props.actions}
            />
          )
        }

    )
  }
})

var GMProjectSearch = React.createClass({
  getInitialState: function () {
    return {dataSource: []}
  },
  componentWillReceiveProps: function (nextProps) {
    const dataSource = nextProps.projects.reduce(function (a, b) {
      return a.concat(b.files.reduce(function (c, d) {
        return c.concat(d.name)
      }, []))
    }, [])
    this.setState({ dataSource })
  },
  render: function () {
    return (
      <ListItem
        primaryText=''
        disabled={true}
        leftIcon={<FontIcon className='material-icons'>search</FontIcon>}
        style={{display: 'inline'}}>
        <AutoComplete
          hintText='Search for files'
          dataSource={this.state.dataSource}
          filter={AutoComplete.caseInsensitiveFilter}
          style={{width: 'inherit'}} />
      </ListItem>
    )
  }
})

class GMProjectMenu extends Component {
  render () {
    return (
      <List>
        <GMProjectList projects={this.props.projects} actions={this.props.projectActions} />
        <Divider />
        <GMImportDialog
          submit={this.props.importDataSubmit}
          projects={this.props.projects}
        />
        <GMProjectSearch projects={this.props.projects} />
      </List>
    )
  }
}

GMProjectMenu.propTypes = {
  projects: PropTypes.array.isRequired,
  projectActions: PropTypes.object.isRequired,
  importDataSubmit: PropTypes.func.isRequired
}

export default GMProjectMenu
<|endoftext|>


<commit_before>/**
  上一个例子的微信墙没有获得用户头像、名字等信息
  这些信息要通过另一类微信API，也就是由服务器主动调用微信获得
  这一类API的安全机制不同于之前，不再通过简单的TOKEN校验
  而需要通过appID、appSecret获得access_token，然后再用
  access_token获取相应的数据

  可以先看以下代码：
  lib/config.js - appID和appSecret配置
  lib/token.js  - 获得有效token
  lib/user.js   - 获得用户信息
  lib/reply.js  - 回复微信的模板
  lib/ws.js     - 简单的websocket
*/

var PORT = require('./lib/config').wxPort;
var getUserInfo = require('./lib/user').getUserInfo;
var replyText = require('./lib/reply').replyText;

var wss = require('./lib/ws.js').wss;

// var PORT = 9529;
var http = require('http');
var qs = require('qs');
var crypto =require('crypto');
var url=require('url');
var xml2js =require('xml2js');
var tmpl = require('tmpl');
var TOKEN = 'rinesnow';

var express = require('express');
app = express();
app.use(express.static(__dirname));
app.listen(require('./lib/config').indexPort);

function checkSignature(params, token){
  //1. 将token、timestamp、nonce三个参数进行字典序排序
  //2. 将三个参数字符串拼接成一个字符串进行sha1加密
  //3. 开发者获得加密后的字符串可与signature对比，标识该请求来源于微信

  var key = [token, params.timestamp, params.nonce].sort().join('');
  var sha1 = require('crypto').createHash('sha1');
  sha1.update(key);

  return  sha1.digest('hex') == params.signature;
}

var server = http.createServer(function (request, response) {

  //解析URL中的query部分，用qs模块(npm install qs)将query解析成json
  var query = require('url').parse(request.url).query;
  var params = qs.parse(query);

  if(!checkSignature(params, TOKEN)){
    //如果签名不对，结束请求并返回
    response.end('signature fail');
    return;
  }

  if(request.method == "GET"){
    //如果请求是GET，返回echostr用于通过服务器有效校验
    response.end(params.echostr);
  }else{
    //否则是微信给开发者服务器的POST请求
    var postdata = "";

    request.addListener("data",function(postchunk){
        postdata += postchunk;
    });

    //获取到了POST数据
    request.addListener("end",function(){
      console.log(postdata);

      var parseString = xml2js.parseString;

      parseString(postdata, function (err, result) {
         // console.log('err'+result);

        if(!err){
         // console.log('if'+result);
          if(result.xml.MsgType[0] === 'text'){
              getUserInfo(result.xml.FromUserName[0], function (userInfo) {
                    result.user = userInfo;
                    var res = replyText(result, '消息推送成功！');
                                       console.log(result);

                    socket.broadcast.emit('newUserInfo',result);
                    response.end(res);

              });
            // getUserInfo(result.xml.FromUserName[0])
            // .then(function(userInfo){
            //   //获得用户信息，合并到消息中
            //   result.user = userInfo;
            //   //将消息通过websocket广播
            //        console.log(result);

            //   console.log('wode shuchu'+result.xml.MsgType[0]);
            //   wss.broadcast(result);
            //   // wss.liu(result);

            //   var res = replyText(result, '消息推送成功！');

            //   response.end(res);
            // })
          }
        }
      });
    });
  }
});
var messages = [];
messages.push('welcome myChat');

var io = require('socket.io').listen(server);
io.sockets.on('connection',function(socket){
  socket.emit('connected');
  console.log('connected');

  socket.broadcast.emit('newClient',new Date());

  socket.on('getAllMessages',function(){
      socket.emit('allMessages',messages);
  });

  socket.on('addMessage',function(message){
      messages.unshift(message);
      io.sockets.emit('newMessage',message);
  });
});

server.listen(PORT);

console.log("Weixin server runing at port: " + PORT + ".");<commit_msg>socketio<commit_after>/**
  上一个例子的微信墙没有获得用户头像、名字等信息
  这些信息要通过另一类微信API，也就是由服务器主动调用微信获得
  这一类API的安全机制不同于之前，不再通过简单的TOKEN校验
  而需要通过appID、appSecret获得access_token，然后再用
  access_token获取相应的数据

  可以先看以下代码：
  lib/config.js - appID和appSecret配置
  lib/token.js  - 获得有效token
  lib/user.js   - 获得用户信息
  lib/reply.js  - 回复微信的模板
  lib/ws.js     - 简单的websocket
*/

var PORT = require('./lib/config').wxPort;
// var getUserInfo = require('./lib/user').getUserInfo;
var replyText = require('./lib/reply').replyText;

// var wss = require('./lib/ws.js').wss;

// var PORT = 9529;
var http = require('http');
var qs = require('qs');
var crypto =require('crypto');
var url=require('url');
var xml2js =require('xml2js');
var tmpl = require('tmpl');
var TOKEN = 'rinesnow';

var express = require('express');
app = express();
app.use(express.static(__dirname));
app.listen(require('./lib/config').indexPort);

function checkSignature(params, token){
  //1. 将token、timestamp、nonce三个参数进行字典序排序
  //2. 将三个参数字符串拼接成一个字符串进行sha1加密
  //3. 开发者获得加密后的字符串可与signature对比，标识该请求来源于微信

  var key = [token, params.timestamp, params.nonce].sort().join('');
  var sha1 = require('crypto').createHash('sha1');
  sha1.update(key);

  return  sha1.digest('hex') == params.signature;
}

var server = http.createServer(function (request, response) {

  //解析URL中的query部分，用qs模块(npm install qs)将query解析成json
  var query = require('url').parse(request.url).query;
  var params = qs.parse(query);

  if(!checkSignature(params, TOKEN)){
    //如果签名不对，结束请求并返回
    response.end('signature fail');
    return;
  }

  if(request.method == "GET"){
    //如果请求是GET，返回echostr用于通过服务器有效校验
    response.end(params.echostr);
  }else{
    //否则是微信给开发者服务器的POST请求
    var postdata = "";

    request.addListener("data",function(postchunk){
        postdata += postchunk;
    });

    //获取到了POST数据
    request.addListener("end",function(){
      console.log(postdata);

      var parseString = xml2js.parseString;

      parseString(postdata, function (err, result) {
         // console.log('err'+result);

        if(!err){
         // console.log('if'+result);
          if(result.xml.MsgType[0] === 'text'){
              getUserInfo(result.xml.FromUserName[0], function (userInfo) {
                    result.user = userInfo;
                    var res = replyText(result, '消息推送成功！');
                    console.log(result);
                    socket.broadcast.emit('newUserInfo',result);
                    response.end(res);

              });
            // getUserInfo(result.xml.FromUserName[0])
            // .then(function(userInfo){
            //   //获得用户信息，合并到消息中
            //   result.user = userInfo;
            //   //将消息通过websocket广播
            //        console.log(result);

            //   console.log('wode shuchu'+result.xml.MsgType[0]);
            //   wss.broadcast(result);
            //   // wss.liu(result);

            //   var res = replyText(result, '消息推送成功！');

            //   response.end(res);
            // })
          }
        }
      });
    });
  }
});

function getUserInfo(openID,callback){
    var token = asses_token.access_token;
    request('https://api.weixin.qq.com/cgi-bin/user/info?access_token='+token+'&openid='+openID+'&lang=zh_CN', function(err, res, data){
        callback(JSON.parse(data));
        //resolve(JSON.parse(data));
    });
}

// function getToken(appID, appSecret){
//         request('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid='+appID+'&secret=' + appSecret, function(err, res, data){
//              asses_token = JSON.parse(data);
//              console.log(asses_token);
//         });
//     console.log('1');
// }

var messages = [];
messages.push('welcome myChat');

var io = require('socket.io').listen(server);
io.sockets.on('connection',function(socket){
  socket.emit('connected');
  console.log('connected');

  socket.broadcast.emit('newClient',new Date());

  socket.on('getAllMessages',function(){
      socket.emit('allMessages',messages);
  });

  socket.on('addMessage',function(message){
      messages.unshift(message);
      io.sockets.emit('newMessage',message);
  });
});

server.listen(PORT);

console.log("Weixin server runing at port: " + PORT + ".");<|endoftext|>


<commit_before>(function (define) {
define(function () {

	return function (options) {

		/**
		 * Base network event strategy function.
		 * @type strategyFunction
		 * @private
		 * @param source {Object} the adapter that sourced the event
		 * @param dest {Object} the adapter receiving the event
		 * @param data {Object} any data associated with the event
		 * @param type {String} the type of event
		 * @param api {Object} helpful functions for strategies
		 */
		return function baseStrategy (source, dest, data, type, api) {
			if (type in dest && source != dest) {
				if (typeof dest[type] != 'function') {
					throw new Error('Hub: ' + type + ' is not a function.');
				}
				dest[type](data);
			}
		};

	};

});
}(
	typeof define == 'function' && define.amd
		? define
		: function (factory) { module.exports = factory(); }
));<commit_msg>more meaningful comment<commit_after>(function (define) {
define(function () {

	/**
	 * Creates a base strategy function.
	 * @param options {Object} not currently used
	 * @return {Function} a strategy function
	 */
	return function (options) {

		return function baseStrategy (source, dest, data, type, api) {
			if (type in dest && source != dest) {
				if (typeof dest[type] != 'function') {
					throw new Error('Hub: ' + type + ' is not a function.');
				}
				dest[type](data);
			}
		};

	};

});
}(
	typeof define == 'function' && define.amd
		? define
		: function (factory) { module.exports = factory(); }
));<|endoftext|>


<commit_before>define(function(require) {
    'use strict';

    var Select2Component;
    var $ = require('jquery');
    var _ = require('underscore');
    var tools = require('oroui/js/tools');
    var Select2View = require('oroform/js/app/views/select2-view');
    var BaseComponent = require('oroui/js/app/components/base/component');

    Select2Component = BaseComponent.extend({

        url: '',
        perPage: 10,
        excluded: [],
        view: null,
        ViewType: Select2View,

        /**
         * @constructor
         * @param {Object} options
         */
        initialize: function(options) {
            var config = options.configs || {};
            this.perPage = _.result(config, 'per_page') || this.perPage;
            this.url = _.result(options, 'url') || '';
            this.excluded = _.result(options, 'excluded') || this.excluded;
            config = this.preConfig(config);
            config = this.setConfig(config);
            if (options._sourceElement.is('select') || config.query || config.ajax || config.data || config.tags) {
                this.view = new this.ViewType(this.prepareViewOptions(options, config));
            }
        },

        /**
         * Prepares options for the related view
         *
         * @param {Object} options - component's options
         * @param {Object} config - select2's options
         * @return {Object}
         */
        prepareViewOptions: function(options, config) {
            return {
                el: options._sourceElement,
                select2Config: config
            };
        },

        preConfig: function(config) {
            var that = this;
            if (this.url) {
                config.ajax = {
                    url: this.url,
                    data: function(query, page) {
                        return {
                            page: page,
                            per_page: that.perPage,
                            name: config.autocomplete_alias,
                            query: that.makeQuery(query, config)
                        };
                    },
                    results: function(data, page) {
                        return data;
                    }
                };
            }

            if (!config.hasOwnProperty('createSearchChoice') &&
                config.hasOwnProperty('ajax') &&
                config.hasOwnProperty('tags')
            ) {
                config.createSearchChoice = function(term, data) {
                    if (!dataHasText(data, term)) {
                        return {
                            id: term,
                            text: term
                        };
                    }
                };
            }

            return config;
        },

        setConfig: function(config) {
            var that = this;
            // configure AJAX object if it exists
            if (config.ajax !== undefined) {
                config.minimumInputLength = _.result(config, 'minimumInputLength', 0);
                config.initSelection = _.result(config, 'initSelection') || _.partial(initSelection, config);
                if (that.excluded) {
                    config.ajax.results = _.wrap(config.ajax.results, function(func, data, page) {
                        var response = func.call(this, data, page);
                        response.results = _.filter(response.results, function(item) {
                            return !item.hasOwnProperty('id') || _.indexOf(that.excluded, item.id) < 0;
                        });
                        return response;
                    });
                }
                config.ajax.quietMillis = _.result(config.ajax, 'quietMillis') || 700;
            } else {
                // configure non AJAX based Select2
                if (config.minimumResultsForSearch === undefined) {
                    config.minimumResultsForSearch = 7;
                }
                config.sortResults = function(results, container, query) {
                    if (!query.term || query.term.length < 1) {
                        return results;
                    }
                    var expression = tools.safeRegExp(query.term, 'im');

                    var sortIteratorDelegate = function(first, second) {
                        var inFirst = first.text.search(expression);
                        var inSecond = second.text.search(expression);

                        if (inFirst === -1 || inSecond === -1) {
                            return inSecond - inFirst;
                        }

                        return inFirst - inSecond;
                    };

                    return results.sort(sortIteratorDelegate);
                };
            }
            // set default values for other Select2 options
            if (config.formatResult === undefined) {
                config.formatResult = formatFabric(config, config.result_template || false);
            }
            if (config.formatSelection === undefined) {
                config.formatSelection = formatFabric(config, config.selection_template || false);
            }
            _.defaults(config, {
                escapeMarkup: function(m) { return m; },
                dropdownAutoWidth: true,
                openOnEnter: null
            });

            return config;
        },

        makeQuery: function(query, configs) {
            return query;
        }
    });

    function initSelection(config, element, callback) {

        function handleResults(data) {
            if (config.multiple === true) {
                callback(data);
            } else {
                var item = data.pop();
                if (!_.isUndefined(item.children) && _.isArray(item.children)) {
                    callback(item.children.pop());
                } else {
                    callback(item);
                }
            }
        }

        function setSelect2ValueById(id) {
            var ids = _.isArray(id) ? id.join(config.separator) : id;
            var select2Obj = element.data('select2');
            var ajaxOptions = select2Obj.opts.ajax;
            var searchData = ajaxOptions.data(ids, 1, true);
            var url = _.isFunction(ajaxOptions.url) ? ajaxOptions.url.call(select2Obj, ids, 1) : ajaxOptions.url;

            searchData.search_by_id = true;
            element.trigger('select2-data-request');
            $.ajax({
                url: url,
                data: searchData,
                success: function(response) {
                    if (_.isFunction(ajaxOptions.results)) {
                        response = ajaxOptions.results.call(select2Obj, response, 1);
                    }
                    if (typeof response.results !== 'undefined') {
                        handleResults(response.results);
                    }
                    element.trigger({type: 'select2-data-loaded', items: response});
                }
            });
        }

        var selectedData;
        var dataIds;
        var currentValue = tools.ensureArray(element.inputWidget('val'));

        if (config.forceSelectedData && element.data('selected-data')) {
            var data = element.data('selected-data');
            var result = [];
            if (!_.isObject(data)) {
                _.each(data.split(config.separator), function(item) {
                    result.push(JSON.parse(item));
                });
            }
            handleResults(result.length > 0 ? result : data);
            return;
        }

        selectedData = _.filter(
            tools.ensureArray(element.data('selected-data')),
            function(item) {
                return _.isObject(item);
            }
        );

        var emptySelection = selectedData.length === 0 ||
            (selectedData.length === 1 && _.first(selectedData).id === null && _.first(selectedData).name === null);

        if (!emptySelection) {
            dataIds = _.map(selectedData, function(item) {
                return item.id;
            });

            // handle case when creation of new item allowed and value should be restored (f.e. validation failed)
            dataIds = _.compact(dataIds);

            if (dataIds.length === 0 ||
                dataIds.sort().join(config.separator) === currentValue.sort().join(config.separator)) {
                handleResults(selectedData);
                return;
            }
        }
        setSelect2ValueById(currentValue);
    }
    function highlightSelection(str, selection) {
        return str && selection && selection.term ?
            str.replace(tools.safeRegExp(selection.term.trim(), 'ig'), '$&') : str;
    }

    function getTitle(data, properties) {
        var title = '';
        var result;
        if (data) {
            if (properties === undefined) {
                if (data.text !== undefined) {
                    title = data.text;
                }
            } else {
                result = [];
                _.each(properties, function(property) {
                    result.push(data[property]);
                });
                title = result.join(' ');
            }
        }
        return title;
    }

    function formatFabric(config, jsTemplate) {
        // pre-compile template if it exists
        if (jsTemplate && !_.isFunction(jsTemplate)) {
            jsTemplate = _.template(jsTemplate);
        }

        return function(object, container, query) {
            if ($.isEmptyObject(object)) {
                return undefined;
            }
            var result = '';
            var highlight = function(str) {
                    return object.children ? str : highlightSelection(str, query);
                };
            if (object._html !== undefined) {
                result = _.escape(object._html);
            } else if (jsTemplate) {
                object = _.clone(object);
                object.highlight = highlight;
                if (config.formatContext !== undefined) {
                    object.context = config.formatContext();
                }
                result = jsTemplate(object);
            } else {
                result = highlight(_.escape(getTitle(object, config.properties)));
            }
            return result;
        };
    }

    function dataHasText(data, text) {
        return _.some(data, function(row) {
            if (!row.hasOwnProperty('children')) {
                return row.text.localeCompare(text) === 0;
            }

            return dataHasText(row.children, text);
        });
    }

    return Select2Component;
});
<commit_msg>BAP-13073: Select2-based selectors don't set cancel icon automatically (#5868)<commit_after>define(function(require) {
    'use strict';

    var Select2Component;
    var $ = require('jquery');
    var _ = require('underscore');
    var tools = require('oroui/js/tools');
    var Select2View = require('oroform/js/app/views/select2-view');
    var BaseComponent = require('oroui/js/app/components/base/component');

    Select2Component = BaseComponent.extend({

        url: '',
        perPage: 10,
        excluded: [],
        view: null,
        ViewType: Select2View,

        /**
         * @constructor
         * @param {Object} options
         */
        initialize: function(options) {
            var config = options.configs || {};
            this.perPage = _.result(config, 'per_page') || this.perPage;
            this.url = _.result(options, 'url') || '';
            this.excluded = _.result(options, 'excluded') || this.excluded;
            config = this.preConfig(config);
            config = this.setConfig(config);
            if (config.allowClear === undefined && !options._sourceElement[0].required) {
                config.allowClear = true;
            }
            if (options._sourceElement.is('select') || config.query || config.ajax || config.data || config.tags) {
                this.view = new this.ViewType(this.prepareViewOptions(options, config));
            }
        },

        /**
         * Prepares options for the related view
         *
         * @param {Object} options - component's options
         * @param {Object} config - select2's options
         * @return {Object}
         */
        prepareViewOptions: function(options, config) {
            return {
                el: options._sourceElement,
                select2Config: config
            };
        },

        preConfig: function(config) {
            var that = this;
            if (this.url) {
                config.ajax = {
                    url: this.url,
                    data: function(query, page) {
                        return {
                            page: page,
                            per_page: that.perPage,
                            name: config.autocomplete_alias,
                            query: that.makeQuery(query, config)
                        };
                    },
                    results: function(data, page) {
                        return data;
                    }
                };
            }

            if (!config.hasOwnProperty('createSearchChoice') &&
                config.hasOwnProperty('ajax') &&
                config.hasOwnProperty('tags')
            ) {
                config.createSearchChoice = function(term, data) {
                    if (!dataHasText(data, term)) {
                        return {
                            id: term,
                            text: term
                        };
                    }
                };
            }

            return config;
        },

        setConfig: function(config) {
            var that = this;
            // configure AJAX object if it exists
            if (config.ajax !== undefined) {
                config.minimumInputLength = _.result(config, 'minimumInputLength', 0);
                config.initSelection = _.result(config, 'initSelection') || _.partial(initSelection, config);
                if (that.excluded) {
                    config.ajax.results = _.wrap(config.ajax.results, function(func, data, page) {
                        var response = func.call(this, data, page);
                        response.results = _.filter(response.results, function(item) {
                            return !item.hasOwnProperty('id') || _.indexOf(that.excluded, item.id) < 0;
                        });
                        return response;
                    });
                }
                config.ajax.quietMillis = _.result(config.ajax, 'quietMillis') || 700;
            } else {
                // configure non AJAX based Select2
                if (config.minimumResultsForSearch === undefined) {
                    config.minimumResultsForSearch = 7;
                }
                config.sortResults = function(results, container, query) {
                    if (!query.term || query.term.length < 1) {
                        return results;
                    }
                    var expression = tools.safeRegExp(query.term, 'im');

                    var sortIteratorDelegate = function(first, second) {
                        var inFirst = first.text.search(expression);
                        var inSecond = second.text.search(expression);

                        if (inFirst === -1 || inSecond === -1) {
                            return inSecond - inFirst;
                        }

                        return inFirst - inSecond;
                    };

                    return results.sort(sortIteratorDelegate);
                };
            }
            // set default values for other Select2 options
            if (config.formatResult === undefined) {
                config.formatResult = formatFabric(config, config.result_template || false);
            }
            if (config.formatSelection === undefined) {
                config.formatSelection = formatFabric(config, config.selection_template || false);
            }
            _.defaults(config, {
                escapeMarkup: function(m) { return m; },
                dropdownAutoWidth: true,
                openOnEnter: null
            });

            return config;
        },

        makeQuery: function(query, configs) {
            return query;
        }
    });

    function initSelection(config, element, callback) {

        function handleResults(data) {
            if (config.multiple === true) {
                callback(data);
            } else {
                var item = data.pop();
                if (!_.isUndefined(item.children) && _.isArray(item.children)) {
                    callback(item.children.pop());
                } else {
                    callback(item);
                }
            }
        }

        function setSelect2ValueById(id) {
            var ids = _.isArray(id) ? id.join(config.separator) : id;
            var select2Obj = element.data('select2');
            var ajaxOptions = select2Obj.opts.ajax;
            var searchData = ajaxOptions.data(ids, 1, true);
            var url = _.isFunction(ajaxOptions.url) ? ajaxOptions.url.call(select2Obj, ids, 1) : ajaxOptions.url;

            searchData.search_by_id = true;
            element.trigger('select2-data-request');
            $.ajax({
                url: url,
                data: searchData,
                success: function(response) {
                    if (_.isFunction(ajaxOptions.results)) {
                        response = ajaxOptions.results.call(select2Obj, response, 1);
                    }
                    if (typeof response.results !== 'undefined') {
                        handleResults(response.results);
                    }
                    element.trigger({type: 'select2-data-loaded', items: response});
                }
            });
        }

        var selectedData;
        var dataIds;
        var currentValue = tools.ensureArray(element.inputWidget('val'));

        if (config.forceSelectedData && element.data('selected-data')) {
            var data = element.data('selected-data');
            var result = [];
            if (!_.isObject(data)) {
                _.each(data.split(config.separator), function(item) {
                    result.push(JSON.parse(item));
                });
            }
            handleResults(result.length > 0 ? result : data);
            return;
        }

        selectedData = _.filter(
            tools.ensureArray(element.data('selected-data')),
            function(item) {
                return _.isObject(item);
            }
        );

        var emptySelection = selectedData.length === 0 ||
            (selectedData.length === 1 && _.first(selectedData).id === null && _.first(selectedData).name === null);

        if (!emptySelection) {
            dataIds = _.map(selectedData, function(item) {
                return item.id;
            });

            // handle case when creation of new item allowed and value should be restored (f.e. validation failed)
            dataIds = _.compact(dataIds);

            if (dataIds.length === 0 ||
                dataIds.sort().join(config.separator) === currentValue.sort().join(config.separator)) {
                handleResults(selectedData);
                return;
            }
        }
        setSelect2ValueById(currentValue);
    }
    function highlightSelection(str, selection) {
        return str && selection && selection.term ?
            str.replace(tools.safeRegExp(selection.term.trim(), 'ig'), '$&') : str;
    }

    function getTitle(data, properties) {
        var title = '';
        var result;
        if (data) {
            if (properties === undefined) {
                if (data.text !== undefined) {
                    title = data.text;
                }
            } else {
                result = [];
                _.each(properties, function(property) {
                    result.push(data[property]);
                });
                title = result.join(' ');
            }
        }
        return title;
    }

    function formatFabric(config, jsTemplate) {
        // pre-compile template if it exists
        if (jsTemplate && !_.isFunction(jsTemplate)) {
            jsTemplate = _.template(jsTemplate);
        }

        return function(object, container, query) {
            if ($.isEmptyObject(object)) {
                return undefined;
            }
            var result = '';
            var highlight = function(str) {
                    return object.children ? str : highlightSelection(str, query);
                };
            if (object._html !== undefined) {
                result = _.escape(object._html);
            } else if (jsTemplate) {
                object = _.clone(object);
                object.highlight = highlight;
                if (config.formatContext !== undefined) {
                    object.context = config.formatContext();
                }
                result = jsTemplate(object);
            } else {
                result = highlight(_.escape(getTitle(object, config.properties)));
            }
            return result;
        };
    }

    function dataHasText(data, text) {
        return _.some(data, function(row) {
            if (!row.hasOwnProperty('children')) {
                return row.text.localeCompare(text) === 0;
            }

            return dataHasText(row.children, text);
        });
    }

    return Select2Component;
});
<|endoftext|>


<commit_before>
var newTextElements = [];
document.addEventListener('DOMContentLoaded', function() {
  var addButton = document.getElementById('add');

  addButton.addEventListener('click', function() {
    //window.open('https://www.facebook.com','_blank');
    addWord();
    // getAllKeys(newTextElements);

  }, false);

  // var settingsButton = document.getElementById('settings');
  // settingsButton.addEventListener('click', function() {

  //   window.open('options.html','_blank');

  // }, false);

}, false);

function addWord() {
  var word = document.getElementById('wordToReplace').value;
  var replacement = document.getElementById('replacementWord').value;
  console.log(word);
  var inputObj = Object();
  inputObj[word] = replacement;
  console.log(inputObj);
  chrome.storage.sync.set(inputObj, function() {
    // Notify that we saved.
    console.log('custom words saved');
  });
}
<commit_msg>add pop up closing functionality<commit_after>
var newTextElements = [];
document.addEventListener('DOMContentLoaded', function() {
  var addButton = document.getElementById('add');

  addButton.addEventListener('click', function() {
    addWord();
    window.close();

  }, false);

  // var settingsButton = document.getElementById('settings');
  // settingsButton.addEventListener('click', function() {

  //   window.open('options.html','_blank');

  // }, false);

}, false);

function addWord() {
  var word = document.getElementById('wordToReplace').value;
  var replacement = document.getElementById('replacementWord').value;
  console.log(word);
  var inputObj = Object();
  inputObj[word] = replacement;
  console.log(inputObj);
  chrome.storage.sync.set(inputObj, function() {
    // Notify that we saved.
    console.log('custom words saved');
  });
}
<|endoftext|>


<commit_before>import Document from './Document'

export default class NextDocument extends Document {
  constructor (documentSchema, nextSchema) {
    super(documentSchema)

    this._nextSchema = nextSchema
    const rootType = nextSchema.rootType
    // for the root node id is always === type
    this.root = this.create({ type: rootType, id: rootType })
  }

  find (cssSelector) {
    if (this.root) {
      return this.root.find(cssSelector)
    }
  }

  findAll (cssSelector) {
    if (this.root) {
      return this.root.findAll(cssSelector)
    }
  }

  newInstance () {
    return new NextDocument(this.schema, this._nextSchema)
  }

  fromXml (xmlStr, context) {
    this._nextSchema.importDocumentFromXml(this, xmlStr, context)
    return this
  }

  fromJson (json) {
    super.fromJson(json)
    // making sure that root points to the correct node, in case the root node has been overwritten
    this.root = this.get(this.root.id)
    return this
  }

  toXml (context, options) {
    return this._nextSchema.exportDocumentToXml(this, context, options)
  }
}
<commit_msg>Introduce doc.type<commit_after>import Document from './Document'

export default class NextDocument extends Document {
  constructor (documentSchema, nextSchema) {
    super(documentSchema)

    this._nextSchema = nextSchema
    const rootType = nextSchema.rootType
    // for the root node id is always === type
    this.root = this.create({ type: rootType, id: rootType })
    this.type = rootType
  }

  find (cssSelector) {
    if (this.root) {
      return this.root.find(cssSelector)
    }
  }

  findAll (cssSelector) {
    if (this.root) {
      return this.root.findAll(cssSelector)
    }
  }

  newInstance () {
    return new NextDocument(this.schema, this._nextSchema)
  }

  fromXml (xmlStr, context) {
    this._nextSchema.importDocumentFromXml(this, xmlStr, context)
    return this
  }

  fromJson (json) {
    super.fromJson(json)
    // making sure that root points to the correct node, in case the root node has been overwritten
    this.root = this.get(this.root.id)
    return this
  }

  toXml (context, options) {
    return this._nextSchema.exportDocumentToXml(this, context, options)
  }
}
<|endoftext|>