<commit_before>"use strict";

module.exports = function(grunt) {

  var loadGruntTasks = require('load-grunt-tasks')(grunt, {
    pattern: 'grunt-*',
    config: './package.json',
    scope: 'dependencies'
  });

  var config, customGrunt;
  var basePath = grunt.option('basePath') || "./";
  var appName = require("../package.json").name;
  var cordovaAppRoot = 'cordova-app/' + appName + '/www/';

  customGrunt = require("./Gruntfile_custom.js");

  try {
    dir.app = require("./bower.json").appPath || dir.app;
  } catch (_error) {}

  config = {
    'browserify': require("./gruntTasks/browserify"),
    'clean':['./styles/css/'],
    'less':require("./gruntTasks/less"),
    'cssmin': {
      'combine': {
        'files': {
          './styles/compiled.css': ['./styles/css/*.css', './bower_components/angular-ui-select/dist/select.css', './bower_components/ng-grid/ng-grid.min.css', './bower_components/angular-ui-grid/ui-grid.min.css']
        }
      }
    },
    'copy': {
      'grid': {
        'src': ['./bower_components/angular-ui-grid/ui-grid.eot','./bower_components/angular-ui-grid/ui-grid.svg', './bower_components/angular-ui-grid/ui-grid.ttf', './bower_components/angular-ui-grid/ui-grid.woff'],
        'dest': './styles/',
        'filter': 'isFile',
        'flatten': true,
        'expand': true
      },
      'viz': {
        'files': {
          'libs/resources/viz.js': "./bower_components/viz.js/index.js"
        }
      },
      'cordova-index': {
        'src': './cordova_template/index.html',
        'dest': cordovaAppRoot + 'index.html'
      },
      'cordova-indexjs': {
        'src': './cordova_template/js/index.js',
        'dest': cordovaAppRoot + 'js/index.js'
      },
      'cordova-js': {
        'src': ['libs/**/*.js', './client/**', './app.js', './appConfig.js'],
        'dest': cordovaAppRoot
      },
      'cordova-css': {
        'src': './styles/compiled.css',
        'dest': cordovaAppRoot + 'compiled.css'
      },
      'cordova-partials': {
        'src': 'generated/partials/**/*.html',
        'dest': cordovaAppRoot
      },
      'cordova-fonts': {
        'src':'fonts/**',
        'dest': cordovaAppRoot + '/fonts',
        'filter': 'isFile',
        'flatten': true,
        'expand': true
      }
    },
    'sails-linker': {
      'resources': {
        'options': {
          'startTag': '<!--RESOURCES-->',
          'endTag': '<!--RESOURCES END-->',
          'fileTmpl': '<script src="%s"></script>',
          'appRoot': './'
        },
        'files': {
          './index.html': ['./libs/resources/*.js'],
          './cordova_template/index.html': ['./libs/resources/*.js']
        }
      },
      'libs': {
        'options': {
          'startTag': '<!--SCRIPTS-->',
          'endTag': '<!--SCRIPTS END-->',
          'fileTmpl': '<script src="%s"></script>',
          'appRoot': './'
        },
        'files': {
          './index.html': ['./libs/*.js'],
          './cordova_template/index.html': ['./libs/*.js']
        }
      },
      'dev': {
        'options': {
          'startTag': '<!--CSS-->',
          'endTag': '<!--CSS END-->',
          'fileTmpl': '<link rel="stylesheet" href="%s" />',
          'appRoot': './'
        },
        'files': {
          './index.html': ['./styles/*.css', './bower_components/select2/select2.css', './bower_components/ng-grid/ng-grid.min.css']
        }
      },
      'prod': {
        'options': {
          'startTag': '<!--CSS-->',
          'endTag': '<!--CSS END-->',
          'fileTmpl': '<link rel="stylesheet" href="%s" />',
          'appRoot': './'
        },
        'files': {
          './index.html': ['./styles/compiled.css']
        }
      }
    },
    uglify: {
      options: {
        mangle: false,
        sourceMap: true,
        beautify: true,
      },
      my_target: {
        files: {
          'libs/modules.min.js': ['./libs/modules/*.js'],
          'libs/vendor.min.js': ['./libs/vendor/*.js']
        }
      }
    },
    'watch': {
      'options': {
        'livereload': true
      },
      'less': {
        'files': './**/*.less',
        'tasks': ['less:common', 'cssmin', 'autoprefixer'],
        'options': {
          'interrupt': true
        }
      },
      'resources': {
        'files': [
          './bower_components/sockjs-client/dist/sockjs.js',
          './generated/*.js',
          './generated/directives/**/*.js',
          './generated/services/**/*.js',
          './client/**/*.js',
          './generated/locale/**/*.js'
        ],
        'tasks': ['browserify:appDep', 'sails-linker:libs'],
        'options': {
          'interrupt': true
        }
      },
      <% _.each(moduleNames.agents, function(moduleName, index) { %>
          '<%=moduleName %>/': {
            'files': './generated/agents/<%=moduleName %>/**/*.js',
            'tasks': ['browserify:appDep', 'sails-linker:libs'],
            'options': {
              'interrupt': true
            }
        },
        <%
      }); %> <% _.each(moduleNames.crud, function(moduleName, index) { %>
          '<%=moduleName %>/': {
            'files': './generated/crud/<%=moduleName %>/**/*.js',
            'tasks': ['browserify:appDep', 'sails-linker:libs'],
            'options': {
              'interrupt': true
            }
        },
        <%
      }); %>
    },
    'autoprefixer': {
      'options': {
        // Task-specific options go here.
      },
      'single_file': {
        'options': {
          // Target-specific options go here.
        },
        'src': './styles/compiled.css',
        'dest': './styles/compiled-prefix.css'
      },
      'sourcemap': {
        'options': {
          'map': true,
          //'annotation': 'main.css.map'
        },

        'src': './styles/compiled.css',
        'dest': './styles/compiled-prefix.css' // -> dest/css/file.css, sourcemap is inlined
      }
    },
    'shell': {
      'install-packages': {
        'command': ['npm install', '$(npm bin)/bower install --verbose'].join('&&'),
        'help': "Runs npm install and bower install"
      },
      'cordova-create': {
        'command': ['sleep 1', 'mkdir cordova-app', 'cd cordova-app', 'cordova create ' + appName + ' com.fed.' + appName + '  ' + appName, 'cd ' + appName, 'cordova platforms add android', 'cordova platforms add ios'].join("&&"),
        'help': "creates cordova app, adds android and iOs platforms"
      },
      'cordova-deploy-android': {
        'command': ['sleep 1', 'cd ./cordova-app/' + appName, 'cordova run android'].join('&&'),
        'help': "Deploys already built project to the android device/emulator"
      },
      'cordova-prepare-android': {
        'command': ['sleep 1', 'cd ./cordova-app/' + appName, 'cordova prepare android'].join('&&'),
        'help': "Deploys already built project to the android device/emulator"
      },
      'cordova-deploy-ios': {
        'command': ['sleep 1', 'cd ./cordova-app/' + appName, 'cordova run ios'].join('&&'),
        'help': "Deploys already built project to the ios device/emulator"
      },
      'cordova-prepare-ios': {
        'command': ['sleep 1', 'cd ./cordova-app/' + appName, 'cordova prepare ios'].join('&&'),
        'help': "Deploys already built project to the ios device/emulator"
      },
      'clean-android': {
        'command': ['sleep 1', 'cd cordova-app/' + appName,'cordova platforms rm android', 'sleep 1', 'cordova platforms add android'].join('&&'),
        'help': "Reinstalls android platform.  Fire the hooks that removes and reinstalls plugins.  NOTE: This WILL destroy android app configs."
      },
      'clean-ios': {
        'command': ['sleep 1', 'cd cordova-app/' + appName,'cordova platforms rm ios', 'sleep 1', 'cordova platforms add ios'].join('&&'),
        'help': "Reinstalls ios platform.  Fires the hooks that removes and reinstalls plugins.  NOTE: This WILL destroy ios project settings."
      }

    }
  };

  grunt.initConfig(config);

  var sharedTasks = ['clean', 'less:common', 'copy:grid', 'copy:viz', 'browserify:build', 'browserify:appDep', 'browserify:libs', 'uglify', 'sails-linker:resources', 'sails-linker:libs', 'copy:custom', 'sails-linker:custom'];
  var serveTasks = ['cssmin', 'autoprefixer', 'sails-linker:prod'];
  var cordovaPrep = ['shell:install-packages', 'clean','less:cordova', 'copy:grid', 'copy:viz', 'browserify:build', 'browserify:appDep', 'browserify:libs', 'sails-linker:resources', 'sails-linker:libs', 'copy:custom', 'sails-linker:custom', 'cssmin', 'copy:cordova-js', 'copy:cordova-css', 'copy:cordova-partials', 'copy:cordova-index', 'copy:cordova-indexjs', 'copy:cordova-fonts'];
  var cordovaDeployAndroid = ['shell:cordova-deploy-android'];
  var cordovaDeployiOS = ['shell:cordova-deploy-ios'];
  var cordovaPrepAndroid = ['shell:cordova-prepare-android'];
  var cordovaPrepiOS = ['shell:cordova-prepare-ios'];

  var allTasks = sharedTasks.concat(serveTasks);

  /* ########## INCORPORATING CUSTOM TASKS DEFINED IN CUSTOMGRUNT ########## */

  /* ########## Recursively merge properties of two objects ########## */

  function MergeRecursive(obj1, obj2) {
    for (var p in obj2) {
      try {
        // Property in destination object set; update its value.
        if (obj2[p].constructor == Object) {
          obj1[p] = MergeRecursive(obj1[p], obj2[p]);
        } else {
          obj1[p] = obj2[p];
        }
      } catch (e) {
        // Property in destination object not set; create it and set its value.
        obj1[p] = obj2[p];
      }
    }
    return obj1;
  }

  for (var key in customGrunt) {

    for (var prop in customGrunt[key]) {

      var obj = {};
      obj[prop] = customGrunt[key][prop];

      if (grunt.config.data[key]) {
        if (grunt.config.data[key][prop]) {
          grunt.config.data[key][prop] = MergeRecursive(grunt.config.data[key][prop], obj[prop]);
        } else {
          grunt.config.data[key][prop] = obj[prop];
        }
      } else {
        if (key == 'unitTest' || key == 'e2eTest') {
          grunt.extendConfig(obj);
          var customTasks = sharedTasks.concat([prop]);
          grunt.registerTask(key, customTasks);
        } else {
          grunt.config.data[key] = {};
          grunt.config.data[key][prop] = obj[prop];
          if (key == 'copy' || key == 'shell') {
            sharedTasks.unshift(key);
          } else {
            sharedTasks.push(key);
          }
        }
      }
    }
  }

  for (var task in config.shell) {
    grunt.registerTask(task, 'shell:' + task);
  }

  var getHelpText = function(task) {
    return config.shell[task].help || "";
  };

  grunt.registerTask('default', allTasks);
  grunt.registerTask('dev', sharedTasks.concat(['sails-linker:dev', 'watch']));
  grunt.registerTask('android-deploy', cordovaPrep.concat(cordovaDeployAndroid));
  grunt.registerTask('ios-deploy', cordovaPrep.concat(cordovaDeployiOS));
  grunt.registerTask('android-prep', cordovaPrep.concat(cordovaPrepAndroid));
  grunt.registerTask('ios-prep', cordovaPrep.concat(cordovaPrepiOS));

  grunt.registerTask('help', 'List available tasks', function(x){
    var Table = require('cli-table');
    var taskTable = new Table({
      chars: { 'top': '' , 'top-mid': '' , 'top-left': '' , 'top-right': ''
             , 'bottom': '' , 'bottom-mid': '' , 'bottom-left': '' , 'bottom-right': ''
             , 'left': '' , 'left-mid': '' , 'mid': '' , 'mid-mid': ''
             , 'right': '' , 'right-mid': '' , 'middle': ' ' },
      style: { 'padding-left': 0, 'padding-right': 0 }
    });

    var tasks = ['default', 'dev'];
    taskTable.push(
      ['default', 'standard build'],
      ['dev', 'Builds with sails-linker:dev and a watch'],
      ['android-prep', 'installs npm and bower, compiles Front End ready to deploying with IDE of choice.'],
      ['ios-prep', 'installs npm and bower, compiles Front End ready to deploying with Xcode.'],
      ['android-deploy', 'installs npm and bower, compiles Front End, deploys to android device/emulator.'],
      ['ios-deploy', 'installs npm and bower, compiles Front End, deploys to iOS device/emulator.']
    );
    for (var task in config.shell) {
      tasks.push(task);
      taskTable.push([task, getHelpText(task)]);
    }

    console.log("Useage:");
    console.log("\t"+ tasks.join(",") + "\n\r");
    console.log("Description:");
    console.log(taskTable.toString());
  });

};
<commit_msg>allow-root<commit_after>"use strict";

module.exports = function(grunt) {

  var loadGruntTasks = require('load-grunt-tasks')(grunt, {
    pattern: 'grunt-*',
    config: './package.json',
    scope: 'dependencies'
  });

  var config, customGrunt;
  var basePath = grunt.option('basePath') || "./";
  var appName = require("../package.json").name;
  var cordovaAppRoot = 'cordova-app/' + appName + '/www/';

  customGrunt = require("./Gruntfile_custom.js");

  try {
    dir.app = require("./bower.json").appPath || dir.app;
  } catch (_error) {}

  config = {
    'browserify': require("./gruntTasks/browserify"),
    'clean':['./styles/css/'],
    'less':require("./gruntTasks/less"),
    'cssmin': {
      'combine': {
        'files': {
          './styles/compiled.css': ['./styles/css/*.css', './bower_components/angular-ui-select/dist/select.css', './bower_components/ng-grid/ng-grid.min.css', './bower_components/angular-ui-grid/ui-grid.min.css']
        }
      }
    },
    'copy': {
      'grid': {
        'src': ['./bower_components/angular-ui-grid/ui-grid.eot','./bower_components/angular-ui-grid/ui-grid.svg', './bower_components/angular-ui-grid/ui-grid.ttf', './bower_components/angular-ui-grid/ui-grid.woff'],
        'dest': './styles/',
        'filter': 'isFile',
        'flatten': true,
        'expand': true
      },
      'viz': {
        'files': {
          'libs/resources/viz.js': "./bower_components/viz.js/index.js"
        }
      },
      'cordova-index': {
        'src': './cordova_template/index.html',
        'dest': cordovaAppRoot + 'index.html'
      },
      'cordova-indexjs': {
        'src': './cordova_template/js/index.js',
        'dest': cordovaAppRoot + 'js/index.js'
      },
      'cordova-js': {
        'src': ['libs/**/*.js', './client/**', './app.js', './appConfig.js'],
        'dest': cordovaAppRoot
      },
      'cordova-css': {
        'src': './styles/compiled.css',
        'dest': cordovaAppRoot + 'compiled.css'
      },
      'cordova-partials': {
        'src': 'generated/partials/**/*.html',
        'dest': cordovaAppRoot
      },
      'cordova-fonts': {
        'src':'fonts/**',
        'dest': cordovaAppRoot + '/fonts',
        'filter': 'isFile',
        'flatten': true,
        'expand': true
      }
    },
    'sails-linker': {
      'resources': {
        'options': {
          'startTag': '<!--RESOURCES-->',
          'endTag': '<!--RESOURCES END-->',
          'fileTmpl': '<script src="%s"></script>',
          'appRoot': './'
        },
        'files': {
          './index.html': ['./libs/resources/*.js'],
          './cordova_template/index.html': ['./libs/resources/*.js']
        }
      },
      'libs': {
        'options': {
          'startTag': '<!--SCRIPTS-->',
          'endTag': '<!--SCRIPTS END-->',
          'fileTmpl': '<script src="%s"></script>',
          'appRoot': './'
        },
        'files': {
          './index.html': ['./libs/*.js'],
          './cordova_template/index.html': ['./libs/*.js']
        }
      },
      'dev': {
        'options': {
          'startTag': '<!--CSS-->',
          'endTag': '<!--CSS END-->',
          'fileTmpl': '<link rel="stylesheet" href="%s" />',
          'appRoot': './'
        },
        'files': {
          './index.html': ['./styles/*.css', './bower_components/select2/select2.css', './bower_components/ng-grid/ng-grid.min.css']
        }
      },
      'prod': {
        'options': {
          'startTag': '<!--CSS-->',
          'endTag': '<!--CSS END-->',
          'fileTmpl': '<link rel="stylesheet" href="%s" />',
          'appRoot': './'
        },
        'files': {
          './index.html': ['./styles/compiled.css']
        }
      }
    },
    uglify: {
      options: {
        mangle: false,
        sourceMap: true,
        beautify: true,
      },
      my_target: {
        files: {
          'libs/modules.min.js': ['./libs/modules/*.js'],
          'libs/vendor.min.js': ['./libs/vendor/*.js']
        }
      }
    },
    'watch': {
      'options': {
        'livereload': true
      },
      'less': {
        'files': './**/*.less',
        'tasks': ['less:common', 'cssmin', 'autoprefixer'],
        'options': {
          'interrupt': true
        }
      },
      'resources': {
        'files': [
          './bower_components/sockjs-client/dist/sockjs.js',
          './generated/*.js',
          './generated/directives/**/*.js',
          './generated/services/**/*.js',
          './client/**/*.js',
          './generated/locale/**/*.js'
        ],
        'tasks': ['browserify:appDep', 'sails-linker:libs'],
        'options': {
          'interrupt': true
        }
      },
      <% _.each(moduleNames.agents, function(moduleName, index) { %>
          '<%=moduleName %>/': {
            'files': './generated/agents/<%=moduleName %>/**/*.js',
            'tasks': ['browserify:appDep', 'sails-linker:libs'],
            'options': {
              'interrupt': true
            }
        },
        <%
      }); %> <% _.each(moduleNames.crud, function(moduleName, index) { %>
          '<%=moduleName %>/': {
            'files': './generated/crud/<%=moduleName %>/**/*.js',
            'tasks': ['browserify:appDep', 'sails-linker:libs'],
            'options': {
              'interrupt': true
            }
        },
        <%
      }); %>
    },
    'autoprefixer': {
      'options': {
        // Task-specific options go here.
      },
      'single_file': {
        'options': {
          // Target-specific options go here.
        },
        'src': './styles/compiled.css',
        'dest': './styles/compiled-prefix.css'
      },
      'sourcemap': {
        'options': {
          'map': true,
          //'annotation': 'main.css.map'
        },

        'src': './styles/compiled.css',
        'dest': './styles/compiled-prefix.css' // -> dest/css/file.css, sourcemap is inlined
      }
    },
    'shell': {
      'install-packages': {
        'command': ['npm install', '$(npm bin)/bower install --verbose --allow-root'].join('&&'),
        'help': "Runs npm install and bower install"
      },
      'cordova-create': {
        'command': ['sleep 1', 'mkdir cordova-app', 'cd cordova-app', 'cordova create ' + appName + ' com.fed.' + appName + '  ' + appName, 'cd ' + appName, 'cordova platforms add android', 'cordova platforms add ios'].join("&&"),
        'help': "creates cordova app, adds android and iOs platforms"
      },
      'cordova-deploy-android': {
        'command': ['sleep 1', 'cd ./cordova-app/' + appName, 'cordova run android'].join('&&'),
        'help': "Deploys already built project to the android device/emulator"
      },
      'cordova-prepare-android': {
        'command': ['sleep 1', 'cd ./cordova-app/' + appName, 'cordova prepare android'].join('&&'),
        'help': "Deploys already built project to the android device/emulator"
      },
      'cordova-deploy-ios': {
        'command': ['sleep 1', 'cd ./cordova-app/' + appName, 'cordova run ios'].join('&&'),
        'help': "Deploys already built project to the ios device/emulator"
      },
      'cordova-prepare-ios': {
        'command': ['sleep 1', 'cd ./cordova-app/' + appName, 'cordova prepare ios'].join('&&'),
        'help': "Deploys already built project to the ios device/emulator"
      },
      'clean-android': {
        'command': ['sleep 1', 'cd cordova-app/' + appName,'cordova platforms rm android', 'sleep 1', 'cordova platforms add android'].join('&&'),
        'help': "Reinstalls android platform.  Fire the hooks that removes and reinstalls plugins.  NOTE: This WILL destroy android app configs."
      },
      'clean-ios': {
        'command': ['sleep 1', 'cd cordova-app/' + appName,'cordova platforms rm ios', 'sleep 1', 'cordova platforms add ios'].join('&&'),
        'help': "Reinstalls ios platform.  Fires the hooks that removes and reinstalls plugins.  NOTE: This WILL destroy ios project settings."
      }

    }
  };

  grunt.initConfig(config);

  var sharedTasks = ['clean', 'less:common', 'copy:grid', 'copy:viz', 'browserify:build', 'browserify:appDep', 'browserify:libs', 'uglify', 'sails-linker:resources', 'sails-linker:libs', 'copy:custom', 'sails-linker:custom'];
  var serveTasks = ['cssmin', 'autoprefixer', 'sails-linker:prod'];
  var cordovaPrep = ['shell:install-packages', 'clean','less:cordova', 'copy:grid', 'copy:viz', 'browserify:build', 'browserify:appDep', 'browserify:libs', 'sails-linker:resources', 'sails-linker:libs', 'copy:custom', 'sails-linker:custom', 'cssmin', 'copy:cordova-js', 'copy:cordova-css', 'copy:cordova-partials', 'copy:cordova-index', 'copy:cordova-indexjs', 'copy:cordova-fonts'];
  var cordovaDeployAndroid = ['shell:cordova-deploy-android'];
  var cordovaDeployiOS = ['shell:cordova-deploy-ios'];
  var cordovaPrepAndroid = ['shell:cordova-prepare-android'];
  var cordovaPrepiOS = ['shell:cordova-prepare-ios'];

  var allTasks = sharedTasks.concat(serveTasks);

  /* ########## INCORPORATING CUSTOM TASKS DEFINED IN CUSTOMGRUNT ########## */

  /* ########## Recursively merge properties of two objects ########## */

  function MergeRecursive(obj1, obj2) {
    for (var p in obj2) {
      try {
        // Property in destination object set; update its value.
        if (obj2[p].constructor == Object) {
          obj1[p] = MergeRecursive(obj1[p], obj2[p]);
        } else {
          obj1[p] = obj2[p];
        }
      } catch (e) {
        // Property in destination object not set; create it and set its value.
        obj1[p] = obj2[p];
      }
    }
    return obj1;
  }

  for (var key in customGrunt) {

    for (var prop in customGrunt[key]) {

      var obj = {};
      obj[prop] = customGrunt[key][prop];

      if (grunt.config.data[key]) {
        if (grunt.config.data[key][prop]) {
          grunt.config.data[key][prop] = MergeRecursive(grunt.config.data[key][prop], obj[prop]);
        } else {
          grunt.config.data[key][prop] = obj[prop];
        }
      } else {
        if (key == 'unitTest' || key == 'e2eTest') {
          grunt.extendConfig(obj);
          var customTasks = sharedTasks.concat([prop]);
          grunt.registerTask(key, customTasks);
        } else {
          grunt.config.data[key] = {};
          grunt.config.data[key][prop] = obj[prop];
          if (key == 'copy' || key == 'shell') {
            sharedTasks.unshift(key);
          } else {
            sharedTasks.push(key);
          }
        }
      }
    }
  }

  for (var task in config.shell) {
    grunt.registerTask(task, 'shell:' + task);
  }

  var getHelpText = function(task) {
    return config.shell[task].help || "";
  };

  grunt.registerTask('default', allTasks);
  grunt.registerTask('dev', sharedTasks.concat(['sails-linker:dev', 'watch']));
  grunt.registerTask('android-deploy', cordovaPrep.concat(cordovaDeployAndroid));
  grunt.registerTask('ios-deploy', cordovaPrep.concat(cordovaDeployiOS));
  grunt.registerTask('android-prep', cordovaPrep.concat(cordovaPrepAndroid));
  grunt.registerTask('ios-prep', cordovaPrep.concat(cordovaPrepiOS));

  grunt.registerTask('help', 'List available tasks', function(x){
    var Table = require('cli-table');
    var taskTable = new Table({
      chars: { 'top': '' , 'top-mid': '' , 'top-left': '' , 'top-right': ''
             , 'bottom': '' , 'bottom-mid': '' , 'bottom-left': '' , 'bottom-right': ''
             , 'left': '' , 'left-mid': '' , 'mid': '' , 'mid-mid': ''
             , 'right': '' , 'right-mid': '' , 'middle': ' ' },
      style: { 'padding-left': 0, 'padding-right': 0 }
    });

    var tasks = ['default', 'dev'];
    taskTable.push(
      ['default', 'standard build'],
      ['dev', 'Builds with sails-linker:dev and a watch'],
      ['android-prep', 'installs npm and bower, compiles Front End ready to deploying with IDE of choice.'],
      ['ios-prep', 'installs npm and bower, compiles Front End ready to deploying with Xcode.'],
      ['android-deploy', 'installs npm and bower, compiles Front End, deploys to android device/emulator.'],
      ['ios-deploy', 'installs npm and bower, compiles Front End, deploys to iOS device/emulator.']
    );
    for (var task in config.shell) {
      tasks.push(task);
      taskTable.push([task, getHelpText(task)]);
    }

    console.log("Useage:");
    console.log("\t"+ tasks.join(",") + "\n\r");
    console.log("Description:");
    console.log(taskTable.toString());
  });

};
<|endoftext|>


<commit_before>var webpack = require('webpack');
module.exports = function(grunt) {
  grunt.initConfig({
    banner: '/*!\n' +
            ' * B3 Default Theme\n' +
            ' * Copyright 2016-<%= grunt.template.today("yyyy") %> Eivind Arvesen\n' +
            ' * Licensed under BSD-3 (https://opensource.org/licenses/BSD-3-Clause)\n' +
            ' */\n',
    less: {
      compileCore: {
        options: {
          strictMath: true,
          sourceMap: true,
          outputSourceFiles: true,
          sourceMapURL: 'style.css.map',
          sourceMapFilename: 'style.css.map',
        },
        files: {
          './styles/style.css': './styles/main.less'
        },
      }
    },
    purifycss: {
      options: {
        whitelist: [
          '*blog-post*',
          '*blog-body*',
          'blockquote',
          'code'
        ]
      },
      target: {
        src: ['./../views/**/*.blade.php'],
        css: ['./styles/style.css'],
        dest: './styles/style.css'
      },
    },
    autoprefixer: {
      options: {
        browsers: [
          'Android 2.3',
          'Android >= 4',
          'Chrome >= 20',
          'Firefox >= 24', // Firefox 24 is the latest ESR
          'Explorer >= 8',
          'iOS >= 6',
          'Opera >= 12',
          'Safari >= 6'
        ]
      },
      core: {
        options: {
          map: true
        },
        src: './styles/style.css'
      }
    },
    cssmin: {
      options: {
        compatibility: 'ie8',
        keepSpecialComments: '*',
        //noAdvanced: true
      },
      core: {
        files: {
          './styles/style.min.css': './styles/style.css'
        }
      }
    },
    usebanner: {
      options: {
        position: 'top',
        banner: '<%= banner %>'
      },
      files: {
        src: './styles/style.css'
      }
    },
    qunit: {
      files: ['test/**/*.html']
    },
    jshint: {
      files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
      options: {
        // options here to override JSHint defaults
        globals: {
          jQuery: true,
          console: true,
          module: true,
          document: true
        }
      }
    },
    clean: {
      dist: [
        './dist/styles/*.css',
        './dist/scripts/*.js',
      ],
      dist_styles: [
        './dist/styles/*.css',
      ],
      dist_scripts: [
        './dist/scripts/*.js',
      ],
      temp: [
        './styles/*.css',
        './*.map'
      ]
    },
    copy: {
      main: {
        cwd: './',
        src: [
          'styles/*.min.css'
        ],
        dest: './dist/',
        expand: true,
      }
    },
    webpack: {
      someName: {
        // webpack options
        entry: "./scripts/main.js",
        output: {
            path: "./dist/scripts/",
            //filename: "script.min.[hash].js",
            filename: "script.min.js",
        },
        plugins: [
          new webpack.optimize.UglifyJsPlugin({minimize: true})
        ],
        stats: {
            // Configure the console output
            colors: true,
            modules: true,
            reasons: true
        },
        watch: false
      }
    },
    replace: {
      styles: {
        src: ['./../views/**/*.blade.php'],
        overwrite: true,
        replacements: [{
        }, {
          from: /(min.).*.css/g,
          to: 'min.css'
        }]
      },
      scripts: {
        src: ['./../views/**/*.blade.php'],
        overwrite: true,
        replacements: [{
        }, {
          from: /(min.).*.js/g,
          to: 'min.js'
        }]
      }
    },
    run: {
      build_content: {
        cmd: 'bash',
        args: ['./../../../../scripts/populate-db.sh']
      }
    },
    cacheBust: {
        styles: {
            options: {
                assets: [
                'styles/*.css'
                ],
                baseDir: './dist/',
                deleteOriginals: true,
                jsonOutput: true
            },
            src: ['./../views/**/*.blade.php']
        },
        scripts: {
            options: {
                assets: [
                'scripts/*.js'
                ],
                baseDir: './dist/',
                deleteOriginals: true,
                jsonOutput: true
            },
            src: ['./../views/**/*.blade.php']
        }
    },
    browserSync: {
        dev: {
            bsFiles: {
                src : [
                        'dist/styles/*.css',
                        'dist/scripts/*.js',
                        //'./../views/**/*.blade.php',
                        './../../../../**/*.php',
                        '!../../../../public/subsites/**/*',
                        //'./../../../**/*.html',
                        './../../../../.env',
                        './../../../../**/*.htaccess',
                    ]
            },
            options: {
                open: false,
                watchTask: true,
                proxy: 'localhost'
            }
        }
    },
    watch: {
        configFiles: {
          files: [ 'Gruntfile.js', 'config/*.js' ],
          options: {
            reload: true
          }
        },
        html: {
            files: ['index.html'],
            tasks: ['htmlhint']
        },
        js: {
            files: ['./scripts/*.js'],
            tasks: ['clean:dist_scripts', 'webpack', 'replace:scripts', 'cacheBust:scripts']
        },
        less: {
            files: ['./styles/*.less'],
            tasks: ['clean:dist_styles', 'less:compileCore', 'purifycss', 'autoprefixer', 'usebanner', 'cssmin', 'copy','replace:styles', 'cacheBust:styles', 'clean:temp'],
        },
        run: {
            files: ['./../../../content/**/*.md'],
            tasks: ['run:build_content']
        }
    }
  });

  // Loading our grunt modules
  grunt.loadNpmTasks('grunt-autoprefixer');
  grunt.loadNpmTasks('grunt-banner');
  grunt.loadNpmTasks('grunt-cache-bust');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-text-replace');
  grunt.loadNpmTasks('grunt-run');

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-qunit');

  grunt.loadNpmTasks('grunt-browser-sync');

  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.loadNpmTasks('grunt-webpack');

  grunt.loadNpmTasks('grunt-purifycss');

  // CSS distribution task.
  // grunt.registerTask('default', ['less:compileCore', 'autoprefixer', 'usebanner', 'cssmin']);
  grunt.registerTask('default', ['clean:dist', 'less:compileCore', 'purifycss', 'autoprefixer', 'usebanner', 'cssmin', 'copy', 'webpack', 'replace:styles', 'cacheBust:styles', 'replace:scripts', 'cacheBust:scripts', 'clean:temp', 'run', 'browserSync', 'watch']);
  // grunt.registerTask('test', ['jshint', 'qunit']);
};
<commit_msg>Make csspurify check content (markdown) and scripts (js) for css selectors<commit_after>var webpack = require('webpack');
module.exports = function(grunt) {
  grunt.initConfig({
    banner: '/*!\n' +
            ' * B3 Default Theme\n' +
            ' * Copyright 2016-<%= grunt.template.today("yyyy") %> Eivind Arvesen\n' +
            ' * Licensed under BSD-3 (https://opensource.org/licenses/BSD-3-Clause)\n' +
            ' */\n',
    less: {
      compileCore: {
        options: {
          strictMath: true,
          sourceMap: true,
          outputSourceFiles: true,
          sourceMapURL: 'style.css.map',
          sourceMapFilename: 'style.css.map',
        },
        files: {
          './styles/style.css': './styles/main.less'
        },
      }
    },
    purifycss: {
      options: {
        whitelist: [
          '*blog-post*',
          '*blog-body*',
          'blockquote',
          'code'
        ]
      },
      target: {
        src: [
          './../views/**/*.blade.php',
          './scripts/*.js',
          './../../../content/**/*.md'
          ],
        css: ['./styles/style.css'],
        dest: './styles/style.css'
      },
    },
    autoprefixer: {
      options: {
        browsers: [
          'Android 2.3',
          'Android >= 4',
          'Chrome >= 20',
          'Firefox >= 24', // Firefox 24 is the latest ESR
          'Explorer >= 8',
          'iOS >= 6',
          'Opera >= 12',
          'Safari >= 6'
        ]
      },
      core: {
        options: {
          map: true
        },
        src: './styles/style.css'
      }
    },
    cssmin: {
      options: {
        compatibility: 'ie8',
        keepSpecialComments: '*',
        //noAdvanced: true
      },
      core: {
        files: {
          './styles/style.min.css': './styles/style.css'
        }
      }
    },
    usebanner: {
      options: {
        position: 'top',
        banner: '<%= banner %>'
      },
      files: {
        src: './styles/style.css'
      }
    },
    qunit: {
      files: ['test/**/*.html']
    },
    jshint: {
      files: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js'],
      options: {
        // options here to override JSHint defaults
        globals: {
          jQuery: true,
          console: true,
          module: true,
          document: true
        }
      }
    },
    clean: {
      dist: [
        './dist/styles/*.css',
        './dist/scripts/*.js',
      ],
      dist_styles: [
        './dist/styles/*.css',
      ],
      dist_scripts: [
        './dist/scripts/*.js',
      ],
      temp: [
        './styles/*.css',
        './*.map'
      ]
    },
    copy: {
      main: {
        cwd: './',
        src: [
          'styles/*.min.css'
        ],
        dest: './dist/',
        expand: true,
      }
    },
    webpack: {
      someName: {
        // webpack options
        entry: "./scripts/main.js",
        output: {
            path: "./dist/scripts/",
            //filename: "script.min.[hash].js",
            filename: "script.min.js",
        },
        plugins: [
          new webpack.optimize.UglifyJsPlugin({minimize: true})
        ],
        stats: {
            // Configure the console output
            colors: true,
            modules: true,
            reasons: true
        },
        watch: false
      }
    },
    replace: {
      styles: {
        src: ['./../views/**/*.blade.php'],
        overwrite: true,
        replacements: [{
        }, {
          from: /(min.).*.css/g,
          to: 'min.css'
        }]
      },
      scripts: {
        src: ['./../views/**/*.blade.php'],
        overwrite: true,
        replacements: [{
        }, {
          from: /(min.).*.js/g,
          to: 'min.js'
        }]
      }
    },
    run: {
      build_content: {
        cmd: 'bash',
        args: ['./../../../../scripts/populate-db.sh']
      }
    },
    cacheBust: {
        styles: {
            options: {
                assets: [
                'styles/*.css'
                ],
                baseDir: './dist/',
                deleteOriginals: true,
                jsonOutput: true
            },
            src: ['./../views/**/*.blade.php']
        },
        scripts: {
            options: {
                assets: [
                'scripts/*.js'
                ],
                baseDir: './dist/',
                deleteOriginals: true,
                jsonOutput: true
            },
            src: ['./../views/**/*.blade.php']
        }
    },
    browserSync: {
        dev: {
            bsFiles: {
                src : [
                        'dist/styles/*.css',
                        'dist/scripts/*.js',
                        //'./../views/**/*.blade.php',
                        './../../../../**/*.php',
                        '!../../../../public/subsites/**/*',
                        //'./../../../**/*.html',
                        './../../../../.env',
                        './../../../../**/*.htaccess',
                    ]
            },
            options: {
                open: false,
                watchTask: true,
                proxy: 'eivindarvesen.test'
            }
        }
    },
    watch: {
        configFiles: {
          files: [ 'Gruntfile.js', 'config/*.js' ],
          options: {
            reload: true
          }
        },
        html: {
            files: ['index.html'],
            tasks: ['htmlhint']
        },
        js: {
            files: ['./scripts/*.js'],
            tasks: ['clean:dist_scripts', 'webpack', 'replace:scripts', 'cacheBust:scripts']
        },
        less: {
            files: ['./styles/*.less'],
            tasks: ['clean:dist_styles', 'less:compileCore', 'purifycss', 'autoprefixer', 'usebanner', 'cssmin', 'copy','replace:styles', 'cacheBust:styles', 'clean:temp'],
        },
        run: {
            files: ['./../../../content/**/*.md'],
            tasks: ['run:build_content']
        }
    }
  });

  // Loading our grunt modules
  grunt.loadNpmTasks('grunt-autoprefixer');
  grunt.loadNpmTasks('grunt-banner');
  grunt.loadNpmTasks('grunt-cache-bust');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks('grunt-contrib-clean');
  grunt.loadNpmTasks('grunt-contrib-copy');
  grunt.loadNpmTasks('grunt-contrib-less');
  grunt.loadNpmTasks('grunt-text-replace');
  grunt.loadNpmTasks('grunt-run');

  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-qunit');

  grunt.loadNpmTasks('grunt-browser-sync');

  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.loadNpmTasks('grunt-webpack');

  grunt.loadNpmTasks('grunt-purifycss');

  // CSS distribution task.
  // grunt.registerTask('default', ['less:compileCore', 'autoprefixer', 'usebanner', 'cssmin']);
  grunt.registerTask('default', ['clean:dist', 'less:compileCore', 'purifycss', 'autoprefixer', 'usebanner', 'cssmin', 'copy', 'webpack', 'replace:styles', 'cacheBust:styles', 'replace:scripts', 'cacheBust:scripts', 'clean:temp', 'run', 'browserSync', 'watch']);
  // grunt.registerTask('test', ['jshint', 'qunit']);
};
<|endoftext|>


<commit_before>/*jslint node: true, white: true, sloppy: true */

/*
 * Copyright 2014 Rackspace
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

var url = require('url');
var http = require('http');
var request = require('request');

var https = require('https');
var fs = require('fs');

var ERR = 'LOG_ERR';
var MAX_JSON_SIZE = 1048576;
var MAX_RETRIES = 5;
var RETRY_DELAY_SECS = 5;

var auth_token = null;
var userAgent = constructUserAgent();

/**
 * posts raw data to a connection. Retries up to MAX_RETRIES times.
 * @param {Object} options HTTP options, including headers..
 * @param {Object} transport http or https module to send data on.
 * @param {String} payload JSON being sent to endpoint.
 * @param {Object} log logging object.
 * @param retry
 */
function postRaw(options, transport, payload, log, retry,username,apiKey) {
  // back out if we've retried too many times.
  if (retry >= MAX_RETRIES) {
    if (log) {
      log.log('Exceeded retries; failed to send to Blueflood: ' + payload, ERR);
    }
    return;
  }

  var request = transport.request(options, function(response) {
    response.on('data', function(data) {
      if (response.statusCode == 401) {
        var reauth_callback = function (token) {
          if(token != null || typeof token != 'undefined') {
            var headers =  {
              'Content-Length': payload.length,
              'Content-Type': 'application/json',
              'User-Agent': userAgent,
              'x-auth-token':token
            }

          authtoken = token
          options.headers = headers
          setTimeout(postRaw.bind(null, options, transport, payload, log, retry + 1), RETRY_DELAY_SECS * 1000);
        }
        else {
          var error = new Error('Reauthentication failed. Cannot send metrics to cloud metrics - check raxusername and raxapikey');
          var message = JSON.stringify(error, ['stack', 'message'], 2)
          console.error(message)
        }
      }
      authenticate(this.tenantId,username,apiKey,reauth_callback);
    }

    if (Math.floor(response.statusCode / 100) === 5) {
      // 5xx back from BF. ouch. retry?
      if (log) {
        log.log(response.statusCode + ' RETRIES: ' + (MAX_RETRIES-retry) + ' PAYLOAD:' + data, ERR);
      }
      setTimeout(postRaw.bind(null, options, transport, payload, log, retry + 1), RETRY_DELAY_SECS * 1000);
    }

    if (Math.floor(response.statusCode / 100) === 4) {
      // 4xx back from BF. clients fault.
      if (log) {
        log.log(response.statusCode + ' ' + data, ERR);
      }
    }
    // else gonna assume it was good.
    });
  });

  request.setTimeout(10000, function(_request) {
    // log this crap.
    request.end();
  });

  request.write(payload);
  request.end();

  request.on('error', function(err) {
    // retry once, don't keep going if it fails again.
    if (log) {
      log.log('Request error: ' + JSON.stringify(err), ERR);
    }
    setTimeout(postRaw.bind(null, options, transport, payload, log, Math.max(4, retry + 1)), RETRY_DELAY_SECS * 1000);
  });
}

function authenticate(tenantid,username,apikey,callback) {
  token = null;
  console.log(apikey);
  console.log(username);

  authjson = {
    "auth": {
      "RAX-KSKEY:apiKeyCredentials": {
        "username": username,
        "apiKey": apikey
      }
    }
  }

  authstring = JSON.stringify(authjson);

  request.post({
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': authstring.length
    },
    url:  'https://identity.api.rackspacecloud.com/v2.0/tokens',
    body: authstring
  }, function(error, response, body){
    if (!error && response.statusCode == 200) {
      var result = JSON.parse(body);
      token = result.access.token.id;
      console.log("Token: "+token);
      callback(token);
    }
    else {
      var message = JSON.stringify(error, ['stack', 'message'], 2);
      console.error(message);
    }
  });
}

/**
 * The Blueflood HTTP endpoint has a maximum allowable body size, currently 1MB (reflected in MAX_JSON_SIZE here).
 * Note that if any single metric is bigger than max || MAX_JSON_SIZE, it will be included (by itself).
 * @param {string} tenantId tenant ID to use in constructed payloads.
 * @param {Number} timestamp timestamp to use in constructed payloads.
 * @param {Array} gauges Gauge objects to place into payloads.
 * @param {Array} counters Counter objects to place into payloads.
 * @param {Array} timers Timer objects to place into payloads.
 * @param {Array} sets Set objects to place into payloads.
 * @param {Number} flushInterval (optional) expected interval that metrics are flushed in. null is ok.
 * @param {Number} max Maximum size per payload (will default to MAX_JSON_SIZE).
 * @returns {Array} An array of JSON payload strings, ready to POST.
 */
function buildPayloads(tenantId, timestamp, gauges, counters, timers, sets, flushInterval, max) {
  var payloads = [],
      basePayload = {
  tenantId: tenantId,
  timestamp: timestamp,
  flushInterval: flushInterval,
  gauges: [],
  counters: [],
  timers: [],
  sets: []
      },
      basePayloadLength = JSON.stringify(basePayload).length,
      curJsonSize = basePayloadLength,
      payloadObj = {gauges: gauges, counters: counters, timers: timers, sets:sets},
      objLength,
      realMax = max || MAX_JSON_SIZE;

  Object.keys(payloadObj).forEach(function(label) {

    payloadObj[label].forEach(function(metric) {
      objLength = JSON.stringify(metric).length;
      // if objLength > realMax, we will end up sending a bundle that is too large. we'll let the client deal with
      // that. there isn't much we can do about it.
      if (curJsonSize + objLength > realMax) {
        // send and start over.
        payloads.push(JSON.stringify(basePayload));
        basePayload = {
          tenantId: tenantId,
          timestamp: timestamp,
          flushInterval: flushInterval,
          gauges: [],
          counters: [],
          timers: [],
          sets: []
        }
        curJsonSize = basePayloadLength;
      }
      basePayload[label].push(metric);
      curJsonSize += objLength;
    });
  });

  payloads.push(JSON.stringify(basePayload));

  return payloads;
}

/**
 * Takes metrics objects and posts them to an endpoint.
 * @param {String} endpoint http[s]://host:port.
 * @param {Object} log logging object.
 * @param {Number} timestamp seconds since epoch.
 * @param {String} tenantId tenant ID to post as.
 * @param {Object} gauges array of gauge objects.
 * @param {Object} counters array of counter objects.
 * @param {Object} timers array of timer objects.
 * @param {Object} sets array of set objects.
 * @param {Number} flushInterval (optional) flush interval as configured in statsD.
 */
function postMetrics(
  endpoint, log, timestamp,
  tenantId, gauges, counters, timers, sets, flushInterval, username, apiKey) {
  console.log("Posting metrics to blueflood");

  var postmetrics = function(token) {
    auth_token = token;
    var payloads = buildPayloads(tenantId, timestamp, gauges, counters, timers, sets, flushInterval, MAX_JSON_SIZE),
        host = url.parse(endpoint),
        options = {
    host: host.hostname,
    port: host.port || 443,
    path: '/v2.0/' + tenantId + '/ingest/aggregated',
    method: 'POST',
    headers: {
            'Content-Length': 0,
            'Content-Type': 'application/json',
            'User-Agent': userAgent,
            'x-auth-token':auth_token
    }
  },
        transport = http;

    // check https
    if (host.protocol.match(/https/)) {
      transport = https;
    }

    payloads.forEach(function(payload) {
      // kinda hacky, but hey...
      options.headers['Content-Length'] = payload.length;
      postRaw(options, transport, payload, log, 0,username,apiKey);
    });
  }

  if (auth_token === null || typeof auth_token === 'undefined') {
    authenticate(this.tenantId,username,apiKey,postmetrics);
  }
  else {
    postmetrics(auth_token);
  }
}

function constructUserAgent() {
  // get the version out of the package.json.
  var version = 'unknown';

  try {
    version = JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'UTF-8')).version;
  } catch (err) {
    // ugh.
  }

  return 'blueflood-statsd-backend/' + version + ' (node.js)';
}

exports.authenticate = authenticate;
exports.postMetrics = postMetrics;
exports.postRaw = postRaw;
exports.buildPayloadsUnsafe = buildPayloads;
<commit_msg>indentation<commit_after>/*jslint node: true, white: true, sloppy: true */

/*
 * Copyright 2014 Rackspace
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

var url = require('url');
var http = require('http');
var request = require('request');

var https = require('https');
var fs = require('fs');

var ERR = 'LOG_ERR';
var MAX_JSON_SIZE = 1048576;
var MAX_RETRIES = 5;
var RETRY_DELAY_SECS = 5;

var auth_token = null;
var userAgent = constructUserAgent();

/**
 * posts raw data to a connection. Retries up to MAX_RETRIES times.
 * @param {Object} options HTTP options, including headers..
 * @param {Object} transport http or https module to send data on.
 * @param {String} payload JSON being sent to endpoint.
 * @param {Object} log logging object.
 * @param retry
 */
function postRaw(options, transport, payload, log, retry,username,apiKey) {
  // back out if we've retried too many times.
  if (retry >= MAX_RETRIES) {
    if (log) {
      log.log('Exceeded retries; failed to send to Blueflood: ' + payload, ERR);
    }
    return;
  }

  var request = transport.request(options, function(response) {
    response.on('data', function(data) {
      if (response.statusCode == 401) {
        var reauth_callback = function (token) {
          if(token != null || typeof token != 'undefined') {
            var headers =  {
              'Content-Length': payload.length,
              'Content-Type': 'application/json',
              'User-Agent': userAgent,
              'x-auth-token':token
            }

            authtoken = token
            options.headers = headers
            setTimeout(postRaw.bind(null, options, transport, payload, log, retry + 1), RETRY_DELAY_SECS * 1000);
          }
          else {
            var error = new Error('Reauthentication failed. Cannot send metrics to cloud metrics - check raxusername and raxapikey');
            var message = JSON.stringify(error, ['stack', 'message'], 2)
            console.error(message)
          }
	}
	authenticate(this.tenantId,username,apiKey,reauth_callback);
      }

      if (Math.floor(response.statusCode / 100) === 5) {
	// 5xx back from BF. ouch. retry?
	if (log) {
          log.log(response.statusCode + ' RETRIES: ' + (MAX_RETRIES-retry) + ' PAYLOAD:' + data, ERR);
	}
	setTimeout(postRaw.bind(null, options, transport, payload, log, retry + 1), RETRY_DELAY_SECS * 1000);
      }

      if (Math.floor(response.statusCode / 100) === 4) {
	// 4xx back from BF. clients fault.
	if (log) {
          log.log(response.statusCode + ' ' + data, ERR);
	}
      }
      // else gonna assume it was good.
    });
  });

  request.setTimeout(10000, function(_request) {
    // log this crap.
    request.end();
  });

  request.write(payload);
  request.end();

  request.on('error', function(err) {
    // retry once, don't keep going if it fails again.
    if (log) {
      log.log('Request error: ' + JSON.stringify(err), ERR);
    }
    setTimeout(postRaw.bind(null, options, transport, payload, log, Math.max(4, retry + 1)), RETRY_DELAY_SECS * 1000);
  });
}

function authenticate(tenantid,username,apikey,callback) {
  token = null;
  console.log(apikey);
  console.log(username);

  authjson = {
    "auth": {
      "RAX-KSKEY:apiKeyCredentials": {
        "username": username,
        "apiKey": apikey
      }
    }
  }

  authstring = JSON.stringify(authjson);

  request.post({
    headers: {
      'Content-Type': 'application/json',
      'Content-Length': authstring.length
    },
    url:  'https://identity.api.rackspacecloud.com/v2.0/tokens',
    body: authstring
  }, function(error, response, body){
    if (!error && response.statusCode == 200) {
      var result = JSON.parse(body);
      token = result.access.token.id;
      console.log("Token: "+token);
      callback(token);
    }
    else {
      var message = JSON.stringify(error, ['stack', 'message'], 2);
      console.error(message);
    }
  });
}

/**
 * The Blueflood HTTP endpoint has a maximum allowable body size, currently 1MB (reflected in MAX_JSON_SIZE here).
 * Note that if any single metric is bigger than max || MAX_JSON_SIZE, it will be included (by itself).
 * @param {string} tenantId tenant ID to use in constructed payloads.
 * @param {Number} timestamp timestamp to use in constructed payloads.
 * @param {Array} gauges Gauge objects to place into payloads.
 * @param {Array} counters Counter objects to place into payloads.
 * @param {Array} timers Timer objects to place into payloads.
 * @param {Array} sets Set objects to place into payloads.
 * @param {Number} flushInterval (optional) expected interval that metrics are flushed in. null is ok.
 * @param {Number} max Maximum size per payload (will default to MAX_JSON_SIZE).
 * @returns {Array} An array of JSON payload strings, ready to POST.
 */
function buildPayloads(tenantId, timestamp, gauges, counters, timers, sets, flushInterval, max) {
  var payloads = [],
      basePayload = {
	tenantId: tenantId,
	timestamp: timestamp,
	flushInterval: flushInterval,
	gauges: [],
	counters: [],
	timers: [],
	sets: []
      },
      basePayloadLength = JSON.stringify(basePayload).length,
      curJsonSize = basePayloadLength,
      payloadObj = {gauges: gauges, counters: counters, timers: timers, sets:sets},
      objLength,
      realMax = max || MAX_JSON_SIZE;

  Object.keys(payloadObj).forEach(function(label) {

    payloadObj[label].forEach(function(metric) {
      objLength = JSON.stringify(metric).length;
      // if objLength > realMax, we will end up sending a bundle that is too large. we'll let the client deal with
      // that. there isn't much we can do about it.
      if (curJsonSize + objLength > realMax) {
        // send and start over.
        payloads.push(JSON.stringify(basePayload));
        basePayload = {
          tenantId: tenantId,
          timestamp: timestamp,
          flushInterval: flushInterval,
          gauges: [],
          counters: [],
          timers: [],
          sets: []
        }
        curJsonSize = basePayloadLength;
      }
      basePayload[label].push(metric);
      curJsonSize += objLength;
    });
  });

  payloads.push(JSON.stringify(basePayload));

  return payloads;
}

/**
 * Takes metrics objects and posts them to an endpoint.
 * @param {String} endpoint http[s]://host:port.
 * @param {Object} log logging object.
 * @param {Number} timestamp seconds since epoch.
 * @param {String} tenantId tenant ID to post as.
 * @param {Object} gauges array of gauge objects.
 * @param {Object} counters array of counter objects.
 * @param {Object} timers array of timer objects.
 * @param {Object} sets array of set objects.
 * @param {Number} flushInterval (optional) flush interval as configured in statsD.
 */
function postMetrics(
  endpoint, log, timestamp,
  tenantId, gauges, counters, timers, sets, flushInterval, username, apiKey) {
  console.log("Posting metrics to blueflood");

  var postmetrics = function(token) {
    auth_token = token;
    var payloads = buildPayloads(tenantId, timestamp, gauges, counters, timers, sets, flushInterval, MAX_JSON_SIZE),
        host = url.parse(endpoint),
        options = {
	  host: host.hostname,
	  port: host.port || 443,
	  path: '/v2.0/' + tenantId + '/ingest/aggregated',
	  method: 'POST',
	  headers: {
            'Content-Length': 0,
            'Content-Type': 'application/json',
            'User-Agent': userAgent,
            'x-auth-token':auth_token
	  }
	},
        transport = http;

    // check https
    if (host.protocol.match(/https/)) {
      transport = https;
    }

    payloads.forEach(function(payload) {
      // kinda hacky, but hey...
      options.headers['Content-Length'] = payload.length;
      postRaw(options, transport, payload, log, 0,username,apiKey);
    });
  }

  if (auth_token === null || typeof auth_token === 'undefined') {
    authenticate(this.tenantId,username,apiKey,postmetrics);
  }
  else {
    postmetrics(auth_token);
  }
}

function constructUserAgent() {
  // get the version out of the package.json.
  var version = 'unknown';

  try {
    version = JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'UTF-8')).version;
  } catch (err) {
    // ugh.
  }

  return 'blueflood-statsd-backend/' + version + ' (node.js)';
}

exports.authenticate = authenticate;
exports.postMetrics = postMetrics;
exports.postRaw = postRaw;
exports.buildPayloadsUnsafe = buildPayloads;
<|endoftext|>


<commit_before>'use strict'

const _ = require('lodash')

module.exports = function (options) {
  let seneca = this
  let name = 'concorda-auth-service'
  let clientDataEntity = 'client_data'

  function validateClient (msg, response) {
    var appkey = msg.data ? msg.data.appkey : msg.appkey
    var context = this

    if (!appkey) {
      return response(null, {ok: false, why: 'All commands require identification appkey'})
    }

    context.act('role: concorda, cmd: loadClient', {appkey: appkey}, function (err, result) {
      if (err) {
        return response(null, {ok: false, why: err})
      }

      if (!result || !result.ok) {
        return response(null, {ok: false, why: 'Client with specified appkey not found. appkey: ' + appkey})
      }

      var client = result.data

      context.prior(msg, function (err, out) {
        if (err) {
          return response(err, out)
        }
        if (!out || !out.ok) {
          return response(err, {ok: false, why: 'Incorrect login information'})
        }

        var user = out.user

        if (!user) {
          return respondWithError({ok: false, why: 'Incorrect login information.'}, response)
        }

        context.act('role: concorda, cmd: loadSettings', function (err, settingsResp) {
          if (err) {
            return response(err, out)
          }
          if (!out || !settingsResp.ok) {
            return response(err, {ok: false, why: 'Internal server error'})
          }
          var settings = settingsResp.data

          if (settings && settings.userPolicy && settings.userPolicy.activateAccount === '1' && !user.emailValidated) {
            return respondWithError({
              ok: false,
              code: 1,
              why: 'Account is disabled. Please confirm your email to activate account.'
            }, response)
          }

          if (settings && settings.userPolicy && settings.userPolicy.forceChangePassword === '1' && user.forcePwdChange) {
            return response(null, {
              ok: false,
              code: 2,
              why: 'You must change your password to be able to login.'
            }, response)
          }

          if (!user.clients) {
            return respondWithError({ok: false, why: 'User cannot login in this application: ' + client.name}, response)
          }

          if (_.findIndex(user.clients,
            function (o) {
              return o.id === client.id
            }) === -1) {
            return respondWithError({ok: false, why: 'User cannot login in this application: ' + client.name}, response)
          }
          response(err, out)
        })
      })
    })

    function respondWithError (responseData, done) {
      context.act('role: auth, cmd: logout', function () {
        done(null, responseData)
      })
    }
  }

  function changePassword (msg, response) {
    var user = msg.req$.seneca.user
    var context = this

    context.make$('sys', 'user').load$({id: user.id}, function (err, user) {
      if (err || !user) {
        return response(null, {ok: false, why: 'Internal server error'})
      }

      context.act('role: auth, cmd: validate_password', user, function (err, resp) {
        if (err) {
          return response(null, {ok: false, why: err})
        }

        if (!resp.ok) {
          return response(null, {ok: false, why: resp.why})
        }

        user.forcePwdChange = false
        user.save$(function () {
          context.prior(msg, response)
        })
      })
    })
  }

  function validatePassword (user, done) {
    var context = this
    context.act('role: concorda, cmd: loadSettings', function (err, settings) {
      if (err) {
        return done(null, {ok: false, why: err})
      }
      if (!user.password) {
        return done(null, {ok: false, why: 'Password Required'})
      }
      if (!user.repeat) {
        return done(null, {ok: false, why: 'Repeat password required'})
      }
      if (user.password !== user.repeat) {
        return done(null, {ok: false, why: 'Confirm Password must match Password'})
      }
      if (user.password === user.email) {
        return done(null, {ok: false, why: 'Cannot set password equal with email'})
      }

      if (user.password.length < settings.minLength) {
        return done(null, {ok: false, why: `Error: Password must contain at least ${settings.minLength} characters!`})
      }

      let re
      if (settings.requireNumeric) {
        re = /[0-9]/
        if (!re.test(user.password)) {
          return done(null, {ok: false, why: 'Error: password must contain at least one number (0-9)!'})
        }
      }

      if (settings.requireLowercase) {
        re = /[a-z]/
        if (!re.test(user.password)) {
          return done(null, {ok: false, why: 'Error: password must contain at least one lowercase letter (a-z)!'})
        }
      }

      if (settings.requireUppercase) {
        re = /[A-Z]/
        if (!re.test(user.password)) {
          return done(null, {ok: false, why: 'Error: password must contain at least one uppercase letter (A-Z)!'})
        }
      }
      return done()
    })
  }

  function identify_service_user (msg, respond) {
    if (!msg.user) {
      return respond(null, {ok: false, why: 'no-user'})
    }

    var user_data = msg.user

    var q = {}

    if (user_data.email) {
      q.email = user_data.email
    }
    else {
      return respond(null, {ok: false, why: 'no-identifier'})
    }

    seneca.act('role: user, get: user', q, respond)
  }

  // this is used to prepare user data from external auth providers to match our user data structure.
  var prepareLoginData = function (args, cb) {
    var profile = args.profile

    var data = {}

    if (profile.emails && profile.emails.length > 0) {
      data.email = profile.emails[0].value
    }
    data.name = profile.displayName

    cb(null, data)
  }

  // this is called for POST /auth/register so I need to verify the appkey
  function createUser (msg, response) {
    let context = this
    let userData = msg.data || msg

    let appkey = msg.appkey || userData.appkey
    delete userData.appkey

    if (!appkey) {
      return response(null, {ok: false, why: 'No appkey provided.'})
    }

    userData.emailValidated = _.contains(userData, 'emailValidated') ? userData.emailValidated : false// not validated if not specified otherwise
    userData.forcePwdChange = _.contains(userData, 'forcePwdChange') ? userData.forcePwdChange : false// should not change pwd on first login if not specified otherwise

    context.act('role: auth, cmd: validate_password', userData, function (err, resp) {
      if (err) {
        return response(null, {ok: false, why: err})
      }

      if (resp && !resp.ok) {
        return response(null, {ok: false, why: resp.why})
      }

      context.act('role: concorda, cmd: loadSettings', function (err, settings) {
        if (err) {
          return response(null, {ok: false, why: err})
        }
        settings = settings.data

        var publicRegister = settings.publicRegister
        if (appkey === 'concorda') {
          publicRegister = settings.concordaPublicRegister
        }

        // this registration can be due to an email invitation with a valid token
        // in this case user can register even if public registration is not active
        var token = userData.token
        delete userData.token

        if (publicRegister && publicRegister !== '1') {
          // verify token
          if (!token) {
            return response(null, {ok: false, why: 'Register available only by invitation.'})
          }

          // now verify token
          verifyToken(token, userData.email, function (err) {
            if (err) {
              return response(null, {ok: false, why: err})
            }

            verifyClient(function (err, client) {
              if (err) {
                return response(null, {ok: false, why: err})
              }

              do_register(client, response)
            })
          })
        }
        else {
          verifyClient(function (err, client) {
            if (err) {
              return response(null, {ok: false, why: err})
            }

            do_register(client, response)
          })
        }
      })

      function verifyClient (cb) {
        context.make$(clientDataEntity).load$({appkey: appkey, fields$: ['name']}, function (err, client) {
          if (err) {
            return cb(err)
          }

          if (client) {
            return cb(null, client)
          }

          // create a client if not exists - this might not be desired feature, should be removed or should be
          // customized from general settings ?
          context.act('role: concorda, cmd: createClient', {data: {appkey: appkey, name: appkey}}, cb)
        })
      }

      function verifyToken (token, email, cb) {
        context.make$(token).load$({token: token, email: email}, function (err, tokenData) {
          if (err) {
            return cb(err)
          }

          if (!tokenData) {
            return cb('No valid token or email.')
          }

          // after this user is registered the token is removed
          tokenData.remove$(cb)
        })
      }

      function do_register (client, cb) {
        userData.clients = userData.clients || []
        userData.clients.push({
          id: client.id,
          name: client.name
        })

        context.act('role: user, cmd: register', userData, function (err, result) {
          if (err) {
            return cb(null, {ok: false, why: err})
          }
          if (!result.ok) {
            return cb(null, {ok: false, why: result.why})
          }

          if (!result.user.emailValidated) {
            context.act('role: util, cmd: sendValidationEmail', result.user, function () {
              return cb(null, {ok: true, data: result.user})
            })
          }
        })
      }
    })
  }

  seneca
    .add('role: auth, cmd: register', createUser)
    .add('role: auth, cmd: login', validateClient)
    .add('role: auth, cmd: change_password', changePassword)
    .add('role: auth, identify: user', identify_service_user)
    .add('role: auth, cmd: validate_password', validatePassword)
    .add({role: 'auth', prepare: 'google_login_data'}, prepareLoginData)

  return {
    name: name
  }
}
<commit_msg>Added test for null client data. Fixes #73<commit_after>'use strict'

const _ = require('lodash')

module.exports = function (options) {
  let seneca = this
  let name = 'concorda-auth-service'
  let clientDataEntity = 'client_data'

  function validateClient (msg, response) {
    var appkey = msg.data ? msg.data.appkey : msg.appkey
    var context = this

    if (!appkey) {
      return response(null, {ok: false, why: 'All commands require identification appkey'})
    }

    context.act('role: concorda, cmd: loadClient', {appkey: appkey}, function (err, result) {
      if (err) {
        return response(null, {ok: false, why: err})
      }

      if (!result || !result.ok) {
        return response(null, {ok: false, why: 'Client with specified appkey not found. appkey: ' + appkey})
      }

      var client = result.data

      context.prior(msg, function (err, out) {
        if (err) {
          return response(err, out)
        }
        if (!out || !out.ok) {
          return response(err, {ok: false, why: 'Incorrect login information'})
        }

        var user = out.user

        if (!user) {
          return respondWithError({ok: false, why: 'Incorrect login information.'}, response)
        }

        context.act('role: concorda, cmd: loadSettings', function (err, settingsResp) {
          if (err) {
            return response(err, out)
          }
          if (!out || !settingsResp.ok) {
            return response(err, {ok: false, why: 'Internal server error'})
          }
          var settings = settingsResp.data

          if (settings && settings.userPolicy && settings.userPolicy.activateAccount === '1' && !user.emailValidated) {
            return respondWithError({
              ok: false,
              code: 1,
              why: 'Account is disabled. Please confirm your email to activate account.'
            }, response)
          }

          if (settings && settings.userPolicy && settings.userPolicy.forceChangePassword === '1' && user.forcePwdChange) {
            return response(null, {
              ok: false,
              code: 2,
              why: 'You must change your password to be able to login.'
            }, response)
          }

          if (!user.clients) {
            return respondWithError({ok: false, why: 'User cannot login in this application: ' + client.name}, response)
          }

          if (_.findIndex(user.clients,
            function (o) {
              if (!o) return false
              return o.id === client.id
            }) === -1) {
            return respondWithError({ok: false, why: 'User cannot login in this application: ' + client.name}, response)
          }
          response(err, out)
        })
      })
    })

    function respondWithError (responseData, done) {
      context.act('role: auth, cmd: logout', function () {
        done(null, responseData)
      })
    }
  }

  function changePassword (msg, response) {
    var user = msg.req$.seneca.user
    var context = this

    context.make$('sys', 'user').load$({id: user.id}, function (err, user) {
      if (err || !user) {
        return response(null, {ok: false, why: 'Internal server error'})
      }

      context.act('role: auth, cmd: validate_password', user, function (err, resp) {
        if (err) {
          return response(null, {ok: false, why: err})
        }

        if (!resp.ok) {
          return response(null, {ok: false, why: resp.why})
        }

        user.forcePwdChange = false
        user.save$(function () {
          context.prior(msg, response)
        })
      })
    })
  }

  function validatePassword (user, done) {
    var context = this
    context.act('role: concorda, cmd: loadSettings', function (err, settings) {
      if (err) {
        return done(null, {ok: false, why: err})
      }
      if (!user.password) {
        return done(null, {ok: false, why: 'Password Required'})
      }
      if (!user.repeat) {
        return done(null, {ok: false, why: 'Repeat password required'})
      }
      if (user.password !== user.repeat) {
        return done(null, {ok: false, why: 'Confirm Password must match Password'})
      }
      if (user.password === user.email) {
        return done(null, {ok: false, why: 'Cannot set password equal with email'})
      }

      if (user.password.length < settings.minLength) {
        return done(null, {ok: false, why: `Error: Password must contain at least ${settings.minLength} characters!`})
      }

      let re
      if (settings.requireNumeric) {
        re = /[0-9]/
        if (!re.test(user.password)) {
          return done(null, {ok: false, why: 'Error: password must contain at least one number (0-9)!'})
        }
      }

      if (settings.requireLowercase) {
        re = /[a-z]/
        if (!re.test(user.password)) {
          return done(null, {ok: false, why: 'Error: password must contain at least one lowercase letter (a-z)!'})
        }
      }

      if (settings.requireUppercase) {
        re = /[A-Z]/
        if (!re.test(user.password)) {
          return done(null, {ok: false, why: 'Error: password must contain at least one uppercase letter (A-Z)!'})
        }
      }
      return done()
    })
  }

  function identify_service_user (msg, respond) {
    if (!msg.user) {
      return respond(null, {ok: false, why: 'no-user'})
    }

    var user_data = msg.user

    var q = {}

    if (user_data.email) {
      q.email = user_data.email
    }
    else {
      return respond(null, {ok: false, why: 'no-identifier'})
    }

    seneca.act('role: user, get: user', q, respond)
  }

  // this is used to prepare user data from external auth providers to match our user data structure.
  var prepareLoginData = function (args, cb) {
    var profile = args.profile

    var data = {}

    if (profile.emails && profile.emails.length > 0) {
      data.email = profile.emails[0].value
    }
    data.name = profile.displayName

    cb(null, data)
  }

  // this is called for POST /auth/register so I need to verify the appkey
  function createUser (msg, response) {
    let context = this
    let userData = msg.data || msg

    let appkey = msg.appkey || userData.appkey
    delete userData.appkey

    if (!appkey) {
      return response(null, {ok: false, why: 'No appkey provided.'})
    }

    userData.emailValidated = _.contains(userData, 'emailValidated') ? userData.emailValidated : false// not validated if not specified otherwise
    userData.forcePwdChange = _.contains(userData, 'forcePwdChange') ? userData.forcePwdChange : false// should not change pwd on first login if not specified otherwise

    context.act('role: auth, cmd: validate_password', userData, function (err, resp) {
      if (err) {
        return response(null, {ok: false, why: err})
      }

      if (resp && !resp.ok) {
        return response(null, {ok: false, why: resp.why})
      }

      context.act('role: concorda, cmd: loadSettings', function (err, settings) {
        if (err) {
          return response(null, {ok: false, why: err})
        }
        settings = settings.data

        var publicRegister = settings.publicRegister
        if (appkey === 'concorda') {
          publicRegister = settings.concordaPublicRegister
        }

        // this registration can be due to an email invitation with a valid token
        // in this case user can register even if public registration is not active
        var token = userData.token
        delete userData.token

        if (publicRegister && publicRegister !== '1') {
          // verify token
          if (!token) {
            return response(null, {ok: false, why: 'Register available only by invitation.'})
          }

          // now verify token
          verifyToken(token, userData.email, function (err) {
            if (err) {
              return response(null, {ok: false, why: err})
            }

            verifyClient(function (err, client) {
              if (err) {
                return response(null, {ok: false, why: err})
              }

              do_register(client, response)
            })
          })
        }
        else {
          verifyClient(function (err, client) {
            if (err) {
              return response(null, {ok: false, why: err})
            }

            do_register(client, response)
          })
        }
      })

      function verifyClient (cb) {
        context.make$(clientDataEntity).load$({appkey: appkey, fields$: ['name']}, function (err, client) {
          if (err) {
            return cb(err)
          }

          if (client) {
            return cb(null, client)
          }

          // create a client if not exists - this might not be desired feature, should be removed or should be
          // customized from general settings ?
          context.act('role: concorda, cmd: createClient', {data: {appkey: appkey, name: appkey}}, cb)
        })
      }

      function verifyToken (token, email, cb) {
        context.make$(token).load$({token: token, email: email}, function (err, tokenData) {
          if (err) {
            return cb(err)
          }

          if (!tokenData) {
            return cb('No valid token or email.')
          }

          // after this user is registered the token is removed
          tokenData.remove$(cb)
        })
      }

      function do_register (client, cb) {
        userData.clients = userData.clients || []
        userData.clients.push({
          id: client.id,
          name: client.name
        })

        context.act('role: user, cmd: register', userData, function (err, result) {
          if (err) {
            return cb(null, {ok: false, why: err})
          }
          if (!result.ok) {
            return cb(null, {ok: false, why: result.why})
          }

          if (!result.user.emailValidated) {
            context.act('role: util, cmd: sendValidationEmail', result.user, function () {
              return cb(null, {ok: true, data: result.user})
            })
          }
        })
      }
    })
  }

  seneca
    .add('role: auth, cmd: register', createUser)
    .add('role: auth, cmd: login', validateClient)
    .add('role: auth, cmd: change_password', changePassword)
    .add('role: auth, identify: user', identify_service_user)
    .add('role: auth, cmd: validate_password', validatePassword)
    .add({role: 'auth', prepare: 'google_login_data'}, prepareLoginData)

  return {
    name: name
  }
}
<|endoftext|>


<commit_before>function(args) {
/*
is_app(true)
is_control(true)
display_name("Input control")
description("This will return the input control")
base_component_id("input_control")
load_once_from_file(true)
properties(
    [
        {
            id:     "label",
            name:   "Label",
            type:   "String"
        }
        ,
        {
            id:     "placeholder",
            name:   "Placeholder",
            type:   "String"
        }
        ,
        {
            id:     "background_color",
            name:   "Background color",
            type:   "String"
        }
        ,
        {
            id:     "text",
            name:   "Text",
            type:   "String"
        }
    ]
)//properties
logo_url("data:image/png;base64,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")
*/

    Vue.component("input_control",{
      props: ["args"]
      ,
      template: `

                    {{args.label}}

                    <input  class="form-control"
                            v-bind:style=   '"width:100%; " +
                                             "background-color: "+    args["background_color"]  +  ";"'
                            v-bind:value='text'

                                             >

                    </input>

`
      ,
      mounted: function() {
            this.text = this.args.text
      }
      ,
      data: function() {
            return {
                text: "aaa"
            }
      }
    })
}
<commit_msg>Trying to fix the VB input control<commit_after>function(args) {
/*
is_app(true)
is_control(true)
display_name("Input control")
description("This will return the input control")
base_component_id("input_control")
load_once_from_file(true)
properties(
    [
        {
            id:     "label",
            name:   "Label",
            type:   "String"
        }
        ,
        {
            id:     "placeholder",
            name:   "Placeholder",
            type:   "String"
        }
        ,
        {
            id:     "background_color",
            name:   "Background color",
            type:   "String"
        }
        ,
        {
            id:     "text",
            name:   "Text",
            type:   "String"
        }
    ]
)//properties
logo_url("data:image/png;base64,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")
*/

    Vue.component("input_control",{
      props: ["args"]
      ,
      template: `

                    {{args.label}}

                    <input  class="form-control2"

                            v-bind:style=   '"width:100%; " +
                                             "background-color: "+    args["background_color"]  +  ";"'

                            v-model='text'>  </input>

                            <button  class='btn btn-info'
                                     v-on:click='alert(text)'>  click  </button>

`
      ,
      mounted: function() {
            //this.text = this.args.text
      }
      ,
      data: function() {
            return {
                text: "aaa"
            }
      }
    })
}
<|endoftext|>