<commit_before>/**
 * jQuery Plugin: Sticky Tabs
 *
 * @author Aidan Lister <aidan@php.net>
 * @version 1.2.0
 */
(function ( $ ) {
  $.fn.stickyTabs = function( options ) {
    var context = this

    var settings = $.extend({
        getHashCallback: function(hash, btn) { return hash }
    }, options );

    // Show the tab corresponding with the hash in the URL, or the first tab.
    var showTabFromHash = function() {
      var hash = window.location.hash;
      var selector = hash ? 'a[href="' + hash + '"]' : 'li.active > a';
      $(selector, context).tab('show');
    }

    // We use pushState if it's available so the page won't jump, otherwise a shim.
    var changeHash = function(hash) {
      if (history && history.pushState) {
        history.pushState(null, null, '#' + hash);
      } else {
        scrollV = document.body.scrollTop;
        scrollH = document.body.scrollLeft;
        window.location.hash = hash;
        document.body.scrollTop = scrollV;
        document.body.scrollLeft = scrollH;
      }
    }

    // Set the correct tab when the page loads
    showTabFromHash(context)

    // Set the correct tab when a user uses their back/forward button
    $(window).on('hashchange', showTabFromHash);

    // Change the URL when tabs are clicked
    $('a', context).on('click', function(e) {
      var hash = this.href.split('#')[1];
      var adjustedhash = settings.getHashCallback(hash, this);
      changeHash(adjustedhash);
    });

    return this;
};
}( jQuery ));

attributes = {};
var root = '/api/apps/';
function load(app_version) {

	$('.nav-tabs').stickyTabs();
  $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
    grid.fixStyle()
  })

  attributes= app_version;

  attributes.files = $.extend(true, [{name:'Main',content:'', disabled: true}],attributes.files)
  attributes.functions = $.extend(true, [{name:'Constructor',content:'', disabled: true}],attributes.functions)

  $('.navbar-header .nav a h4').html('API - '+api.name);

  $('#version').html((attributes.summary || 'Working Version'));

  new gform({
    name: 'resources',
    data:attributes,
    actions:[],
    fields:[
      // {type:'output',name:'test',format:{value:'

'},label:false},
      {
        "name":"resources",
        "array":{
          "min": 0,
          "max": 100
        },
        "type":"fieldset",
        "fields": [
          // "database": {}
          // {label: false, name:'database',type:'select', required: true,choices:'/api/proxy/databases',label_key:'name',value_key:'id'}
          {label: "Name", name:'name', required: false, columns:6},
          // {label: 'Type', name:'type', type:'select',options:[
          //   {label: 'MySQL Database',value: 'mysql'},
          //   {label: 'Oracle Database', value:'oracle'},
          //   {label: 'Value', value:'value'},
          //   {label: 'Secret Value (Encrypted at Rest)', value:'secret'}
          // ], columns:6}

        ]
      }
    ]
  },'.resources')

  var options = {
    data: attributes.routes,
		el: '.routes',
    schema:[
      {label: 'Description',name: 'description'},
      {label: 'Path', name:'path',required:true, validate:[{type:'custom',test:e =>{
        if (!/^[\/][a-zA-Z0-9_\/-]*[a-zA-Z0-9]$/.test(e.value)) {
          return  'Must be a valid url path begining with a / and ending in a number or letter. Please see examples in help text';
        }
      }}], help:'i.e. /example/route or /my-example_2'},
      {label: 'Function Name', name:'function_name', required:true, validate:[{type:'custom',test:e =>{
        if(!/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/.test(e.value)) {
          return  'API name must be a valid php function name';
        }
      }}]},
      {label: 'Verb',name:'verb',type:'select',options:["ALL", "GET", "POST", "PUT", "DELETE"], required:true},
      {
            "show":false,
            "name": "parameters",
            "label": "Parameters",
            "template":'{{#attributes.required}}{{name}}
 {{/attributes.required}}{{#attributes.optional}}{{#name}}{{name}}
{{/name}} {{/attributes.optional}}',

      }
    ],
    actions:[
      {name:"create"},'|',
      {name:"edit"},{'name': 'required', 'label': ' Required Parameters',min:1,max:1},{'name': 'optional', 'label': ' Optional Parameters',min:1,max:1},'|',
      {name:"delete"}
    ]
  };

  if(typeof grid !== 'undefined'){
    grid.destroy();
  }
  grid = new GrapheneDataGrid(options)
  grid.on('model:required',function(e){
     new gform({
      data:e.model.attributes,
      legend:'Required Parameters',
      name:'required',
      "fields": [
        {
          "name":"required",
          "label": false,
          "array":{min:1,max:100},
          "type":"fieldset",
          fields:[
            {'name':'name','label':'Name',"inline":true,columns:6},
            {'name':'example','label':'Example',"inline":true, columns:6},
            {'name':'description','label':'Description','type':'textarea',"inline":true}
            // {'name':'required','label':'Required?','type':'checkbox',falsestate:0,"inline":true,columns:4},
          ]
        }
  ]
      }).on('save',function(e){
        this.update(e.form.get());
        e.form.dispatch('close')
      }.bind(e.model)).on('cancel',function(e){e.form.dispatch('close')}).modal()

  })
  grid.on('model:optional',function(e){
    new gform({
      data:e.model.attributes,
      legend:'Optional Parameters',
      name:'optional',
      "fields": [
        {
          "name":"optional",
          "label": false,
          "array":{min:1,max:100},
          "type":"fieldset",
          fields:[
            {'name':'name','label':'Name',"inline":true,columns:6},
            {'name':'example','label':'Example',"inline":true, columns:6},
            {'name':'description','label':'Description','type':'textarea',"inline":true}
            // {'name':'required','label':'Required?','type':'checkbox',falsestate:0,"inline":true,columns:4},
          ]
        }
  ]
      }).on('save',function(e){
        this.update(e.form.get());
        e.form.dispatch('close')
      }.bind(e.model)).on('cancel',function(e){e.form.dispatch('close')}).modal()

  })

  //adjust ace height to fill page
  $('body').append('')

  //add file managers here
  filepage = new fileManager('.files',{name:'scripts', items:attributes.files, mode:'ace/mode/php', label:'File'});
  functionpage = new fileManager('.functions',{name:'functions', items:attributes.functions, mode:'ace/mode/php', label:'Function', inlinemode:true});
}
load(loaded);
orig = $.extend({},loaded);

$(document).keydown(function(e) {
  if ((e.which == '115' || e.which == '83' ) && (e.ctrlKey || e.metaKey)) {
      e.preventDefault();
      $('#save').click()
  }
  return true;
});

$('#save').on('click',function() {
  script_errors =filepage.errors();
  script_errors +=functionpage.errors();
  var data = attributes;
  data.routes = grid.toJSON();
  data.resources = gform.instances.resources.get().resources;
  var errorCount = script_errors.length;//+ css_errors.length

  if(!errorCount){
    data.files = filepage.toJSON();
    data.functions = functionpage.toJSON();
    data.updated_at = attributes.updated_at;
    toastr.info('', 'Saving...')

    $.ajax({
      url: '/api/proxy/'+slug+'/apis/'+attributes.api_id+'/code',
      method: 'PUT',
      data: data,
      success:function(e) {
        if(typeof e.updated_at !== 'undefined'){
          attributes.updated_at = e.updated_at;
        }
        toastr.clear()

        toastr.success('', 'Successfully Saved')
      },
      error:function(e) {
        toastr.error(e.statusText, 'ERROR');
      },
        // statusCode: {
        //   404: function() {
        //     toastr.error('You are no longer logged in', 'Logged Out')
        //   },
        //   409: function(error) {
        //     test = JSON.parse(JSON.parse(error.responseText).error.message);
        //     toastr.warning('conflict detected:'+error.statusText, 'NOT SAVED')
        //     conflictResults = {};
        //     conflictResults.sources = (JSON.stringify(test.sources) !== JSON.stringify(this.model.sources));
        //     conflictResults.css = (JSON.stringify(test.css) !== JSON.stringify(this.model.css));
        //     conflictResults.options = (JSON.stringify(test.options) !== JSON.stringify(this.model.options));
        //     conflictResults.scripts = (JSON.stringify(test.script) !== JSON.stringify(this.model.script));
        //     conflictResults.template = (JSON.stringify(test.template) !== JSON.stringify(this.model.template));
        //     modal({headerClass:'bg-danger' ,title: 'Conflict(s) detected', content: render('conflict', conflictResults)})//, footer:'
Force Save
'})
        //   }.bind(this),
        //   401: function() {
        //     toastr.error('You are not authorized to perform this action', 'Not Authorized')
        //   }
        // }
    })
  }else{
    toastr.error('Please correct the compile/syntax errors ('+ errorCount +')', 'Errors Found')
    modal({headerClass:'danger' ,title: 'Syntax Error(s)', content: render('error', {count:errorCount, temp: template_errors, script: script_errors/*, css: css_errors*/})})//, footer:'
Force Save
'})
  }
})

$('#import').on('click', function() {
    new gform({
      name: 'update',
      legend: ' Update API',
      fields: [
        {label: 'Descriptor', type: 'textarea'}
      ]
    }).on('save', e => {
      $.ajax({
        url: '/api/proxy/'+slug+'/apis/'+attributes.api_id+'/code',
        method: 'PUT',
        data: {force: true, updated_at:'', ...JSON.parse(e.form.get('descriptor'))},
        success: () => {
          e.form.trigger('close');
          window.location.reload()
        },
        error: e =>{
          toastr.error(e.statusText, 'ERROR');
        }

      })
  }).on('cancel',function(e){e.form.dispatch('close')}).modal();
});

$('#publish').on('click', function() {
    new gform({
      name: 'publish',
      legend: ' Publish API',
      fields: [
        {label: 'Summary', required: true},
        {label: 'Description', type: 'textarea'}
      ]}).on('save', e => {
        if(e.form.validate()){
          $.ajax({
            url: '/api/proxy/'+slug+'/apis/'+attributes.api_id+'/publish',
            data: e.form.get(),
            method: 'PUT',
            success: () => {
              e.form.trigger('close');
              toastr.success('', 'Successfully Published')
            },
            error: e =>{
              toastr.error(e.responseJSON.message, 'ERROR');
            }
          })
        }
    }).on('cancel',function(e){e.form.dispatch('close')}).modal();
});

$('#instances').on('click', function() {
  viewTemplate = Hogan.compile('
{{#items}}
{{name}} ({{environment.name}})
{{/items}}
');
  $.get('/api/proxy/'+slug+'/api_instances', function(data) {
    data = _.where(data, {api_id:api.id})
    if(data.length > 0){
      modal({title: 'This API has the following instances', content: viewTemplate.render({items: data})});
    }else{
      modal({title: 'No instances Found', content: 'This App not currently instantiated.'});
    }
  })
});

$('#versions').on('click', function() {
  $.ajax({
    url: "/api/proxy/"+slug+"/apis/"+api.id+"/versions",
    success: function(data) {
      console.log(data);
      data = _.where(data,{stable:1})
      if(!orig.stable) {
        data.unshift({id:orig.id,summary:'Working Version'})
      }

      new gform({
        actions:[{type:'cancel'},{type:'save',label: 'Switch'}],
        name:'modal',
        data:{api_version_id:loaded.id},
        legend:'Select Version',
        fields:[
          {
            label: 'Version',
            name:'api_version_id',
            options:data,
            type:'select',
            format:{
              label:"{{summary}}",
              value:versions=>versions.id
            }
          },
      ]}).on('save', e => {
        // switch version
        e.form.trigger('close');

        $.ajax({
          url: '/api/proxy/'+slug+'/api_versions/'+e.form.get('api_version_id'),
          method: 'get',
          success:function(data) {
            loaded = data;
            load(loaded);
          }
        })
      })
      .on('cancel', e => {
        e.form.trigger('close');
      }).modal()
    }
  })
})

<commit_msg>fix #162<commit_after>/**
 * jQuery Plugin: Sticky Tabs
 *
 * @author Aidan Lister <aidan@php.net>
 * @version 1.2.0
 */
(function ($) {
  $.fn.stickyTabs = function (options) {
    var context = this

    var settings = $.extend({
      getHashCallback: function (hash, btn) { return hash }
    }, options);

    // Show the tab corresponding with the hash in the URL, or the first tab.
    var showTabFromHash = function () {
      var hash = window.location.hash;
      var selector = hash ? 'a[href="' + hash + '"]' : 'li.active > a';
      $(selector, context).tab('show');
    }

    // We use pushState if it's available so the page won't jump, otherwise a shim.
    var changeHash = function (hash) {
      if (history && history.pushState) {
        history.pushState(null, null, '#' + hash);
      } else {
        scrollV = document.body.scrollTop;
        scrollH = document.body.scrollLeft;
        window.location.hash = hash;
        document.body.scrollTop = scrollV;
        document.body.scrollLeft = scrollH;
      }
    }

    // Set the correct tab when the page loads
    showTabFromHash(context)

    // Set the correct tab when a user uses their back/forward button
    $(window).on('hashchange', showTabFromHash);

    // Change the URL when tabs are clicked
    $('a', context).on('click', function (e) {
      var hash = this.href.split('#')[1];
      var adjustedhash = settings.getHashCallback(hash, this);
      changeHash(adjustedhash);
    });

    return this;
  };
}(jQuery));

attributes = {};
var root = '/api/apps/';
function load(app_version) {

  $('.nav-tabs').stickyTabs();
  $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
    grid.fixStyle()
  })

  attributes = app_version;

  attributes.files = $.extend(true, [{ name: 'Main', content: '', disabled: true }], attributes.files)
  attributes.functions = $.extend(true, [{ name: 'Constructor', content: '', disabled: true }], attributes.functions)

  $('.navbar-header .nav a h4').html('API - ' + api.name);

  $('#version').html((attributes.summary || 'Working Version'));

  new gform({
    name: 'resources',
    data: attributes,
    actions: [],
    fields: [
      // {type:'output',name:'test',format:{value:'

'},label:false},
      {
        "name": "resources",
        "array": {
          "min": 0,
          "max": 100
        },
        "type": "fieldset",
        "fields": [
          // "database": {}
          // {label: false, name:'database',type:'select', required: true,choices:'/api/proxy/databases',label_key:'name',value_key:'id'}
          { label: "Name", name: 'name', required: false, columns: 6 },
          // {label: 'Type', name:'type', type:'select',options:[
          //   {label: 'MySQL Database',value: 'mysql'},
          //   {label: 'Oracle Database', value:'oracle'},
          //   {label: 'Value', value:'value'},
          //   {label: 'Secret Value (Encrypted at Rest)', value:'secret'}
          // ], columns:6}

        ]
      }
    ]
  }, '.resources')

  var options = {
    data: attributes.routes,
    el: '.routes',
    schema: [
      { label: 'Description', name: 'description' },
      {
        label: 'Path', name: 'path', required: true, validate: [{
          type: 'custom', test: e => {
            if (!/^[\/][a-zA-Z0-9_\/-]*[a-zA-Z0-9]$/.test(e.value)) {
              return 'Must be a valid url path begining with a / and ending in a number or letter. Please see examples in help text';
            }
          }
        }], help: 'i.e. /example/route or /my-example_2'
      },
      {
        label: 'Function Name', name: 'function_name', required: true, validate: [{
          type: 'custom', test: e => {
            if (!/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/.test(e.value)) {
              return 'API name must be a valid php function name';
            }
          }
        }]
      },
      { label: 'Verb', name: 'verb', type: 'select', options: ["ALL", "GET", "POST", "PUT", "DELETE"], required: true },
      {
        "show": false,
        "name": "parameters",
        "label": "Parameters",
        "template": '{{#attributes.required}}{{name}}
 {{/attributes.required}}{{#attributes.optional}}{{#name}}{{name}}
{{/name}} {{/attributes.optional}}',

      },
      {
        type: "fieldset", label: "Required", name: "required", show: false, parse: true, showColumn: false, array: { max: 100 }, fields: [
          { 'name': 'name', 'label': 'Name', "inline": true, columns: 6 },
          { 'name': 'example', 'label': 'Example', "inline": true, columns: 6 },
          { 'name': 'description', 'label': 'Description', 'type': 'textarea', "inline": true }
          // {'name':'required','label':'Required?','type':'checkbox',falsestate:0,"inline":true,columns:4},
        ]
      },
      {
        type: "fieldset", label: "Optional", name: "optional", show: false, parse: true, showColumn: false, array: { max: 100 }, fields: [
          { 'name': 'name', 'label': 'Name', "inline": true, columns: 6 },
          { 'name': 'example', 'label': 'Example', "inline": true, columns: 6 },
          { 'name': 'description', 'label': 'Description', 'type': 'textarea', "inline": true }
          // {'name':'required','label':'Required?','type':'checkbox',falsestate:0,"inline":true,columns:4},
        ]
      }
    ],
    actions: [
      { name: "create" }, '|',
      { name: "edit" }, { 'name': 'required', 'label': ' Required Parameters', min: 1, max: 1 }, { 'name': 'optional', 'label': ' Optional Parameters', min: 1, max: 1 }, '|',
      { name: "delete" }
    ]
  };

  if (typeof grid !== 'undefined') {
    grid.destroy();
  }
  grid = new GrapheneDataGrid(options)
  grid.on('model:edited', e => {
    debugger;
  })
  grid.on('model:required', function (e) {
    new gform({
      data: e.model.attributes,
      legend: 'Required Parameters',
      name: 'required',
      "fields": [
        {
          "name": "required",
          "label": false,
          "array": { min: 1, max: 100 },
          "type": "fieldset",
          fields: [
            { 'name': 'name', 'label': 'Name', "inline": true, columns: 6 },
            { 'name': 'example', 'label': 'Example', "inline": true, columns: 6 },
            { 'name': 'description', 'label': 'Description', 'type': 'textarea', "inline": true }
            // {'name':'required','label':'Required?','type':'checkbox',falsestate:0,"inline":true,columns:4},
          ]
        }
      ]
    }).on('save', function (e) {
      this.update(e.form.get());
      e.form.dispatch('close')
    }.bind(e.model)).on('cancel', function (e) { e.form.dispatch('close') }).modal()

  })
  grid.on('model:optional', function (e) {
    new gform({
      data: e.model.attributes,
      legend: 'Optional Parameters',
      name: 'optional',
      "fields": [
        {
          "name": "optional",
          "label": false,
          "array": { min: 1, max: 100 },
          "type": "fieldset",
          fields: [
            { 'name': 'name', 'label': 'Name', "inline": true, columns: 6 },
            { 'name': 'example', 'label': 'Example', "inline": true, columns: 6 },
            { 'name': 'description', 'label': 'Description', 'type': 'textarea', "inline": true }
            // {'name':'required','label':'Required?','type':'checkbox',falsestate:0,"inline":true,columns:4},
          ]
        }
      ]
    }).on('save', function (e) {
      this.update(e.form.get());
      e.form.dispatch('close')
    }.bind(e.model)).on('cancel', function (e) { e.form.dispatch('close') }).modal()

  })

  //adjust ace height to fill page
  $('body').append('')

  //add file managers here
  filepage = new fileManager('.files', { name: 'scripts', items: attributes.files, mode: 'ace/mode/php', label: 'File' });
  functionpage = new fileManager('.functions', { name: 'functions', items: attributes.functions, mode: 'ace/mode/php', label: 'Function', inlinemode: true });
}
load(loaded);
orig = $.extend({}, loaded);

$(document).keydown(function (e) {
  if ((e.which == '115' || e.which == '83') && (e.ctrlKey || e.metaKey)) {
    e.preventDefault();
    $('#save').click()
  }
  return true;
});

$('#save').on('click', function () {
  script_errors = filepage.errors();
  script_errors += functionpage.errors();
  var data = attributes;
  data.routes = grid.toJSON();
  data.resources = gform.instances.resources.get().resources;
  var errorCount = script_errors.length;//+ css_errors.length

  if (!errorCount) {
    data.files = filepage.toJSON();
    data.functions = functionpage.toJSON();
    data.updated_at = attributes.updated_at;
    toastr.info('', 'Saving...')

    $.ajax({
      url: '/api/proxy/' + slug + '/apis/' + attributes.api_id + '/code',
      method: 'PUT',
      data: data,
      success: function (e) {
        if (typeof e.updated_at !== 'undefined') {
          attributes.updated_at = e.updated_at;
        }
        toastr.clear()

        toastr.success('', 'Successfully Saved')
      },
      error: function (e) {
        toastr.error(e.statusText, 'ERROR');
      },
      // statusCode: {
      //   404: function() {
      //     toastr.error('You are no longer logged in', 'Logged Out')
      //   },
      //   409: function(error) {
      //     test = JSON.parse(JSON.parse(error.responseText).error.message);
      //     toastr.warning('conflict detected:'+error.statusText, 'NOT SAVED')
      //     conflictResults = {};
      //     conflictResults.sources = (JSON.stringify(test.sources) !== JSON.stringify(this.model.sources));
      //     conflictResults.css = (JSON.stringify(test.css) !== JSON.stringify(this.model.css));
      //     conflictResults.options = (JSON.stringify(test.options) !== JSON.stringify(this.model.options));
      //     conflictResults.scripts = (JSON.stringify(test.script) !== JSON.stringify(this.model.script));
      //     conflictResults.template = (JSON.stringify(test.template) !== JSON.stringify(this.model.template));
      //     modal({headerClass:'bg-danger' ,title: 'Conflict(s) detected', content: render('conflict', conflictResults)})//, footer:'
Force Save
'})
      //   }.bind(this),
      //   401: function() {
      //     toastr.error('You are not authorized to perform this action', 'Not Authorized')
      //   }
      // }
    })
  } else {
    toastr.error('Please correct the compile/syntax errors (' + errorCount + ')', 'Errors Found')
    modal({ headerClass: 'danger', title: 'Syntax Error(s)', content: render('error', { count: errorCount, temp: template_errors, script: script_errors/*, css: css_errors*/ }) })//, footer:'
Force Save
'})
  }
})

$('#import').on('click', function () {
  new gform({
    name: 'update',
    legend: ' Update API',
    fields: [
      { label: 'Descriptor', type: 'textarea' }
    ]
  }).on('save', e => {
    $.ajax({
      url: '/api/proxy/' + slug + '/apis/' + attributes.api_id + '/code',
      method: 'PUT',
      data: { force: true, updated_at: '', ...JSON.parse(e.form.get('descriptor')) },
      success: () => {
        e.form.trigger('close');
        window.location.reload()
      },
      error: e => {
        toastr.error(e.statusText, 'ERROR');
      }

    })
  }).on('cancel', function (e) { e.form.dispatch('close') }).modal();
});

$('#publish').on('click', function () {
  new gform({
    name: 'publish',
    legend: ' Publish API',
    fields: [
      { label: 'Summary', required: true },
      { label: 'Description', type: 'textarea' }
    ]
  }).on('save', e => {
    if (e.form.validate()) {
      $.ajax({
        url: '/api/proxy/' + slug + '/apis/' + attributes.api_id + '/publish',
        data: e.form.get(),
        method: 'PUT',
        success: () => {
          e.form.trigger('close');
          toastr.success('', 'Successfully Published')
        },
        error: e => {
          toastr.error(e.responseJSON.message, 'ERROR');
        }
      })
    }
  }).on('cancel', function (e) { e.form.dispatch('close') }).modal();
});

$('#instances').on('click', function () {
  viewTemplate = Hogan.compile('
{{#items}}
{{name}} ({{environment.name}})
{{/items}}
');
  $.get('/api/proxy/' + slug + '/api_instances', function (data) {
    data = _.where(data, { api_id: api.id })
    if (data.length > 0) {
      modal({ title: 'This API has the following instances', content: viewTemplate.render({ items: data }) });
    } else {
      modal({ title: 'No instances Found', content: 'This App not currently instantiated.' });
    }
  })
});

$('#versions').on('click', function () {
  $.ajax({
    url: "/api/proxy/" + slug + "/apis/" + api.id + "/versions",
    success: function (data) {
      console.log(data);
      data = _.where(data, { stable: 1 })
      if (!orig.stable) {
        data.unshift({ id: orig.id, summary: 'Working Version' })
      }

      new gform({
        actions: [{ type: 'cancel' }, { type: 'save', label: 'Switch' }],
        name: 'modal',
        data: { api_version_id: loaded.id },
        legend: 'Select Version',
        fields: [
          {
            label: 'Version',
            name: 'api_version_id',
            options: data,
            type: 'select',
            format: {
              label: "{{summary}}",
              value: versions => versions.id
            }
          },
        ]
      }).on('save', e => {
        // switch version
        e.form.trigger('close');

        $.ajax({
          url: '/api/proxy/' + slug + '/api_versions/' + e.form.get('api_version_id'),
          method: 'get',
          success: function (data) {
            loaded = data;
            load(loaded);
          }
        })
      })
        .on('cancel', e => {
          e.form.trigger('close');
        }).modal()
    }
  })
})

<|endoftext|>


<commit_before>var express = require('express'),
port = 8080,
host = "::";
var fs = require('fs');
var path = require('path');
var url = require('url');
var request = require('request');
var dns = require('dns');
var tmp = require('tmp');
var os = require("os");

var metahub_srv = "bokeh-metahub.service.consul."
var photohub_srv = "bokeh-photohub-3000.service.consul."
var thumbhub_srv = "bokeh-thumbhub.service.consul."

function findBestSRV(records, func) {
	preference_table = {
	"fog8"	: [ "fog8"	, "fog11"	, "g6fog"	, "fog12"	, "fog9a"	,"fog10"],
	"fog9a"	: [ "fog9a"	, "fog11"	, "fog12"	, "fog8"	, "g6fog"	,"fog10"],
	"fog10"	: [ "fog10"	, "fog11"	, "fog12"	, "g6fog"	, "fog8"	,"fog9a"],
	"fog11"	: [ "fog11"	, "fog12"	, "fog8"	, "g6fog"	, "fog9a"	,"fog10"],
	"fog12"	: [ "fog12"	, "fog11"	, "g6fog"	, "fog8"	, "fog9a"	,"fog10"],
	"g6fog"	: [ "g6fog"	, "fog8"	, "fog12"	, "fog11"	, "fog9a"	,"fog10"],
	};

	myname = os.hostname();

	pref = Array();
	try{
		pref = preference_table[myname];
	} catch (err) {
	}
	best_idx = 100;
	best_record = undefined;
	records.forEach(function(i, index, array) {
		host = i.name.split(".")[0];
		idx = pref.findIndex(host);
		if (idx >= 0 && idx < best) {
			best_idx = idx;
			best_record = i
		}
		if (index == (array.length-1)) {
			if (best < 100) {
				func(best_record);
			}
		}
	});
}

function pickupSRV(name, func) {
	dns.resolveSrv(name, function (err, results) {
		if (results instanceof Array) {
			// Pickup a random result from the different resolved names
			//result = results[Math.floor(Math.random()*results.length)];
			//func(result);
			findBestSRV(results, func)
		} else {
			console.log("Error resolving: " + name);
			//func("");
		}
	});
}

render_function = function(res, func) {
	pickupSRV(metahub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + '/photos';
		console.log("Requesting Metahub: "+url.format(myurl));
		request({uri: myurl}).on('response', function(response) {
			var str = '';
			response.on('data', function (chunk) { str += chunk; });
			response.on('end', function () {
				var resp = JSON.parse(str);
				if (resp) {
					func(res, resp["photos"]);
				} else {
					res.status(500).send('Bad response from Metahub');
				}
			});
		}).on('error', function (error) {
			res.status(500).send('Bad response from Metahub');
		}).end();
	});
};

var app = express();
app.set('view engine', 'pug');

// Render full page
app.get('/', function (req, res) {
	console.log("Rendering whole page");
	res.render('index', { title: 'Hey', message: 'Hello there!'});
});

// Render div containing list of photos
app.get('/photos', function (req,res) {
	console.log("Rendering Photos div");
	render_function(res, function(r, photos) {
		r.render('includes/div_ng1', { title: 'Hey', message: 'Hello there!', photolist: photos});
	});
});

// Render JSON for nanoGallery
app.get('/nanoPhotosProvider.php', function (req,res) {
	console.log("Rendering Photos JSON");
	table = new Array();
	render_function(res, function(r, photos) {
		if (photos) {
			for (var i=0; i<photos.length; i++) {
				e = new Object();
				e.title = photos[i]["description"];
				e.description = photos[i]["description"];
				e.kind = 'image';
				e.ID = photos[i]["uuid"];
				e.albumID = '0'
				e.src = 'photos/hash/' + photos[i]["hash"];
				e.srct = 'thumbs/' + photos[i]["hash"];
				e.imgWidth = 150
				e.imgHeight = 100
				table.push(e);
			}
		}
		r.setHeader('Content-Type', 'application/json');
		r.send(JSON.stringify(table));
	});
});

// Pipe files like css
app.get(/files\/.+\.css$/i, function(req, res){
	var staticFiles = "./";
	var filePath = path.join(staticFiles, req.path),
	fstream;

	console.log("Get " + req.path);

	//filePath = decodeURI(filePath);

	fs.stat(filePath, function(err){
		if (err){
			return common.error(req, res, next, 404, 'File not found', err);
		}
		fstream = fs.createReadStream(filePath);
		fstream.on('error', function(err){
			return common.error(req, res, next, 404, 'File not found', err);
		});

		return fstream.pipe(res);
	});
});

// Proxy thumbs requests to thumbhub
app.get(/thumbs\/.+$/i, function(req, res, next){
//app.get(/thumbs\/.+(\.(png|jpg|bmp|jpeg|gif|tif))$/i, function (req, res) {
	console.log("Get " + req.path);
	pickupSRV(thumbhub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + req.path;
		console.log("Proxying request to thumbhub: "+url.format(myurl));
		request({uri: myurl}).on('response', function(response) {
			response.on('data', function (chunk) { res.write(chunk); });
			response.on('end', function () {
				//res.writeHead(response.statusCode);
				res.end();
			});
			response.on('close', function(){
				//res.writeHead(response.statusCode);
				res.end();
			});
		}).on('error', function(e) {
			console.log(e.message);
			res.writeHead(500);
			res.end();
		}).end();
	});
});

// Proxy photo requests to photohub
//app.get(/photos\/.+(\.(jpg|bmp|jpeg|gif|png|tif))$/i, function (req, res) {
app.get(/photos\/hash\/.+$/i, function(req, res, next){
	console.log("Get " + req.path);
	pickupSRV(photohub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + req.path;
		console.log("Proxying request to photohub: " + myurl);
		request({uri: myurl}).on('response', function(response) {
			response.on('data', function (chunk) { res.write(chunk); });
			response.on('end', function () {
				//res.writeHead(response.statusCode);
				res.end();
			});
			response.on('close', function(){
				//res.writeHead(response.statusCode);
				res.end();
			});
		}).on('error', function(e) {
			console.log(e.message);
			res.writeHead(500);
			res.end();
		}).end();
	});
});

// Proxy photo requests to photohub (faster?)
//app.get(/photos\/.+(\.(jpg|bmp|jpeg|gif|png|tif))$/i, function (req, res) {
app.get(/photox\/hash\/.+$/i, function(req, res, next){
	console.log("Get " + req.path);
	a = req.path.split('/');
	a[1]= 'photos';
	mypath = a.join('/');
	var tmpobj = tmp.fileSync();
	pickupSRV(photohub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + mypath;
		console.log('Uploading photo from PhotoHub: '+myurl);
		// Set timout for 42sec
		request({url: myurl, agentOptions: { timeout: 420000 }})
		.on('error', function(err) {
			res.writeHead(500);
			res.end();
		})
		.on('response', function(response) {
			console.log("Reply from photohub: " + response.statusCode);
			response.on('end', function () {
				console.log("Written file: " + tmpobj.name);
				fstream = fs.createReadStream(tmpobj.name);
				return fstream.pipe(res);
			});
		}).pipe(fs.createWriteStream(tmpobj.name));
	});
});

// Proxy photo delete requests to photohub
app.delete(/photos\/.+$/i, function(req, res, next){
	console.log("Delete " + req.path);
	pickupSRV(metahub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + req.path;
		console.log("Proxying request to metahub: " + myurl);
		request({method: 'DELETE', uri: myurl}).on('response', function(response) {
			response.on('end', function () {
				res.end();
			});
			response.on('close', function(){
				res.end();
			});
		}).on('error', function(e) {
			console.log(e.message);
			res.writeHead(500);
			res.end();
		}).end();
	});
});

// Upload a photo
app.post("/photos", function (req, res) {
	var multiparty = require('multiparty');
	var form = new multiparty.Form();
	form.on('file', function(name,file){
		var tempPath = file.path;
		var origName = file.originalFilename;
		var creationDate = Date.now();
		fs.stat(tempPath,function (err, stats) {
			if (!err) {
				creationDate = stats.ctime.getTime();
			}
		});

		var formData = {
			custom_file: {
				value: fs.createReadStream(tempPath),
				options: {
					filename: origName
				}
			}
		}
		pickupSRV(photohub_srv, function(record) {
			myurl = url.parse("http://bokeh-photohub.service.dc1.consul:3000/photos");
			myurl.hostname = record.name;
			myurl.port = record.port;
			console.log("Uploading photo to Photohub: http://"+myurl.hostname+":"+myurl.port);
			request.post({url: myurl, formData: formData}, function (err, resp, body){
				if (err) {
					console.log("Failed to upload image to Photohub!:" + origName);
					res.writeHead(500);
					res.end();
					return;
				}
				console.log("Successfully uploaded photo to Photohub: " + body);
				var r = JSON.parse(body);
				if (!r) {
					console.log("Failed to read Photohub response!");
					res.writeHead(500);
					res.end();
					return;
				} else {
					hash = r['hash'];
					type = r['type'];
					console.log("Hash: " + hash);
					pickupSRV(metahub_srv, function(record) {
						myurl = url.parse("http://bokeh-metahub.service.dc1.consul:5000/photos");
						myurl.hostname = record.name;
						myurl.port = record.port;
						console.log("Uploading metadata to Metahub: http://"+myurl.hostname+":"+myurl.port);
						request({
							url: myurl,
							method: 'POST',
							json: {
								'hash': hash,
								'type': type,
								'url': encodeURIComponent(origName),
								'timestamp': creationDate.toString(),
								'description': origName
							}
						}, function (err2, resp2, body2){
							if (err2) {
								console.log("Failed to upload description to Metahub!:" + origName + "Resp: " + resp2);
								res.writeHead(500);
								res.end();
							}
							b = JSON.stringify(body2)
							console.log("Successfully uploaded description to Metahub: " + b);
							res.write(b)
							res.end();
							return;
						});
					});
				}
			});
		});
	});
	form.parse(req);
});

app.listen(port, host);
console.log('Bokeh-WebUI listening on ' + host  + ':' + port);
<commit_msg>WebUI: Ooops index<commit_after>var express = require('express'),
port = 8080,
host = "::";
var fs = require('fs');
var path = require('path');
var url = require('url');
var request = require('request');
var dns = require('dns');
var tmp = require('tmp');
var os = require("os");

var metahub_srv = "bokeh-metahub.service.consul."
var photohub_srv = "bokeh-photohub-3000.service.consul."
var thumbhub_srv = "bokeh-thumbhub.service.consul."

function findBestSRV(records, func) {
	preference_table = {
	"fog8"	: [ "fog8"	, "fog11"	, "g6fog"	, "fog12"	, "fog9a"	,"fog10"],
	"fog9a"	: [ "fog9a"	, "fog11"	, "fog12"	, "fog8"	, "g6fog"	,"fog10"],
	"fog10"	: [ "fog10"	, "fog11"	, "fog12"	, "g6fog"	, "fog8"	,"fog9a"],
	"fog11"	: [ "fog11"	, "fog12"	, "fog8"	, "g6fog"	, "fog9a"	,"fog10"],
	"fog12"	: [ "fog12"	, "fog11"	, "g6fog"	, "fog8"	, "fog9a"	,"fog10"],
	"g6fog"	: [ "g6fog"	, "fog8"	, "fog12"	, "fog11"	, "fog9a"	,"fog10"],
	};

	myname = os.hostname();

	pref = Array();
	try{
		pref = preference_table[myname];
	} catch (err) {
	}
	best_idx = 100;
	best_record = undefined;
	records.forEach(function(i, index, array) {
		host = i.name.split(".")[0];
		idx = pref.indexOf(host);
		if (idx >= 0 && idx < best) {
			best_idx = idx;
			best_record = i
		}
		if (index == (array.length-1)) {
			if (best < 100) {
				func(best_record);
			}
		}
	});
}

function pickupSRV(name, func) {
	dns.resolveSrv(name, function (err, results) {
		if (results instanceof Array) {
			// Pickup a random result from the different resolved names
			//result = results[Math.floor(Math.random()*results.length)];
			//func(result);
			findBestSRV(results, func)
		} else {
			console.log("Error resolving: " + name);
			//func("");
		}
	});
}

render_function = function(res, func) {
	pickupSRV(metahub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + '/photos';
		console.log("Requesting Metahub: "+url.format(myurl));
		request({uri: myurl}).on('response', function(response) {
			var str = '';
			response.on('data', function (chunk) { str += chunk; });
			response.on('end', function () {
				var resp = JSON.parse(str);
				if (resp) {
					func(res, resp["photos"]);
				} else {
					res.status(500).send('Bad response from Metahub');
				}
			});
		}).on('error', function (error) {
			res.status(500).send('Bad response from Metahub');
		}).end();
	});
};

var app = express();
app.set('view engine', 'pug');

// Render full page
app.get('/', function (req, res) {
	console.log("Rendering whole page");
	res.render('index', { title: 'Hey', message: 'Hello there!'});
});

// Render div containing list of photos
app.get('/photos', function (req,res) {
	console.log("Rendering Photos div");
	render_function(res, function(r, photos) {
		r.render('includes/div_ng1', { title: 'Hey', message: 'Hello there!', photolist: photos});
	});
});

// Render JSON for nanoGallery
app.get('/nanoPhotosProvider.php', function (req,res) {
	console.log("Rendering Photos JSON");
	table = new Array();
	render_function(res, function(r, photos) {
		if (photos) {
			for (var i=0; i<photos.length; i++) {
				e = new Object();
				e.title = photos[i]["description"];
				e.description = photos[i]["description"];
				e.kind = 'image';
				e.ID = photos[i]["uuid"];
				e.albumID = '0'
				e.src = 'photos/hash/' + photos[i]["hash"];
				e.srct = 'thumbs/' + photos[i]["hash"];
				e.imgWidth = 150
				e.imgHeight = 100
				table.push(e);
			}
		}
		r.setHeader('Content-Type', 'application/json');
		r.send(JSON.stringify(table));
	});
});

// Pipe files like css
app.get(/files\/.+\.css$/i, function(req, res){
	var staticFiles = "./";
	var filePath = path.join(staticFiles, req.path),
	fstream;

	console.log("Get " + req.path);

	//filePath = decodeURI(filePath);

	fs.stat(filePath, function(err){
		if (err){
			return common.error(req, res, next, 404, 'File not found', err);
		}
		fstream = fs.createReadStream(filePath);
		fstream.on('error', function(err){
			return common.error(req, res, next, 404, 'File not found', err);
		});

		return fstream.pipe(res);
	});
});

// Proxy thumbs requests to thumbhub
app.get(/thumbs\/.+$/i, function(req, res, next){
//app.get(/thumbs\/.+(\.(png|jpg|bmp|jpeg|gif|tif))$/i, function (req, res) {
	console.log("Get " + req.path);
	pickupSRV(thumbhub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + req.path;
		console.log("Proxying request to thumbhub: "+url.format(myurl));
		request({uri: myurl}).on('response', function(response) {
			response.on('data', function (chunk) { res.write(chunk); });
			response.on('end', function () {
				//res.writeHead(response.statusCode);
				res.end();
			});
			response.on('close', function(){
				//res.writeHead(response.statusCode);
				res.end();
			});
		}).on('error', function(e) {
			console.log(e.message);
			res.writeHead(500);
			res.end();
		}).end();
	});
});

// Proxy photo requests to photohub
//app.get(/photos\/.+(\.(jpg|bmp|jpeg|gif|png|tif))$/i, function (req, res) {
app.get(/photos\/hash\/.+$/i, function(req, res, next){
	console.log("Get " + req.path);
	pickupSRV(photohub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + req.path;
		console.log("Proxying request to photohub: " + myurl);
		request({uri: myurl}).on('response', function(response) {
			response.on('data', function (chunk) { res.write(chunk); });
			response.on('end', function () {
				//res.writeHead(response.statusCode);
				res.end();
			});
			response.on('close', function(){
				//res.writeHead(response.statusCode);
				res.end();
			});
		}).on('error', function(e) {
			console.log(e.message);
			res.writeHead(500);
			res.end();
		}).end();
	});
});

// Proxy photo requests to photohub (faster?)
//app.get(/photos\/.+(\.(jpg|bmp|jpeg|gif|png|tif))$/i, function (req, res) {
app.get(/photox\/hash\/.+$/i, function(req, res, next){
	console.log("Get " + req.path);
	a = req.path.split('/');
	a[1]= 'photos';
	mypath = a.join('/');
	var tmpobj = tmp.fileSync();
	pickupSRV(photohub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + mypath;
		console.log('Uploading photo from PhotoHub: '+myurl);
		// Set timout for 42sec
		request({url: myurl, agentOptions: { timeout: 420000 }})
		.on('error', function(err) {
			res.writeHead(500);
			res.end();
		})
		.on('response', function(response) {
			console.log("Reply from photohub: " + response.statusCode);
			response.on('end', function () {
				console.log("Written file: " + tmpobj.name);
				fstream = fs.createReadStream(tmpobj.name);
				return fstream.pipe(res);
			});
		}).pipe(fs.createWriteStream(tmpobj.name));
	});
});

// Proxy photo delete requests to photohub
app.delete(/photos\/.+$/i, function(req, res, next){
	console.log("Delete " + req.path);
	pickupSRV(metahub_srv, function(record) {
		var myurl = 'http://' + record.name + ':' + record.port + req.path;
		console.log("Proxying request to metahub: " + myurl);
		request({method: 'DELETE', uri: myurl}).on('response', function(response) {
			response.on('end', function () {
				res.end();
			});
			response.on('close', function(){
				res.end();
			});
		}).on('error', function(e) {
			console.log(e.message);
			res.writeHead(500);
			res.end();
		}).end();
	});
});

// Upload a photo
app.post("/photos", function (req, res) {
	var multiparty = require('multiparty');
	var form = new multiparty.Form();
	form.on('file', function(name,file){
		var tempPath = file.path;
		var origName = file.originalFilename;
		var creationDate = Date.now();
		fs.stat(tempPath,function (err, stats) {
			if (!err) {
				creationDate = stats.ctime.getTime();
			}
		});

		var formData = {
			custom_file: {
				value: fs.createReadStream(tempPath),
				options: {
					filename: origName
				}
			}
		}
		pickupSRV(photohub_srv, function(record) {
			myurl = url.parse("http://bokeh-photohub.service.dc1.consul:3000/photos");
			myurl.hostname = record.name;
			myurl.port = record.port;
			console.log("Uploading photo to Photohub: http://"+myurl.hostname+":"+myurl.port);
			request.post({url: myurl, formData: formData}, function (err, resp, body){
				if (err) {
					console.log("Failed to upload image to Photohub!:" + origName);
					res.writeHead(500);
					res.end();
					return;
				}
				console.log("Successfully uploaded photo to Photohub: " + body);
				var r = JSON.parse(body);
				if (!r) {
					console.log("Failed to read Photohub response!");
					res.writeHead(500);
					res.end();
					return;
				} else {
					hash = r['hash'];
					type = r['type'];
					console.log("Hash: " + hash);
					pickupSRV(metahub_srv, function(record) {
						myurl = url.parse("http://bokeh-metahub.service.dc1.consul:5000/photos");
						myurl.hostname = record.name;
						myurl.port = record.port;
						console.log("Uploading metadata to Metahub: http://"+myurl.hostname+":"+myurl.port);
						request({
							url: myurl,
							method: 'POST',
							json: {
								'hash': hash,
								'type': type,
								'url': encodeURIComponent(origName),
								'timestamp': creationDate.toString(),
								'description': origName
							}
						}, function (err2, resp2, body2){
							if (err2) {
								console.log("Failed to upload description to Metahub!:" + origName + "Resp: " + resp2);
								res.writeHead(500);
								res.end();
							}
							b = JSON.stringify(body2)
							console.log("Successfully uploaded description to Metahub: " + b);
							res.write(b)
							res.end();
							return;
						});
					});
				}
			});
		});
	});
	form.parse(req);
});

app.listen(port, host);
console.log('Bokeh-WebUI listening on ' + host  + ':' + port);
<|endoftext|>


<commit_before>var _ = require('lodash');
var async = require('async');
var debug = require('debug')('seguir:comment');

module.exports = function (api) {
  var client = api.client;
  var q = client.queries;

  function createComment (keyspace, user, post, commented, commentdata, next) {
    var newCommentRow = [client.generateId(), user, post, commented, commentdata];
    var newComment = _.zipObject(['comment', 'user', 'post', 'commented', 'commentdata'], newCommentRow);

    async.parallel([
      function getNewComment (cb) {
        api.user.mapUserIdToUser(keyspace, newComment, ['user'], cb);
      },
      function insertComment (cb) {
        debug('insert into comments:', 'comments', newCommentRow);
        client.execute(q(keyspace, 'insertComment'), newCommentRow, {}, cb);
      },
      function insertCommentsTimeline (cb) {
        var newCommentTimelineRow = [post, client.generateTimeId(commented), newComment.comment];
        debug('insert into comments timeline:', 'comments_timeline', newCommentTimelineRow);
        client.execute(q(keyspace, 'insertCommentsTimeline'), newCommentTimelineRow, {}, cb);
      }
    ], function (err, results) {
      if (err) { return next(err); }

      var countUpdate = [1, post.toString()];
      debug('update comment counts:', 'counts', countUpdate);
      client.execute(q(keyspace, 'updateCounter', {TYPE: 'comment'}), countUpdate, {cacheKey: 'count:comment:' + post}, function (err) {
        if (err) { return next(err); }

        api.metrics.increment('comment.add');
        next(null, results[0]);
      });
    });
  }

  function updateComment (keyspace, user, comment, commentdata, next) {
    debug('select comment:', 'comments', [comment]);
    client.get(q(keyspace, 'selectComment'), [comment], {cacheKey: 'comment:' + comment}, function (err, commentRecord) {
      if (err) { return next(err); }
      if (commentRecord.user.toString() !== user.toString()) {
        return next(new Error('Unable to update comment created by user ' + commentRecord.user));
      }

      commentRecord.commentdata = commentdata;
      async.parallel([
        function (cb) {
          api.user.mapUserIdToUser(keyspace, commentRecord, ['user'], cb);
        },
        function (cb) {
          var commentUpdate = [commentdata, comment];
          debug('update comment:', 'comments', commentUpdate);
          client.execute(q(keyspace, 'updateComment'), commentUpdate, {cacheKey: 'comment:' + comment}, cb);
        }
      ], function (err, results) {
        if (err) { return next(err); }

        next(null, results[0]);
      });
    });
  }

  function deleteComment (keyspace, user, comment, next) {
    client.get(q(keyspace, 'selectComment'), [comment], {cacheKey: 'comment:' + comment}, function (err, commentRecord) {
      if (err) { return next(err); }
      if (commentRecord.user.toString() !== user.toString()) {
        return next(new Error('Unable to delete comment created by user ' + commentRecord.user));
      }

      var commentDeletion = [comment];
      debug('delete comment:', 'comments', commentDeletion);
      client.execute(q(keyspace, 'deleteComment'), commentDeletion, {cacheKey: 'comment:' + comment}, function (err) {
        if (err) { return next(err); }

        var countUpdate = [-1, commentRecord.post.toString()];
        debug('update comment counts:', 'counts', countUpdate);
        client.execute(q(keyspace, 'updateCounter', {TYPE: 'comment'}), countUpdate, {cacheKey: 'count:comment:' + commentRecord.post}, next);
      });
    });
  }

  // returns upto latest 5000 (default fetchSize of cassandra-driver) comments for a post
  function getComments (keyspace, liu, post, next) {
    client.get(q(keyspace, 'selectCount', {TYPE: 'comment'}), [post.toString()], {cacheKey: 'count:comment:' + post}, function (err, result) {
      if (err) { return next(err); }

      if (result && +result.count > 0) {
        api.metrics.increment('comments_timeline.list');
        debug('select comments timeline:', 'comments_timeline', [post]);
        client.execute(q(keyspace, 'selectCommentsTimeline'), [post], {}, function (err, timeline, nextPageState) {
          if (err) { return next(err); }

          async.mapSeries(timeline, function (timelineEntry, cb) {
            debug('select comment:', 'comments', [timelineEntry.comment]);
            client.get(q(keyspace, 'selectComment'), [timelineEntry.comment], {cacheKey: 'comment:' + timelineEntry.comment}, function (err, comment) {
              if (err || !comment) { return cb(err); }

              api.like.checkLike(keyspace, liu, comment.comment, function (err, likeStatus) {
                if (err) { return cb(err); }

                comment.userLiked = likeStatus.userLiked;
                comment.likedTotal = likeStatus.likedTotal;
                cb(null, comment);
              });
            });
          }, function (err, comments) {
            if (err) { return next(err); }

            api.user.mapUserIdToUser(keyspace, comments.filter(_.identity), ['user'], function (err, commentsWithUser) {
              if (err) { return next(err); }

              next(null, {
                total: +result.count,
                comments: commentsWithUser,
                nextPageState: nextPageState
              });
            });
          });
        });
      } else {
        next(null, {
          total: 0,
          comments: [],
          nextPageState: null
        });
      }
    });
  }

  return {
    createComment: createComment,
    getComments: getComments,
    updateComment: updateComment,
    deleteComment: deleteComment
  };
};
<commit_msg>return updated/new comment along with userLiked and likedTotal field<commit_after>var _ = require('lodash');
var async = require('async');
var debug = require('debug')('seguir:comment');

module.exports = function (api) {
  var client = api.client;
  var q = client.queries;

  function getComment (keyspace, liu, comment, next) {
    debug('select comment:', 'comments', [comment]);
    client.get(q(keyspace, 'selectComment'), [comment], {cacheKey: 'comment:' + comment}, function (err, result) {
      if (err || !result) { return next(err); }

      api.like.checkLike(keyspace, liu, comment, function (err, likeStatus) {
        if (err) { return next(err); }

        result.userLiked = likeStatus.userLiked;
        result.likedTotal = likeStatus.likedTotal;
        next(null, result);
      });
    });
  }

  function createComment (keyspace, user, post, commented, commentdata, next) {
    var comment = client.generateId();
    var newCommentRow = [comment, user, post, commented, commentdata];

    async.parallel([
      function insertComment (cb) {
        debug('insert into comments:', 'comments', newCommentRow);
        client.execute(q(keyspace, 'insertComment'), newCommentRow, {}, cb);
      },
      function insertCommentsTimeline (cb) {
        var newCommentTimelineRow = [post, client.generateTimeId(commented), comment];
        debug('insert into comments timeline:', 'comments_timeline', newCommentTimelineRow);
        client.execute(q(keyspace, 'insertCommentsTimeline'), newCommentTimelineRow, {}, cb);
      }
    ], function (err, results) {
      if (err) { return next(err); }

      var countUpdate = [1, post.toString()];
      debug('update comment counts:', 'counts', countUpdate);
      client.execute(q(keyspace, 'updateCounter', {TYPE: 'comment'}), countUpdate, {cacheKey: 'count:comment:' + post}, function (err) {
        if (err) { return next(err); }

        api.metrics.increment('comment.add');
        getComment(keyspace, user, comment, function (err, newComment) {
          if (err) { return next(err); }
          api.user.mapUserIdToUser(keyspace, newComment, ['user'], next);
        });
      });
    });
  }

  function updateComment (keyspace, user, comment, commentdata, next) {
    debug('select comment:', 'comments', [comment]);
    client.get(q(keyspace, 'selectComment'), [comment], {cacheKey: 'comment:' + comment}, function (err, commentRecord) {
      if (err) { return next(err); }
      if (commentRecord.user.toString() !== user.toString()) {
        return next(new Error('Unable to update comment created by user ' + commentRecord.user));
      }

      var commentUpdate = [commentdata, comment];
      debug('update comment:', 'comments', commentUpdate);
      client.execute(q(keyspace, 'updateComment'), commentUpdate, {cacheKey: 'comment:' + comment}, function (err) {
        if (err) { return next(err); }

        getComment(keyspace, user, comment, function (err, updatedComment) {
          if (err) { return next(err); }
          api.user.mapUserIdToUser(keyspace, updatedComment, ['user'], next);
        });
      });
    });
  }

  function deleteComment (keyspace, user, comment, next) {
    client.get(q(keyspace, 'selectComment'), [comment], {cacheKey: 'comment:' + comment}, function (err, commentRecord) {
      if (err) { return next(err); }
      if (commentRecord.user.toString() !== user.toString()) {
        return next(new Error('Unable to delete comment created by user ' + commentRecord.user));
      }

      var commentDeletion = [comment];
      debug('delete comment:', 'comments', commentDeletion);
      client.execute(q(keyspace, 'deleteComment'), commentDeletion, {cacheKey: 'comment:' + comment}, function (err) {
        if (err) { return next(err); }

        var countUpdate = [-1, commentRecord.post.toString()];
        debug('update comment counts:', 'counts', countUpdate);
        client.execute(q(keyspace, 'updateCounter', {TYPE: 'comment'}), countUpdate, {cacheKey: 'count:comment:' + commentRecord.post}, next);
      });
    });
  }

  // returns upto latest 5000 (default fetchSize of cassandra-driver) comments for a post
  function getComments (keyspace, liu, post, next) {
    client.get(q(keyspace, 'selectCount', {TYPE: 'comment'}), [post.toString()], {cacheKey: 'count:comment:' + post}, function (err, result) {
      if (err) { return next(err); }

      if (result && +result.count > 0) {
        api.metrics.increment('comments_timeline.list');
        debug('select comments timeline:', 'comments_timeline', [post]);
        client.execute(q(keyspace, 'selectCommentsTimeline'), [post], {}, function (err, timeline, nextPageState) {
          if (err) { return next(err); }

          async.mapSeries(timeline, function (timelineEntry, cb) {
            getComment(keyspace, liu, timelineEntry.comment, cb);
          }, function (err, comments) {
            if (err) { return next(err); }

            api.user.mapUserIdToUser(keyspace, comments.filter(_.identity), ['user'], function (err, commentsWithUser) {
              if (err) { return next(err); }

              next(null, {
                total: +result.count,
                comments: commentsWithUser,
                nextPageState: nextPageState
              });
            });
          });
        });
      } else {
        next(null, {
          total: 0,
          comments: [],
          nextPageState: null
        });
      }
    });
  }

  return {
    createComment: createComment,
    getComments: getComments,
    updateComment: updateComment,
    deleteComment: deleteComment
  };
};
<|endoftext|>


<commit_before>/* JavaScript for DbFrontend List page
 * Script allows inline editing of the table contents
 * For any DbFrontend page using this javascript, check the following things to make if work properly:
 *  1. That the SiteDefs::OBJECT_TO_SCRIPT values for the page is 'Modal'
 *  2. Javascript classes are being added to the html elements properly
 *  3. The page which displays all the records return a single record if its 'id' is specified in GET params
 */

Ensembl.DbFrontendList = {

  // Row Class
  Row: Base.extend({
    constructor: function(row, panel) {
      this.id = (row.className.match(/_dbf_row_([^\s]+)/) || []).pop();
      this.panel = panel;
      for (var i in this.panel.data) {
        if (this.panel.data[i]) {
          new Ensembl.DbFrontendList.Cell(row.cells[i], this.panel.data[i], this);
        } else if (row.cells[i].className.match(/_dbf_row_handle/)) {
          this.handleCell = new Ensembl.DbFrontendList.HandleCell(row.cells[i], this);
        }
      }
    }
  }),

  // Cell Class - extended from js base class for DbFrontend
  Cell: Ensembl.DbFrontend.extend({

    // @override
    constructor: function(cell, data, row) {
      var self  = this;
      this.url  = data.url;
      this.name = data.name;
      this.row  = row;
      this.el   = $('
').html(cell.innerHTML).appendTo($(cell).empty()).append($('').click(function() {
        self.buttonClick(this);
      }));
      $('._dbf_list_view', this.el).click(function(e) {
        e.preventDefault();
        var handle = self.row.handleCell;
        handle.createForm();
        handle.makeRequest(this, handle.form, {
          url:      this.href,
          data:     {},
          success:  function(json) {
            $('._dbf_button', handle.getResponseNode(json)
              .children().wrapAll('
').end()
              .appendTo(handle.form.empty())).first().before('Close');
            ;
          }
        });
      });
    },

    // @override
    buttonClick: function(button) {
      this.createForm();
      this.makeRequest(button, this.form, {
        url:      this.url,
        data:     {id: this.row.id, _list: 1},
        success:  function(json) {
          var form   = $('form', this.getResponseNode(json));
          var field  = $('[name=' + this.name.replace('.', '\\.') + ']', form).first().parents('div').first().removeAttr('class');
          var button = $('[type=submit]', form).first().parents('div').first().attr('class', 'dbf-list-buttons');
          $('input', this.form.empty().append(form.empty().append(field, $('
').append(button)))).first().focus();
          this.initForm();
        }
      });
      this.el.hide();
    },

    // @override
    showLoading: function(target, flag) {
      if (flag !== false) {
        target.empty().html('Loading…').show();
      }
    },

    // @override
    cancelButtonClick: function(button) {
      this.el.show('fast');
      this.form.hide('fast', function() {
        this.innerHTML = '';
      });
    },

    // @override
    formSubmit: function(form) {
      this.makeRequest(form, this.form, {
        data:    $(form).serialize() + '&id=' + this.row.id,
        success: function(json) {
          if (json.redirectURL) {
            var url = json.redirectURL;
            this.success = !url.match(/Problem/);
            if (this.success) {
              url = window.location.href.split('#')[0];
              url = url + (url.match(/\?/) ? '&' : '?') + 'id=' + this.row.id;
            }
            this.makeRequest({}, this.form, {
              async: false,
              url: url,
              success: function(json) {
                if (this.success) {
                  var resTr = $('._dbf_row_' + this.row.id, this.getResponseNode(json));
                  if (resTr.length) {
                    this.row.panel.updateRow(resTr.children().map(function(i, cell) {return cell.innerHTML;}), this.el.parents('tr')[0]);
                  }
                } else {
                  this.showError(this.form, this.getResponseNode(json).children('p').first().html(), this.el);
                }
                this.afterResponse(this.success);
              }
            });
          } else {
            this.showError(this.form, '', this.el);
          }
        }
      });
    }
  }),

  // HandleCell Class - cell that contains edit, delete and duplicate link
  HandleCell: Ensembl.DbFrontendRow.extend({

    // @override
    constructor: function(cell, row) {
      this.row = row;
      this.base(cell);
    },

    // @override
    createForm: function() {
      if (!this.form) {
        var tr = this.el[0].parentNode;
        var bg = (' ' + tr.className + ' ').match(/\s{1}bg(1|2)\s{1}/)[0];
        this.form = $('
').appendTo($('').appendTo($('
').insertAfter(tr).attr('class', 'dbf-list-form ' + bg)).attr('colspan', tr.cells.length));
      }
    },

    // @override
    buttonClick: function(button) {
      if (this.form) {
        this.form.parents('tr').first().remove();
        this.form = false;
      }
      this.base(button);
    },

    // @override
    cancelButtonClick: function(button) {
      var previous = $(button).parents('._dbf_form_wrap').prev()[0];
      if (previous) {
        $(previous).show().next().remove();
      } else {
        this.form.children().first().slideUp(function() {
          $(this).parents('tr').first().remove();
        });
        this.form = false;
      }
    },

    // @override
    deleteButtonClick: function(button) {
      this.makeRequest(button, this.form, {
        success: function(json) {
          if (json.redirectURL) {
            if (json.redirectURL.match(/Problem/)) {
              this.makeRequest({}, this.form, {
                async: false,
                url: json.redirectURL,
                success: function(json) {
                  this.form.html(this.getResponseNode(json).html());
                }
              });
            } else {
              this.form.parents('tr').first().remove();
              this.row.panel.deleteRow(this.el.parents('tr')[0]);
              for (var i in this) {
                delete this[i];
              }
            }
          }
        }
      });
    },

    // @override
    formSubmit: function(form) {
      this.action = form.className.match(/_dbf_add/) ? 'add' : 'edit';
      this.makeRequest(form, this.form.children(':last'), {
        success: function(json) {
          if (json.redirectURL) {
            var url = json.redirectURL;
            this.success = !url.match(/Problem/);
            if (this.success) {
              var id = (url.match(/(\?|&|;)id\=([0-9]+)/) || []).pop() || 0;
              if (id) {
                url = window.location.href.split('#')[0];
                url = url + (url.match(/\?/) ? '&' : '?') + 'id=' + id;
              }
            }
            this.makeRequest({}, this.form.children(':last'), {
              async   : false,
              url     : url,
              success : function(json) {
                if (this.success) {
                  var resTr = $('._dbf_row_' + id, this.getResponseNode(json));
                  if (resTr.length) {
                    this.row.panel[this.action === 'add' ? 'addRow' : 'updateRow'](resTr.children().map(function(i, cell) {return cell.innerHTML;}), this.el.parents('tr')[0], resTr[0].className);
                  }
                  this.afterResponse(this.success);
                  this.form.parents('tr').first().remove();
                } else {
                  this.showError(this.form, this.getResponseNode(json).children('p').first().html());
                }
              }
            });
          }
        }
      });
    },

    initDataStructure: function() {} //ignore datastructure in list view
  })
};

// DbFrontendList Panel class
Ensembl.Panel.DbFrontendList = Ensembl.Panel.extend({

  init: function() {
    var self = this;
    this.base();
    this.data = [];

    this.el.find('table._dbf_list').each(function() {
      var list = $(this);
      list.find('thead th').each(function() {
        var inps = $('input[type=hidden]', this);
        self.data.push(inps.length ? {url: inps[0].value, name: inps[0].name, title: $(this).text(), type: inps[0].className} : false);
      });
      list.find('tbody tr').each(function () {
        self.initRow(this);
      });
      return false;
    });
  },

  // constructs a DbFrontendList.Row object from an already existing TR element
  initRow: function(row) {
    new Ensembl.DbFrontendList.Row(row, this);
  },

  // Adds a new TR element (after a given reference TR element) and initiates it as a DbFrontendList.Row object taking in account the DataTables settings
  addRow: function(data, refRow, classes) {
    var table = $(refRow).parents('table').first();
    var row   = false;
    if (table.hasClass('data_table')) {
      table = table.dataTable();
      row = table.fnGetNodes(table.fnAddData(data));
      $(row).addClass(classes.replace(/bg(1|2)/, ''));
    } else {
      row = $('
').attr('class', classes).append($.map(data, function(html) { return $('').html(html)[0]; })).insertAfter(refRow)[0];
      this.setRowBGs();
    }
    if (row) {
      $(row.cells).each(function(i) {this.className = refRow.cells[i].className});
      new Ensembl.DbFrontendList.Row(row, this);
      this.highlightRow(row);
    }
  },

  // Update an already existing TR element with given data and re-initiates it as DbFrontendList.Row taking in account the DataTables settings
  updateRow: function(data, row) {
    $(row.cells).each(function(i) {this.innerHTML = data[i]});
    var table = $(row).parents('table').first();
    if (table.hasClass('data_table')) {
      table.dataTable().fnUpdate($.map(row.cells, function(cell) {return cell.innerHTML; }), row);
    }
    new Ensembl.DbFrontendList.Row(row, this);
    this.highlightRow(row);
  },

  // Removes a TR element from the table, taking in account the DataTables settings
  deleteRow: function(row) {
    var table = $(row).parents('table').first();
    if (table.hasClass('data_table')) {
      table.dataTable().fnDeleteRow(row);
    } else {
      $(row).remove();
      this.setRowBGs();
    }
  },

  // Maintains the even-odd backgroundColor order of the TR elements
  setRowBGs: function() {
    var bg = ['bg1', 'bg2'];
    this.el.find('table._dbf_list').each(function() {
      $('tbody tr', this).each(function() {
        if (this.className.match(/_dbf_row_/)) {
          $(this).removeClass('bg1 bg2').addClass(bg[0]);
          bg = bg.reverse();
        }
      });
    });
  },

  // Highlights a row for 1 second
  highlightRow: function(row) {
    $(row).effect('highlight', {'color': '#ddddff'}, 1000);
  }
});<commit_msg>initialise the form to bind event handlers<commit_after>/* JavaScript for DbFrontend List page
 * Script allows inline editing of the table contents
 * For any DbFrontend page using this javascript, check the following things to make if work properly:
 *  1. That the SiteDefs::OBJECT_TO_SCRIPT values for the page is 'Modal'
 *  2. Javascript classes are being added to the html elements properly
 *  3. The page which displays all the records return a single record if its 'id' is specified in GET params
 */

Ensembl.DbFrontendList = {

  // Row Class
  Row: Base.extend({
    constructor: function(row, panel) {
      this.id = (row.className.match(/_dbf_row_([^\s]+)/) || []).pop();
      this.panel = panel;
      for (var i in this.panel.data) {
        if (this.panel.data[i]) {
          new Ensembl.DbFrontendList.Cell(row.cells[i], this.panel.data[i], this);
        } else if (row.cells[i].className.match(/_dbf_row_handle/)) {
          this.handleCell = new Ensembl.DbFrontendList.HandleCell(row.cells[i], this);
        }
      }
    }
  }),

  // Cell Class - extended from js base class for DbFrontend
  Cell: Ensembl.DbFrontend.extend({

    // @override
    constructor: function(cell, data, row) {
      var self  = this;
      this.url  = data.url;
      this.name = data.name;
      this.row  = row;
      this.el   = $('
').html(cell.innerHTML).appendTo($(cell).empty()).append($('').click(function() {
        self.buttonClick(this);
      }));
      $('._dbf_list_view', this.el).click(function(e) {
        e.preventDefault();
        var handle = self.row.handleCell;
        handle.createForm();
        handle.makeRequest(this, handle.form, {
          url:      this.href,
          data:     {},
          success:  function(json) {
            $('._dbf_button', handle.getResponseNode(json)
              .children().wrapAll('
').end()
              .appendTo(handle.form.empty())).first().before('Close');
            ;
            this.initForm();
          }
        });
      });
    },

    // @override
    buttonClick: function(button) {
      this.createForm();
      this.makeRequest(button, this.form, {
        url:      this.url,
        data:     {id: this.row.id, _list: 1},
        success:  function(json) {
          var form   = $('form', this.getResponseNode(json));
          var field  = $('[name=' + this.name.replace('.', '\\.') + ']', form).first().parents('div').first().removeAttr('class');
          var button = $('[type=submit]', form).first().parents('div').first().attr('class', 'dbf-list-buttons');
          $('input', this.form.empty().append(form.empty().append(field, $('
').append(button)))).first().focus();
          this.initForm();
        }
      });
      this.el.hide();
    },

    // @override
    showLoading: function(target, flag) {
      if (flag !== false) {
        target.empty().html('Loading…').show();
      }
    },

    // @override
    cancelButtonClick: function(button) {
      this.el.show('fast');
      this.form.hide('fast', function() {
        this.innerHTML = '';
      });
    },

    // @override
    formSubmit: function(form) {
      this.makeRequest(form, this.form, {
        data:    $(form).serialize() + '&id=' + this.row.id,
        success: function(json) {
          if (json.redirectURL) {
            var url = json.redirectURL;
            this.success = !url.match(/Problem/);
            if (this.success) {
              url = window.location.href.split('#')[0];
              url = url + (url.match(/\?/) ? '&' : '?') + 'id=' + this.row.id;
            }
            this.makeRequest({}, this.form, {
              async: false,
              url: url,
              success: function(json) {
                if (this.success) {
                  var resTr = $('._dbf_row_' + this.row.id, this.getResponseNode(json));
                  if (resTr.length) {
                    this.row.panel.updateRow(resTr.children().map(function(i, cell) {return cell.innerHTML;}), this.el.parents('tr')[0]);
                  }
                } else {
                  this.showError(this.form, this.getResponseNode(json).children('p').first().html(), this.el);
                }
                this.afterResponse(this.success);
              }
            });
          } else {
            this.showError(this.form, '', this.el);
          }
        }
      });
    }
  }),

  // HandleCell Class - cell that contains edit, delete and duplicate link
  HandleCell: Ensembl.DbFrontendRow.extend({

    // @override
    constructor: function(cell, row) {
      this.row = row;
      this.base(cell);
    },

    // @override
    createForm: function() {
      if (!this.form) {
        var tr = this.el[0].parentNode;
        var bg = (' ' + tr.className + ' ').match(/\s{1}bg(1|2)\s{1}/)[0];
        this.form = $('
').appendTo($('').appendTo($('
').insertAfter(tr).attr('class', 'dbf-list-form ' + bg)).attr('colspan', tr.cells.length));
      }
    },

    // @override
    buttonClick: function(button) {
      if (this.form) {
        this.form.parents('tr').first().remove();
        this.form = false;
      }
      this.base(button);
    },

    // @override
    cancelButtonClick: function(button) {
      var previous = $(button).parents('._dbf_form_wrap').prev()[0];
      if (previous) {
        $(previous).show().next().remove();
      } else {
        this.form.children().first().slideUp(function() {
          $(this).parents('tr').first().remove();
        });
        this.form = false;
      }
    },

    // @override
    deleteButtonClick: function(button) {
      this.makeRequest(button, this.form, {
        success: function(json) {
          if (json.redirectURL) {
            if (json.redirectURL.match(/Problem/)) {
              this.makeRequest({}, this.form, {
                async: false,
                url: json.redirectURL,
                success: function(json) {
                  this.form.html(this.getResponseNode(json).html());
                }
              });
            } else {
              this.form.parents('tr').first().remove();
              this.row.panel.deleteRow(this.el.parents('tr')[0]);
              for (var i in this) {
                delete this[i];
              }
            }
          }
        }
      });
    },

    // @override
    formSubmit: function(form) {
      this.action = form.className.match(/_dbf_add/) ? 'add' : 'edit';
      this.makeRequest(form, this.form.children(':last'), {
        success: function(json) {
          if (json.redirectURL) {
            var url = json.redirectURL;
            this.success = !url.match(/Problem/);
            if (this.success) {
              var id = (url.match(/(\?|&|;)id\=([0-9]+)/) || []).pop() || 0;
              if (id) {
                url = window.location.href.split('#')[0];
                url = url + (url.match(/\?/) ? '&' : '?') + 'id=' + id;
              }
            }
            this.makeRequest({}, this.form.children(':last'), {
              async   : false,
              url     : url,
              success : function(json) {
                if (this.success) {
                  var resTr = $('._dbf_row_' + id, this.getResponseNode(json));
                  if (resTr.length) {
                    this.row.panel[this.action === 'add' ? 'addRow' : 'updateRow'](resTr.children().map(function(i, cell) {return cell.innerHTML;}), this.el.parents('tr')[0], resTr[0].className);
                  }
                  this.afterResponse(this.success);
                  this.form.parents('tr').first().remove();
                } else {
                  this.showError(this.form, this.getResponseNode(json).children('p').first().html());
                }
              }
            });
          }
        }
      });
    },

    initDataStructure: function() {} //ignore datastructure in list view
  })
};

// DbFrontendList Panel class
Ensembl.Panel.DbFrontendList = Ensembl.Panel.extend({

  init: function() {
    var self = this;
    this.base();
    this.data = [];

    this.el.find('table._dbf_list').each(function() {
      var list = $(this);
      list.find('thead th').each(function() {
        var inps = $('input[type=hidden]', this);
        self.data.push(inps.length ? {url: inps[0].value, name: inps[0].name, title: $(this).text(), type: inps[0].className} : false);
      });
      list.find('tbody tr').each(function () {
        self.initRow(this);
      });
      return false;
    });
  },

  // constructs a DbFrontendList.Row object from an already existing TR element
  initRow: function(row) {
    new Ensembl.DbFrontendList.Row(row, this);
  },

  // Adds a new TR element (after a given reference TR element) and initiates it as a DbFrontendList.Row object taking in account the DataTables settings
  addRow: function(data, refRow, classes) {
    var table = $(refRow).parents('table').first();
    var row   = false;
    if (table.hasClass('data_table')) {
      table = table.dataTable();
      row = table.fnGetNodes(table.fnAddData(data));
      $(row).addClass(classes.replace(/bg(1|2)/, ''));
    } else {
      row = $('
').attr('class', classes).append($.map(data, function(html) { return $('').html(html)[0]; })).insertAfter(refRow)[0];
      this.setRowBGs();
    }
    if (row) {
      $(row.cells).each(function(i) {this.className = refRow.cells[i].className});
      new Ensembl.DbFrontendList.Row(row, this);
      this.highlightRow(row);
    }
  },

  // Update an already existing TR element with given data and re-initiates it as DbFrontendList.Row taking in account the DataTables settings
  updateRow: function(data, row) {
    $(row.cells).each(function(i) {this.innerHTML = data[i]});
    var table = $(row).parents('table').first();
    if (table.hasClass('data_table')) {
      table.dataTable().fnUpdate($.map(row.cells, function(cell) {return cell.innerHTML; }), row);
    }
    new Ensembl.DbFrontendList.Row(row, this);
    this.highlightRow(row);
  },

  // Removes a TR element from the table, taking in account the DataTables settings
  deleteRow: function(row) {
    var table = $(row).parents('table').first();
    if (table.hasClass('data_table')) {
      table.dataTable().fnDeleteRow(row);
    } else {
      $(row).remove();
      this.setRowBGs();
    }
  },

  // Maintains the even-odd backgroundColor order of the TR elements
  setRowBGs: function() {
    var bg = ['bg1', 'bg2'];
    this.el.find('table._dbf_list').each(function() {
      $('tbody tr', this).each(function() {
        if (this.className.match(/_dbf_row_/)) {
          $(this).removeClass('bg1 bg2').addClass(bg[0]);
          bg = bg.reverse();
        }
      });
    });
  },

  // Highlights a row for 1 second
  highlightRow: function(row) {
    $(row).effect('highlight', {'color': '#ddddff'}, 1000);
  }
});<|endoftext|>


<commit_before>CLASS({
  package: 'foam.core.dao',
  name: 'StripPropertiesDAO',
  extendsModel: 'ProxyDAO',
  properties: [
    {
      model_: 'StringArrayProperty',
      name: 'propertyNames'
    }
  ],
  methods: {
    process_: function(obj) {
      obj = obj.clone();
      for ( var i = 0 ; i < this.propertyNames.length ; i++ ) {
        obj.clearProperty(this.propertyNames[i]);
      }
      return obj;
    },
    select: function(sink, options) {
      sink = sink || [].sink;
      var self = this;
      var future = afuture();

      this.SUPER({
        put: function(obj, _, fc) {
          sink.put && sink.put(self.process_(obj), null, fc);
        },
        error: function() {
          sink.error && sink.error.apply(sink, arguments);
        },
        eof: function() {
          sink.eof && sink.eof();
        }
      }, options)(function() {
        future.set(sink);
      });

      return future.get;
    }
  }
});
<commit_msg>Add missing license headers<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

CLASS({
  package: 'foam.core.dao',
  name: 'StripPropertiesDAO',
  extendsModel: 'ProxyDAO',
  properties: [
    {
      model_: 'StringArrayProperty',
      name: 'propertyNames'
    }
  ],
  methods: {
    process_: function(obj) {
      obj = obj.clone();
      for ( var i = 0 ; i < this.propertyNames.length ; i++ ) {
        obj.clearProperty(this.propertyNames[i]);
      }
      return obj;
    },
    select: function(sink, options) {
      sink = sink || [].sink;
      var self = this;
      var future = afuture();

      this.SUPER({
        put: function(obj, _, fc) {
          sink.put && sink.put(self.process_(obj), null, fc);
        },
        error: function() {
          sink.error && sink.error.apply(sink, arguments);
        },
        eof: function() {
          sink.eof && sink.eof();
        }
      }, options)(function() {
        future.set(sink);
      });

      return future.get;
    }
  }
});
<|endoftext|>


<commit_before>/**
 * Created by Awesome on 2/6/2016.
 */

// use strict
'use strict';

// require local dependencies
const bar         = require ('nanobar');
const riot        = require ('riot');
const history     = require ('history').createBrowserHistory;
const riotcontrol = require ('riotcontrol');

// get local dependencies
const tags  = require ('app/cache/tags.min.js');
const store = require ('./store');

/**
 * build bootstrap class
 */
class router {
  /**
   * construct bootstrap class
   */
  constructor () {
    // set mount
    this._bar   = false;
    this._mount = false;
    this._store = store;

    // add store to riotcontrol
    riotcontrol.addStore (this._store);

    // set store values
    for (var key in window.eden) {
      // set to store
      this._store.set (key, window.eden[key]);
    }

    // create history
    this.history = history ();

    // bind methods
    this.go         = this.go.bind (this);
    this.load       = this.load.bind (this);
    this.submit     = this.submit.bind (this);
    this.initialize = this.initialize.bind (this);

    // bind private methods
    this._tags   = this._tags.bind (this);
    this._form   = this._form.bind (this);
    this._route  = this._route.bind (this);
    this._layout = this._layout.bind (this);

    // run on document ready
    jQuery (document).ready (() => {
      // push state
      this.history.replace ({
        'state'    : {
          'page'  : this._store.get ('page'),
          'state' : this._store.get ('state'),
          'mount' : this._store.get ('mount')
        },
        'pathname' : this._store.get ('page').url
      });

      // initialize
      this.initialize ();
    });
  }

  /**
   * initialize functionality
   */
  initialize () {
    // set that
    let that = this;

    // mount bar
    this._bar = new bar ();

    // mount riot tag
    this._mount = riot.mount (document.querySelector ('body').children[0], window.eden)[0];

    // listen to go
    this._store.on ('navigate', this.go);

    // run on document ready
    jQuery (document).on ('click', 'a[href^="/"]', function (e) {
      // check link
      if (that._route (jQuery (this).attr ('href'))) {
        // prevent default
        e.preventDefault  ();
        e.stopPropagation ();
      }
    });

    // run on form submit
    jQuery (document).on ('submit', 'form', function (e) {
      // check form
      if (that._form (jQuery (this))) {
        // prevent default
        e.preventDefault  ();
        e.stopPropagation ();
      }
    });

    // on state change
    this.history.listen ((location, action) => {
      // check state
      if (location.state && Object.keys (location.state).length > 0) {
        // set progress go
        this._bar.go (100);

        // set title
        document.title = (location.state.page.title ? location.state.page.title + ' | ' : '') + this._store.get ('config').title;

        // trigger
        for (var key in location.state) {
          // set data
          this._store.set (key, location.state[key]);
        }

        // check layout
        this._layout (location.state);

        // check tags
        this._tags (location.state.page);

        // do route
        // we do this as a seperate trigger to prevent double rendering
        // the riot layout (seperate mount and state events still fired)
        this._store.trigger ('route', {
          'mount' : location.state.mount,
          'state' : location.state.state
        });
      }
    });
  }

  /**
   * loads url
   *
   * @param  {String} url
   */
  go (url) {
    // progress bar
    this._bar.go (50);

    // create location
    this.history.push ({
      'state'    : {},
      'pathname' : url
    });

    // check if ajax
    if (url.indexOf ('/ajx') !== 0) {
      url = '/ajx' + url;
    }

    // load json from url
    jQuery.getJSON (url, (data, status, request) => {
      // load data
      this.load (data);
    }).fail (data => {
      // check data
      if (!data || !data.responseJSON) {
        // send error
        eden.alert.alert ('error', 'failed to load next page');

        // go -1
        this.history.go (-1);
      }

      // load failure
      this.load (data.responseJSON);
    });
  }

  /**
   * load data
   *
   * @param {Object} data
   */
  load (data) {
    // check if redirect
    if (data.redirect) return this._route (data.redirect);

    // push state
    this.history.replace ({
      'state'    : data,
      'pathname' : data.mount.url
    });
  }

  /**
   * submits form via ajax
   *
   * @param {HTMLElement} form
   */
  submit (form) {
    // get url
    let url = form.attr ('action') || window.location.href.split (this._store.get ('config').domain)[1];

    // create location
    this.history.push ({
      'state'    : {},
      'pathname' : url
    });

    // check if ajax
    if (url.indexOf ('/ajx') !== 0) {
      url = '/ajx' + url;
    }

    // submit form via ajax
    form.ajaxSubmit ({
      'url'            : url,
      'error'          : data => {
        // load
        this.load (data.responseJSON);
      },
      'success'        : this.load,
      'dataType'       : 'json',
      'uploadProgress' : (event, position, total, percent) => {
        // update bar position
        this._bar.go (percent < 90 ? percent : 90);
      }
    });
  }

  /**
   * replace head tags from state
   *
   * @param {Object} state
   *
   * @private
   */
  _tags (page) {
    // check head
    if (!page.head) return;

    // set header information
    let found   = false;
    let newHead = jQuery (page.head);
    let nowHead = jQuery ('[data-eden="head"]').nextAll ('*');

    // loop all elements
    for (var a = 0; a < nowHead.length; a++) {
      // check now head
      found = false;

      // loop now head
      for (var b = 0; b < newHead.length; b++) {
        // check if found
        if (nowHead[a].outerHTML == newHead[b].outerHTML) {
          // set found
          found = true;
        }
      }

      // remove if not found
      if (!found) nowHead[a].remove ();
    }

    // set html
    let nowHTML = jQuery ('[data-eden="head"]').nextAll ('*');

    // loop all new elements
    for (var c = 0; c < newHead.length; c++) {
      // check now head
      found = false;

      // loop now head
      for (var d = 0; d < nowHead.length; d++) {
        // check if found
        if (newHead[c].outerHTML == nowHead[d].outerHTML) {
          // set found
          found = true;
        }
      }

      // remove if not found
      if (!found) jQuery ('head').append (newHead[c]);
    }
  }

  /**
   * submits form via ajax
   *
   * @param {HTMLElement} form
   *
   * @private
   * @return {Boolean}
   */
  _form (form) {
    // get form url
    let url = form.attr ('action') || window.location.href.split (this._store.get ('config').domain)[1];

    // check if actual redirect
    if (url.indexOf ('//') > -1 || url.indexOf ('#') === 0) {
      // return
      return false;
    }

    // submit form
    this.submit (form);

    // return true
    return true;
  }

  /**
   * redirects to url
   *
   * @param  {String} url
   *
   * @private
   * @return {Boolean}
   */
  _route (url) {
    // check if actual redirect
    if (url.indexOf ('//') > -1 || url.indexOf ('#') === 0) return false;

    // load next redirect
    this.go (url);

    // return true
    return true;
  }

  /**
   * checks for correct layout
   *
   * @param  {Object} state
   *
   * @private
   * @return {Boolean}
   */
  _layout (state) {
    // set layout variable
    let layout = (state.mount.layout || 'main-layout');

    // get current layout
    let current = document.querySelector ('body').children[0];

    // check if layout needs replacing
    if (current.tagName.toLowerCase () === layout.toLowerCase ()) {
      return false;
    }

    // unmount tag
    this._mount.unmount (true);

    // replace with
    current.replaceWith (document.createElement (layout));

    // mount new tag
    this._mount = riot.mount (document.querySelector ('body').children[0], state)[0];

    // return true
    return true;
  }
}

/**
 * export new bootstrap function
 *
 * @return {bootstrap}
 */
exports = module.exports = window.eden.router = new router ();
<commit_msg>scroll to top after page change<commit_after>/**
 * Created by Awesome on 2/6/2016.
 */

// use strict
'use strict';

// require local dependencies
const bar         = require ('nanobar');
const riot        = require ('riot');
const history     = require ('history').createBrowserHistory;
const riotcontrol = require ('riotcontrol');

// get local dependencies
const store = require ('./store');

// require tags
require ('app/cache/tags.min.js');

/**
 * build bootstrap class
 */
class router {
  /**
   * construct bootstrap class
   */
  constructor () {
    // set mount
    this._bar   = false;
    this._mount = false;
    this._store = store;

    // add store to riotcontrol
    riotcontrol.addStore (this._store);

    // set store values
    for (var key in window.eden) {
      // set to store
      this._store.set (key, window.eden[key]);
    }

    // create history
    this.history = history ();

    // bind methods
    this.go         = this.go.bind (this);
    this.load       = this.load.bind (this);
    this.submit     = this.submit.bind (this);
    this.initialize = this.initialize.bind (this);

    // bind private methods
    this._tags   = this._tags.bind (this);
    this._form   = this._form.bind (this);
    this._route  = this._route.bind (this);
    this._layout = this._layout.bind (this);

    // run on document ready
    jQuery (document).ready (() => {
      // push state
      this.history.replace ({
        'state'    : {
          'page'  : this._store.get ('page'),
          'state' : this._store.get ('state'),
          'mount' : this._store.get ('mount')
        },
        'pathname' : this._store.get ('page').url
      });

      // initialize
      this.initialize ();
    });
  }

  /**
   * initialize functionality
   */
  initialize () {
    // set that
    let that = this;

    // mount bar
    this._bar = new bar ();

    // mount riot tag
    this._mount = riot.mount (document.querySelector ('body').children[0], window.eden)[0];

    // listen to go
    this._store.on ('navigate', this.go);

    // run on document ready
    jQuery (document).on ('click', 'a[href^="/"]', function (e) {
      // check link
      if (that._route (jQuery (this).attr ('href'))) {
        // prevent default
        e.preventDefault  ();
        e.stopPropagation ();
      }
    });

    // run on form submit
    jQuery (document).on ('submit', 'form', function (e) {
      // check form
      if (that._form (jQuery (this))) {
        // prevent default
        e.preventDefault  ();
        e.stopPropagation ();
      }
    });

    // on state change
    this.history.listen ((location) => {
      // check state
      if (location.state && Object.keys (location.state).length > 0) {
        // scroll to top
        jQuery ('body').animate ({
          'scrollTop' : 0
        }, 500);

        // set progress go
        this._bar.go (100);

        // set title
        document.title = (location.state.page.title ? location.state.page.title + ' | ' : '') + this._store.get ('config').title;

        // trigger
        for (var key in location.state) {
          // set data
          this._store.set (key, location.state[key]);
        }

        // check layout
        this._layout (location.state);

        // check tags
        this._tags (location.state.page);

        // do route
        // we do this as a seperate trigger to prevent double rendering
        // the riot layout (seperate mount and state events still fired)
        this._store.trigger ('route', {
          'mount' : location.state.mount,
          'state' : location.state.state
        });
      }
    });
  }

  /**
   * loads url
   *
   * @param  {String} url
   */
  go (url) {
    // progress bar
    this._bar.go (50);

    // create location
    this.history.push ({
      'state'    : {},
      'pathname' : url
    });

    // check if ajax
    if (url.indexOf ('/ajx') !== 0) {
      url = '/ajx' + url;
    }

    // load json from url
    jQuery.getJSON (url, (data) => {
      // load data
      this.load (data);
    }).fail (data => {
      // check data
      if (!data || !data.responseJSON) {
        // send error
        eden.alert.alert ('error', 'failed to load next page');

        // go -1
        this.history.go (-1);
      }

      // load failure
      this.load (data.responseJSON);
    });
  }

  /**
   * load data
   *
   * @param {Object} data
   */
  load (data) {
    // check if redirect
    if (data.redirect) return this._route (data.redirect);

    // push state
    this.history.replace ({
      'state'    : data,
      'pathname' : data.mount.url
    });
  }

  /**
   * submits form via ajax
   *
   * @param {HTMLElement} form
   */
  submit (form) {
    // get url
    let url = form.attr ('action') || window.location.href.split (this._store.get ('config').domain)[1];

    // create location
    this.history.push ({
      'state'    : {},
      'pathname' : url
    });

    // check if ajax
    if (url.indexOf ('/ajx') !== 0) {
      url = '/ajx' + url;
    }

    // submit form via ajax
    form.ajaxSubmit ({
      'url'            : url,
      'error'          : data => {
        // load
        this.load (data.responseJSON);
      },
      'success'        : this.load,
      'dataType'       : 'json',
      'uploadProgress' : (event, position, total, percent) => {
        // update bar position
        this._bar.go (percent < 90 ? percent : 90);
      }
    });
  }

  /**
   * replace head tags from state
   *
   * @param {Object} state
   *
   * @private
   */
  _tags (page) {
    // check head
    if (!page.head) return;

    // set header information
    let found   = false;
    let newHead = jQuery (page.head);
    let nowHead = jQuery ('[data-eden="head"]').nextAll ('*');

    // loop all elements
    for (var a = 0; a < nowHead.length; a++) {
      // check now head
      found = false;

      // loop now head
      for (var b = 0; b < newHead.length; b++) {
        // check if found
        if (nowHead[a].outerHTML === newHead[b].outerHTML) {
          // set found
          found = true;
        }
      }

      // remove if not found
      if (!found) nowHead[a].remove ();
    }

    // loop all new elements
    for (var c = 0; c < newHead.length; c++) {
      // check now head
      found = false;

      // loop now head
      for (var d = 0; d < nowHead.length; d++) {
        // check if found
        if (newHead[c].outerHTML === nowHead[d].outerHTML) {
          // set found
          found = true;
        }
      }

      // remove if not found
      if (!found) jQuery ('head').append (newHead[c]);
    }
  }

  /**
   * submits form via ajax
   *
   * @param {HTMLElement} form
   *
   * @private
   * @return {Boolean}
   */
  _form (form) {
    // get form url
    let url = form.attr ('action') || window.location.href.split (this._store.get ('config').domain)[1];

    // check if actual redirect
    if (url.indexOf ('//') > -1 || url.indexOf ('#') === 0) {
      // return
      return false;
    }

    // submit form
    this.submit (form);

    // return true
    return true;
  }

  /**
   * redirects to url
   *
   * @param  {String} url
   *
   * @private
   * @return {Boolean}
   */
  _route (url) {
    // check if actual redirect
    if (url.indexOf ('//') > -1 || url.indexOf ('#') === 0) return false;

    // load next redirect
    this.go (url);

    // return true
    return true;
  }

  /**
   * checks for correct layout
   *
   * @param  {Object} state
   *
   * @private
   * @return {Boolean}
   */
  _layout (state) {
    // set layout variable
    let layout = (state.mount.layout || 'main-layout');

    // get current layout
    let current = document.querySelector ('body').children[0];

    // check if layout needs replacing
    if (current.tagName.toLowerCase () === layout.toLowerCase ()) {
      return false;
    }

    // unmount tag
    this._mount.unmount (true);

    // replace with
    current.replaceWith (document.createElement (layout));

    // mount new tag
    this._mount = riot.mount (document.querySelector ('body').children[0], state)[0];

    // return true
    return true;
  }
}

/**
 * export new bootstrap function
 *
 * @return {bootstrap}
 */
exports = module.exports = window.eden.router = new router ();
<|endoftext|>