<commit_before>// local vars to share in hash (kind of macro preprocessing)
var OBJ = 'GLOB'// differentiate instances of distributed SUPRO
var DB  = 'supro_' + OBJ
// global config
config = {
/* NOTE: this is not JSON just JavaScript
 *       idea is taken from github.com/louischatriot/mongo-edit
 */
    /* standard configuration of extjs+node[.js -webkit] application */

    lang: 'ru',// base localization, must be provided by any module as fallback
    log: 'log/',

    //TODO: uid gid new ids for process after start or partial init
    modules:{// cfg for stack of things from 'app_modules'
    // order matters: before auth module there are no restrictions to config

    // NOTE config: one level copy of this properties into default settings
        suprolftpd:{
            OBJ:OBJ
        },
        supromongod:{
            db_path: '/data/supromongod/' + OBJ + '/',
            db_name: DB// as in depended modules
        },
    // auth module overwrites default and sets up per-user auth module loading
        userman:{//#0: authentication and authorization (plus Chat)
            store: 'fs' // TODO: fs || db
            //sess_maxage: //null: browser lifetime; default: ~9.3 hours one working day
           ,sess_puzl: 'puzzle-word$54321X'
           ,data: '/data/um/'// store fs: chat logs
           ,rbac:{
               can:{// list of permissions with arbitrary positive value
                    'module.example': true
                   ,'modile.shoesupro': true
                }
               ,roles:{
                    'user.test':[
                        'module.example'// select this `can`, etc.
                       ,'App.um.wes', '/um/lib/wes'// NOTE: include this for any role
                       ,'App.um.controller.Chat', 'App.um.view.Chat'
                       ,'/um/lib/chat'
                    ]
                }
               ,users:{
                    'utest':{
                        pass: '9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684',
                        roles:[ 'user.test' ],
                        name: 'Test User'
                    }
                }
            },
            extjs:{
                wes:{// wait events
                     // this in UI: `App.cfg.modules.userman.extjs.wes.timeout`
                    timeout: 7777777,// 2.16 hours vs max on backend: (1 << 23) = 2.33
                    defer: 77777// if error on minute and half
                }
            }
        }
       /* after auth anything can go in no particular order */
       ,example: true
       ,pingback: true
       ,shoesupro:{
           OBJ:OBJ,
           db_name: DB,// same as in `supromongod`
           dependencies:{ userman: '+', supromongod: '+', suprolftpd: '+' }
        }
    },
    extjs:{
        path: 'extjs-4.2/',// find and provide this path; 'extjs/' is for web
        launch:  null,/*{ css:[ ], js:[ ]} loaded by `extjs_launch()` */
        modules: null,/*{ css:[ ], js:[ ]} */
        fading:  true// visual effects for content appearance
    },
    backend:{
        file: 'app_main/app_back.js',
        job_port: 3007,
        ctl_port: 3008,
        ctl_on_done: null,// set app module handlers for ctl close/app exit
        init_timeout: 123
       ,extjs:{
            pathFile: 'extjs.txt'// search this file (extjs.txt)
       }
    }
}
<commit_msg>cfg_mongo_lftp.js: add comments<commit_after>// local vars to share in hash (kind of macro preprocessing)
var OBJ = 'GLOB'// differentiate instances of distributed SUPRO
var DB  = 'supro_' + OBJ
// global config
config = {
/* NOTE: this is not JSON just JavaScript
 *       idea is taken from github.com/louischatriot/mongo-edit
 */
    /* standard configuration of extjs+node[.js -webkit] application */

    lang: 'ru',// base localization, must be provided by any module as fallback
    log: 'log/',

    //TODO: uid gid new ids for process after start or partial init
    modules:{// cfg for stack of things from 'app_modules'
    // order matters: before auth module there are no restrictions to config

    // NOTE config: one level copy of this properties into default settings
        suprolftpd:{// hardcoded data path is '$PWD/data/suprolftpd/'
            OBJ:OBJ
        },
        supromongod:{
            db_path: '/data/supromongod/' + OBJ + '/',
            db_name: DB// as in depended modules
        },
    // auth module overwrites default and sets up per-user auth module loading
        userman:{//#0: authentication and authorization (plus Chat)
            store: 'fs' // TODO: fs || db
            //sess_maxage: //null: browser lifetime; default: ~9.3 hours one working day
           ,sess_puzl: 'puzzle-word$54321X'
           ,data: '/data/um/'// store fs: chat logs
           ,rbac:{
               can:{// list of permissions with arbitrary positive value
                    'module.example': true
                   ,'modile.shoesupro': true
                }
               ,roles:{
                    'user.test':[
                        'module.example'// select this `can`, etc.
                       ,'App.um.wes', '/um/lib/wes'// NOTE: include this for any role
                       ,'App.um.controller.Chat', 'App.um.view.Chat'
                       ,'/um/lib/chat'
                    ]
                }
               ,users:{
                    'utest':{
                        pass: '9d4e1e23bd5b727046a9e3b4b7db57bd8d6ee684',
                        roles:[ 'user.test' ],
                        name: 'Test User'
                    }
                }
            },
            extjs:{
                wes:{// wait events
                     // this in UI: `App.cfg.modules.userman.extjs.wes.timeout`
                    timeout: 7777777,// 2.16 hours vs max on backend: (1 << 23) = 2.33
                    defer: 77777// if error on minute and half
                }
            }
        }
       /* after auth anything can go in no particular order */
       ,example: true
       ,pingback: true
       ,shoesupro:{
           OBJ:OBJ,    // same as in `suprolftpd`
           db_name: DB,// same as in `supromongod`
           dependencies:{ userman: '+', supromongod: '+', suprolftpd: '+' }
        }
    },
    extjs:{
        path: 'extjs-4.2/',// find and provide this path; 'extjs/' is for web
        launch:  null,/*{ css:[ ], js:[ ]} loaded by `extjs_launch()` */
        modules: null,/*{ css:[ ], js:[ ]} */
        fading:  true// visual effects for content appearance
    },
    backend:{
        file: 'app_main/app_back.js',
        job_port: 3007,
        ctl_port: 3008,
        ctl_on_done: null,// set app module handlers for ctl close/app exit
        init_timeout: 123
       ,extjs:{
            pathFile: 'extjs.txt'// search this file (extjs.txt)
       }
    }
}
<|endoftext|>


<commit_before>
/*
 * application JS library, UI independent code
 *
 */

/* these can be "overwritten" by the UI */
var busyImage = '';
var menuArrowImage = 'ui/dressprow/images/menuarrow.png';
var menuArrowActiveImagesrc = 'ui/dressprow/images/menuarrow_active.png';

function urlParameter( name, link) {
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( link );
  if( results == null )
    return "";
  else
    return results[1];
}

var updateMessages = new Array();
var updateLock = false;
function messagesStatusUpdate() {
  if (updateLock) return;
  updateLock = true;
  for (var i = 0; i < updateMessages.length; i++) {
    messageStatusUpdate(updateMessages[i]);
  }
  updateLock = false;
}

function messageStatusUpdate(msgid) {
   if (msgid) {
      $('#messagestatus'+msgid).load('./?page=pageaction&ajaxed=true&action=msgstatus&id='+msgid,"",function() {});
   }
}

function getServerTime() {
   $('#servertime').load('./?page=pageaction&ajaxed=true&action=getservertime',"",function() {
   });
   setTimeout("getServerTime()",60100); // just over a minute
}

function refreshCriteriaList() {
  var id = urlParameter('id',document.location);
  $("#existingCriteria").html(busyImage);
  $("#existingCriteria").load('./?page=pageaction&ajaxed=true&action=listcriteria&id='+id);
}

function refreshExport() {
 // alert('Refresh '+document.location);
  document.location = document.location;
}

function openHelpDialog(url) {
  $("#dialog").dialog({
    minHeight: 400,
    width: 600,
    modal: true
  });
  $("#dialog").html('
'+busyImage+'
');
  var destpage = urlParameter('page',url);
  url = url.replace(/page=/,'origpage=');
  $("#dialog").load(url+'&ajaxed=true&page=pageaction&action='+destpage);
  $(".ui-widget-overlay").click(function() {
    $("#dialog").dialog('close');
  });
}

function initialiseTranslation(text) {
  $("#dialog").dialog({
    minHeight: 400,
    width: 600,
    modal: true
  });
  $("#dialog").html('
'+text+'
'+busyImage+'
');
  $("#dialog").load('./?ajaxed=true&page=pageaction&action=initlanguage');
}

function totalSentUpdate(msgid) {
   $('#totalsent'+msgid).load('./?page=pageaction&ajaxed=true&action=msgsent&id='+msgid,"",function() {
   });
   setTimeout("totalSentUpdate("+msgid+")",5000);
}

$(document).ready(function() {
  $(".note .hide").click(function() {
    $(this).parents('.note').hide();
  });

  $(".configurelink").click(function() {
   // alert(this.href);
    $("#configurecontent").load('./?page=ajaxcall&action=test');
    $("#configurecontent").show();
    return false;
  });

  $("a.ajaxable").click(function() {
    var url = this.href;
    var thispage = urlParameter('page',window.location.href);
    var action = urlParameter('action',url);
    if (action == "") {
      url += '&action='+thispage;
    }
    parent = $(this).parent();
    parent.html(busyImage);
    url = url.replace(/page=/,'origpage=');
  //  alert(url+'&ajaxed=true&page=pageaction');
    parent.load(url+'&ajaxed=true&page=pageaction');
    return false;
  });

  $("input:checkbox.checkallcheckboxes").click(function() {
    if (this.checked) {
      $("input[type=checkbox]:not(:checked)").each(function(){
        this.checked = true;
      });
    } else {
      $("input[type=checkbox]:checked").each(function(){
        this.checked = false;
      });
    }
  });

  var stop = false;

  if ($.fn.accordion) {
    $(".accordion").accordion({
        autoHeight: false,
        navigation: true,
        collapsible: true
      });
  }

  $(".opendialog").click(function() {
    openHelpDialog(this.href);
    return false;
  });
  $(".helpdialog").click(function() {
    openHelpDialog(this.href);
    return false;
  });
  $(".closedialog").click(function() {
    $("#dialog").dialog('close');
  });

  //dropbuttons
  $("div.dropButton img.arrow").click(function(){
      submenu = $(this).parent().parent().find("div.submenu");
      if(submenu.css('display')=="block"){
        submenu.hide();
        $(this).attr('src',menuArrowImagesrc);
      } else {
        submenu.fadeIn();
        $(this).attr('src',menuArrowActiveImagesrc);
      }
      return false;
  });

  /* hmm, doesn't work yet, but would be nice at some point
  $("#emailsearch").autocomplete({
    source: "?page=pageaction&ajaxed=true&action=searchemail",
    minLength: 2,
    select: function(event, ui) {
    log(ui.item ? ("Selected: " + ui.item.value + " aka " + ui.item.id) : "Nothing selected, input was " + this.value);
    }
  });
  */

  if ($.fn.tabs) {
    $(".tabbed").tabs({
      //http://jqueryui.com/upgrade-guide/1.9/#deprecated-ajaxoptions-and-cache-options-added-beforeload-event
        ajaxOptions: {
        error: function(xhr, status, index, anchor) {
          $(anchor.hash).html("Error fetching page");
        }
      }
    });
    $(".tabbed1").tabs();
  }

  $("#remoteurlinput").focus(function() {
    if (this.value == 'e.g. http://www.phplist.com/testcampaign.html') {
      this.value = "";
    }
  })

  $("#remoteurlinput").blur(function() {
    if (this.value == "") {
      this.value = "e.g. http://www.phplist.com/testcampaign.html";
      return;
    }
    $("#remoteurlstatus").html(busyImage);
    $("#remoteurlstatus").load("./?page=pageaction&action=checkurl&ajaxed=true&url="+this.value);
  });

  $("input:radio[name=sendmethod]").change(function() {
    if (this.value == "remoteurl") {
      $("#remoteurl").show();
      $("#messagecontent").hide();
    } else {
      $("#remoteurl").hide();
      $("#messagecontent").show();
    }
  });

  $("a.savechanges").click(function() {
    if (changed) {
      document.sendmessageform.followupto.value = this.href;
      document.location.hash=""
      document.sendmessageform.submit();
      return false;
    }
  });

  $("#criteriaSelect").change(function() {
    var val = $("#criteriaSelect").val();
    var operator = '';
    switch (aT[val]) {
      case 'checkbox':
        $("#criteriaAttributeOperator").html('<input type="hidden" name="criteria_operator" value="is" />');
        $("#criteriaAttributeValues").html('CHECKED <input type="radio" name="criteria_values" value="checked" /> UNCHECKED <input type="radio" name="criteria_values" value="unchecked" />');
        break;
      case 'checkboxgroup':
      case 'select':
      case 'radio':
        $("#criteriaAttributeOperator").html('IS <input type="radio" name="criteria_operator" value="is" checked="checked" /> IS NOT <input type="radio" name="criteria_operator" value="isnot" />');
        $("#criteriaAttributeValues").html(busyImage);
        $("#criteriaAttributeValues").load("./?page=pageaction&ajaxed=true&action=attributevalues&name=criteria_values&type=multiselect&attid="+val);
        break;
      case 'date':
        $("#criteriaAttributeOperator").html('IS <input type="radio" name="criteria_operator" value="is" checked="checked" /> IS NOT <input type="radio" name="criteria_operator" value="isnot" /> IS BEFORE <input type="radio" name="criteria_operator" value="isbefore" /> IS AFTER <input type="radio" name="criteria_operator" value="isafter" />');
        $("#criteriaAttributeValues").html('<input type="text" id="datepicker" name="criteria_values" size="30"/>');
        $("#datepicker").datepicker({dateFormat: 'yy-mm-dd' });
        break;
      default:
        $("#criteriaAttributeOperator").html('');
        $("#criteriaAttributeValues").html('');
        break;
    }
  });

  $("#initialadminpassword").keyup(function() {
    if (this.value.length >= 8) {
      $("#initialisecontinue").removeAttr('disabled');
    }
  });
  $("#initialiseform").submit(function() {
    $("#dialog").dialog({
      minHeight: 400,
      width: 600,
      modal: true
    });
    $("#dialog").html('
Initialising phpList, please wait.
'+busyImage+'
');
  });

  // export page
  $("input:radio[name=column]").change(function() {
    if (this.value == 'nodate') {
      $("#exportdates").hide();
    } else {
      $("#exportdates").show();
    }
  });

  $("#processexport").click(function() {
    // for export, refresh underlying page, to get a new security token
    setTimeout("refreshExport()",2000);
  })

  //fade out 'actionresult' user feedback
  $('.actionresult').delay(4000).fadeOut(4000);
  //fade out 'result' user feedback
  $('.result').delay(4000).fadeOut(4000);

//  $("#processqueueoutput").html('Processing queue, please wait');
  $("#spinner").html(busyImage);

  $("#stopqueue").click(function() {
    $("#processqueueoutput").html('Processing cancelled');
    $("#spinner").html(' ');
    $("#stopqueue").hide();
    $("#resumequeue").show();
  });

  var docurl = document.location.search;
  document.cookie="browsetrail="+escape(docurl);

/* future dev
  $("#listinvalid").load("./?page=pageaction&action=listinvalid&ajaxed=true",function() {
 //  alert("Loaded")
  });
  $("#refreshCriteria").click(refreshCriteriaList);

  $("#addcriterionbutton").click(function() {
    $("#addcriterionbutton").addClass('disabled');
    var request = document.location.search+'&'+$("#sendmessageform").serialize();
    var attr = urlParameter('criteria_attribute',request);
    if (attr == '') {
      alert('Select an attribute to add');
      return false;
    };
    var vals = urlParameter('criteria_values',request);
    var arrVals = urlParameter('criteria_values[]',request);
    if (vals == '' && arrVals) {
      alert('Select a value to add');
      return false;
    };

    request = request.replace(/\?/,'');
    request = request.replace(/page=/,'origpage=');
    request = './?page=pageaction&action=storemessage&'+request;
    alert(request);
    $("#existingCriteria").html(busyImage);
 //   $("#hiddendiv").load(request);
 //   $("#sendmessageform").submit();
 //   setTimeout("refreshCriteriaList()",5000);

//    refreshCriteriaList();
    return true;
  });
*/

});

function allDone(text) {
  if (!text) {
    text = 'All done';
  }
  $("#processqueueoutput").html(''+text+'');
  $("#spinner").html(' ');
  $("#stopqueue").hide();
  $("#resumequeue").show();
  $("#progressmeter").hide();
}

var overallTotal = 0;
var overallSent = 0;

/* this one keeps track of the total between multiple processqueue runs */
$.fn.updateSendProgress = function() {
  var args = arguments[0].split(',') || {};
  var total = parseInt(args[1]);
  var done = parseInt(args[0]);
  if (total > overallTotal) {    overallTotal = total;  } else {    total = overallTotal;  }
  if (done > 0) {
    overallSent += done;
  }
  $.fn.updateProgress(overallSent,overallTotal);
}

$.fn.updateProgress = function() {
  if ($.isNumeric(arguments[0])) {
    var total = parseInt(arguments[1]);
    var done = parseInt(arguments[0]);
  } else {
    var args = arguments[0].split(',') || {};
    var total = parseInt(args[1]);
    var done = parseInt(args[0]);
  }
  var perc;
  if (total == 0) {
    perc = 0;
  } else {
    perc = parseInt((done / total) * 100);
  }
  $("#progresscount").html(done + ' / '+ total);
  $("#progresscount").show();
  $("#progressbar" ).progressbar({max: 100, value: +perc});
};

/*
 * old library of stuff that needs to be ported to jQuery style JS
 */

function deleteRec(url) {
	if (confirm("Are you sure you want to delete this record?")) {
		document.location = url;
	}
}

// @@TODO rewrite opening in pop-over
function viewImage(url,w,h) {
   alert("needs rewriting");
}
<commit_msg>increase page refresh<commit_after>
/*
 * application JS library, UI independent code
 *
 */

/* these can be "overwritten" by the UI */
var busyImage = '';
var menuArrowImage = 'ui/dressprow/images/menuarrow.png';
var menuArrowActiveImagesrc = 'ui/dressprow/images/menuarrow_active.png';

function urlParameter( name, link) {
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( link );
  if( results == null )
    return "";
  else
    return results[1];
}

var updateMessages = new Array();
var updateLock = false;
function messagesStatusUpdate() {
  if (updateLock) return;
  updateLock = true;
  for (var i = 0; i < updateMessages.length; i++) {
    messageStatusUpdate(updateMessages[i]);
  }
  updateLock = false;
}

function messageStatusUpdate(msgid) {
   if (msgid) {
      $('#messagestatus'+msgid).load('./?page=pageaction&ajaxed=true&action=msgstatus&id='+msgid,"",function() {});
   }
}

function getServerTime() {
   $('#servertime').load('./?page=pageaction&ajaxed=true&action=getservertime',"",function() {
   });
   setTimeout("getServerTime()",60100); // just over a minute
}

function refreshCriteriaList() {
  var id = urlParameter('id',document.location);
  $("#existingCriteria").html(busyImage);
  $("#existingCriteria").load('./?page=pageaction&ajaxed=true&action=listcriteria&id='+id);
}

function refreshExport() {
 // alert('Refresh '+document.location);
  document.location = document.location;
}

function openHelpDialog(url) {
  $("#dialog").dialog({
    minHeight: 400,
    width: 600,
    modal: true
  });
  $("#dialog").html('
'+busyImage+'
');
  var destpage = urlParameter('page',url);
  url = url.replace(/page=/,'origpage=');
  $("#dialog").load(url+'&ajaxed=true&page=pageaction&action='+destpage);
  $(".ui-widget-overlay").click(function() {
    $("#dialog").dialog('close');
  });
}

function initialiseTranslation(text) {
  $("#dialog").dialog({
    minHeight: 400,
    width: 600,
    modal: true
  });
  $("#dialog").html('
'+text+'
'+busyImage+'
');
  $("#dialog").load('./?ajaxed=true&page=pageaction&action=initlanguage');
}

function totalSentUpdate(msgid) {
   $('#totalsent'+msgid).load('./?page=pageaction&ajaxed=true&action=msgsent&id='+msgid,"",function() {
   });
   setTimeout("totalSentUpdate("+msgid+")",5000);
}

$(document).ready(function() {
  $(".note .hide").click(function() {
    $(this).parents('.note').hide();
  });

  $(".configurelink").click(function() {
   // alert(this.href);
    $("#configurecontent").load('./?page=ajaxcall&action=test');
    $("#configurecontent").show();
    return false;
  });

  $("a.ajaxable").click(function() {
    var url = this.href;
    var thispage = urlParameter('page',window.location.href);
    var action = urlParameter('action',url);
    if (action == "") {
      url += '&action='+thispage;
    }
    parent = $(this).parent();
    parent.html(busyImage);
    url = url.replace(/page=/,'origpage=');
  //  alert(url+'&ajaxed=true&page=pageaction');
    parent.load(url+'&ajaxed=true&page=pageaction');
    return false;
  });

  $("input:checkbox.checkallcheckboxes").click(function() {
    if (this.checked) {
      $("input[type=checkbox]:not(:checked)").each(function(){
        this.checked = true;
      });
    } else {
      $("input[type=checkbox]:checked").each(function(){
        this.checked = false;
      });
    }
  });

  var stop = false;

  if ($.fn.accordion) {
    $(".accordion").accordion({
        autoHeight: false,
        navigation: true,
        collapsible: true
      });
  }

  $(".opendialog").click(function() {
    openHelpDialog(this.href);
    return false;
  });
  $(".helpdialog").click(function() {
    openHelpDialog(this.href);
    return false;
  });
  $(".closedialog").click(function() {
    $("#dialog").dialog('close');
  });

  //dropbuttons
  $("div.dropButton img.arrow").click(function(){
      submenu = $(this).parent().parent().find("div.submenu");
      if(submenu.css('display')=="block"){
        submenu.hide();
        $(this).attr('src',menuArrowImagesrc);
      } else {
        submenu.fadeIn();
        $(this).attr('src',menuArrowActiveImagesrc);
      }
      return false;
  });

  /* hmm, doesn't work yet, but would be nice at some point
  $("#emailsearch").autocomplete({
    source: "?page=pageaction&ajaxed=true&action=searchemail",
    minLength: 2,
    select: function(event, ui) {
    log(ui.item ? ("Selected: " + ui.item.value + " aka " + ui.item.id) : "Nothing selected, input was " + this.value);
    }
  });
  */

  if ($.fn.tabs) {
    $(".tabbed").tabs({
      //http://jqueryui.com/upgrade-guide/1.9/#deprecated-ajaxoptions-and-cache-options-added-beforeload-event
        ajaxOptions: {
        error: function(xhr, status, index, anchor) {
          $(anchor.hash).html("Error fetching page");
        }
      }
    });
    $(".tabbed1").tabs();
  }

  $("#remoteurlinput").focus(function() {
    if (this.value == 'e.g. http://www.phplist.com/testcampaign.html') {
      this.value = "";
    }
  })

  $("#remoteurlinput").blur(function() {
    if (this.value == "") {
      this.value = "e.g. http://www.phplist.com/testcampaign.html";
      return;
    }
    $("#remoteurlstatus").html(busyImage);
    $("#remoteurlstatus").load("./?page=pageaction&action=checkurl&ajaxed=true&url="+this.value);
  });

  $("input:radio[name=sendmethod]").change(function() {
    if (this.value == "remoteurl") {
      $("#remoteurl").show();
      $("#messagecontent").hide();
    } else {
      $("#remoteurl").hide();
      $("#messagecontent").show();
    }
  });

  $("a.savechanges").click(function() {
    if (changed) {
      document.sendmessageform.followupto.value = this.href;
      document.location.hash=""
      document.sendmessageform.submit();
      return false;
    }
  });

  $("#criteriaSelect").change(function() {
    var val = $("#criteriaSelect").val();
    var operator = '';
    switch (aT[val]) {
      case 'checkbox':
        $("#criteriaAttributeOperator").html('<input type="hidden" name="criteria_operator" value="is" />');
        $("#criteriaAttributeValues").html('CHECKED <input type="radio" name="criteria_values" value="checked" /> UNCHECKED <input type="radio" name="criteria_values" value="unchecked" />');
        break;
      case 'checkboxgroup':
      case 'select':
      case 'radio':
        $("#criteriaAttributeOperator").html('IS <input type="radio" name="criteria_operator" value="is" checked="checked" /> IS NOT <input type="radio" name="criteria_operator" value="isnot" />');
        $("#criteriaAttributeValues").html(busyImage);
        $("#criteriaAttributeValues").load("./?page=pageaction&ajaxed=true&action=attributevalues&name=criteria_values&type=multiselect&attid="+val);
        break;
      case 'date':
        $("#criteriaAttributeOperator").html('IS <input type="radio" name="criteria_operator" value="is" checked="checked" /> IS NOT <input type="radio" name="criteria_operator" value="isnot" /> IS BEFORE <input type="radio" name="criteria_operator" value="isbefore" /> IS AFTER <input type="radio" name="criteria_operator" value="isafter" />');
        $("#criteriaAttributeValues").html('<input type="text" id="datepicker" name="criteria_values" size="30"/>');
        $("#datepicker").datepicker({dateFormat: 'yy-mm-dd' });
        break;
      default:
        $("#criteriaAttributeOperator").html('');
        $("#criteriaAttributeValues").html('');
        break;
    }
  });

  $("#initialadminpassword").keyup(function() {
    if (this.value.length >= 8) {
      $("#initialisecontinue").removeAttr('disabled');
    }
  });
  $("#initialiseform").submit(function() {
    $("#dialog").dialog({
      minHeight: 400,
      width: 600,
      modal: true
    });
    $("#dialog").html('
Initialising phpList, please wait.
'+busyImage+'
');
  });

  // export page
  $("input:radio[name=column]").change(function() {
    if (this.value == 'nodate') {
      $("#exportdates").hide();
    } else {
      $("#exportdates").show();
    }
  });

  $("#processexport").click(function() {
    // for export, refresh underlying page, to get a new security token
    setTimeout("refreshExport()",10000);
  })

  //fade out 'actionresult' user feedback
  $('.actionresult').delay(4000).fadeOut(4000);
  //fade out 'result' user feedback
  $('.result').delay(4000).fadeOut(4000);

//  $("#processqueueoutput").html('Processing queue, please wait');
  $("#spinner").html(busyImage);

  $("#stopqueue").click(function() {
    $("#processqueueoutput").html('Processing cancelled');
    $("#spinner").html(' ');
    $("#stopqueue").hide();
    $("#resumequeue").show();
  });

  var docurl = document.location.search;
  document.cookie="browsetrail="+escape(docurl);

/* future dev
  $("#listinvalid").load("./?page=pageaction&action=listinvalid&ajaxed=true",function() {
 //  alert("Loaded")
  });
  $("#refreshCriteria").click(refreshCriteriaList);

  $("#addcriterionbutton").click(function() {
    $("#addcriterionbutton").addClass('disabled');
    var request = document.location.search+'&'+$("#sendmessageform").serialize();
    var attr = urlParameter('criteria_attribute',request);
    if (attr == '') {
      alert('Select an attribute to add');
      return false;
    };
    var vals = urlParameter('criteria_values',request);
    var arrVals = urlParameter('criteria_values[]',request);
    if (vals == '' && arrVals) {
      alert('Select a value to add');
      return false;
    };

    request = request.replace(/\?/,'');
    request = request.replace(/page=/,'origpage=');
    request = './?page=pageaction&action=storemessage&'+request;
    alert(request);
    $("#existingCriteria").html(busyImage);
 //   $("#hiddendiv").load(request);
 //   $("#sendmessageform").submit();
 //   setTimeout("refreshCriteriaList()",5000);

//    refreshCriteriaList();
    return true;
  });
*/

});

function allDone(text) {
  if (!text) {
    text = 'All done';
  }
  $("#processqueueoutput").html(''+text+'');
  $("#spinner").html(' ');
  $("#stopqueue").hide();
  $("#resumequeue").show();
  $("#progressmeter").hide();
}

var overallTotal = 0;
var overallSent = 0;

/* this one keeps track of the total between multiple processqueue runs */
$.fn.updateSendProgress = function() {
  var args = arguments[0].split(',') || {};
  var total = parseInt(args[1]);
  var done = parseInt(args[0]);
  if (total > overallTotal) {    overallTotal = total;  } else {    total = overallTotal;  }
  if (done > 0) {
    overallSent += done;
  }
  $.fn.updateProgress(overallSent,overallTotal);
}

$.fn.updateProgress = function() {
  if ($.isNumeric(arguments[0])) {
    var total = parseInt(arguments[1]);
    var done = parseInt(arguments[0]);
  } else {
    var args = arguments[0].split(',') || {};
    var total = parseInt(args[1]);
    var done = parseInt(args[0]);
  }
  var perc;
  if (total == 0) {
    perc = 0;
  } else {
    perc = parseInt((done / total) * 100);
  }
  $("#progresscount").html(done + ' / '+ total);
  $("#progresscount").show();
  $("#progressbar" ).progressbar({max: 100, value: +perc});
};

/*
 * old library of stuff that needs to be ported to jQuery style JS
 */

function deleteRec(url) {
	if (confirm("Are you sure you want to delete this record?")) {
		document.location = url;
	}
}

// @@TODO rewrite opening in pop-over
function viewImage(url,w,h) {
   alert("needs rewriting");
}
<|endoftext|>


<commit_before>/**
  Artist Graph Model

  Bridge between Spotify's models.Artist model and
  the vis.Graph object.
*/
var ArtistGraph = function(element, artist, config) {

  // the DOM element where the canvas should be put on
  // to be later on passed to the vis.Graph object
  this.element = element;

  // Spotify's models.Artist object
  this.artist = artist;
  // id of the node to be passed on to the vis.Graph object
  this.artist.nodeid = 1;

  // ArtistGraph Events
  this.events = {};
  // vis.Graph Events
  this.graphevents = {};

  // load branching value from config if present
  // otherwise, load ArtistGraph.DEFAULT_BRANCHING
  this.branching = (config && config.branching) ||
    ArtistGraph.DEFAULT_BRANCHING;
  // load depth value from config if present
  // otherwise, load ArtistGraph.DEFAULT_DEPTH
  this.depth = (config && config.depth) ||
    ArtistGraph.DEFAULT_DEPTH;

  // load treemode value from config if present
  // otherwise, load ArtistGraph.DEFAULT_TREEMODE
  if (config && typeof config.treemode !== 'undefined')
    this.treemode = config.treemode;
  else this.treemode = ArtistGraph.DEFAULT_TREEMODE;

  // options to be passed on to vis.Graph object
  this.options = (config && config.options) ||
    ArtistGraph.DEFAULT_OPTIONS;

  this.initGraph();

  // create the vis.Graph Object
  this.graph =
    new vis.Graph(this.element, this.data, this.options);
};

// Default values to be used to construct the graph
// if no configuration values are specified when
// constructing the ArtistGraph object
ArtistGraph.DEFAULT_BRANCHING = 4;
ArtistGraph.DEFAULT_DEPTH = 2;
ArtistGraph.DEFAULT_TREEMODE = true;
ArtistGraph.DEFAULT_OPTIONS = {};

// ArtistGraph.colors = {
//   node: {
//     border: "white",
//     background: "black",
//     highlight: {
//       border: "white",
//       background: "black"
//     }
//   },
//   edge: {
//     border: "white",
//     background: "black",
//     highlight: {
//       border: "white",
//       background: "black"
//     }
//   }
// };

ArtistGraph.prototype = {

  // initiates state properties of the graph
  initGraph: function() {
    // list of related artist of the main artist of the graph
    this.relatedArtists = [];

    // extra edges that are missing from the graph in treemode form
    this.extraEdges = [];

    // current id value for the vis.Graph's nodes
    this.currentNodeId = 1;

    // data object to be passed on to the vis.Graph object
    this.data = {
      nodes: [{
        id: this.currentNodeId,
        label: this.artist.name,
        fontColor: '#313336', // TODO refactor colors
        color: {
          background: '#dfe0e6',
          highlight: {
            border: '#7fb701'
          }
        },

        // artist and isLeaf are helper properties
        // for future reference
        artist: this.artist,
        // isLeaf simply indicates if the node is a leaf
        // in the graph or not
        isLeaf: false,
        // is this the root node?
        isRoot: true
      }],
      edges: []
    };
  },

  // alias to initGraph
  reset: function() {
    this.initGraph();
  },

  // Resets state variables and starts constructing the graph
  buildGraph: function(done) {
    this.isBuildingGraph = true;
    // Current number of iterations (recursive calls)
    // done to construct the graph
    var currentIteration = 1;

    // Maximum number of iterations that will be performed
    // to construct the graph.
    this.maxIterations =
      (function lambda(i) {
      return Math.pow(this.branching, i) +
        (i < this.depth ? lambda.bind(this)(i + 1) : 0);
    }).bind(this)(0);

    // maxIterations is equal to:
    //
    //   d
    //   ∑ b^i
    //  i=0
    //
    //  which is the sum of the branching value to the power
    //  of i, given that i goes from zero to the depth value.
    //
    //   depth
    //    ___
    //   |
    //    \
    //    /    branching ^ i
    //   |___
    //   i = 0

    // start constructing the graph recursively
    this.expandNode(
      this.depth - 1,
      this.artist,
      iterationUpdate.bind(this)
    );

    function iterationUpdate() {
      // Update the number of iterations done and
      currentIteration += this.branching;

      // If the number of iterations done is enough to have the
      // full graph constructed, then stop recursion and
      // draw the final graph.
      if (currentIteration >= this.maxIterations) {
        this.drawGraph(true);
        if (done)
          done();
      }
    }
  },

  // Expands the node of the parent artist by this.branching.
  // It recursively decreases the depth parameter.
  // The update parameter is the callback to be called
  // after all the callbacks of the child nodes of the root node
  // have finished.
  expandNode: function(depth, parentArtist, done) {

    // after expanding, the node will stop being a leaf
    var node = this.getNode(parentArtist);

    if (node)
      node.isLeaf = false;

    // load the related artists property
    parentArtist.load('related').done(this, function(parentArtist) {
      // when done loading, load the current snapshot of the array
      // of artists, with this.branching length
      parentArtist.related
        .snapshot(0, this.branching).done(this, function(snapshot) {
          // when done loading, load name and uri properties
          // of each artist in the snapshot
          snapshot.loadAll(['name', 'uri'])
          // call forEachRelated on each artist
          .each(this, forEachRelated)
          // when done on each artist update the number of iterations
          .done(done);
        });
    });

    // Updates the graph given the artist parameter.
    // This function will be called on each child node
    // (artist parameter) of the root node.
    // note: this means that this function will be called
    //       exactly this.branching times.
    function forEachRelated(childArtist) {

      // Try to find repeated nodes in the graph
      // given the name of the artist is the same
      var duplicated = this.getNode(childArtist);

      // Is the artist node already in the graph?
      // If there is a duplicate then create an
      // edge between the two artists:
      // the child artist and parent artist
      if (duplicated) {

        // try to find repeated edges in the graph
        var edgeExists = _.findWhere(this.data.edges, {
          to: duplicated.id,
          from: parentArtist.nodeid
        });
        // try to find repeated edges (even if inverse)
        var inverseEdgeExists = _.findWhere(this.data.edges, {
          from: duplicated.id,
          to: parentArtist.nodeid
        });

        // if the edge we are trying to insert
        // does not exist in the graph yet AND
        // the two nodes connecting the edge are not the same one
        // then insert edge.
        //
        // The latter test was added after metadata errors were found:
        // sometimes, an artist would exist itself in its related
        // artists list, which created a edge that went from it to
        // itself.
        if (!edgeExists && !inverseEdgeExists &&
          childArtist.uri !== parentArtist.uri) {

          // Create the extra edge.
          var extraEdge = {
            from: parentArtist.nodeid,
            to: duplicated.id,
          };
          this.extraEdges.push(extraEdge);

          // The extra edge concept is related to the treemode of
          // the graph:
          // If treemode is ENABLED, then the extra edges
          // are NOT added to the graph. This causes the graph to
          // have less edges (only one pass through) and therefore
          // the graph creation algorithm is one of a tree creation
          // algorithm, which, as expected, creates a tree.
          //
          // Otherwise, if treemode is DISABLED, then all the
          // possible edges will be added to the vis.Graph object,
          // which means that the graph will not be a tree,
          // with a much higher number of edges.
          if (!this.treemode)
            this.data.edges.push(extraEdge);
        }
      }
      // if the node is new/unique to the graph
      else {

        // then add it to the list of nodes
        this.data.nodes.push({
          id: ++this.currentNodeId,
          label: childArtist.name,
          artist: childArtist,
          // if the depth value of the graph is zero
          // then this is most definitely a leaf node
          isLeaf: depth <= 0
        });

        // also create the edge to connect the new node to
        // its parent
        this.data.edges.push({
          from: parentArtist.nodeid,
          to: this.currentNodeId
        });

        this.relatedArtists.push(childArtist);

        childArtist.nodeid = this.currentNodeId;
      }

      // if a leaf node as not been reached, then continue
      // constructing the graph, now with this child node
      // as the root node
      if (depth > 0)
        this.expandNode(depth - 1, childArtist, done);
      // note: the condition to end the recursion is: if depth <= 0
    }

  },

  // what it says...
  drawGraph: function(debug) {
    // binds all the graph events previously declared to the object.
    this.bindAllGraphEvents();

    // sets the previously computed graph data
    // note: no animation starts at this point
    this.graph.setData(this.data, {
      disableStart: true
    });

    // starts the animation to draw the graph
    this.graph.start();

    // updates the tagsmenu UI component
    this.events.updateTagsMenu();

    // Debug information about the graph creation
    if (debug) {
      console.log('#### Stats for ' + this.artist.name);
      console.log('# iterations: ' + this.maxIterations);
      console.log('# nodes: ' + this.data.nodes.length);
      console.log('# edges: ' + this.data.edges.length);
    }

  },
  redrawGraph: function() {
    this.graph.redraw();
  },

  // Updates the graph with the given config object
  // it is expected that config is defined
  updateGraph: function(config, done) {
    this.branching = config.branching || this.branching;
    this.depth = config.depth || this.depth;

    if (typeof config.treemode != 'undefined')
      this.treemode = config.treemode;

    this.reset();
    this.buildGraph(done);
  },

  // Refresh vis.Graph's data objects
  updateData: function() {
    this.updateNodes();
    this.updateEdges();

    this.events.updateTagsMenu();
  },
  updateNodes: function() {
    this.graph.nodesData.update(this.data.nodes);
  },
  updateEdges: function() {
    this.graph.edgesData.update(this.data.edges);
  },

  // Get the node of the given artist.
  // return undefined if not found.
  getNode: function(artist) {
    return _.findWhere(
      this.data.nodes, {
        id: artist.nodeid
      }
    );
  },

  // Events

  // binds the previously saved vis.Graph's events to the
  // graph object
  bindAllGraphEvents: function() {
    for (var event in this.graphevents) {
      this.graph.on(event, this.graphevents[event]);
    }
  },

  // saves the given event, given the proper eventHandler.
  on: function(event, eventHandler) {
    this.events[event] = eventHandler;
  },

  // saves a vis.Graph event, given the proper eventHandler.
  onGraph: function(event, eventHandler) {
    this.graphevents[event] = eventHandler;
  }
};

ArtistGraph.prototype.constructor = ArtistGraph;

// Exports for the spotify's require system
require(['$api/models'], function(_models) {
  exports.ArtistGraph = ArtistGraph;
});<commit_msg>spotify uri added to nodes: to be used as ID's<commit_after>/**
  Artist Graph Model

  Bridge between Spotify's models.Artist model and
  the vis.Graph object.
*/
var ArtistGraph = function(element, artist, config) {

  // the DOM element where the canvas should be put on
  // to be later on passed to the vis.Graph object
  this.element = element;

  // Spotify's models.Artist object
  this.artist = artist;
  // id of the node to be passed on to the vis.Graph object
  this.artist.nodeid = 1;

  // ArtistGraph Events
  this.events = {};
  // vis.Graph Events
  this.graphevents = {};

  // load branching value from config if present
  // otherwise, load ArtistGraph.DEFAULT_BRANCHING
  this.branching = (config && config.branching) ||
    ArtistGraph.DEFAULT_BRANCHING;
  // load depth value from config if present
  // otherwise, load ArtistGraph.DEFAULT_DEPTH
  this.depth = (config && config.depth) ||
    ArtistGraph.DEFAULT_DEPTH;

  // load treemode value from config if present
  // otherwise, load ArtistGraph.DEFAULT_TREEMODE
  if (config && typeof config.treemode !== 'undefined')
    this.treemode = config.treemode;
  else this.treemode = ArtistGraph.DEFAULT_TREEMODE;

  // options to be passed on to vis.Graph object
  this.options = (config && config.options) ||
    ArtistGraph.DEFAULT_OPTIONS;

  this.initGraph();

  // create the vis.Graph Object
  this.graph =
    new vis.Graph(this.element, this.data, this.options);
};

// Default values to be used to construct the graph
// if no configuration values are specified when
// constructing the ArtistGraph object
ArtistGraph.DEFAULT_BRANCHING = 4;
ArtistGraph.DEFAULT_DEPTH = 2;
ArtistGraph.DEFAULT_TREEMODE = true;
ArtistGraph.DEFAULT_OPTIONS = {};

// ArtistGraph.colors = {
//   node: {
//     border: "white",
//     background: "black",
//     highlight: {
//       border: "white",
//       background: "black"
//     }
//   },
//   edge: {
//     border: "white",
//     background: "black",
//     highlight: {
//       border: "white",
//       background: "black"
//     }
//   }
// };

ArtistGraph.prototype = {

  // initiates state properties of the graph
  initGraph: function() {
    // list of related artist of the main artist of the graph
    this.relatedArtists = [];

    // extra edges that are missing from the graph in treemode form
    this.extraEdges = [];

    // current id value for the vis.Graph's nodes
    this.currentNodeId = 1;

    // data object to be passed on to the vis.Graph object
    this.data = {
      nodes: [{
        id: this.currentNodeId,
        label: this.artist.name,
        fontColor: '#313336', // TODO refactor colors
        color: {
          background: '#dfe0e6',
          highlight: {
            border: '#7fb701'
          }
        },

        // artist and isLeaf are helper properties
        // for future reference
        artist: this.artist,
        uri: this.artist.uri,
        // isLeaf simply indicates if the node is a leaf
        // in the graph or not
        isLeaf: false,
        // is this the root node?
        isRoot: true
      }],
      edges: []
    };
  },

  // alias to initGraph
  reset: function() {
    this.initGraph();
  },

  // Resets state variables and starts constructing the graph
  buildGraph: function(done) {
    this.isBuildingGraph = true;
    // Current number of iterations (recursive calls)
    // done to construct the graph
    var currentIteration = 1;

    // Maximum number of iterations that will be performed
    // to construct the graph.
    this.maxIterations =
      (function lambda(i) {
      return Math.pow(this.branching, i) +
        (i < this.depth ? lambda.bind(this)(i + 1) : 0);
    }).bind(this)(0);

    // maxIterations is equal to:
    //
    //   d
    //   ∑ b^i
    //  i=0
    //
    //  which is the sum of the branching value to the power
    //  of i, given that i goes from zero to the depth value.
    //
    //   depth
    //    ___
    //   |
    //    \
    //    /    branching ^ i
    //   |___
    //   i = 0

    // start constructing the graph recursively
    this.expandNode(
      this.depth - 1,
      this.artist,
      iterationUpdate.bind(this)
    );

    function iterationUpdate() {
      // Update the number of iterations done and
      currentIteration += this.branching;

      // If the number of iterations done is enough to have the
      // full graph constructed, then stop recursion and
      // draw the final graph.
      if (currentIteration >= this.maxIterations) {
        this.drawGraph(true);
        if (done)
          done();
      }
    }
  },

  // Expands the node of the parent artist by this.branching.
  // It recursively decreases the depth parameter.
  // The update parameter is the callback to be called
  // after all the callbacks of the child nodes of the root node
  // have finished.
  expandNode: function(depth, parentArtist, done) {

    // after expanding, the node will stop being a leaf
    var node = this.getNode(parentArtist);

    if (node)
      node.isLeaf = false;

    // load the related artists property
    parentArtist.load('related').done(this, function(parentArtist) {
      // when done loading, load the current snapshot of the array
      // of artists, with this.branching length
      parentArtist.related
        .snapshot(0, this.branching).done(this, function(snapshot) {
          // when done loading, load name and uri properties
          // of each artist in the snapshot
          snapshot.loadAll(['name', 'uri'])
          // call forEachRelated on each artist
          .each(this, forEachRelated)
          // when done on each artist update the number of iterations
          .done(done);
        });
    });

    // Updates the graph given the artist parameter.
    // This function will be called on each child node
    // (artist parameter) of the root node.
    // note: this means that this function will be called
    //       exactly this.branching times.
    function forEachRelated(childArtist) {

      // Try to find repeated nodes in the graph
      // given the name of the artist is the same
      var duplicated = this.getNode(childArtist);

      // Is the artist node already in the graph?
      // If there is a duplicate then create an
      // edge between the two artists:
      // the child artist and parent artist
      if (duplicated) {

        // try to find repeated edges in the graph
        var edgeExists = _.findWhere(this.data.edges, {
          to: duplicated.id,
          from: parentArtist.nodeid
        });
        // try to find repeated edges (even if inverse)
        var inverseEdgeExists = _.findWhere(this.data.edges, {
          from: duplicated.id,
          to: parentArtist.nodeid
        });

        // if the edge we are trying to insert
        // does not exist in the graph yet AND
        // the two nodes connecting the edge are not the same one
        // then insert edge.
        //
        // The latter test was added after metadata errors were found:
        // sometimes, an artist would exist itself in its related
        // artists list, which created a edge that went from it to
        // itself.
        if (!edgeExists && !inverseEdgeExists &&
          childArtist.uri !== parentArtist.uri) {

          // Create the extra edge.
          var extraEdge = {
            from: parentArtist.nodeid,
            to: duplicated.id,
          };
          this.extraEdges.push(extraEdge);

          // The extra edge concept is related to the treemode of
          // the graph:
          // If treemode is ENABLED, then the extra edges
          // are NOT added to the graph. This causes the graph to
          // have less edges (only one pass through) and therefore
          // the graph creation algorithm is one of a tree creation
          // algorithm, which, as expected, creates a tree.
          //
          // Otherwise, if treemode is DISABLED, then all the
          // possible edges will be added to the vis.Graph object,
          // which means that the graph will not be a tree,
          // with a much higher number of edges.
          if (!this.treemode)
            this.data.edges.push(extraEdge);
        }
      }
      // if the node is new/unique to the graph
      else {

        // then add it to the list of nodes
        this.data.nodes.push({
          id: ++this.currentNodeId,
          label: childArtist.name,
          artist: childArtist,
          uri: childArtist.uri,
          // if the depth value of the graph is zero
          // then this is most definitely a leaf node
          isLeaf: depth <= 0
        });

        // also create the edge to connect the new node to
        // its parent
        this.data.edges.push({
          from: parentArtist.nodeid,
          to: this.currentNodeId
        });

        this.relatedArtists.push(childArtist);

        childArtist.nodeid = this.currentNodeId;
      }

      // if a leaf node as not been reached, then continue
      // constructing the graph, now with this child node
      // as the root node
      if (depth > 0)
        this.expandNode(depth - 1, childArtist, done);
      // note: the condition to end the recursion is: if depth <= 0
    }

  },

  // what it says...
  drawGraph: function(debug) {
    // binds all the graph events previously declared to the object.
    this.bindAllGraphEvents();

    // sets the previously computed graph data
    // note: no animation starts at this point
    this.graph.setData(this.data, {
      disableStart: true
    });

    // starts the animation to draw the graph
    this.graph.start();

    // updates the tagsmenu UI component
    this.events.updateTagsMenu();

    // Debug information about the graph creation
    if (debug) {
      console.log('#### Stats for ' + this.artist.name);
      console.log('# iterations: ' + this.maxIterations);
      console.log('# nodes: ' + this.data.nodes.length);
      console.log('# edges: ' + this.data.edges.length);
    }

  },
  redrawGraph: function() {
    this.graph.redraw();
  },

  // Updates the graph with the given config object
  // it is expected that config is defined
  updateGraph: function(config, done) {
    this.branching = config.branching || this.branching;
    this.depth = config.depth || this.depth;

    if (typeof config.treemode != 'undefined')
      this.treemode = config.treemode;

    this.reset();
    this.buildGraph(done);
  },

  // Refresh vis.Graph's data objects
  updateData: function() {
    this.updateNodes();
    this.updateEdges();

    this.events.updateTagsMenu();
  },
  updateNodes: function() {
    this.graph.nodesData.update(this.data.nodes);
  },
  updateEdges: function() {
    this.graph.edgesData.update(this.data.edges);
  },

  // Get the node of the given artist.
  // return undefined if not found.
  getNode: function(artist) {
    return _.findWhere(
      this.data.nodes, {
        uri: artist.uri
      }
    );
  },

  // Events

  // binds the previously saved vis.Graph's events to the
  // graph object
  bindAllGraphEvents: function() {
    for (var event in this.graphevents) {
      this.graph.on(event, this.graphevents[event]);
    }
  },

  // saves the given event, given the proper eventHandler.
  on: function(event, eventHandler) {
    this.events[event] = eventHandler;
  },

  // saves a vis.Graph event, given the proper eventHandler.
  onGraph: function(event, eventHandler) {
    this.graphevents[event] = eventHandler;
  }
};

ArtistGraph.prototype.constructor = ArtistGraph;

// Exports for the spotify's require system
require(['$api/models'], function(_models) {
  exports.ArtistGraph = ArtistGraph;
});<|endoftext|>


<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');
var mbaasApi = require('fh-mbaas-api');
var mbaasExpress = mbaasApi.mbaasExpress();
var queryString = require('querystring');
var request = require('request');

function auth() {
  var router = express.Router();
  mbaasApi.db({
          "act": "read",
          "type": 'account'
  }, function(err, noterr) {
    if (err) {
     console.log('Boo! ' + err);
    } else {
      console.log('OOB! ' + JSON.stringify(noterr));
    }
  });
  router.all('/init', function(req, res) {
        var params = req.body; // params.userId params.password
        if (params && params.userId) {
          console.log('Checking credentials for user ' + params.userId);
          initSession(params.userId, function(err, result){
            if (err) {
              console.log('Invalid credentials for user ' + params.userId);
              res.status(400);
              res.json({message: 'Invalid credentials'});
              return;
            } else {
              res.json({
                status: 'ok',
                userId: params.userId,
                sessionToken: result + '_sessiontoken',
                authResponse: result
              });
              return;
            }
          });

        } else {
          res.status(400);
          res.json({message: params.userId});
        }

  });

  /**
   * Validate the user's session info with Google, and create a session object.
   * This will also create a account object if necessary.
   *
   * @param {String} idToken an idToken provided by an Android Client (see : https://developers.google.com/identity/sign-in/android/backend-auth)
   * @param {Callback} nodeCallback a node callback
   */
  function initSession(idToken, nodeCallback) {
      //validate with google
      var options = {

          url: 'https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' + (idToken),
          method: 'GET'
      };

      request(options, handleGoogleResponse(idToken, nodeCallback)).end();

  }

  /**
   *
   * This callback consumes Google's response, and performs the appropriate
   * actions on the session. (Set session optionally creating the account).
   *
   * @param {type} idToken an idToken received from Android
   * @param {type} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function handleGoogleResponse(idToken, nodeCallback) {
      return function (error, response, body) {

          //the whole response has been recieved, so we just print it out here
          if (error) {
           nodeCallback(e);
          } else {
            var resObject = JSON.parse(body);
            if (resObject.error_description) {
              nodeCallback(resObject.error_description, null);
            } else {
              checkAccount(resObject, idToken, nodeCallback);
            }
          }

      };
  }

  /**
   * Creates a user if one does not exist
   *
   * @param {Object}  Google user Token response (see : https://developers.google.com/identity/sign-in/android/backend-auth#calling-the-tokeninfo-endpoint)
   * @param {Sting} sessionToken
   * @param {NodeCallback} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function checkAccount(googleUserToken, sessionToken, nodeCallback) {
      console.log('checkAccountAndCreateSession');
      var handleResults = function (error, responseData) {
        console.log('checkAccountAndCreateSession_callback');
          if (error) {
              nodeCallback(error, null);
          } else {
              if (!responseData || responseData.count === 0) {
                  //no account, must create.
                  createAccount(googleUserToken, nodeCallback);
              } else if (responseData.count > 1) {
                  nodeCallback("error : Too many accounts returned", null);
              } else {
                console.log('found ' + JSON.stringify(responseData) );
                  nodeCallback(null, googleUserToken.sub);
              }
          }
      };

      mbaasApi.db({
          "act": "list",
          "type": 'account',
          "eq": {"id": googleUserToken.sub},
      }, handleResults);
  }

  /**
   *  Creates an account and sets the session to that account.
   * @param {Object}  Google user Token response (see : https://developers.google.com/identity/sign-in/android/backend-auth#calling-the-tokeninfo-endpoint)
   * @param {NodeCallback} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function createAccount(googleUserToken, nodeCallback) {
    authResponse.authToken = '';
      mbaasApi.db({
          "act": "create",
          "type": 'account',
          "fields": [googleUserToken]
      }, function (error, data) {
          if (error) {
            console.log('createAccountAndSetSession_cb_error + ' + error);
               nodeCallback(error);
          } else {
            console.log('createAccountAndSetSession_cb_created + ' + data);
               nodeCallback(null, googleUserToken.sub);
          }
      });
  }

  return router;

}

module.exports = auth;
<commit_msg>update file /lib/auth.js<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');
var mbaasApi = require('fh-mbaas-api');
var mbaasExpress = mbaasApi.mbaasExpress();
var queryString = require('querystring');
var request = require('request');

function auth() {
  var router = express.Router();
  mbaasApi.db({
          "act": "list",
          "type": 'account'
  }, function(err, noterr) {
    if (err) {
     console.log('Boo! ' + err);
    } else {
      console.log('OOB! ' + JSON.stringify(noterr));
    }
  });
  router.all('/init', function(req, res) {
        var params = req.body; // params.userId params.password
        if (params && params.userId) {
          console.log('Checking credentials for user ' + params.userId);
          initSession(params.userId, function(err, result){
            if (err) {
              console.log('Invalid credentials for user ' + params.userId);
              res.status(400);
              res.json({message: 'Invalid credentials'});
              return;
            } else {
              res.json({
                status: 'ok',
                userId: params.userId,
                sessionToken: result + '_sessiontoken',
                authResponse: result
              });
              return;
            }
          });

        } else {
          res.status(400);
          res.json({message: params.userId});
        }

  });

  /**
   * Validate the user's session info with Google, and create a session object.
   * This will also create a account object if necessary.
   *
   * @param {String} idToken an idToken provided by an Android Client (see : https://developers.google.com/identity/sign-in/android/backend-auth)
   * @param {Callback} nodeCallback a node callback
   */
  function initSession(idToken, nodeCallback) {
      //validate with google
      var options = {

          url: 'https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' + (idToken),
          method: 'GET'
      };

      request(options, handleGoogleResponse(idToken, nodeCallback)).end();

  }

  /**
   *
   * This callback consumes Google's response, and performs the appropriate
   * actions on the session. (Set session optionally creating the account).
   *
   * @param {type} idToken an idToken received from Android
   * @param {type} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function handleGoogleResponse(idToken, nodeCallback) {
      return function (error, response, body) {

          //the whole response has been recieved, so we just print it out here
          if (error) {
           nodeCallback(e);
          } else {
            var resObject = JSON.parse(body);
            if (resObject.error_description) {
              nodeCallback(resObject.error_description, null);
            } else {
              checkAccount(resObject, idToken, nodeCallback);
            }
          }

      };
  }

  /**
   * Creates a user if one does not exist
   *
   * @param {Object}  Google user Token response (see : https://developers.google.com/identity/sign-in/android/backend-auth#calling-the-tokeninfo-endpoint)
   * @param {Sting} sessionToken
   * @param {NodeCallback} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function checkAccount(googleUserToken, sessionToken, nodeCallback) {
      console.log('checkAccountAndCreateSession');
      var handleResults = function (error, responseData) {
        console.log('checkAccountAndCreateSession_callback');
          if (error) {
              nodeCallback(error, null);
          } else {
              if (!responseData || responseData.count === 0) {
                  //no account, must create.
                  createAccount(googleUserToken, nodeCallback);
              } else if (responseData.count > 1) {
                  nodeCallback("error : Too many accounts returned", null);
              } else {
                console.log('found ' + JSON.stringify(responseData) );
                  nodeCallback(null, googleUserToken.sub);
              }
          }
      };

      mbaasApi.db({
          "act": "list",
          "type": 'account',
          "eq": {"id": googleUserToken.sub},
      }, handleResults);
  }

  /**
   *  Creates an account and sets the session to that account.
   * @param {Object}  Google user Token response (see : https://developers.google.com/identity/sign-in/android/backend-auth#calling-the-tokeninfo-endpoint)
   * @param {NodeCallback} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function createAccount(googleUserToken, nodeCallback) {
    authResponse.authToken = '';
      mbaasApi.db({
          "act": "create",
          "type": 'account',
          "fields": [googleUserToken]
      }, function (error, data) {
          if (error) {
            console.log('createAccountAndSetSession_cb_error + ' + error);
               nodeCallback(error);
          } else {
            console.log('createAccountAndSetSession_cb_created + ' + data);
               nodeCallback(null, googleUserToken.sub);
          }
      });
  }

  return router;

}

module.exports = auth;
<|endoftext|>


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

  var compassConfig = {
    options: {
      sassDir: '<%= yeoman.app %>/<%= yeoman.styles %>',
      cssDir: '.temp/<%= yeoman.styles %>',
      generatedImagesDir: '.temp/<%= yeoman.images %>/generated',
      imagesDir: '<%= yeoman.app %>/<%= yeoman.images %>',
      javascriptsDir: '<%= yeoman.app %>/<%= yeoman.scripts %>',
      fontsDir: '<%= yeoman.app %>/<%= yeoman.styles %>/fonts',
      importPath: '<%= yeoman.app %>/bower_components',
      httpImagesPath: '/<%= yeoman.images %>',
      httpGeneratedImagesPath: '/<%= yeoman.images %>/generated',
      httpFontsPath: '/<%= yeoman.styles %>/fonts',
      relativeAssets: false,
      assetCacheBuster: false,
      bundleExec: true,
      raw: 'Sass::Script::Number.precision = 10\n',
      specify: '<%= yeoman.app %>/<%= yeoman.styles %>/main.scss'
    },
    dist: {
      options: {
        outputStyle: 'compressed',
        force: true,
        cssDir: '<%= yeoman.styles %>/<%= yeoman.dist %>',
        generatedImagesDir: '<%= yeoman.dist %>/<%= yeoman.images %>/generated'
      }
    },
    staging: {
      options: {
        outputStyle: 'compressed',
        force: true,
        cssDir: '<%= yeoman.styles %>/<%= yeoman.app %>',
        generatedImagesDir: '<%= yeoman.dist %>/<%= yeoman.images %>/generated'
      }
    },
    server: {
      options: {
        debugInfo: true
      }
    }
  };

  return module.exports = compassConfig;
})();
<commit_msg>darn it<commit_after>(function() {
  'use strict';

  var compassConfig = {
    options: {
      sassDir: '<%= yeoman.app %>/<%= yeoman.styles %>',
      cssDir: '.temp/<%= yeoman.styles %>',
      generatedImagesDir: '.temp/<%= yeoman.images %>/generated',
      imagesDir: '<%= yeoman.app %>/<%= yeoman.images %>',
      javascriptsDir: '<%= yeoman.app %>/<%= yeoman.scripts %>',
      fontsDir: '<%= yeoman.app %>/<%= yeoman.styles %>/fonts',
      importPath: '<%= yeoman.app %>/bower_components',
      httpImagesPath: '/<%= yeoman.images %>',
      httpGeneratedImagesPath: '/<%= yeoman.images %>/generated',
      httpFontsPath: '/<%= yeoman.styles %>/fonts',
      relativeAssets: false,
      assetCacheBuster: false,
      bundleExec: true,
      raw: 'Sass::Script::Number.precision = 10\n',
      specify: '<%= yeoman.app %>/<%= yeoman.styles %>/main.scss'
    },
    dist: {
      options: {
        outputStyle: 'compressed',
        force: true,
        cssDir: '<%= yeoman.dist %>/<%= yeoman.styles %>',
        generatedImagesDir: '<%= yeoman.dist %>/<%= yeoman.images %>/generated'
      }
    },
    staging: {
      options: {
        outputStyle: 'compressed',
        force: true,
        cssDir: '<%= yeoman.app %>/<%= yeoman.styles %>',
        generatedImagesDir: '<%= yeoman.dist %>/<%= yeoman.images %>/generated'
      }
    },
    server: {
      options: {
        debugInfo: true
      }
    }
  };

  return module.exports = compassConfig;
})();
<|endoftext|>


<commit_before>// Copyright 2013 Joyent, Inc.  All rights reserved.

var assert = require('assert');
var util = require('util');

var httpSignature = require('./node-http-signature');
var restify = require('restify');

///--- Globals

var BadRequestError = restify.BadRequestError;
var InvalidCredentialsError = restify.InvalidCredentialsError;
var NotAuthorizedError = restify.NotAuthorizedError;
var ResourceNotFoundError = restify.ResourceNotFoundError;

///--- Messages

var DEF_401 = 'You must make authenticated requests to use CloudAPI';
var FORBIDDEN = 'You do not have permission to access %s';
var INVALID_ERR = 'Invalid %s %s';
var INVALID_AUTHZ = 'Invalid authorization header';
var INVALID_KEY = 'Invalid KeyId %s in authorization header';
var INVALID_SIG = 'The signature we calculated does not match the one you sent';
var INVALID_DEV = 'The token provided is not authorized for this application';
var MISSING_CREDS = 'Missing credentials';
var SCHEME_NOT_SUPPORTED = 'The authorization scheme you requested is ' +
    'not supported';
var USER_404 = 'User %s does not exist';

///--- Middleware

function basicAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'basic') {
        return next();
    }

    // basic auth is deprecated on 7.0+
    if (! /6\.5/.test(req.getVersion())) {
        return next(new InvalidCredentialsError(SCHEME_NOT_SUPPORTED));
    }

    var log = req.log;
    var ufds = req.sdc.ufds;

    var user = req.authorization.basic.username;
    var pass = req.authorization.basic.password;
    if (!user || !pass) {
        return next(new InvalidCredentialsError(MISSING_CREDS));
    }

    log.debug('basicAuth: authorization=%j', req.authorization);
    return ufds.authenticate(user, pass, function (err, customer) {
        if (err) {
            log.error({err: err}, 'UFDS.authenticate error');
            if (err.statusCode === 404) {
                return next(new InvalidCredentialsError(USER_404, user));
            }
            return next(new InvalidCredentialsError(DEF_401));
        }
        return ufds.getMetadata(customer, 'cloudapi',
                    function (mderr, metadata) {
            if (mderr || metadata === null ||
                    metadata.require_http_signature !== 'true') {
                req.caller = customer;
                req.username = req.caller.login;
                return next();
            } else {
                return next(new InvalidCredentialsError(SCHEME_NOT_SUPPORTED));
            }
        });
    });
}

function signatureAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'signature') {
        return next();
    }

    var keyId;
    var log = req.log;
    var sig = req.authorization.signature;
    var ufds = req.sdc.ufds;

    try {
        keyId = sig.keyId.split('/');
    } catch (e) {
        log.error('Error parsing authorization header: %s', e.stack);
        return next(new BadRequestError(INVALID_AUTHZ));
    }

    if (!keyId || keyId.length !== 4 || keyId[2] !== 'keys') {
        return next(new InvalidCredentialsError(INVALID_KEY, sig.keyId));
    }

    return ufds.getUser(keyId[1], function (err, account) {
        if (err) {
            log.trace({err: err}, 'UFDS.getUser error');
            return next(new InvalidCredentialsError(INVALID_ERR,
                                                    'account',
                                                    keyId[1]));
        }

        return account.getKey(keyId[3], function (err2, key) {
            if (err2) {
                log.trace({err: err2}, 'UFDS.getKey error');
                return next(new InvalidCredentialsError(INVALID_ERR,
                                                        'key',
                                                        keyId[3]));
            }

            log.debug('authSig using key %s => %s', sig.keyId, key.pkcs);
            try {
                if (!httpSignature.verifySignature(sig, key.pkcs)) {
                    log.debug('authSig::verify FAIL: %j, key=%s',
                                sig, key.standard);
                    return next(new InvalidCredentialsError(INVALID_SIG));
                }

                if (!req.header('X-Auth-Token')) {
                    req.caller = account;
                    req.username = account.login;
                    return next();
                } else {
                    var token = JSON.parse(req.header('X-Auth-Token'));
                    return req.sdc.keyapi.detoken(token,
                            function(clientobj, clienterr) {
                            if (clienterr) {
                                log.error({err: clienterr}, 'keyapi error');
                                return next(new InvalidCredentialsError(
                                    INVALID_DEV));
                            }
                            if (clientobj.expires) {
                                var expires = new Date(clientobj.expires);
                                var now = new Date();
                                if (now > expires) {
                                    return next(new InvalidCredentialsError(
                                    INVALID_DEV));
                                }
                            }
                            if (clientobj.devkeyId !== sig.keyId) {
                                return next(new InvalidCredentialsError(
                                        INVALID_DEV));
                            }
                            if (!clientobj.permissions.cloudapi) {
                                return next(new InvalidCredentialsError(
                                        INVALID_DEV));
                            }
                            var perms = clientobj.permissions.cloudapi;
                            var authorized = false;
                            for (var i = 0; i < perms.length; i++) {
                                var currperm = perms[i].split('/*');
                                if (currperm[0] == req.url) {
                                    authorized = true;
                                    break;
                                }
                                if (req.url.search(currperm[0]) === 0) {
                                    if (currperm.length == 2 &&
                                        req.url.charAt(currperm[0].length) ===
                                        '/') {
                                        authorized = true;
                                        break;
                                    }
                                }
                            }

                            if (authorized === true) {
                                req.caller = clientobj.account;
                                req.username = clientobj.account.login;
                                return next();
                            } else {
                                return next(new InvalidCredentialsError(
                                    INVALID_DEV));
                            }
                        });
                }

            } catch (e) {
                log.error('Error calling http_signature: ' + e.stack);
                return next(new InvalidCredentialsError(INVALID_SIG));
            }

        });
    });
}

function assertAuthenticated(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    if (typeof (req.caller) !== 'object') {
        return next(new InvalidCredentialsError(DEF_401));
    }

    return next();
}

function loadAccount(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    assert.ok(req.sdc);

    var log = req.log;
    var ufds = req.sdc.ufds;

    if (req.params.account === 'my') {
        req.params.account = req.caller.login;
    }
    req.params.account = decodeURIComponent(req.params.account);

    return ufds.getUser(req.params.account, function (err, customer) {
        if (err) {
            log.error({err: err}, 'UFDS.getUser error');
            return next(err);
        }

        log.debug('loaded account %j', customer);
        req.account = customer;
        return next();
    });
}

function authorize(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    assert.ok(req.account);
    assert.ok(req.caller);

    var account = req.account;
    var caller = req.caller;

    if ((account.uuid !== caller.uuid) && !caller.isAdmin())
        return next(new NotAuthorizedError(FORBIDDEN, account.login));

    return next();
}

function mount(server) {
    assert.argument(server, 'object', server);

    server.use(basicAuth);
    server.use(signatureAuth);
    server.use(assertAuthenticated);
    server.use(loadAccount);
    server.use(authorize);

    return server;
}

///--- Exports

module.exports = {
    basicAuth: basicAuth,
    signatureAuth: signatureAuth,
    assertAuthenticated: assertAuthenticated,
    loadAccount: loadAccount,
    authorize: authorize
};
<commit_msg>[More LINT]<commit_after>// Copyright 2013 Joyent, Inc.  All rights reserved.

var assert = require('assert');
var util = require('util');

var httpSignature = require('./node-http-signature');
var restify = require('restify');

///--- Globals

var BadRequestError = restify.BadRequestError;
var InvalidCredentialsError = restify.InvalidCredentialsError;
var NotAuthorizedError = restify.NotAuthorizedError;
var ResourceNotFoundError = restify.ResourceNotFoundError;

///--- Messages

var DEF_401 = 'You must make authenticated requests to use CloudAPI';
var FORBIDDEN = 'You do not have permission to access %s';
var INVALID_ERR = 'Invalid %s %s';
var INVALID_AUTHZ = 'Invalid authorization header';
var INVALID_KEY = 'Invalid KeyId %s in authorization header';
var INVALID_SIG = 'The signature we calculated does not match the one you sent';
var INVALID_DEV = 'The token provided is not authorized for this application';
var MISSING_CREDS = 'Missing credentials';
var SCHEME_NOT_SUPPORTED = 'The authorization scheme you requested is ' +
    'not supported';
var USER_404 = 'User %s does not exist';

///--- Middleware

function basicAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'basic') {
        return next();
    }

    // basic auth is deprecated on 7.0+
    if (! /6\.5/.test(req.getVersion())) {
        return next(new InvalidCredentialsError(SCHEME_NOT_SUPPORTED));
    }

    var log = req.log;
    var ufds = req.sdc.ufds;

    var user = req.authorization.basic.username;
    var pass = req.authorization.basic.password;
    if (!user || !pass) {
        return next(new InvalidCredentialsError(MISSING_CREDS));
    }

    log.debug('basicAuth: authorization=%j', req.authorization);
    return ufds.authenticate(user, pass, function (err, customer) {
        if (err) {
            log.error({err: err}, 'UFDS.authenticate error');
            if (err.statusCode === 404) {
                return next(new InvalidCredentialsError(USER_404, user));
            }
            return next(new InvalidCredentialsError(DEF_401));
        }
        return ufds.getMetadata(customer, 'cloudapi',
                    function (mderr, metadata) {
            if (mderr || metadata === null ||
                    metadata.require_http_signature !== 'true') {
                req.caller = customer;
                req.username = req.caller.login;
                return next();
            } else {
                return next(new InvalidCredentialsError(SCHEME_NOT_SUPPORTED));
            }
        });
    });
}

function signatureAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'signature') {
        return next();
    }

    var keyId;
    var log = req.log;
    var sig = req.authorization.signature;
    var ufds = req.sdc.ufds;

    try {
        keyId = sig.keyId.split('/');
    } catch (e) {
        log.error('Error parsing authorization header: %s', e.stack);
        return next(new BadRequestError(INVALID_AUTHZ));
    }

    if (!keyId || keyId.length !== 4 || keyId[2] !== 'keys') {
        return next(new InvalidCredentialsError(INVALID_KEY, sig.keyId));
    }

    return ufds.getUser(keyId[1], function (err, account) {
        if (err) {
            log.trace({err: err}, 'UFDS.getUser error');
            return next(new InvalidCredentialsError(INVALID_ERR,
                                                    'account',
                                                    keyId[1]));
        }

        return account.getKey(keyId[3], function (err2, key) {
            if (err2) {
                log.trace({err: err2}, 'UFDS.getKey error');
                return next(new InvalidCredentialsError(INVALID_ERR,
                                                        'key',
                                                        keyId[3]));
            }

            log.debug('authSig using key %s => %s', sig.keyId, key.pkcs);
            try {
                if (!httpSignature.verifySignature(sig, key.pkcs)) {
                    log.debug('authSig::verify FAIL: %j, key=%s',
                                sig, key.standard);
                    return next(new InvalidCredentialsError(INVALID_SIG));
                }

                if (!req.header('X-Auth-Token')) {
                    req.caller = account;
                    req.username = account.login;
                    return next();
                } else {
                    var token = JSON.parse(req.header('X-Auth-Token'));
                    return req.sdc.keyapi.detoken(token,
                            function (clientobj, clienterr) {
                            if (clienterr) {
                                log.error({err: clienterr}, 'keyapi error');
                                return next(new InvalidCredentialsError(
                                    INVALID_DEV));
                            }
                            if (clientobj.expires) {
                                var expires = new Date(clientobj.expires);
                                var now = new Date();
                                if (now > expires) {
                                    return next(new InvalidCredentialsError(
                                    INVALID_DEV));
                                }
                            }
                            if (clientobj.devkeyId !== sig.keyId) {
                                return next(new InvalidCredentialsError(
                                        INVALID_DEV));
                            }
                            if (!clientobj.permissions.cloudapi) {
                                return next(new InvalidCredentialsError(
                                        INVALID_DEV));
                            }
                            var perms = clientobj.permissions.cloudapi;
                            var authorized = false;
                            for (var i = 0; i < perms.length; i++) {
                                var currperm = perms[i].split('/*');
                                if (currperm[0] == req.url) {
                                    authorized = true;
                                    break;
                                }
                                if (req.url.search(currperm[0]) === 0) {
                                    if (currperm.length == 2 &&
                                        req.url.charAt(currperm[0].length) ===
                                        '/') {
                                        authorized = true;
                                        break;
                                    }
                                }
                            }

                            if (authorized === true) {
                                req.caller = clientobj.account;
                                req.username = clientobj.account.login;
                                return next();
                            } else {
                                return next(new InvalidCredentialsError(
                                    INVALID_DEV));
                            }
                        });
                }

            } catch (e) {
                log.error('Error calling http_signature: ' + e.stack);
                return next(new InvalidCredentialsError(INVALID_SIG));
            }

        });
    });
}

function assertAuthenticated(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    if (typeof (req.caller) !== 'object') {
        return next(new InvalidCredentialsError(DEF_401));
    }

    return next();
}

function loadAccount(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    assert.ok(req.sdc);

    var log = req.log;
    var ufds = req.sdc.ufds;

    if (req.params.account === 'my') {
        req.params.account = req.caller.login;
    }
    req.params.account = decodeURIComponent(req.params.account);

    return ufds.getUser(req.params.account, function (err, customer) {
        if (err) {
            log.error({err: err}, 'UFDS.getUser error');
            return next(err);
        }

        log.debug('loaded account %j', customer);
        req.account = customer;
        return next();
    });
}

function authorize(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    assert.ok(req.account);
    assert.ok(req.caller);

    var account = req.account;
    var caller = req.caller;

    if ((account.uuid !== caller.uuid) && !caller.isAdmin())
        return next(new NotAuthorizedError(FORBIDDEN, account.login));

    return next();
}

function mount(server) {
    assert.argument(server, 'object', server);

    server.use(basicAuth);
    server.use(signatureAuth);
    server.use(assertAuthenticated);
    server.use(loadAccount);
    server.use(authorize);

    return server;
}

///--- Exports

module.exports = {
    basicAuth: basicAuth,
    signatureAuth: signatureAuth,
    assertAuthenticated: assertAuthenticated,
    loadAccount: loadAccount,
    authorize: authorize
};
<|endoftext|>


<commit_before>/*global require: false, module: false */
'use strict';
var _ = require('../underscore_ext');
var Rx = require('rx');
var {map, find} = _;
var xenaQuery = require('../xenaQuery');
var heatmapColors = require('../heatmapColors');
var widgets = require('../columnWidgets');

// XXX might want to automatically wrap all of these in xenaQuery.
var datasetProbeValues = xenaQuery.dsID_fn(xenaQuery.dataset_probe_values);
var datasetGenesValues = xenaQuery.dsID_fn(xenaQuery.dataset_genes_values);
var datasetGeneProbesValues = xenaQuery.dsID_fn(xenaQuery.dataset_gene_probe_values);
var datasetCodes = xenaQuery.dsID_fn(xenaQuery.code_list);

function second(x, y) {
	return y;
}

function saveMissing(fn) {
	return function (v) {
		return v == null ? v : fn(v);
	};
}

function subbykey(subtrahend, key, val) {
	return val - subtrahend[key];
}

// Decide whether to normalize, perfering the user setting to the
// dataset default setting.
function shouldNormalize(vizSettings, dataset) {
	var user = _.getIn(vizSettings, ['colNormalization']),
		dataDefault = _.getIn(dataset, ['colnormalization']);
	return user === 'subset' || user == null && dataDefault;
}

// Returns 2d array of numbers, probes X samples.
// [[number, ...], [number, ...]]
// Performs sorting and normalization.
function computeHeatmap(vizSettings, data, fields, samples, dataset) {
	if (!data) {
		return [];
	}
	var {mean, probes, values} = data,
		colnormalization = shouldNormalize(vizSettings, dataset),
		transform = (colnormalization && mean && _.partial(subbykey, mean)) || second;

	return map(probes || fields, function (p) {
		var suTrans = saveMissing(v => transform(p, v));
		return map(samples, s => suTrans(_.getIn(values[p], [s])));
	});
}

var hasViz = vizSettings => !isNaN(_.getIn(vizSettings, ['min']));

function dataToHeatmap(column, vizSettings, data, samples, dataset) {
	if (!data) {
		return null;
	}
	var {req, codes = {}} = data;
	var fields = _.get(req, 'probes', column.fields);
	var heatmap = computeHeatmap(vizSettings, req, fields, samples, dataset),
		colors = map(fields, (p, i) =>
					 heatmapColors.colorSpec(column, vizSettings,
											 codes, heatmap[i], dataset)),
		// Provide a legend scheme if more than one field.
		multiScaled = fields.length > 1 && !hasViz(vizSettings),
		legend = multiScaled ?
			{legend: {colors: heatmapColors.defaultColors(dataset), labels: ['lower', '', 'higher']}} :
			null;

	return {fields, heatmap, colors, ...legend};
}

//
// sort
//

function cmpNumberOrNull(v1, v2) {
	if (v1 == null && v2 == null) {
		return 0;
	} else if (v1 == null) {
		return 1;
	} else if (v2 == null) {
		return -1;
	}
	return v2 - v1;
}

function cmpSamples(probes, data, s1, s2) {
	var diff = data && find(probes, function (f) {
			return data[f] && cmpNumberOrNull(data[f][s1], data[f][s2]);
		});
	if (diff) {
		return cmpNumberOrNull(data[diff][s1], data[diff][s2]);
	} else {
		return 0;
	}
}

var cmp = ({fields}, {req: {values, probes}} = {req: {}}) =>
	(s1, s2) => cmpSamples(probes || fields, values, s1, s2); // XXX having probes here is unfortunate.

//
// data fetches
//

// Convert nanstr and compute mean.
function meanNanResponse(probes, data) {
	var values = _.map(data, field => _.map(field, xenaQuery.nanstr)),
		mean = _.map(data, _.meannan);

	return {values, mean};
}

function indexProbeGeneResponse(data) {
	var probes = data[0],
		vals = data[1];
	return _.extend({probes: probes}, meanNanResponse(probes, vals));
}

function orderByQuery(genes, data) {
	var indx = _.invert(_.pluck(data, 'gene'));
	return _.map(genes, function (g) {
		var i = indx[g];
		return i && data[i].scores[0]; // XXX extra level of array in g.SCORES??
	});
}

function indexGeneResponse(genes, data) {
	return meanNanResponse(genes, orderByQuery(genes, data));
}

var fetch = ({dsID, fields}, [samples]) => datasetProbeValues(dsID, samples, fields)
	.map(resp => ({req: meanNanResponse(fields, resp)}));

var fetchGeneProbes = ({dsID, fields}, [samples]) => datasetGeneProbesValues(dsID, samples, fields)
	.map(resp => ({req: indexProbeGeneResponse(resp)}));

// This should really be fetchCoded. Further, it should only return a single
// code list, i.e. either a single clinical coded field, or a list of genomic
// fields all with the same code values.
var fetchFeature = ({dsID, fields}, [samples]) => Rx.Observable.zipArray(
		datasetProbeValues(dsID, samples, fields)
			.map(resp => meanNanResponse(fields, resp)),
		datasetCodes(dsID, fields)
	).map(([req, codes]) => ({req, codes: _.values(codes)[0]}));

var fetchGene = ({dsID, fields}, [samples]) => datasetGenesValues(dsID, samples, fields)
			.map(resp => ({req: indexGeneResponse(fields, resp)}));

['probeMatrix', 'geneProbesMatrix', 'geneMatrix', 'clinicalMatrix'].forEach(dataType => {
	widgets.transform.add(dataType, dataToHeatmap);
	widgets.cmp.add(dataType, cmp);
});

widgets.fetch.add("probeMatrix", fetch);
widgets.fetch.add("geneProbesMatrix", fetchGeneProbes);
widgets.fetch.add("geneMatrix", fetchGene);
widgets.fetch.add("clinicalMatrix", fetchFeature);

module.exports = {
	fetch,
	fetchGeneProbes,
	fetchGene,
	fetchFeature
};
<commit_msg>Don't index by probe.<commit_after>/*global require: false, module: false */
'use strict';
var _ = require('../underscore_ext');
var Rx = require('rx');
var {map, find} = _;
var xenaQuery = require('../xenaQuery');
var heatmapColors = require('../heatmapColors');
var widgets = require('../columnWidgets');

// XXX might want to automatically wrap all of these in xenaQuery.
var datasetProbeValues = xenaQuery.dsID_fn(xenaQuery.dataset_probe_values);
var datasetGenesValues = xenaQuery.dsID_fn(xenaQuery.dataset_genes_values);
var datasetGeneProbesValues = xenaQuery.dsID_fn(xenaQuery.dataset_gene_probe_values);
var datasetCodes = xenaQuery.dsID_fn(xenaQuery.code_list);

function second(x, y) {
	return y;
}

function saveMissing(fn) {
	return function (v) {
		return v == null ? v : fn(v);
	};
}

function subbykey(subtrahend, key, val) {
	return val - subtrahend[key];
}

// Decide whether to normalize, perfering the user setting to the
// dataset default setting.
function shouldNormalize(vizSettings, dataset) {
	var user = _.getIn(vizSettings, ['colNormalization']),
		dataDefault = _.getIn(dataset, ['colnormalization']);
	return user === 'subset' || user == null && dataDefault;
}

// Returns 2d array of numbers, probes X samples.
// [[number, ...], [number, ...]]
// Performs sorting and normalization.
function computeHeatmap(vizSettings, data, fields, samples, dataset) {
	if (!data) {
		return [];
	}
	var {mean, probes, values} = data,
		colnormalization = shouldNormalize(vizSettings, dataset),
		transform = (colnormalization && mean && _.partial(subbykey, mean)) || second;

	return map(probes || fields, function (p, i) {
		var suTrans = saveMissing(v => transform(i, v));
		return map(samples, s => suTrans(_.getIn(values, [i, s])));
	});
}

var hasViz = vizSettings => !isNaN(_.getIn(vizSettings, ['min']));

function dataToHeatmap(column, vizSettings, data, samples, dataset) {
	if (!data) {
		return null;
	}
	var {req, codes = {}} = data;
	var fields = _.get(req, 'probes', column.fields);
	var heatmap = computeHeatmap(vizSettings, req, fields, samples, dataset),
		colors = map(fields, (p, i) =>
					 heatmapColors.colorSpec(column, vizSettings,
											 codes, heatmap[i], dataset)),
		// Provide a legend scheme if more than one field.
		multiScaled = fields.length > 1 && !hasViz(vizSettings),
		legend = multiScaled ?
			{legend: {colors: heatmapColors.defaultColors(dataset), labels: ['lower', '', 'higher']}} :
			null;

	return {fields, heatmap, colors, ...legend};
}

//
// sort
//

function cmpNumberOrNull(v1, v2) {
	if (v1 == null && v2 == null) {
		return 0;
	} else if (v1 == null) {
		return 1;
	} else if (v2 == null) {
		return -1;
	}
	return v2 - v1;
}

function cmpSamples(probes, data, s1, s2) {
	var diff = data && find(data, function (f) {
			return cmpNumberOrNull(f[s1], f[s2]);
		});
	if (diff) {
		return cmpNumberOrNull(diff[s1], diff[s2]);
	} else {
		return 0;
	}
}

var cmp = ({fields}, {req: {values, probes}} = {req: {}}) =>
	(s1, s2) => cmpSamples(probes || fields, values, s1, s2); // XXX having probes here is unfortunate.

//
// data fetches
//

// Convert nanstr and compute mean.
function meanNanResponse(probes, data) {
	var values = _.map(data, field => _.map(field, xenaQuery.nanstr)),
		mean = _.map(data, _.meannan);

	return {values, mean};
}

function indexProbeGeneResponse(data) {
	var probes = data[0],
		vals = data[1];
	return _.extend({probes: probes}, meanNanResponse(probes, vals));
}

function orderByQuery(genes, data) {
	var indx = _.invert(_.pluck(data, 'gene'));
	return _.map(genes, function (g) {
		var i = indx[g];
		return i && data[i].scores[0]; // XXX extra level of array in g.SCORES??
	});
}

function indexGeneResponse(genes, data) {
	return meanNanResponse(genes, orderByQuery(genes, data));
}

var fetch = ({dsID, fields}, [samples]) => datasetProbeValues(dsID, samples, fields)
	.map(resp => ({req: meanNanResponse(fields, resp)}));

var fetchGeneProbes = ({dsID, fields}, [samples]) => datasetGeneProbesValues(dsID, samples, fields)
	.map(resp => ({req: indexProbeGeneResponse(resp)}));

// This should really be fetchCoded. Further, it should only return a single
// code list, i.e. either a single clinical coded field, or a list of genomic
// fields all with the same code values.
var fetchFeature = ({dsID, fields}, [samples]) => Rx.Observable.zipArray(
		datasetProbeValues(dsID, samples, fields)
			.map(resp => meanNanResponse(fields, resp)),
		datasetCodes(dsID, fields)
	).map(([req, codes]) => ({req, codes: _.values(codes)[0]}));

var fetchGene = ({dsID, fields}, [samples]) => datasetGenesValues(dsID, samples, fields)
			.map(resp => ({req: indexGeneResponse(fields, resp)}));

['probeMatrix', 'geneProbesMatrix', 'geneMatrix', 'clinicalMatrix'].forEach(dataType => {
	widgets.transform.add(dataType, dataToHeatmap);
	widgets.cmp.add(dataType, cmp);
});

widgets.fetch.add("probeMatrix", fetch);
widgets.fetch.add("geneProbesMatrix", fetchGeneProbes);
widgets.fetch.add("geneMatrix", fetchGene);
widgets.fetch.add("clinicalMatrix", fetchFeature);

module.exports = {
	fetch,
	fetchGeneProbes,
	fetchGene,
	fetchFeature
};
<|endoftext|>