<commit_before>
    var create_type = 0;
    var interval = null;

    head(function(){

      $('div.geom_type span').click(function(ev){
        ev.stopPropagation();
        ev.preventDefault();
        if (!$(this).hasClass('selected')) {
          $('div.geom_type span').removeClass('selected');
          $(this).addClass('selected');
        }
      });

      $('div.geom_type span a').click(function(ev){
        ev.stopPropagation();
        ev.preventDefault();
        $(this).parent().trigger('click');
      });

      //Create new table
      $('a.new_table').click(function(ev){
         stopPropagation(ev);
         resetUploadFile();
         $('div.create_window').show();
         $('div.mamufas').fadeIn();
         bindESC();
      });

      $('div.select_file input#url_txt').focusin(function(){
         $(this).val('');
         $('div.create_window span.bottom input').removeClass('disabled');
 	    });

      $('div.select_file input#url_txt').focusout(function(){
   	    if ($(this).val() == ""){
       	  $(this).val('Insert a valid URL...');
           $('div.create_window span.bottom input').addClass('disabled');
   	    }else{
     	    $('div.create_window span.bottom input').removeClass('disabled');
   	    }
 	    });

      $('div.create_window ul li a').click(function(ev){
        stopPropagation(ev);
        create_type = $(this).closest('li').index();

        if (!$(this).parent().hasClass('selected') && !$(this).parent().hasClass('disabled') && !$(this).parent().is("span")) {
          $('div.create_window ul li').removeClass('selected');
          $(this).parent().addClass('selected');
        }

				if (($(this).closest('li').index()==1) || ($(this).closest('li').index()==2) && $('div.select_file input#url_txt').val() == "Insert a valid URL...") {
					$('div.create_window span.bottom input').addClass('disabled');
				} else {
					$('div.create_window span.bottom input').removeClass('disabled');
				}
      });

      $('span.file input').hover(function(ev){
        $('span.file a').addClass('hover');
        $(document).css('cursor','pointer');
      },function(ev){
        $('span.file a').removeClass('hover');
        $(document).css('cursor','default');
      });

      $('form#import_file').submit(function(ev){
        ev.stopPropagation();
        ev.preventDefault();
        if (!$('div.create_window span.bottom input').hasClass('disabled')) {
          if (create_type==0) {
            var geom_type = $('div.geom_type span.selected a').text().toLowerCase();

            if (geom_type=="point") {
              geom_type="multipoint";
            } else if (geom_type=="polygon") {
              geom_type="multipolygon";
            } else {
              geom_type="multilinestring";
            }

            createNewToFinish(geom_type,'');
          } else if (create_type==2) {
            createNewToFinish('',$('div.select_file input#url_txt').val(),true);
          }
        }
      });

      //Uploader for the modal window
      var uploader = new qq.FileUploader({
        element: document.getElementById('uploader'),
        action: '/upload',
        params: {},
        allowedExtensions: ['csv', 'xls', 'xlsx', 'zip', 'kml', 'geojson', 'json', 'js', 'ods', 'kmz'],
        sizeLimit: 0, // max size
        minSizeLimit: 0, // min size
        debug: false,
        onSubmit: function(id, fileName){
          $('div.create_window ul li:eq(0)').addClass('disabled');
          $('form input[type="submit"]').addClass('disabled');
          $('span.file').addClass('uploading');
        },
        onProgress: function(id, fileName, loaded, total){
          var percentage = loaded / total;
          $('span.progress').width((346*percentage)/1);
        },
        onComplete: function(id, fileName, responseJSON){
          createNewToFinish('',responseJSON.file_uri);
        },
        onCancel: function(id, fileName){},
        showMessage: function(message){
          $('div.select_file p').html(message);
          $('div.select_file p').addClass('error');
        }
      });

      //Uploader for the whole page (dashboard only)
      var hugeUploader = new qq.FileUploader({
      	element: document.getElementById('hugeUploader'),
      	action: '/upload',
      	params: {},
        allowedExtensions: ['csv', 'xls', 'xlsx', 'zip', 'kml', 'geojson', 'json', 'ods', 'kmz'],
      	sizeLimit: 0,
      	minSizeLimit: 0,
      	debug: false,

      	onSubmit: function(id, fileName){
        	resetUploadFile();
      		$('div.create_window ul li:eq(0)').addClass('disabled');
      		$('form input[type="submit"]').addClass('disabled');
      		$('span.file').addClass('uploading');
      		$('div.create_window ul li:eq(1) a').click();
          $('#hugeUploader').hide();
          $('div.create_window').show();
          $('div.mamufas').fadeIn();
          bindESC();
      	},
      	onProgress: function(id, fileName, loaded, total){
      		var percentage = loaded / total;
      		$('span.progress').width((346*percentage)/1);
      	},
      	onComplete: function(id, fileName, responseJSON){
      		createNewToFinish('',responseJSON.file_uri);
      		$('#hugeUploader').hide();
      	},
      	onCancel: function(id, fileName){},
      	showMessage: function(message){
      		 $('div.select_file p').html(message);
      		 $('div.select_file p').addClass('error');
      	}
      });
    });

    function resetUploadFile() {
      create_type = 0;
      $('div.create_window ul li:eq(0)').removeClass('disabled');
      $('div.create_window ul li').removeClass('selected');
      $('div.create_window ul li:eq(0)').addClass('selected');
      $('div.create_window div.inner_ form').show();
      $('div.create_window div.inner_ form').css('opacity',1);
      $('div.create_window div.inner_').css('border-color','#CCCCCC');
      $('div.create_window a.close_create').removeClass('last');
      $('div.create_window div.inner_').css('height','auto');
      $('div.create_window div.inner_ span.loading').hide();
      $('div.create_window div.inner_ span.loading').css('opacity',0);
      $('form input[type="submit"]').removeClass('disabled');
      $('span.file').removeClass('uploading');
      $('div.create_window div.inner_ span.loading').removeClass('error');
      $('span.file input[type="file"]').attr('value','');
      $('div.select_file p').text('You can import .csv, .xls and .zip files');
      $('div.select_file p').removeClass('error');
      $('span.progress').width(5);
      $('div.create_window ul li:eq(1)').removeClass('finished');
      $('div.create_window').removeClass('georeferencing');
      $('div.create_window div.inner_ span.loading p').html('It\'s not gonna be a lot of time. Just a few seconds, ok?');
      $('div.create_window div.inner_ span.loading h5').html('We are creating your table...');
      $('div.qq-upload-drop-area').hide();
    }

    function createNewToFinish (type,url,out) {
      $('div.create_window div.inner_').animate({borderColor:'#FFC209', height:'68px'},500);
      $('div.create_window div.inner_ form').animate({opacity:0},300,function(){
        $('div.create_window div.inner_ span.loading').show();
				$('div.create_window a.close_create').hide();
        $('div.create_window div.inner_ span.loading').animate({opacity:1},200, function(){
          var params = {}
          if (url!='') {
            if (!out) {
              params = {file:'http://'+window.location.host + url};
            } else {
              params = {file:url};
            }
          } else {
            params = {the_geom_type:type}
          }
          $.ajax({
            type: "POST",
            url: global_api_url+'tables/',
            data: params,
            headers: {'cartodbclient':true},
            success: function(data, textStatus, XMLHttpRequest) {
              window.location.href = "/tables/"+data.id;
            },
            error: function(e) {
							var json = $.parseJSON(e.responseText);
              $('div.create_window div.inner_ span.loading').addClass('error');
              $('div.create_window div.inner_ span.loading p').html(json.raw_error +'

'+json.hint);
              $('div.create_window div.inner_ span.loading h5').text(json.message);
							$('div.create_window a.close_create').show().addClass('last');
              $('div.create_window div.inner_').height($('div.create_window div.inner_ span.loading').height() + 30);
            }
          });
        });
      });
      setTimeout(function(){$('div.create_window a.close_create').addClass('last');},250);
    }

    function retryImportTable() {
      $('div.create_window a.close_create').show().removeClass('last');
      $('div.create_window div.inner_').animate({borderColor:'#CCCCCC', height:'254px'},500,function(){
        $('div.create_window div.inner_').css('height','auto');
      });
      $('div.create_window ul li:eq(0)').removeClass('disabled');
      $('form input[type="submit"]').removeClass('disabled');
      $('span.file').removeClass('uploading');
      $('div.create_window div.inner_ span.loading').animate({opacity:0},300,function(){
        $('div.create_window div.inner_ span.loading').hide();
        $('div.create_window div.inner_ span.loading').removeClass('error');
        $('div.create_window div.inner_ span.loading p').html('It\'s not gonna be a lot of time. Just a few seconds, ok?');
        $('div.create_window div.inner_ span.loading h5').html('We are creating your table...');
        $('div.create_window div.inner_ form').show();
        $('div.create_window div.inner_ form').animate({opacity:1},200);
        $('div.select_file p').text('You can import .csv, .xls and .zip files');
        $('div.select_file p').removeClass('error');
      });
    }

<commit_msg>removed js extension from import supported files<commit_after>
    var create_type = 0;
    var interval = null;

    head(function(){

      $('div.geom_type span').click(function(ev){
        ev.stopPropagation();
        ev.preventDefault();
        if (!$(this).hasClass('selected')) {
          $('div.geom_type span').removeClass('selected');
          $(this).addClass('selected');
        }
      });

      $('div.geom_type span a').click(function(ev){
        ev.stopPropagation();
        ev.preventDefault();
        $(this).parent().trigger('click');
      });

      //Create new table
      $('a.new_table').click(function(ev){
         stopPropagation(ev);
         resetUploadFile();
         $('div.create_window').show();
         $('div.mamufas').fadeIn();
         bindESC();
      });

      $('div.select_file input#url_txt').focusin(function(){
         $(this).val('');
         $('div.create_window span.bottom input').removeClass('disabled');
 	    });

      $('div.select_file input#url_txt').focusout(function(){
   	    if ($(this).val() == ""){
       	  $(this).val('Insert a valid URL...');
           $('div.create_window span.bottom input').addClass('disabled');
   	    }else{
     	    $('div.create_window span.bottom input').removeClass('disabled');
   	    }
 	    });

      $('div.create_window ul li a').click(function(ev){
        stopPropagation(ev);
        create_type = $(this).closest('li').index();

        if (!$(this).parent().hasClass('selected') && !$(this).parent().hasClass('disabled') && !$(this).parent().is("span")) {
          $('div.create_window ul li').removeClass('selected');
          $(this).parent().addClass('selected');
        }

				if (($(this).closest('li').index()==1) || ($(this).closest('li').index()==2) && $('div.select_file input#url_txt').val() == "Insert a valid URL...") {
					$('div.create_window span.bottom input').addClass('disabled');
				} else {
					$('div.create_window span.bottom input').removeClass('disabled');
				}
      });

      $('span.file input').hover(function(ev){
        $('span.file a').addClass('hover');
        $(document).css('cursor','pointer');
      },function(ev){
        $('span.file a').removeClass('hover');
        $(document).css('cursor','default');
      });

      $('form#import_file').submit(function(ev){
        ev.stopPropagation();
        ev.preventDefault();
        if (!$('div.create_window span.bottom input').hasClass('disabled')) {
          if (create_type==0) {
            var geom_type = $('div.geom_type span.selected a').text().toLowerCase();

            if (geom_type=="point") {
              geom_type="multipoint";
            } else if (geom_type=="polygon") {
              geom_type="multipolygon";
            } else {
              geom_type="multilinestring";
            }

            createNewToFinish(geom_type,'');
          } else if (create_type==2) {
            createNewToFinish('',$('div.select_file input#url_txt').val(),true);
          }
        }
      });

      //Uploader for the modal window
      var uploader = new qq.FileUploader({
        element: document.getElementById('uploader'),
        action: '/upload',
        params: {},
        allowedExtensions: ['csv', 'xls', 'xlsx', 'zip', 'kml', 'geojson', 'json', 'ods', 'kmz'],
        sizeLimit: 0, // max size
        minSizeLimit: 0, // min size
        debug: false,
        onSubmit: function(id, fileName){
          $('div.create_window ul li:eq(0)').addClass('disabled');
          $('form input[type="submit"]').addClass('disabled');
          $('span.file').addClass('uploading');
        },
        onProgress: function(id, fileName, loaded, total){
          var percentage = loaded / total;
          $('span.progress').width((346*percentage)/1);
        },
        onComplete: function(id, fileName, responseJSON){
          createNewToFinish('',responseJSON.file_uri);
        },
        onCancel: function(id, fileName){},
        showMessage: function(message){
          $('div.select_file p').html(message);
          $('div.select_file p').addClass('error');
        }
      });

      //Uploader for the whole page (dashboard only)
      var hugeUploader = new qq.FileUploader({
      	element: document.getElementById('hugeUploader'),
      	action: '/upload',
      	params: {},
        allowedExtensions: ['csv', 'xls', 'xlsx', 'zip', 'kml', 'geojson', 'json', 'ods', 'kmz'],
      	sizeLimit: 0,
      	minSizeLimit: 0,
      	debug: false,

      	onSubmit: function(id, fileName){
        	resetUploadFile();
      		$('div.create_window ul li:eq(0)').addClass('disabled');
      		$('form input[type="submit"]').addClass('disabled');
      		$('span.file').addClass('uploading');
      		$('div.create_window ul li:eq(1) a').click();
          $('#hugeUploader').hide();
          $('div.create_window').show();
          $('div.mamufas').fadeIn();
          bindESC();
      	},
      	onProgress: function(id, fileName, loaded, total){
      		var percentage = loaded / total;
      		$('span.progress').width((346*percentage)/1);
      	},
      	onComplete: function(id, fileName, responseJSON){
      		createNewToFinish('',responseJSON.file_uri);
      		$('#hugeUploader').hide();
      	},
      	onCancel: function(id, fileName){},
      	showMessage: function(message){
      		 $('div.select_file p').html(message);
      		 $('div.select_file p').addClass('error');
      	}
      });
    });

    function resetUploadFile() {
      create_type = 0;
      $('div.create_window ul li:eq(0)').removeClass('disabled');
      $('div.create_window ul li').removeClass('selected');
      $('div.create_window ul li:eq(0)').addClass('selected');
      $('div.create_window div.inner_ form').show();
      $('div.create_window div.inner_ form').css('opacity',1);
      $('div.create_window div.inner_').css('border-color','#CCCCCC');
      $('div.create_window a.close_create').removeClass('last');
      $('div.create_window div.inner_').css('height','auto');
      $('div.create_window div.inner_ span.loading').hide();
      $('div.create_window div.inner_ span.loading').css('opacity',0);
      $('form input[type="submit"]').removeClass('disabled');
      $('span.file').removeClass('uploading');
      $('div.create_window div.inner_ span.loading').removeClass('error');
      $('span.file input[type="file"]').attr('value','');
      $('div.select_file p').text('You can import .csv, .xls and .zip files');
      $('div.select_file p').removeClass('error');
      $('span.progress').width(5);
      $('div.create_window ul li:eq(1)').removeClass('finished');
      $('div.create_window').removeClass('georeferencing');
      $('div.create_window div.inner_ span.loading p').html('It\'s not gonna be a lot of time. Just a few seconds, ok?');
      $('div.create_window div.inner_ span.loading h5').html('We are creating your table...');
      $('div.qq-upload-drop-area').hide();
    }

    function createNewToFinish (type,url,out) {
      $('div.create_window div.inner_').animate({borderColor:'#FFC209', height:'68px'},500);
      $('div.create_window div.inner_ form').animate({opacity:0},300,function(){
        $('div.create_window div.inner_ span.loading').show();
				$('div.create_window a.close_create').hide();
        $('div.create_window div.inner_ span.loading').animate({opacity:1},200, function(){
          var params = {}
          if (url!='') {
            if (!out) {
              params = {file:'http://'+window.location.host + url};
            } else {
              params = {file:url};
            }
          } else {
            params = {the_geom_type:type}
          }
          $.ajax({
            type: "POST",
            url: global_api_url+'tables/',
            data: params,
            headers: {'cartodbclient':true},
            success: function(data, textStatus, XMLHttpRequest) {
              window.location.href = "/tables/"+data.id;
            },
            error: function(e) {
							var json = $.parseJSON(e.responseText);
              $('div.create_window div.inner_ span.loading').addClass('error');
              $('div.create_window div.inner_ span.loading p').html(json.raw_error +'

'+json.hint);
              $('div.create_window div.inner_ span.loading h5').text(json.message);
							$('div.create_window a.close_create').show().addClass('last');
              $('div.create_window div.inner_').height($('div.create_window div.inner_ span.loading').height() + 30);
            }
          });
        });
      });
      setTimeout(function(){$('div.create_window a.close_create').addClass('last');},250);
    }

    function retryImportTable() {
      $('div.create_window a.close_create').show().removeClass('last');
      $('div.create_window div.inner_').animate({borderColor:'#CCCCCC', height:'254px'},500,function(){
        $('div.create_window div.inner_').css('height','auto');
      });
      $('div.create_window ul li:eq(0)').removeClass('disabled');
      $('form input[type="submit"]').removeClass('disabled');
      $('span.file').removeClass('uploading');
      $('div.create_window div.inner_ span.loading').animate({opacity:0},300,function(){
        $('div.create_window div.inner_ span.loading').hide();
        $('div.create_window div.inner_ span.loading').removeClass('error');
        $('div.create_window div.inner_ span.loading p').html('It\'s not gonna be a lot of time. Just a few seconds, ok?');
        $('div.create_window div.inner_ span.loading h5').html('We are creating your table...');
        $('div.create_window div.inner_ form').show();
        $('div.create_window div.inner_ form').animate({opacity:1},200);
        $('div.select_file p').text('You can import .csv, .xls and .zip files');
        $('div.select_file p').removeClass('error');
      });
    }

<|endoftext|>


<commit_before>var _ = require('underscore');
var backbone = require('backbone');
var backboneBase = require('backbone-base');

module.exports = {
  ConfirmationView: backbone.BaseView.extend({
    // Activate overlay along with dialog box
    activate: function(state) {
      window.APP.$('#overlay').prop('hidden', !(_.isUndefined(state) || state));
      backbone.BaseView.prototype.activate.call(this, state);
      return this;
    },
  })
};
<commit_msg>Set up confirmation dialog message (#10)<commit_after>var _ = require('underscore');
var backbone = require('backbone');
var backboneBase = require('backbone-base');

module.exports = {
  ConfirmationView: backbone.BaseView.extend({
    // Activate overlay along with dialog box
    activate: function(state) {
      window.APP.$('#overlay').prop('hidden', !(_.isUndefined(state) || state));
      backbone.BaseView.prototype.activate.call(this, state);
      return this;
    },

    setMessage: function(text) { this.$('[data-id=message]').html(text || ''); return this; }
  })
};
<|endoftext|>


<commit_before>// This function will target a particular target, using no delegation ( because it needs to be recalled each time ).
// When the plugin is initialized the plugin will

(function( $, window, document, undefined ){

	$.fn.hexSlide = function( options ){

		options = $.extend(true, {}, {
			container: ".container",
			interval: 3000,
			pauseOnHover: true,
			navigation: true,
			alwaysShowNav: false
		}, options)

		var timers = [],

	}

})( jQuery, window, document )<commit_msg>Added vars<commit_after>// This function will target a particular target, using no delegation ( because it needs to be recalled each time ).
// When the plugin is initialized the plugin will

(function( $, window, document, undefined ){

	$.fn.hexSlide = function( options ){

		options = $.extend(true, {}, {
			container: ".container",
			interval: 3000,
			pauseOnHover: true,
			navigation: true,
			alwaysShowNav: false
		}, options)

		var timers = [],
			slideshowImg = [],
			originalSrc,
			dummy,
			container,
			listArray

	}

})( jQuery, window, document )<|endoftext|>


<commit_before>/* global DocumentFragment */

var Cache = require('../cache')
var templateCache = new Cache(100)

var map = {
  legend   : [1, '<fieldset>', '</fieldset>'],
  tr       : [2, '

', '

'],
  col      : [2, '

<colgroup>', '</colgroup>
'],
  _default : [0, '', '']
}

map.td =
map.th = [3, '

', '

']

map.option =
map.optgroup = [1, '<select multiple="multiple">', '</select>']

map.thead =
map.tbody =
map.colgroup =
map.caption =
map.tfoot = [1, '
', '
']

map.g =
map.defs =
map.text =
map.circle =
map.ellipse =
map.line =
map.path =
map.polygon =
map.polyline =
map.rect = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>']

var TAG_RE = /<([\w:]+)/

/**
 * Convert a string template to a DocumentFragment.
 * Determines correct wrapping by tag types. Wrapping strategy
 * originally from jQuery, scooped from component/domify.
 *
 * @param {String} templateString
 * @return {DocumentFragment}
 */

function stringToFragment (templateString) {
  // try a cache hit first
  var hit = templateCache.get(templateString)
  if (hit) {
    return hit
  }

  var frag = document.createDocumentFragment()
  var tagMatch = TAG_RE.exec(templateString)

  if (!tagMatch) {
    // text only, return a single text node.
    frag.appendChild(document.createTextNode(templateString))
  } else {

    var tag    = tagMatch[1]
    var wrap   = map[tag] || map._default
    var depth  = wrap[0]
    var prefix = wrap[1]
    var suffix = wrap[2]
    var node   = document.createElement('div')

    node.innerHTML = prefix + templateString.trim() + suffix
    while (depth--) {
      node = node.lastChild
    }

    var child
    /* jshint boss:true */
    while (child = node.firstChild) {
      if (node.nodeType === 1) {
        frag.appendChild(child)
      }
    }
  }

  templateCache.put(templateString, frag)
  return frag
}

/**
 * Convert a template node to a DocumentFragment.
 *
 * @param {Node} node
 * @return {DocumentFragment}
 */

function nodeToFragment (node) {
  var tag = node.tagName
  // if its a template tag and the browser supports it,
  // its content is already a document fragment.
  if (tag === 'TEMPLATE' && node.content instanceof DocumentFragment) {
    return node.content
  }
  return tag === 'SCRIPT'
    ? stringToFragment(node.textContent)
    : stringToFragment(node.innerHTML)
}

/**
 * Process the template option and normalizes it into a
 * a DocumentFragment that can be used as a partial or a
 * instance template.
 *
 * @param {*} template
 *    Possible values include:
 *    - DocumentFragment object
 *    - Node object of type Template
 *    - id selector: '#some-template-id'
 *    - template string: '
my template
'
 * @return {DocumentFragment|undefined}
 */

exports.parse = function (template) {
  var node, frag

  // if the template is already a document fragment -- do nothing
  if (template instanceof DocumentFragment) {
    return template
  }

  if (typeof template === 'string') {
    // id selector
    if (template.charAt(0) === '#') {
      // id selector can be cached too
      frag = templateCache.get(template)
      if (!frag) {
        node = document.getElementById(template.slice(1))
        if (node) {
          frag = nodeToFragment(node)
          // save selector to cache
          templateCache.put(template, frag)
        }
      }
    } else {
      // normal string template
      frag = stringToFragment(template)
    }
  } else if (template.nodeType) {
    // a direct node
    frag = nodeToFragment(template)
  }

  return frag
}<commit_msg>remove faulty nodeType check when parsing template<commit_after>/* global DocumentFragment */

var Cache = require('../cache')
var templateCache = new Cache(100)

var map = {
  legend   : [1, '<fieldset>', '</fieldset>'],
  tr       : [2, '

', '

'],
  col      : [2, '

<colgroup>', '</colgroup>
'],
  _default : [0, '', '']
}

map.td =
map.th = [3, '

', '

']

map.option =
map.optgroup = [1, '<select multiple="multiple">', '</select>']

map.thead =
map.tbody =
map.colgroup =
map.caption =
map.tfoot = [1, '
', '
']

map.g =
map.defs =
map.text =
map.circle =
map.ellipse =
map.line =
map.path =
map.polygon =
map.polyline =
map.rect = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>']

var TAG_RE = /<([\w:]+)/

/**
 * Convert a string template to a DocumentFragment.
 * Determines correct wrapping by tag types. Wrapping strategy
 * originally from jQuery, scooped from component/domify.
 *
 * @param {String} templateString
 * @return {DocumentFragment}
 */

function stringToFragment (templateString) {
  // try a cache hit first
  var hit = templateCache.get(templateString)
  if (hit) {
    return hit
  }

  var frag = document.createDocumentFragment()
  var tagMatch = TAG_RE.exec(templateString)

  if (!tagMatch) {
    // text only, return a single text node.
    frag.appendChild(document.createTextNode(templateString))
  } else {

    var tag    = tagMatch[1]
    var wrap   = map[tag] || map._default
    var depth  = wrap[0]
    var prefix = wrap[1]
    var suffix = wrap[2]
    var node   = document.createElement('div')

    node.innerHTML = prefix + templateString.trim() + suffix
    while (depth--) {
      node = node.lastChild
    }

    var child
    /* jshint boss:true */
    while (child = node.firstChild) {
      frag.appendChild(child)
    }
  }

  templateCache.put(templateString, frag)
  return frag
}

/**
 * Convert a template node to a DocumentFragment.
 *
 * @param {Node} node
 * @return {DocumentFragment}
 */

function nodeToFragment (node) {
  var tag = node.tagName
  // if its a template tag and the browser supports it,
  // its content is already a document fragment.
  if (tag === 'TEMPLATE' && node.content instanceof DocumentFragment) {
    return node.content
  }
  return tag === 'SCRIPT'
    ? stringToFragment(node.textContent)
    : stringToFragment(node.innerHTML)
}

/**
 * Process the template option and normalizes it into a
 * a DocumentFragment that can be used as a partial or a
 * instance template.
 *
 * @param {*} template
 *    Possible values include:
 *    - DocumentFragment object
 *    - Node object of type Template
 *    - id selector: '#some-template-id'
 *    - template string: '
my template
'
 * @return {DocumentFragment|undefined}
 */

exports.parse = function (template) {
  var node, frag

  // if the template is already a document fragment -- do nothing
  if (template instanceof DocumentFragment) {
    return template
  }

  if (typeof template === 'string') {
    // id selector
    if (template.charAt(0) === '#') {
      // id selector can be cached too
      frag = templateCache.get(template)
      if (!frag) {
        node = document.getElementById(template.slice(1))
        if (node) {
          frag = nodeToFragment(node)
          // save selector to cache
          templateCache.put(template, frag)
        }
      }
    } else {
      // normal string template
      frag = stringToFragment(template)
    }
  } else if (template.nodeType) {
    // a direct node
    frag = nodeToFragment(template)
  }

  return frag
}<|endoftext|>


<commit_before>/**
 * Specifies an envMap on an entity, without replacing any existing material
 * properties.
 */
module.exports = AFRAME.registerComponent('cube-env-map', {
  schema: {
    path: {default: ''},
    extension: {default: 'jpg'},
    format: {default: 'RGBFormat'},
    enableBackground: {default: false}
  },

  init: function () {
    const data = this.data;

    this.texture = new THREE.CubeTextureLoader().load([
      data.path + 'posx.' + data.extension, data.path + 'negx.' + data.extension,
      data.path + 'posy.' + data.extension, data.path + 'negy.' + data.extension,
      data.path + 'posz.' + data.extension, data.path + 'negz.' + data.extension
    ]);
    this.texture.format = THREE[data.format];

    if (data.enableBackground) {
      this.el.sceneEl.object3D.background = this.texture;
    }

    this.applyEnvMap();
    this.el.addEventListener('object3dset', this.applyEnvMap.bind(this));
  },

  applyEnvMap: function () {
    const mesh = this.el.getObject3D('mesh');
    const envMap = this.texture;

    if (!mesh) return;

    mesh.traverse(function (node) {
      if (node.material && 'envMap' in node.material) {
        node.material.envMap = envMap;
        node.material.needsUpdate = true;
      }
    });
  }
});
<commit_msg>Added support for meshes with multiple materials.<commit_after>/**
 * Specifies an envMap on an entity, without replacing any existing material
 * properties.
 */
module.exports = AFRAME.registerComponent('cube-env-map', {
  schema: {
    path: {default: ''},
    extension: {default: 'jpg'},
    format: {default: 'RGBFormat'},
    enableBackground: {default: false}
  },

  init: function () {
    const data = this.data;

    this.texture = new THREE.CubeTextureLoader().load([
      data.path + 'posx.' + data.extension, data.path + 'negx.' + data.extension,
      data.path + 'posy.' + data.extension, data.path + 'negy.' + data.extension,
      data.path + 'posz.' + data.extension, data.path + 'negz.' + data.extension
    ]);
    this.texture.format = THREE[data.format];

    if (data.enableBackground) {
      this.el.sceneEl.object3D.background = this.texture;
    }

    this.applyEnvMap();
    this.el.addEventListener('object3dset', this.applyEnvMap.bind(this));
  },

  applyEnvMap: function () {
    const mesh = this.el.getObject3D('mesh');
    const envMap = this.texture;

    if (!mesh) return;

    mesh.traverse(node => {
      const materials = this.ensureMaterialArray(node.material)

      materials.forEach(material => {
        if (material && 'envMap' in material) {
          material.envMap = envMap;
          material.needsUpdate = true;
        }
      });
    });
  },

  ensureMaterialArray: function (material) {
    if(!material) {
      return []
    } else if(Array.isArray(material)) {
      return material
    } else if(material.materials) {
      return material.materials
    } else {
      return [material]
    }
  }
});
<|endoftext|>


<commit_before>const path = require('path');
const jsdom = require('jsdom');
const _ = require('lodash');
const fs = require('fs-jetpack');
const globals = require('./globals');
const utilities = require('./utilities');
const getFile = require('./get-file');

function initializeJSDom (file, callback) {
 const doc = jsdom.env({
    html: file,
    scripts: [
      'http://code.jquery.com/jquery.js'
    ],
    done: (err, window) => {
      callback(err, window);
    }
  });

  return doc;
}

function getBlockSource (block) {
  const blockSource = _.trimEnd(_.trimStart(
    block
      .clone()
      .removeAttr('data-type') // Remove junk attributes.
      .removeAttr('data-title')
      .removeAttr('data-picture')
      .wrap('<div/>')
      .parent()
      .html()
  ));

  return blockSource;
}

function getBlockFeatures (block) {
  let features = {
    videoBackground: false,
    imageBackground: false,
    colorBackground: false,
    countdown: false,
    formInput: false
  };

  if (block.find('.countdown').length) {
    features = _.assign({}, features, {
      countdown: true
    });
  }

  if (block.find('.background-cover-color').length) {
    features = _.assign({}, features, {
      colorBackground: true
    });
  }

  if (block.find('.background-image-holder').length ||
      _.startsWith(block.attr('style'), 'background-image')) {
    features = _.assign({}, features, {
      imageBackground: true
    });
  }

  if (block.find('.background-video-holder').length) {
    features = _.assign({}, features, {
      videoBackground: true
    });
  }

  if (_.startsWith(block.attr('style'), 'background-image')) {
    features = _.assign({}, features, {
      imageBackground: true
    });
  }

  if (block.find('form').length > 0) {
    features = _.assign({}, features, {
      formInput: true
    });
  }

  return features;
}

function parseHTMLAssets ($, assets) {
  const result = [];

  if (!$ || !assets) {
    return result;
  }

  assets.each(function parse () {
    const asset = $(this);
    const assetType = asset.attr('data-asset');

    switch (assetType) {
      case 'css':
      case 'stylesheet':
      case 'js':
      case 'javascript': {
        let assetSource = asset.attr('src') || asset.attr('href');
        const { dir, base } = path.parse(assetSource);

        if (dir === 'assets') {
          assetSource = path.join(dir, 'template', base).split(path.sep).join('/');
        }

        result.push({
          type: 'external-asset',
          target: assetType,
          value: assetSource
        });

        break;
      }

      default:
        break;
    }
  });

  return result;
}

function createBlockComponent(blockInfo, blockSource = '') {
  const { source: fileName } = blockInfo;

  const componentHTML = `
<!DOCTYPE html>
<head>
  <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Arkio Template by Trip-Trax 2017</title>

    <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700|Crimson+Text:400,700" rel="stylesheet" data-asset="stylesheet">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" data-asset="stylesheet">
    <link rel="stylesheet" href="../../assets/template/template.css">

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js" data-asset="javascript"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js" data-asset="javascript"></script>
    <script src="../../assets/template/template.js"></script>

</head>
<body>
${blockSource}
</body>
`;

  fs.write(path.join(globals.currentDir, 'build', 'components', fileName), componentHTML);
}

function parseBlocks ($, blocks) {
  const result = [];

  blocks.each(function () {
    const block = $(this);
    const info = {
      type: null,
      title: null,
      source: null
    };

    block.contents()
      .filter(function () {
        return this.nodeType === 8;
      })
      .each(function () {
        const text = this.nodeValue;
        const lines = text.split('\n');

        _.map(lines, (line) => {
          if (line.length === 0) {
            return;
          }

          const tagRegex = /@{1}[^\s]+/g;
          const search = tagRegex.exec(line);
          let tag = null;
          let tagInfo = null;

          if (search !== null) {
            tag = search[0];
            tagInfo = line.substring(tag.length + '- '.length + 3);
          }

          if (!tag || !tagInfo) {
            return;
          }

          if (tag === '@title') {
            info.title = tagInfo;
          } else if (tag === '@category') {
            info.type = tagInfo;
          } else if (tag === '@source') {
            info.source = tagInfo;
          }
        });
    });

    // Remove comments
    block.contents().each(function removeComments () {
      if (this.nodeType === 8) {
        $(this).remove();
      }
    });

    if (!info.type || !info.title) {
      return;
    }

    const blockID = Math.random().toString(36).substr(2, 33);
    const blockSource = getBlockSource(block);
    const features = getBlockFeatures(block);

    createBlockComponent(info, blockSource);

    result.push({
      type: 'block',
      target: info.type,
      value: _.assign({}, { features }, {
        id: blockID,
        title: info.title,
        thumbnail: `assets/template/thumbnails/${blockID.toString()}.jpeg`,
        source: blockSource,
        query: `.${block.attr('class').split(' ').join('.')}`,

        // Development related
        dev: {
          source: info.source
        }
      })
    });
  });

  return result;
}

function parseHTML (fileSource, buildDir, callback) {
  if (!fileSource) {
    throw Error('No source defined');
  }

  const file = getFile(fileSource);

  initializeJSDom(file, (err, window) => {
    const $ = window.$;
    const assets = parseHTMLAssets($, $('[data-asset]'));
    const blocks = parseBlocks($, $('body').children(), buildDir);

    setTimeout(() => {
      const data = _.concat(assets, blocks);
      callback(data);
    }, 1000);
  });
}

module.exports = parseHTML;
<commit_msg>Style fix<commit_after>const path = require('path');
const jsdom = require('jsdom');
const _ = require('lodash');
const fs = require('fs-jetpack');
const globals = require('./globals');
const utilities = require('./utilities');
const getFile = require('./get-file');

function initializeJSDom (file, callback) {
 const doc = jsdom.env({
    html: file,
    scripts: [
      'http://code.jquery.com/jquery.js'
    ],
    done: (err, window) => {
      callback(err, window);
    }
  });

  return doc;
}

function getBlockSource (block) {
  const blockSource = _.trimEnd(_.trimStart(
    block
      .clone()
      .removeAttr('data-type') // Remove junk attributes.
      .removeAttr('data-title')
      .removeAttr('data-picture')
      .wrap('<div/>')
      .parent()
      .html()
  ));

  return blockSource;
}

function getBlockFeatures (block) {
  let features = {
    videoBackground: false,
    imageBackground: false,
    colorBackground: false,
    countdown: false,
    formInput: false
  };

  if (block.find('.countdown').length) {
    features = _.assign({}, features, {
      countdown: true
    });
  }

  if (block.find('.background-cover-color').length) {
    features = _.assign({}, features, {
      colorBackground: true
    });
  }

  if (block.find('.background-image-holder').length ||
      _.startsWith(block.attr('style'), 'background-image')) {
    features = _.assign({}, features, {
      imageBackground: true
    });
  }

  if (block.find('.background-video-holder').length) {
    features = _.assign({}, features, {
      videoBackground: true
    });
  }

  if (_.startsWith(block.attr('style'), 'background-image')) {
    features = _.assign({}, features, {
      imageBackground: true
    });
  }

  if (block.find('form').length > 0) {
    features = _.assign({}, features, {
      formInput: true
    });
  }

  return features;
}

function parseHTMLAssets ($, assets) {
  const result = [];

  if (!$ || !assets) {
    return result;
  }

  assets.each(function parse () {
    const asset = $(this);
    const assetType = asset.attr('data-asset');

    switch (assetType) {
      case 'css':
      case 'stylesheet':
      case 'js':
      case 'javascript': {
        let assetSource = asset.attr('src') || asset.attr('href');
        const { dir, base } = path.parse(assetSource);

        if (dir === 'assets') {
          assetSource = path.join(dir, 'template', base).split(path.sep).join('/');
        }

        result.push({
          type: 'external-asset',
          target: assetType,
          value: assetSource
        });

        break;
      }

      default:
        break;
    }
  });

  return result;
}

function createBlockComponent (blockInfo, blockSource = '') {
  const { source: fileName } = blockInfo;

  const componentHTML = `
<!DOCTYPE html>
<head>
  <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Arkio Template by Trip-Trax 2017</title>

    <link href="https://fonts.googleapis.com/css?family=Montserrat:400,700|Crimson+Text:400,700" rel="stylesheet" data-asset="stylesheet">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" data-asset="stylesheet">
    <link rel="stylesheet" href="../../assets/template/template.css">

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js" data-asset="javascript"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.4.1/snap.svg-min.js" data-asset="javascript"></script>
    <script src="../../assets/template/template.js"></script>

</head>
<body>
${blockSource}
</body>
`;

  fs.write(path.join(globals.currentDir, 'build', 'components', fileName), componentHTML);
}

function parseBlocks ($, blocks) {
  const result = [];

  blocks.each(function () {
    const block = $(this);
    const info = {
      type: null,
      title: null,
      source: null
    };

    block.contents()
      .filter(function () {
        return this.nodeType === 8;
      })
      .each(function () {
        const text = this.nodeValue;
        const lines = text.split('\n');

        _.map(lines, (line) => {
          if (line.length === 0) {
            return;
          }

          const tagRegex = /@{1}[^\s]+/g;
          const search = tagRegex.exec(line);
          let tag = null;
          let tagInfo = null;

          if (search !== null) {
            tag = search[0];
            tagInfo = line.substring(tag.length + '- '.length + 3);
          }

          if (!tag || !tagInfo) {
            return;
          }

          if (tag === '@title') {
            info.title = tagInfo;
          } else if (tag === '@category') {
            info.type = tagInfo;
          } else if (tag === '@source') {
            info.source = tagInfo;
          }
        });
    });

    // Remove comments
    block.contents().each(function removeComments () {
      if (this.nodeType === 8) {
        $(this).remove();
      }
    });

    if (!info.type || !info.title) {
      return;
    }

    const blockID = Math.random().toString(36).substr(2, 33);
    const blockSource = getBlockSource(block);
    const features = getBlockFeatures(block);

    createBlockComponent(info, blockSource);

    result.push({
      type: 'block',
      target: info.type,
      value: _.assign({}, { features }, {
        id: blockID,
        title: info.title,
        thumbnail: `assets/template/thumbnails/${blockID.toString()}.jpeg`,
        source: blockSource,
        query: `.${block.attr('class').split(' ').join('.')}`,

        // Development related
        dev: {
          source: info.source
        }
      })
    });
  });

  return result;
}

function parseHTML (fileSource, buildDir, callback) {
  if (!fileSource) {
    throw Error('No source defined');
  }

  const file = getFile(fileSource);

  initializeJSDom(file, (err, window) => {
    const $ = window.$;
    const assets = parseHTMLAssets($, $('[data-asset]'));
    const blocks = parseBlocks($, $('body').children(), buildDir);

    setTimeout(() => {
      const data = _.concat(assets, blocks);
      callback(data);
    }, 1000);
  });
}

module.exports = parseHTML;
<|endoftext|>


<commit_before>/*eslint-env node*/
/*eslint-disable no-console */

const spawn = require('child_process').spawn;
const CODECLIMATE_CODE = require('path').dirname(__dirname);
const CODECLIMATE_TMP = isWin() ? `${__dirname[0]}:\\tmp\\cc` : '/tmp/cc';

const codeclimate = spawn(
  'docker',
  [
    'run', '--rm',
    ...(process.stdout.isTTY ? ['-it'] : []),
    '-e', `CODECLIMATE_CODE="${normPath(CODECLIMATE_CODE)}"`,
    '-e', `CODECLIMATE_TMP="${normPath(CODECLIMATE_TMP)}"`,
    '-v', `${CODECLIMATE_CODE}:/code`,
    '-v', `${CODECLIMATE_TMP}:/tmp/cc`,
    '-v', '/var/run/docker.sock:/var/run/docker.sock',
    'codeclimate/codeclimate', ...process.argv.slice(2)
  ],
  {shell: true, stdio: 'inherit'}
);

codeclimate.on('close', (code) => {
  process.exit(code);
});

function isWin() {
  return /^win/.test(process.platform);
}

function normPath(path) {
  return isWin() ? '/' + path.replace(/\\/g, '/').replace(':', '') : path;
}
<commit_msg>code cleanup<commit_after>/*eslint-env node*/

const spawn = require('child_process').spawn;
const CODECLIMATE_CODE = require('path').dirname(__dirname);
const CODECLIMATE_TMP = isWin() ? `${__dirname[0]}:\\tmp\\cc` : '/tmp/cc';

const codeclimate = spawn(
  'docker',
  [
    'run', '--rm',
    ...(process.stdout.isTTY ? ['-it'] : []),
    '-e', `CODECLIMATE_CODE="${normPath(CODECLIMATE_CODE)}"`,
    '-e', `CODECLIMATE_TMP="${normPath(CODECLIMATE_TMP)}"`,
    '-v', `${CODECLIMATE_CODE}:/code`,
    '-v', `${CODECLIMATE_TMP}:/tmp/cc`,
    '-v', '/var/run/docker.sock:/var/run/docker.sock',
    'codeclimate/codeclimate', ...process.argv.slice(2)
  ],
  {shell: true, stdio: 'inherit'}
);

codeclimate.on('close', (code) => {
  process.exit(code);
});

function isWin() {
  return /^win/.test(process.platform);
}

function normPath(path) {
  return isWin() ? '/' + path.replace(/\\/g, '/').replace(':', '') : path;
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2016 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import I18n from 'i18n!theme_collection_view'
import React from 'react'
import PropTypes from 'prop-types'
import _ from 'underscore'
import $ from 'jquery'
import customTypes from './PropTypes'
import submitHtmlForm from './submitHtmlForm'
import ThemeCard from './ThemeCard'

const blankConfig = {
  name: I18n.t('Default Template'),
  brand_config: {
    md5: '',
    variables: {}
  }
}

const isSystemTheme = (sharedBrandConfig) => !sharedBrandConfig.account_id

export default React.createClass({
  displayName: 'CollectionView',

  propTypes: {
    sharedBrandConfigs: PropTypes.arrayOf(customTypes.sharedBrandConfig).isRequired,
    activeBrandConfig: customTypes.brandConfig.isRequired,
    accountID: PropTypes.string.isRequired,
    brandableVariableDefaults: customTypes.brandableVariableDefaults,
    baseBrandableVariables: customTypes.variables
  },

  getInitialState () {
    return {
      newThemeModalIsOpen: false,
      brandConfigBeingDeleted: null
    }
  },

  brandVariableValue (brandConfig, variableName) {
    const explicitValue = brandConfig && brandConfig.variables[variableName]
    if (explicitValue) return explicitValue

    const variableInfo = this.props.brandableVariableDefaults[variableName]
    const _default = variableInfo.default
    if (_default && _default[0] === '$') {
      return this.brandVariableValue(brandConfig, _default.substring(1))
    }
    return this.props.baseBrandableVariables[variableName]
  },

  startFromBlankSlate() {
    const md5 = ''
    this.saveToSession(md5)
  },

  startEditing({md5ToActivate, sharedBrandConfigToStartEditing}) {
    if (md5ToActivate === (this.props.activeBrandConfig && this.props.activeBrandConfig.md5)) {
      md5ToActivate = undefined
    }
    if (sharedBrandConfigToStartEditing) {
      sessionStorage.setItem('sharedBrandConfigBeingEdited', JSON.stringify(sharedBrandConfigToStartEditing))
    } else {
      sessionStorage.removeItem('sharedBrandConfigBeingEdited')
    }
    submitHtmlForm(`/accounts/${this.props.accountID}/brand_configs/save_to_user_session`, 'POST', md5ToActivate)
  },

  deleteSharedBrandConfig (sharedBrandConfigId) {
    $.ajaxJSON(`/api/v1/shared_brand_configs/${sharedBrandConfigId}`, 'DELETE', {}, () => {
      window.location.reload()
    })
  },

  isActiveBrandConfig (brandConfig) {
    if (this.props.activeBrandConfig) {
      return brandConfig.md5 === this.props.activeBrandConfig.md5
    } else {
      return brandConfig === blankConfig.brand_config
    }
  },

  isActiveEditableTheme (sharedBrandConfig) {
    return !isSystemTheme(sharedBrandConfig) && (
      this.props.activeBrandConfig &&
      this.props.activeBrandConfig.md5 === sharedBrandConfig.brand_config.md5
    )
  },

  multipleThemesReflectActiveOne () {
    return this.props.sharedBrandConfigs.filter(this.isActiveEditableTheme).length > 1
  },

  isDeletable (sharedBrandConfig) {
    // Globally-shared themes and the active theme (if there is only one active) are not deletable
    return !isSystemTheme(sharedBrandConfig) && (
      !this.isActiveBrandConfig(sharedBrandConfig.brand_config) ||
      this.multipleThemesReflectActiveOne()
    )
  },

  thingsToShow () {
    const thingsToShow = [blankConfig].concat(this.props.sharedBrandConfigs)
    const isActive = (sharedBrandConfig) => this.isActiveBrandConfig(sharedBrandConfig.brand_config)

    // Add in a tile for the active theme if it is otherwise not present in the shared ones
    if (this.props.activeBrandConfig && !_.find(this.props.sharedBrandConfigs, isActive)) {
      const cardForActiveBrandConfig = {
        brand_config: this.props.activeBrandConfig,
        account_id: this.props.accountID
      }
      thingsToShow.unshift(cardForActiveBrandConfig)
    }

    // Make sure the active theme shows up first
    const sorted = _.sortBy(thingsToShow, thing => !isActive(thing))

    // split the globally shared themes and the ones that people in this account have shared apart
    return _.groupBy(sorted, (sbc) => isSystemTheme(sbc) ? 'globalThemes' : 'accountSpecificThemes')
  },

  renderCard (sharedConfig) {
    const onClick = () => {
      const isReadOnly = isSystemTheme(sharedConfig)
      this.startEditing({
        md5ToActivate: sharedConfig.brand_config.md5,
        sharedBrandConfigToStartEditing: !isReadOnly && sharedConfig
      })
    }

    // even if this theme's md5 is active, don't mark it as active if it is a system theme
    // and there is an account-shared theme that also matches the active md5
    const isActiveBrandConfig = this.isActiveBrandConfig(sharedConfig.brand_config) && (
      !isSystemTheme(sharedConfig) ||
      !this.props.sharedBrandConfigs.some(this.isActiveEditableTheme)
    )

    return (
      <ThemeCard
        key={sharedConfig.id + sharedConfig.brand_config.md5}
        name={sharedConfig.name}
        isActiveBrandConfig={isActiveBrandConfig}
        showMultipleCurrentThemesMessage={isActiveBrandConfig && this.multipleThemesReflectActiveOne()}
        getVariable   ={this.brandVariableValue.bind(this, sharedConfig.brand_config)}
        open          ={onClick}
        isDeletable   ={this.isDeletable(sharedConfig)}
        isBeingDeleted={this.state.brandConfigBeingDeleted === sharedConfig}
        startDeleting ={() => this.setState({brandConfigBeingDeleted: sharedConfig})}
        cancelDeleting={() => this.setState({brandConfigBeingDeleted: null})}
        onDelete      ={() => this.deleteSharedBrandConfig(sharedConfig.id)}
      />
    )
  },

  render () {
    const thingsToShow = this.thingsToShow()
    return (

{I18n.t('Themes')}

              <button
                className="al-trigger Button Button--primary"
                type="button"
                title={I18n.t('Add Theme')}
                aria-label={I18n.t('Add Theme')}
              >

                {I18n.t('Theme')}

              </button>

                  {I18n.t('Create theme based on')} …

                {
                  ['globalThemes', 'accountSpecificThemes'].map(collection =>
                    _.map(thingsToShow[collection], sharedConfig =>

                         this.startEditing({md5ToActivate: sharedConfig.brand_config.md5})}
                        >
                          {sharedConfig.name}

                    )
                  )
                }

        {thingsToShow.globalThemes &&

                {I18n.t('Templates')}
                <button
                  type="button"
                  className="Button Button--icon-action"
                  data-tooltip='{"tooltipClass":"popover popover-padded", "position":"left"}'
                  title={I18n.t('Default templates are used as starting points for new themes and cannot be deleted.')}
                >

                </button>

              {thingsToShow.globalThemes.map(this.renderCard)}

        }

        {thingsToShow.accountSpecificThemes &&

                {I18n.t('My Themes')}

              {thingsToShow.accountSpecificThemes.map(this.renderCard)}

        }

    )
  }
})
<commit_msg>change Templates and My Themes headings to h2<commit_after>/*
 * Copyright (C) 2016 - present Instructure, Inc.
 *
 * This file is part of Canvas.
 *
 * Canvas is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Affero General Public License as published by the Free
 * Software Foundation, version 3 of the License.
 *
 * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Affero General Public License along
 * with this program. If not, see <http://www.gnu.org/licenses/>.
 */

import I18n from 'i18n!theme_collection_view'
import React from 'react'
import PropTypes from 'prop-types'
import _ from 'underscore'
import $ from 'jquery'
import customTypes from './PropTypes'
import submitHtmlForm from './submitHtmlForm'
import ThemeCard from './ThemeCard'

const blankConfig = {
  name: I18n.t('Default Template'),
  brand_config: {
    md5: '',
    variables: {}
  }
}

const isSystemTheme = (sharedBrandConfig) => !sharedBrandConfig.account_id

export default React.createClass({
  displayName: 'CollectionView',

  propTypes: {
    sharedBrandConfigs: PropTypes.arrayOf(customTypes.sharedBrandConfig).isRequired,
    activeBrandConfig: customTypes.brandConfig.isRequired,
    accountID: PropTypes.string.isRequired,
    brandableVariableDefaults: customTypes.brandableVariableDefaults,
    baseBrandableVariables: customTypes.variables
  },

  getInitialState () {
    return {
      newThemeModalIsOpen: false,
      brandConfigBeingDeleted: null
    }
  },

  brandVariableValue (brandConfig, variableName) {
    const explicitValue = brandConfig && brandConfig.variables[variableName]
    if (explicitValue) return explicitValue

    const variableInfo = this.props.brandableVariableDefaults[variableName]
    const _default = variableInfo.default
    if (_default && _default[0] === '$') {
      return this.brandVariableValue(brandConfig, _default.substring(1))
    }
    return this.props.baseBrandableVariables[variableName]
  },

  startFromBlankSlate() {
    const md5 = ''
    this.saveToSession(md5)
  },

  startEditing({md5ToActivate, sharedBrandConfigToStartEditing}) {
    if (md5ToActivate === (this.props.activeBrandConfig && this.props.activeBrandConfig.md5)) {
      md5ToActivate = undefined
    }
    if (sharedBrandConfigToStartEditing) {
      sessionStorage.setItem('sharedBrandConfigBeingEdited', JSON.stringify(sharedBrandConfigToStartEditing))
    } else {
      sessionStorage.removeItem('sharedBrandConfigBeingEdited')
    }
    submitHtmlForm(`/accounts/${this.props.accountID}/brand_configs/save_to_user_session`, 'POST', md5ToActivate)
  },

  deleteSharedBrandConfig (sharedBrandConfigId) {
    $.ajaxJSON(`/api/v1/shared_brand_configs/${sharedBrandConfigId}`, 'DELETE', {}, () => {
      window.location.reload()
    })
  },

  isActiveBrandConfig (brandConfig) {
    if (this.props.activeBrandConfig) {
      return brandConfig.md5 === this.props.activeBrandConfig.md5
    } else {
      return brandConfig === blankConfig.brand_config
    }
  },

  isActiveEditableTheme (sharedBrandConfig) {
    return !isSystemTheme(sharedBrandConfig) && (
      this.props.activeBrandConfig &&
      this.props.activeBrandConfig.md5 === sharedBrandConfig.brand_config.md5
    )
  },

  multipleThemesReflectActiveOne () {
    return this.props.sharedBrandConfigs.filter(this.isActiveEditableTheme).length > 1
  },

  isDeletable (sharedBrandConfig) {
    // Globally-shared themes and the active theme (if there is only one active) are not deletable
    return !isSystemTheme(sharedBrandConfig) && (
      !this.isActiveBrandConfig(sharedBrandConfig.brand_config) ||
      this.multipleThemesReflectActiveOne()
    )
  },

  thingsToShow () {
    const thingsToShow = [blankConfig].concat(this.props.sharedBrandConfigs)
    const isActive = (sharedBrandConfig) => this.isActiveBrandConfig(sharedBrandConfig.brand_config)

    // Add in a tile for the active theme if it is otherwise not present in the shared ones
    if (this.props.activeBrandConfig && !_.find(this.props.sharedBrandConfigs, isActive)) {
      const cardForActiveBrandConfig = {
        brand_config: this.props.activeBrandConfig,
        account_id: this.props.accountID
      }
      thingsToShow.unshift(cardForActiveBrandConfig)
    }

    // Make sure the active theme shows up first
    const sorted = _.sortBy(thingsToShow, thing => !isActive(thing))

    // split the globally shared themes and the ones that people in this account have shared apart
    return _.groupBy(sorted, (sbc) => isSystemTheme(sbc) ? 'globalThemes' : 'accountSpecificThemes')
  },

  renderCard (sharedConfig) {
    const onClick = () => {
      const isReadOnly = isSystemTheme(sharedConfig)
      this.startEditing({
        md5ToActivate: sharedConfig.brand_config.md5,
        sharedBrandConfigToStartEditing: !isReadOnly && sharedConfig
      })
    }

    // even if this theme's md5 is active, don't mark it as active if it is a system theme
    // and there is an account-shared theme that also matches the active md5
    const isActiveBrandConfig = this.isActiveBrandConfig(sharedConfig.brand_config) && (
      !isSystemTheme(sharedConfig) ||
      !this.props.sharedBrandConfigs.some(this.isActiveEditableTheme)
    )

    return (
      <ThemeCard
        key={sharedConfig.id + sharedConfig.brand_config.md5}
        name={sharedConfig.name}
        isActiveBrandConfig={isActiveBrandConfig}
        showMultipleCurrentThemesMessage={isActiveBrandConfig && this.multipleThemesReflectActiveOne()}
        getVariable   ={this.brandVariableValue.bind(this, sharedConfig.brand_config)}
        open          ={onClick}
        isDeletable   ={this.isDeletable(sharedConfig)}
        isBeingDeleted={this.state.brandConfigBeingDeleted === sharedConfig}
        startDeleting ={() => this.setState({brandConfigBeingDeleted: sharedConfig})}
        cancelDeleting={() => this.setState({brandConfigBeingDeleted: null})}
        onDelete      ={() => this.deleteSharedBrandConfig(sharedConfig.id)}
      />
    )
  },

  render () {
    const thingsToShow = this.thingsToShow()
    return (

{I18n.t('Themes')}

              <button
                className="al-trigger Button Button--primary"
                type="button"
                title={I18n.t('Add Theme')}
                aria-label={I18n.t('Add Theme')}
              >

                {I18n.t('Theme')}

              </button>

                  {I18n.t('Create theme based on')} …

                {
                  ['globalThemes', 'accountSpecificThemes'].map(collection =>
                    _.map(thingsToShow[collection], sharedConfig =>

                         this.startEditing({md5ToActivate: sharedConfig.brand_config.md5})}
                        >
                          {sharedConfig.name}

                    )
                  )
                }

        {thingsToShow.globalThemes &&

                {I18n.t('Templates')}
                <button
                  type="button"
                  className="Button Button--icon-action"
                  data-tooltip='{"tooltipClass":"popover popover-padded", "position":"left"}'
                  title={I18n.t('Default templates are used as starting points for new themes and cannot be deleted.')}
                >

                </button>

              {thingsToShow.globalThemes.map(this.renderCard)}

        }

        {thingsToShow.accountSpecificThemes &&

                {I18n.t('My Themes')}

              {thingsToShow.accountSpecificThemes.map(this.renderCard)}

        }

    )
  }
})
<|endoftext|>


<commit_before>girderTest.importPlugin('candela');
girderTest.startApp();

describe('Test the candela UI.', function () {
    it('register a user', girderTest.createUser(
        'johndoe', 'john.doe@email.com', 'John', 'Doe', 'password!'
    ));

    it('uploads the data file', function () {
        runs(function () {
            expect($('#g-user-action-menu.open').length).toBe(0);
            $('.g-user-text>a:first').click();
        });
        girderTest.waitForLoad();

        runs(function () {
            expect($('#g-user-action-menu.open').length).toBe(1);
            $('a.g-my-folders').click();
        });
        girderTest.waitForLoad();

        waitsFor(function () {
            return $('a.g-folder-list-link').length === 2;
        }, 'Public and Private folders to appear');

        runs(function () {
            $('a.g-folder-list-link:last').click();
        });
        girderTest.waitForLoad();

        waitsFor(function () {
            return $('ol.breadcrumb>li.active').text() === 'Public' &&
                   $('.g-empty-parent-message:visible').length === 1;
        }, 'descending into Public folder');

        girderTest.binaryUpload('clients/web/test/testFile.csv');

        runs(function () {
            $('.g-item-list-link:first').click();
        });
    });

    it('sets up candela inputs and renders the visualization', function () {
        waitsFor(function () {
            return $('.g-item-candela-component').length === 1;
        }, 'the candela component selector to appear');

        runs(function () {
            expect($('.g-item-candela-component option').length).toBeGreaterThan(10);
            $('.g-item-candela-component').val('BarChart').change();
        });

        waitsFor(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            return inputs.length > 3;
        }, 'the bar chart options to be available');

        runs(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            expect(inputs.eq(0).find('label').text()).toBe('Width');
            expect(inputs.eq(1).find('label').text()).toBe('Height');
            expect(inputs.eq(2).find('label').text()).toBe('X');
            var values = [];
            for (var i = 0; i < 4; i += 1) {
                values.push(inputs.eq(2).find('option').eq(i).text());
            }
            values.sort();
            expect(values).toEqual(['(none)', 'a_b', 'c', 'id']);
            $('.g-candela-update-vis').click();
        });

        waitsFor(function () {
            return $('.g-candela-vis').find('canvas').length === 1;
        }, 'the vis canvas to be drawn');

        runs(function () {
            $('.g-item-candela-component').val('TreeHeatmap').change();
        });

        waitsFor(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            return inputs.length === 9;
        }, 'the visualization type to change to TreeHeatmap');

        runs(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            expect(inputs.eq(2).find('label').text()).toBe('Identifier column');
            expect(inputs.eq(3).find('label').text()).toBe('Color scale');
            expect(inputs.eq(3).find('option').eq(1).text()).toBe('row');
        });

        runs(function () {
            $('.g-item-candela-component').val('Histogram').change();
        });

        waitsFor(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            return inputs.length > 3;
        }, 'the visualization type to change to BulletChart');

        runs(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            expect(inputs.eq(2).find('label').text()).toBe('X');
        });
    });

    it('uploads a bad data file', function () {
        runs(function () {
            expect($('#g-user-action-menu.open').length).toBe(0);
            $('.g-user-text>a:first').click();
        });
        girderTest.waitForLoad();

        runs(function () {
            expect($('#g-user-action-menu.open').length).toBe(1);
            $('a.g-my-folders').click();
        });
        girderTest.waitForLoad();

        runs(function () {
            $('a.g-folder-list-link:last').click();
        });
        girderTest.waitForLoad();

        waitsFor(function () {
            return $('ol.breadcrumb>li.active').text() === 'Public' &&
                   $('.g-item-list-link').length === 1;
        }, 'descending into Public folder');

        girderTest.binaryUpload('clients/web/test/testFileBad.csv');

        runs(function () {
            $('.g-item-list-link').eq(1).click();
        });

        waitsFor(function () {
            return $('.alert-danger').length === 1;
        }, 'the error message to appear');

        runs(function () {
            expect($('.alert-danger').text()).toContain('An error occurred while attempting to read and parse the data file.');
        });
    });
});
<commit_msg>Remove unneeded waitForLoad calls<commit_after>girderTest.importPlugin('candela');
girderTest.startApp();

describe('Test the candela UI.', function () {
    it('register a user', girderTest.createUser(
        'johndoe', 'john.doe@email.com', 'John', 'Doe', 'password!'
    ));

    it('uploads the data file', function () {
        runs(function () {
            expect($('#g-user-action-menu.open').length).toBe(0);
            $('.g-user-text>a:first').click();
            expect($('#g-user-action-menu.open').length).toBe(1);
            $('a.g-my-folders').click();
        });

        waitsFor(function () {
            return $('a.g-folder-list-link').length === 2;
        }, 'Public and Private folders to appear');

        runs(function () {
            $('a.g-folder-list-link:last').click();
        });
        girderTest.waitForLoad();

        waitsFor(function () {
            return $('ol.breadcrumb>li.active').text() === 'Public' &&
                   $('.g-empty-parent-message:visible').length === 1;
        }, 'descending into Public folder');

        girderTest.binaryUpload('clients/web/test/testFile.csv');

        runs(function () {
            $('.g-item-list-link:first').click();
        });
    });

    it('sets up candela inputs and renders the visualization', function () {
        waitsFor(function () {
            return $('.g-item-candela-component').length === 1;
        }, 'the candela component selector to appear');

        runs(function () {
            expect($('.g-item-candela-component option').length).toBeGreaterThan(10);
            $('.g-item-candela-component').val('BarChart').change();
        });

        waitsFor(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            return inputs.length > 3;
        }, 'the bar chart options to be available');

        runs(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            expect(inputs.eq(0).find('label').text()).toBe('Width');
            expect(inputs.eq(1).find('label').text()).toBe('Height');
            expect(inputs.eq(2).find('label').text()).toBe('X');
            var values = [];
            for (var i = 0; i < 4; i += 1) {
                values.push(inputs.eq(2).find('option').eq(i).text());
            }
            values.sort();
            expect(values).toEqual(['(none)', 'a_b', 'c', 'id']);
            $('.g-candela-update-vis').click();
        });

        waitsFor(function () {
            return $('.g-candela-vis').find('canvas').length === 1;
        }, 'the vis canvas to be drawn');

        runs(function () {
            $('.g-item-candela-component').val('TreeHeatmap').change();
        });

        waitsFor(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            return inputs.length === 9;
        }, 'the visualization type to change to TreeHeatmap');

        runs(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            expect(inputs.eq(2).find('label').text()).toBe('Identifier column');
            expect(inputs.eq(3).find('label').text()).toBe('Color scale');
            expect(inputs.eq(3).find('option').eq(1).text()).toBe('row');
        });

        runs(function () {
            $('.g-item-candela-component').val('Histogram').change();
        });

        waitsFor(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            return inputs.length > 3;
        }, 'the visualization type to change to BulletChart');

        runs(function () {
            var inputs = $('.g-candela-inputs-container').children().eq(1).children().children();
            expect(inputs.eq(2).find('label').text()).toBe('X');
        });
    });

    it('uploads a bad data file', function () {
        runs(function () {
            expect($('#g-user-action-menu.open').length).toBe(0);
            $('.g-user-text>a:first').click();
        });
        girderTest.waitForLoad();

        runs(function () {
            expect($('#g-user-action-menu.open').length).toBe(1);
            $('a.g-my-folders').click();
        });
        girderTest.waitForLoad();

        runs(function () {
            $('a.g-folder-list-link:last').click();
        });
        girderTest.waitForLoad();

        waitsFor(function () {
            return $('ol.breadcrumb>li.active').text() === 'Public' &&
                   $('.g-item-list-link').length === 1;
        }, 'descending into Public folder');

        girderTest.binaryUpload('clients/web/test/testFileBad.csv');

        runs(function () {
            $('.g-item-list-link').eq(1).click();
        });

        waitsFor(function () {
            return $('.alert-danger').length === 1;
        }, 'the error message to appear');

        runs(function () {
            expect($('.alert-danger').text()).toContain('An error occurred while attempting to read and parse the data file.');
        });
    });
});
<|endoftext|>


<commit_before>/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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.
 */
'use strict';

// Sample trigger function that copies new Firebase data to a Google Sheet

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const {OAuth2Client} = require('google-auth-library');
const {google} = require('googleapis');

// TODO: Use firebase functions:config:set to configure your googleapi object:
// googleapi.client_id = Google API client ID,
// googleapi.client_secret = client secret, and
// googleapi.sheet_id = Google Sheet id (long string in middle of sheet URL)
const CONFIG_CLIENT_ID = functions.config().googleapi.client_id;
const CONFIG_CLIENT_SECRET = functions.config().googleapi.client_secret;
const CONFIG_SHEET_ID = functions.config().googleapi.sheet_id;

// TODO: Use firebase functions:config:set to configure your watchedpaths object:
// watchedpaths.data_path = Firebase path for data to be synced to Google Sheet
const CONFIG_DATA_PATH = functions.config().watchedpaths.data_path;

// The OAuth Callback Redirect.
const FUNCTIONS_REDIRECT = `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com/oauthcallback`;

// setup for authGoogleAPI
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
const functionsOauthClient = new OAuth2Client(CONFIG_CLIENT_ID, CONFIG_CLIENT_SECRET,
  FUNCTIONS_REDIRECT);

// OAuth token cached locally.
let oauthTokens = null;

// visit the URL for this Function to request tokens
exports.authgoogleapi = functions.https.onRequest((req, res) => {
  res.set('Cache-Control', 'private, max-age=0, s-maxage=0');
  res.redirect(functionsOauthClient.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
    prompt: 'consent',
  }));
});

// setup for OauthCallback
const DB_TOKEN_PATH = '/api_tokens';

// after you grant access, you will be redirected to the URL for this Function
// this Function stores the tokens to your Firebase database
exports.oauthcallback = functions.https.onRequest(async (req, res) => {
  res.set('Cache-Control', 'private, max-age=0, s-maxage=0');
  const code = req.query.code;
  try {
    const tokens = await functionsOauthClient.getToken(code);
    // Now tokens contains an access_token and an optional refresh_token. Save them.
    await admin.database().ref(DB_TOKEN_PATH).set(tokens);
    return res.status(200).send('App successfully configured with new Credentials. '
        + 'You can now close this page.');
  } catch (error) {
    return res.status(400).send(error);
  }
});

// trigger function to write to Sheet when new data comes in on CONFIG_DATA_PATH
exports.appendrecordtospreadsheet = functions.database.ref(`${CONFIG_DATA_PATH}/{ITEM}`).onCreate(
    (snap) => {
      const newRecord = snap.val();
      return appendPromise({
        spreadsheetId: CONFIG_SHEET_ID,
        range: 'A:C',
        valueInputOption: 'USER_ENTERED',
        insertDataOption: 'INSERT_ROWS',
        resource: {
          values: [[newRecord.firstColumn, newRecord.secondColumn, newRecord.thirdColumn]],
        },
      });
    });

// accepts an append request, returns a Promise to append it, enriching it with auth
function appendPromise(requestWithoutAuth) {
  return new Promise((resolve, reject) => {
    return getAuthorizedClient().then((client) => {
      const sheets = google.sheets('v4');
      const request = requestWithoutAuth;
      request.auth = client;
      return sheets.spreadsheets.values.append(request, (err, response) => {
        if (err) {
          console.log(`The API returned an error: ${err}`);
          return reject(err);
        }
        return resolve(response.data);
      });
    });
  });
}

// checks if oauthTokens have been loaded into memory, and if not, retrieves them
async function getAuthorizedClient() {
  if (oauthTokens) {
    return functionsOauthClient;
  }
  const snapshot = await admin.database().ref(DB_TOKEN_PATH).once('value');
  oauthTokens = snapshot.val();
  functionsOauthClient.setCredentials(oauthTokens);
  return functionsOauthClient;
}

// HTTPS function to write new data to CONFIG_DATA_PATH, for testing
exports.testsheetwrite = functions.https.onRequest(async (req, res) => {
  const random1 = Math.floor(Math.random() * 100);
  const random2 = Math.floor(Math.random() * 100);
  const random3 = Math.floor(Math.random() * 100);
  const ID = new Date().getUTCMilliseconds();
  await admin.database().ref(`${CONFIG_DATA_PATH}/${ID}`).set({
    firstColumn: random1,
    secondColumn: random2,
    thirdColumn: random3,
  });
  res.send(`Wrote ${random1}, ${random2}, ${random3} to DB, trigger should now update Sheet.`);
});
<commit_msg>Update getToken from functinosOauthClient<commit_after>/**
 * Copyright 2017 Google Inc. All Rights Reserved.
 *
 * 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.
 */
'use strict';

// Sample trigger function that copies new Firebase data to a Google Sheet

const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp();
const {OAuth2Client} = require('google-auth-library');
const {google} = require('googleapis');

// TODO: Use firebase functions:config:set to configure your googleapi object:
// googleapi.client_id = Google API client ID,
// googleapi.client_secret = client secret, and
// googleapi.sheet_id = Google Sheet id (long string in middle of sheet URL)
const CONFIG_CLIENT_ID = functions.config().googleapi.client_id;
const CONFIG_CLIENT_SECRET = functions.config().googleapi.client_secret;
const CONFIG_SHEET_ID = functions.config().googleapi.sheet_id;

// TODO: Use firebase functions:config:set to configure your watchedpaths object:
// watchedpaths.data_path = Firebase path for data to be synced to Google Sheet
const CONFIG_DATA_PATH = functions.config().watchedpaths.data_path;

// The OAuth Callback Redirect.
const FUNCTIONS_REDIRECT = `https://${process.env.GCLOUD_PROJECT}.firebaseapp.com/oauthcallback`;

// setup for authGoogleAPI
const SCOPES = ['https://www.googleapis.com/auth/spreadsheets'];
const functionsOauthClient = new OAuth2Client(CONFIG_CLIENT_ID, CONFIG_CLIENT_SECRET,
  FUNCTIONS_REDIRECT);

// OAuth token cached locally.
let oauthTokens = null;

// visit the URL for this Function to request tokens
exports.authgoogleapi = functions.https.onRequest((req, res) => {
  res.set('Cache-Control', 'private, max-age=0, s-maxage=0');
  res.redirect(functionsOauthClient.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
    prompt: 'consent',
  }));
});

// setup for OauthCallback
const DB_TOKEN_PATH = '/api_tokens';

// after you grant access, you will be redirected to the URL for this Function
// this Function stores the tokens to your Firebase database
exports.oauthcallback = functions.https.onRequest(async (req, res) => {
  res.set('Cache-Control', 'private, max-age=0, s-maxage=0');
  const code = req.query.code;
  try {
    const {tokens} = await functionsOauthClient.getToken(code);
    // Now tokens contains an access_token and an optional refresh_token. Save them.
    await admin.database().ref(DB_TOKEN_PATH).set(tokens);
    return res.status(200).send('App successfully configured with new Credentials. '
        + 'You can now close this page.');
  } catch (error) {
    return res.status(400).send(error);
  }
});

// trigger function to write to Sheet when new data comes in on CONFIG_DATA_PATH
exports.appendrecordtospreadsheet = functions.database.ref(`${CONFIG_DATA_PATH}/{ITEM}`).onCreate(
    (snap) => {
      const newRecord = snap.val();
      return appendPromise({
        spreadsheetId: CONFIG_SHEET_ID,
        range: 'A:C',
        valueInputOption: 'USER_ENTERED',
        insertDataOption: 'INSERT_ROWS',
        resource: {
          values: [[newRecord.firstColumn, newRecord.secondColumn, newRecord.thirdColumn]],
        },
      });
    });

// accepts an append request, returns a Promise to append it, enriching it with auth
function appendPromise(requestWithoutAuth) {
  return new Promise((resolve, reject) => {
    return getAuthorizedClient().then((client) => {
      const sheets = google.sheets('v4');
      const request = requestWithoutAuth;
      request.auth = client;
      return sheets.spreadsheets.values.append(request, (err, response) => {
        if (err) {
          console.log(`The API returned an error: ${err}`);
          return reject(err);
        }
        return resolve(response.data);
      });
    });
  });
}

// checks if oauthTokens have been loaded into memory, and if not, retrieves them
async function getAuthorizedClient() {
  if (oauthTokens) {
    return functionsOauthClient;
  }
  const snapshot = await admin.database().ref(DB_TOKEN_PATH).once('value');
  oauthTokens = snapshot.val();
  functionsOauthClient.setCredentials(oauthTokens);
  return functionsOauthClient;
}

// HTTPS function to write new data to CONFIG_DATA_PATH, for testing
exports.testsheetwrite = functions.https.onRequest(async (req, res) => {
  const random1 = Math.floor(Math.random() * 100);
  const random2 = Math.floor(Math.random() * 100);
  const random3 = Math.floor(Math.random() * 100);
  const ID = new Date().getUTCMilliseconds();
  await admin.database().ref(`${CONFIG_DATA_PATH}/${ID}`).set({
    firstColumn: random1,
    secondColumn: random2,
    thirdColumn: random3,
  });
  res.send(`Wrote ${random1}, ${random2}, ${random3} to DB, trigger should now update Sheet.`);
});
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2013, David Farrell <shokku.ra@gmail.com>
 *  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of iBot nor the names of its contributors may be used to
 *   endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

exports.mod = function(context)
{
	this.segmentDuration = 10000;
	this.segmentInterval = undefined;
	this.schedules = [];

	this._load = function(data)
	{

	}

	this._save = function()
	{

	}

	this._suspend = function()
	{
		var data =
		{
			lastCbInterval: this.lastCbInterval,
			segmentInterval: this.segmentInterval,
			schedules: this.schedules
		};
		return data;
	}

	this._resume = function(data)
	{
		if(data.lastCbInterval !== undefined) this.lastCbInterval = data.lastCbInterval;
		if(data.segmentInterval !== undefined) this.segmentInterval = data.segmentInterval;
		if(data.schedules !== undefined) this.schedules = data.schedules;
	}

	this._loaded = function(server)
	{
		var ms = 0;

		if(this.segmentInterval !== undefined)
		{
			clearInterval(this.segmentInterval);

			if(this.lastCbInterval !== undefined)
			{
				var diff = process.hrtime(this.lastCbInterval);
				ms = this.segmentDuration - (diff[0] * 1000 + diff[1] / 1000000);
			}
		}
		else
		{
			ms = this.segmentDuration;
		}

		setTimeout(function()
		{
			this.cbInterval(server);
			this.segmentInterval = setInterval(function()
			{
				this.cbInterval(server);
			}.bind(this), this.segmentDuration);
		}.bind(this), ms);
	}

	this._unloaded = function(server)
	{
		clearInterval(this.segmentInterval);
		this.segmentInterval = undefined;
	}

	this.core$cmd = function(server, prefix, target, cmd, params)
	{
		if(cmd === 'scheduler')
		{
			switch(params[0])
			{
				case '+':
					var offset = parseInt(params[1]);
					var segments = parseInt(params[2]);
					if(isNaN(offset)) offset = 0;
					if(isNaN(segments)) segments = 0;

					server.do('scheduler$schedule', server, offset, segments);
					break;
				case '?':
					server.send('PRIVMSG ' + target + ' :Number of schedules: ' + this.schedules.length);
					break;
			}
		}
	}

	this._schedule = function(server, offset, segments)
	{
		var newOffset = offset;

		if(this.lastCbInterval !== undefined)
		{
			var diff = process.hrtime(this.lastCbInterval);
			newOffset += Math.floor(diff[0] * 1000 + diff[1] / 1000000);
		}
		this.schedules.push({offset:newOffset, segments:segments});
	}

	this._unschedule = function(index)
	{
		// TODO
	}

	this.cbInterval = function(server)
	{
		this.lastCbInterval = process.hrtime();

		for(var iSchedule in this.schedules)
		{
			--this.schedules[iSchedule].segments;

			server.do('log$logTargets', server, iSchedule + ': ' + this.schedules[iSchedule].offset + ',' + this.schedules[iSchedule].segments);

			if(this.schedules[iSchedule].segments <= 0)
			{
				setTimeout(function()
				{
					server.do('log$logTargets', server, iSchedule + ': fired!');
				}.bind(this), this.schedules[iSchedule].offset);
				this.schedules.splice(iSchedule, 1);
			}
		}
	}.bind(this);
}
<commit_msg>Finished writing scheduler module.<commit_after>/*
 * Copyright (c) 2013, David Farrell <shokku.ra@gmail.com>
 *  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * - Neither the name of iBot nor the names of its contributors may be used to
 *   endorse or promote products derived from this software without specific
 *   prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

exports.mod = function(context)
{
	this.segmentDuration = 10000;
	this.segmentInterval = undefined;
	this.schedules = [];
	this.timeouts = [];

	this._load = function(data)
	{

	}

	this._save = function()
	{

	}

	this._suspend = function()
	{
		var data =
		{
			lastCbInterval: this.lastCbInterval,
			segmentInterval: this.segmentInterval,
			schedules: this.schedules,
			timeouts: this.timeouts
		};
		return data;
	}

	this._resume = function(data)
	{
		if(data.lastCbInterval !== undefined) this.lastCbInterval = data.lastCbInterval;
		if(data.segmentInterval !== undefined) this.segmentInterval = data.segmentInterval;
		if(data.schedules !== undefined) this.schedules = data.schedules;
		if(data.timeouts !== undefined) this.timeouts = data.timeouts;
	}

	this._loaded = function(server)
	{
		var ms = 0;

		if(this.segmentInterval !== undefined)
		{
			clearInterval(this.segmentInterval);

			if(this.lastCbInterval !== undefined)
			{
				var diff = process.hrtime(this.lastCbInterval);
				ms = this.segmentDuration - (diff[0] * 1000 + diff[1] / 1000000);
			}
		}
		else
		{
			ms = this.segmentDuration;
		}

		setTimeout(function()
		{
			this.cbInterval(server);
			this.segmentInterval = setInterval(function()
			{
				this.cbInterval(server);
			}.bind(this), this.segmentDuration);
		}.bind(this), ms);
	}

	this._unloaded = function(server)
	{
		clearInterval(this.segmentInterval);

		for(var iTimeout in this.timeouts)
		{
			clearTimeout(this.timeouts[iTimeout]);
		}
	}

	this.core$cmd = function(server, prefix, target, cmd, params)
	{
		if(cmd === 'scheduler')
		{
			switch(params[0])
			{
				case '+':
					var offset = parseInt(params[1]);
					var segments = parseInt(params[2]);
					if(isNaN(offset)) offset = 0;
					if(isNaN(segments)) segments = 0;

					server.do('scheduler$schedule', server, offset, segments, function()
					{
						server.do('core$cmd', server, prefix, target, params[3], params.slice(4));
					});
					break;
				case '?':
					server.send('PRIVMSG ' + target + ' :Number of schedules: ' + this.schedules.length);
					break;
			}
		}
	}

	this._schedule = function(server, offset, segments, callback)
	{
		if(segments > 0)
		{
			var newOffset = offset;

			if(this.lastCbInterval !== undefined)
			{
				var diff = process.hrtime(this.lastCbInterval);
				newOffset += Math.floor(diff[0] * 1000 + diff[1] / 1000000);
			}

			this.schedules.push({offset:newOffset, segments:segments, callback:callback});
		}
		else
		{
			this.timeouts.push(setTimeout(callback, offset));
		}
	}

	this._unschedule = function(index)
	{
		// TODO
	}

	this.cbInterval = function(server)
	{
		this.lastCbInterval = process.hrtime();

		for(var iSchedule in this.schedules)
		{
			--this.schedules[iSchedule].segments;

			server.do('log$logTargets', server, iSchedule + ': ' + this.schedules[iSchedule].offset + ',' + this.schedules[iSchedule].segments);

			if(this.schedules[iSchedule].segments <= 0)
			{
				this.timeouts.push(setTimeout(this.schedules[iSchedule].callback, this.schedules[iSchedule].offset));
				this.schedules.splice(iSchedule, 1);
			}
		}
	}.bind(this);
}
<|endoftext|>