<commit_before>import Ember from 'ember';
import { object } from './util/computed';
import { destroyObject } from './util/destroy';

const {
  getOwner,
  assign
} = Ember;

export default Ember.Service.extend({

  openStores: object().readOnly(),

  createStore(opts) {
    let stores = this;
    return getOwner(this).factoryFor('documents:store').create(assign({ stores }, opts));
  },

  store(opts) {
    let { url } = opts;
    let open = this.get('openStores');
    let store = open[url];
    if(!store) {
      store = this.createStore(opts);
      open[url] = store;
    }
    return store;
  },

  willDestroy() {
    let stores = this.cacheFor('openStores');
    if(stores) {
      destroyObject(stores);
    }
    this._super();
  }

});
<commit_msg>stores.storeFactory<commit_after>import Ember from 'ember';
import { object } from './util/computed';
import { destroyObject } from './util/destroy';

const {
  getOwner,
  assign
} = Ember;

export default Ember.Service.extend({

  openStores: object().readOnly(),

  _storeFactory: null,

  storeFactory() {
    let Factory = this._storeFactory;
    if(!Factory) {
      Factory = getOwner(this).factoryFor('documents:store');
      this._storeFactory = Factory;
    }
    return Factory;
  },

  createStore(opts) {
    let stores = this;
    return this.storeFactory().create(assign({ stores }, opts));
  },

  store(opts) {
    let { url } = opts;
    let open = this.get('openStores');
    let store = open[url];
    if(!store) {
      store = this.createStore(opts);
      open[url] = store;
    }
    return store;
  },

  willDestroy() {
    let stores = this.cacheFor('openStores');
    if(stores) {
      destroyObject(stores);
    }
    this._super();
  }

});
<|endoftext|>


<commit_before>import { List, Record, Map } from 'immutable';

import doExecuteRequest from '../gettable/RequestExecutor';
import { awaitingResponse, receiveResponse, receiveError } from './response';
import { readProject } from '../utils/projectUtils';

export const UPSERT_PROJECT = 'UPSERT_PROJECT';
export const CLOSE_PROJECT = 'CLOSE_PROJECT';
export const ADD_REQUEST = 'ADD_REQUEST';
export const UPDATE_REQUEST = 'UPDATE_REQUEST';
export const DELETE_REQUEST = 'DELETE_REQUEST';
export const EXECUTE_REQUEST = 'EXECUTE_REQUEST';

export const Request = Record({
  id: undefined,
  projectId: undefined,
  name: undefined,
  method: undefined,
  url: undefined,
  body: undefined,
  headers: new Map()
});

export const Project = Record({
  id: undefined,
  name: undefined,
  path: undefined,
  requests: new List()
});

function randomId() {
  return Math.random().toString(32).slice(2).substr(0, 5);
}

export function upsertProject(projectPath) {
  return dispatch => {
    readProject(projectPath).then((project) => {
      const requests = project.requests.map(r => {
        r.projectId = project.id;
        return r;
      });

      dispatch({
        type: UPSERT_PROJECT,
        project: {
          path: projectPath,
          id: project.id,
          name: project.name,
          requests: requests
        }
      });
    });
  };
}

export function newProject(path, name) {
  return {
    type: UPSERT_PROJECT,
    project: {
      id: randomId(),
      path: `${path}/${name}.json`,
      name: name,
      requests: []
    }
  }
}

export function closeProject(project) {
  return {
    type: CLOSE_PROJECT,
    project: project
  }
}

export function addRequest(request, projectId) {
  const newRequest = request.set('id', randomId()).set('projectId', projectId);

  return {
    type: ADD_REQUEST,
    projectId: projectId,
    request: newRequest
  }
}

export function updateRequest(request) {
  return {
    type: UPDATE_REQUEST,
    request: request
  }
}

export function deleteRequest(request) {
  return {
    type: DELETE_REQUEST,
    request: request
  }
}

export function executeRequest(method:string, url:string, headers:Object, body:?string) {
  return dispatch => {
    dispatch(awaitingResponse());

    doExecuteRequest(method, url, headers, body).then((response) => {
      dispatch(receiveResponse(response));
    }).catch((error) => {
      dispatch(receiveError(error));
    });
  };
}
<commit_msg>Join paths using path.join for cross platform compatibility<commit_after>import { List, Record, Map } from 'immutable';
const nodePath = require('path');

import doExecuteRequest from '../gettable/RequestExecutor';
import { awaitingResponse, receiveResponse, receiveError } from './response';
import { readProject } from '../utils/projectUtils';

export const UPSERT_PROJECT = 'UPSERT_PROJECT';
export const CLOSE_PROJECT = 'CLOSE_PROJECT';
export const ADD_REQUEST = 'ADD_REQUEST';
export const UPDATE_REQUEST = 'UPDATE_REQUEST';
export const DELETE_REQUEST = 'DELETE_REQUEST';
export const EXECUTE_REQUEST = 'EXECUTE_REQUEST';

export const Request = Record({
  id: undefined,
  projectId: undefined,
  name: undefined,
  method: undefined,
  url: undefined,
  body: undefined,
  headers: new Map()
});

export const Project = Record({
  id: undefined,
  name: undefined,
  path: undefined,
  requests: new List()
});

function randomId() {
  return Math.random().toString(32).slice(2).substr(0, 5);
}

export function upsertProject(projectPath) {
  return dispatch => {
    readProject(projectPath).then((project) => {
      const requests = project.requests.map(r => {
        r.projectId = project.id;
        return r;
      });

      dispatch({
        type: UPSERT_PROJECT,
        project: {
          path: projectPath,
          id: project.id,
          name: project.name,
          requests: requests
        }
      });
    });
  };
}

export function newProject(path, name) {
  return {
    type: UPSERT_PROJECT,
    project: {
      id: randomId(),
      path: nodePath.join(path, `${name}.json`),
      name: name,
      requests: []
    }
  }
}

export function closeProject(project) {
  return {
    type: CLOSE_PROJECT,
    project: project
  }
}

export function addRequest(request, projectId) {
  const newRequest = request.set('id', randomId()).set('projectId', projectId);

  return {
    type: ADD_REQUEST,
    projectId: projectId,
    request: newRequest
  }
}

export function updateRequest(request) {
  return {
    type: UPDATE_REQUEST,
    request: request
  }
}

export function deleteRequest(request) {
  return {
    type: DELETE_REQUEST,
    request: request
  }
}

export function executeRequest(method:string, url:string, headers:Object, body:?string) {
  return dispatch => {
    dispatch(awaitingResponse());

    doExecuteRequest(method, url, headers, body).then((response) => {
      dispatch(receiveResponse(response));
    }).catch((error) => {
      dispatch(receiveError(error));
    });
  };
}
<|endoftext|>


<commit_before>// CSS Browser Selector   v0.2.3b (M@: added noscript support)
// Documentation:         http://rafael.adm.br/css_browser_selector
// License:               http://creativecommons.org/licenses/by/2.5/
// Author:                Rafael Lima (http://rafael.adm.br)
// Contributors:          http://rafael.adm.br/css_browser_selector#contributors
var css_browser_selector = function() {
  var
    ua = navigator.userAgent.toLowerCase(),
    is = function(t){ return ua.indexOf(t) != -1; },
    h = document.getElementsByTagName('html')[0],
    b = (!(/opera|webtv/i.test(ua)) && /msie (\d)/.test(ua)) ? ((is('mac') ? 'ieMac ' : '') + 'ie ie' + RegExp.$1)
      : is('gecko/') ? 'gecko' : is('opera') ? 'opera' : is('konqueror') ? 'konqueror' : is('applewebkit/') ? 'webkit safari' : is('mozilla/') ? 'gecko' : '',
    os = (is('x11') || is('linux')) ? ' linux' : is('mac') ? ' mac' : is('win') ? ' win' : '';
  var c = b+os+' js';
  h.className = h.className.replace('noscript', '') + h.className?' '+c:c;
}();

// List View Functions
var ComatoseList = {
  save_node_state: true,
  state_store: 'cookie', // Only 'cookie' for now
  state_key: 'ComatoseTreeState',

  init: function() {
    var items = ComatoseList._read_state();
    items.each(function(node){
      ComatoseList.expand_node(node.replace('page_controller_', ''))
    });
  },

  toggle_tree_nodes : function(img, id) {
    if(/expanded/.test(img.src)) {
      $('page_list_'+ id).addClassName('collapsed');
      img.src = img.src.replace(/expanded/, 'collapsed')
      if(ComatoseList.save_node_state) {
        var items = ComatoseList._read_state();
        items = items.select(function(id){ return id != img.id; })
        ComatoseList._write_state(items);
      }
    } else {
      $('page_list_'+ id).removeClassName('collapsed');
      img.src = img.src.replace(/collapsed/, 'expanded')
      if(ComatoseList.save_node_state) {
        var items = ComatoseList._read_state();
        items.push(img.id);
        ComatoseList._write_state(items);
      }
    }
  },

  expand_node: function(id) {
    $('page_list_'+ id).removeClassName('collapsed');
    $('page_controller_'+ id).src = $('page_controller_'+ id).src.replace(/collapsed/, 'expanded')
  },

  collapse_node: function(id) {
    $('page_list_'+ id).addClassName('collapsed');
    $('page_controller_'+ id).src = $('page_controller_'+ id).src.replace(/expanded/, 'collapsed')
  },

  item_hover : function(node, state, is_delete) {
    if( state == 'over') {
      $(node).addClassName( (is_delete) ? 'hover-delete' : 'hover' );
    } else {
      $(node).removeClassName( (is_delete) ? 'hover-delete' : 'hover' );
    }
  },

  toggle_reorder: function(node, anc, id, reorder_text, finished_text) {
    if( $(node).hasClassName('do-reorder') ) {
      $(node).removeClassName( 'do-reorder' );
      $(anc).removeClassName('reordering');
      $(anc).innerHTML = reorder_text;
    } else {
      $(node).addClassName( 'do-reorder' );
      $(anc).addClassName('reordering');
      $(anc).innerHTML = finished_text;
      // Make sure the children are visible...
      ComatoseList.expand_node(id);
    }
  },

  _write_state: function(items) {
    var cookie = {}; var options = {}; var expiration = new Date();
    cookie[ ComatoseList.state_key ] = items.join(',');
    expiration.setDate(expiration.getDate()+30)
    options['expires'] = expiration;
    Cookie.write( cookie, options );
  },

  _read_state: function() {
    var state = Cookie.read( ComatoseList.state_key );
    return (state != "" && state != null) ? state.split(',') : [];
  }
}

// Edit Form Functions
var ComatoseEditForm = {

  default_data: {},
  last_preview: {},
  last_title_slug: '',
  mode : null,
  liquid_horiz: false, // changed from true to false by terceiro
  width_offset: 325,

  // Initialize the page...
  init : function(mode) {
    this.mode = mode;
    this.default_data = Form.serialize(document.forms[0]);
    if(mode == 'new') {
      this.last_title_slug = $('page_title').value.toSlug();
      Event.observe('page_title', 'blur', ComatoseEditForm.title_updated_aggressive);
    } else {
      Event.observe('page_title', 'blur', ComatoseEditForm.title_updated);
    }
    $('page_title').focus();
    Hide.these(
      'preview-area',
      'slug_row',
      'parent_row',
      'keywords_row',
      'filter_row',
      'created_row'
    );
    $('page_title').select();
    // Create the horizontal liquidity of the fields
    if(this.liquid_horiz) {
      xOffset = this.width_offset;
      new Layout.LiquidHoriz((xOffset + 50), 'page_title');
      new Layout.LiquidHoriz(xOffset, 'page_slug','page_keywords','page_parent','page_body');
    }
  },
  // For use when updating an existing page...
  title_updated : function() {
    slug = $('page_slug');
    if(slug.value == "") {
      title = $('page_title');
      slug.value = title.value.toSlug();
    }
  },
  // For use when creating a new page...
  title_updated_aggressive : function() {
    slug = $('page_slug');
    title = $('page_title');
    if(slug.value == "" || slug.value == this.last_title ) {
      slug.value = title.value.toSlug();
    }
    this.last_title = slug.value;
  },
  // Todo: Make the meta fields remember their visibility?
  toggle_extra_fields : function(anchor, more_label, less_label) {
    if(anchor.innerHTML == more_label) {
      Show.these(
        'slug_row',
        'keywords_row',
        'parent_row',
        'filter_row',
        'created_row'
      );
      anchor.innerHTML = less_label;
    } else {
      Hide.these(
        'slug_row',
        'keywords_row',
        'parent_row',
        'filter_row',
        'created_row'
      );
      anchor.innerHTML = more_label;
    }
  },
  // Uses server to create preview of content...
  preview_content : function(preview_url, preview_label) {
    $('preview-area').show();
    var params = Form.serialize(document.forms[0]);
    if( params != this.last_preview ) {
      $('preview-panel').innerHTML = "" + preview_label + "";
      new Ajax.Updater(
         'preview-panel',
         preview_url,
         { parameters: params }
      );
    }
    this.last_preview = params;
  },
  cancel : function(url, cancel_warning) {
    var current_data = Form.serialize(document.forms[0]);
    var data_changed = (this.default_data != current_data)
    if(data_changed) {
      if( confirm(cancel_warning) ) {
        location.href = url;
      }
    } else {
      location.href = url;
    }

  }
}

var Hide = {
  these : function() {
    for (var i = 0; i < arguments.length; i++) {
      try {
        $(arguments[i]).hide();
      } catch (e) {}
    }
  }
}

var Show = {
  these : function() {
    for (var i = 0; i < arguments.length; i++) {
      try {
        $(arguments[i]).show();
      } catch (e) {}
    }
  }
}

// Layout namespace
var Layout = {};

// This class allows dom objects to stretch with the browser
// (for when a good, cross-browser, CSS approach can't be found)
Layout.LiquidBase = Class.create();
// Base class for all Liquid* layouts...
Object.extend(Layout.LiquidBase.prototype, {
  enabled: true,
  elems: [],
  offset: null,
  // Constructor is (offset, **array_of_elements)
  initialize: function() {
    args = $A(arguments)
    this.offset = args.shift();
    this.elems = args.select( function(elem){ return ($(elem) != null) } );
    if( this.elems.length > 0 ) {
      this.on_resize(); // Initial size
      Event.observe(window, 'resize', this.on_resize.bind(this) );
      Event.observe(window, 'load', this.on_resize.bind(this) );
    }
  },
  resize_in: function(timeout) {
    setTimeout( this.on_resize.bind(this), timeout );
  },
  on_resize: function() {
    // Need to override!
    alert('Override on_resize, please!');
  }
});

// Liquid vertical layout
Layout.LiquidVert = Class.create();
Object.extend(Layout.LiquidVert.prototype, Object.extend(Layout.LiquidBase.prototype, {
  on_resize: function() {
    if( this.offset != null && this.enabled ) {
      var new_height = ((window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - this.offset) +"px";
      this.elems.each(function(e){ $(e).style.height = new_height; })
    }
  }
}) );

// Liquid horizontal layout
Layout.LiquidHoriz = Class.create();
Object.extend(Layout.LiquidHoriz.prototype, Object.extend(Layout.LiquidBase.prototype, {
  on_resize: function() {
    if( this.offset != null && this.enabled ) {
      var new_width = ((window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - this.offset) +"px";
      this.elems.each( function(e){ $(e).style.width = new_width; })
    }
  }
}) );

// String Extensions... Yes, these are from Radiant! ;-)
Object.extend(String.prototype, {
  upcase: function() {
    return this.toUpperCase();
  },
  downcase: function() {
    return this.toLowerCase();
  },
  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },
  toInteger: function() {
    return parseInt(this);
  },
  toSlug: function() {
    // M@: Modified from Radiant's version, removes multple --'s next to each other
    // This is the same RegExp as the one on the page model...
    return this.strip().downcase().replace(/[^-a-z0-9~\s\.:;+=_]/g, '').replace(/[\s\.:;=_+]+/g, '-').replace(/[\-]{2,}/g, '-');
  }
});

// Run a spinner when an AJAX request in running...
var ComatoseAJAXSpinner = {
  busy : function () {
    if($('spinner') && Ajax.activeRequestCount > 0) {
      Effect.Appear('spinner',{duration:0.5,queue:'end'});
    }
  },

  notBusy: function() {
    if($('spinner') && Ajax.activeRequestCount == 0) {
      Effect.Fade('spinner',{duration:0.5,queue:'end'});
    }
  }
}
// Register it with Prototype...
Ajax.Responders.register({
  onCreate: ComatoseAJAXSpinner.busy,
  onComplete: ComatoseAJAXSpinner.notBusy
});

if(!window.Cookie)
  (function (){
    // From Mephisto!
    window.Cookie = {
      version: '0.7',
      cookies: {},
      _each: function(iterator) {
        $H(this.cookies).each(iterator);
      },

      getAll: function() {
        this.cookies = {};
        $A(document.cookie.split('; ')).each(function(cookie) {
          var seperator = cookie.indexOf('=');
          this.cookies[cookie.substring(0, seperator)] =
              unescape(cookie.substring(seperator + 1, cookie.length));
        }.bind(this));
        return this.cookies;
      },

      read: function() {
        var cookies = $A(arguments), results = [];
        this.getAll();
        cookies.each(function(name) {
          if (this.cookies[name]) results.push(this.cookies[name]);
          else results.push(null);
        }.bind(this));
        return results.length > 1 ? results : results[0];
      },

      write: function(cookies, options) {
        if (cookies.constructor == Object && cookies.name) cookies = [cookies];
        if (cookies.constructor == Array) {
          $A(cookies).each(function(cookie) {
            this._write(cookie.name, cookie.value, cookie.expires,
                        cookie.path, cookie.domain);
          }.bind(this));
        } else {
          options = options || {expires: false, path: '', domain: ''};
          for (name in cookies){
            this._write(name, cookies[name],
                        options.expires, options.path, options.domain);
          }
        }
      },

      _write: function(name, value, expires, path, domain) {
        if (name.indexOf('=') != -1) return;
        var cookieString = name + '=' + escape(value);
        if (expires) cookieString += '; expires=' + expires.toGMTString();
        if (path) cookieString += '; path=' + path;
        if (domain) cookieString += '; domain=' + domain;
        document.cookie = cookieString;
      },

      erase: function(cookies) {
        var cookiesToErase = {};
        $A(arguments).each(function(cookie) {
          cookiesToErase[cookie] = '';
        });

        this.write(cookiesToErase, {expires: (new Date((new Date()).getTime() - 1e11))});
        this.getAll();
      },

      eraseAll: function() {
        this.erase.apply(this, $H(this.getAll()).keys());
      }
    };

    Object.extend(Cookie, {
      get: Cookie.read,
      set: Cookie.write,

      add: Cookie.read,
      remove: Cookie.erase,
      removeAll: Cookie.eraseAll,

      wipe: Cookie.erase,
      wipeAll: Cookie.eraseAll,
      destroy: Cookie.erase,
      destroyAll: Cookie.eraseAll
    });
  })();
<commit_msg>ActionItem57: generating slug transliterating utf-8 characters to ascii<commit_after>// CSS Browser Selector   v0.2.3b (M@: added noscript support)
// Documentation:         http://rafael.adm.br/css_browser_selector
// License:               http://creativecommons.org/licenses/by/2.5/
// Author:                Rafael Lima (http://rafael.adm.br)
// Contributors:          http://rafael.adm.br/css_browser_selector#contributors
var css_browser_selector = function() {
  var
    ua = navigator.userAgent.toLowerCase(),
    is = function(t){ return ua.indexOf(t) != -1; },
    h = document.getElementsByTagName('html')[0],
    b = (!(/opera|webtv/i.test(ua)) && /msie (\d)/.test(ua)) ? ((is('mac') ? 'ieMac ' : '') + 'ie ie' + RegExp.$1)
      : is('gecko/') ? 'gecko' : is('opera') ? 'opera' : is('konqueror') ? 'konqueror' : is('applewebkit/') ? 'webkit safari' : is('mozilla/') ? 'gecko' : '',
    os = (is('x11') || is('linux')) ? ' linux' : is('mac') ? ' mac' : is('win') ? ' win' : '';
  var c = b+os+' js';
  h.className = h.className.replace('noscript', '') + h.className?' '+c:c;
}();

// List View Functions
var ComatoseList = {
  save_node_state: true,
  state_store: 'cookie', // Only 'cookie' for now
  state_key: 'ComatoseTreeState',

  init: function() {
    var items = ComatoseList._read_state();
    items.each(function(node){
      ComatoseList.expand_node(node.replace('page_controller_', ''))
    });
  },

  toggle_tree_nodes : function(img, id) {
    if(/expanded/.test(img.src)) {
      $('page_list_'+ id).addClassName('collapsed');
      img.src = img.src.replace(/expanded/, 'collapsed')
      if(ComatoseList.save_node_state) {
        var items = ComatoseList._read_state();
        items = items.select(function(id){ return id != img.id; })
        ComatoseList._write_state(items);
      }
    } else {
      $('page_list_'+ id).removeClassName('collapsed');
      img.src = img.src.replace(/collapsed/, 'expanded')
      if(ComatoseList.save_node_state) {
        var items = ComatoseList._read_state();
        items.push(img.id);
        ComatoseList._write_state(items);
      }
    }
  },

  expand_node: function(id) {
    $('page_list_'+ id).removeClassName('collapsed');
    $('page_controller_'+ id).src = $('page_controller_'+ id).src.replace(/collapsed/, 'expanded')
  },

  collapse_node: function(id) {
    $('page_list_'+ id).addClassName('collapsed');
    $('page_controller_'+ id).src = $('page_controller_'+ id).src.replace(/expanded/, 'collapsed')
  },

  item_hover : function(node, state, is_delete) {
    if( state == 'over') {
      $(node).addClassName( (is_delete) ? 'hover-delete' : 'hover' );
    } else {
      $(node).removeClassName( (is_delete) ? 'hover-delete' : 'hover' );
    }
  },

  toggle_reorder: function(node, anc, id, reorder_text, finished_text) {
    if( $(node).hasClassName('do-reorder') ) {
      $(node).removeClassName( 'do-reorder' );
      $(anc).removeClassName('reordering');
      $(anc).innerHTML = reorder_text;
    } else {
      $(node).addClassName( 'do-reorder' );
      $(anc).addClassName('reordering');
      $(anc).innerHTML = finished_text;
      // Make sure the children are visible...
      ComatoseList.expand_node(id);
    }
  },

  _write_state: function(items) {
    var cookie = {}; var options = {}; var expiration = new Date();
    cookie[ ComatoseList.state_key ] = items.join(',');
    expiration.setDate(expiration.getDate()+30)
    options['expires'] = expiration;
    Cookie.write( cookie, options );
  },

  _read_state: function() {
    var state = Cookie.read( ComatoseList.state_key );
    return (state != "" && state != null) ? state.split(',') : [];
  }
}

// Edit Form Functions
var ComatoseEditForm = {

  default_data: {},
  last_preview: {},
  last_title_slug: '',
  mode : null,
  liquid_horiz: false, // changed from true to false by terceiro
  width_offset: 325,

  // Initialize the page...
  init : function(mode) {
    this.mode = mode;
    this.default_data = Form.serialize(document.forms[0]);
    if(mode == 'new') {
      this.last_title_slug = $('page_title').value.toSlug();
      Event.observe('page_title', 'blur', ComatoseEditForm.title_updated_aggressive);
    } else {
      Event.observe('page_title', 'blur', ComatoseEditForm.title_updated);
    }
    $('page_title').focus();
    Hide.these(
      'preview-area',
      'slug_row',
      'parent_row',
      'keywords_row',
      'filter_row',
      'created_row'
    );
    $('page_title').select();
    // Create the horizontal liquidity of the fields
    if(this.liquid_horiz) {
      xOffset = this.width_offset;
      new Layout.LiquidHoriz((xOffset + 50), 'page_title');
      new Layout.LiquidHoriz(xOffset, 'page_slug','page_keywords','page_parent','page_body');
    }
  },
  // For use when updating an existing page...
  title_updated : function() {
    slug = $('page_slug');
    if(slug.value == "") {
      title = $('page_title');
      slug.value = title.value.toSlug();
    }
  },
  // For use when creating a new page...
  title_updated_aggressive : function() {
    slug = $('page_slug');
    title = $('page_title');
    if(slug.value == "" || slug.value == this.last_title ) {
      slug.value = title.value.toSlug();
    }
    this.last_title = slug.value;
  },
  // Todo: Make the meta fields remember their visibility?
  toggle_extra_fields : function(anchor, more_label, less_label) {
    if(anchor.innerHTML == more_label) {
      Show.these(
        'slug_row',
        'keywords_row',
        'parent_row',
        'filter_row',
        'created_row'
      );
      anchor.innerHTML = less_label;
    } else {
      Hide.these(
        'slug_row',
        'keywords_row',
        'parent_row',
        'filter_row',
        'created_row'
      );
      anchor.innerHTML = more_label;
    }
  },
  // Uses server to create preview of content...
  preview_content : function(preview_url, preview_label) {
    $('preview-area').show();
    var params = Form.serialize(document.forms[0]);
    if( params != this.last_preview ) {
      $('preview-panel').innerHTML = "" + preview_label + "";
      new Ajax.Updater(
         'preview-panel',
         preview_url,
         { parameters: params }
      );
    }
    this.last_preview = params;
  },
  cancel : function(url, cancel_warning) {
    var current_data = Form.serialize(document.forms[0]);
    var data_changed = (this.default_data != current_data)
    if(data_changed) {
      if( confirm(cancel_warning) ) {
        location.href = url;
      }
    } else {
      location.href = url;
    }

  }
}

var Hide = {
  these : function() {
    for (var i = 0; i < arguments.length; i++) {
      try {
        $(arguments[i]).hide();
      } catch (e) {}
    }
  }
}

var Show = {
  these : function() {
    for (var i = 0; i < arguments.length; i++) {
      try {
        $(arguments[i]).show();
      } catch (e) {}
    }
  }
}

// Layout namespace
var Layout = {};

// This class allows dom objects to stretch with the browser
// (for when a good, cross-browser, CSS approach can't be found)
Layout.LiquidBase = Class.create();
// Base class for all Liquid* layouts...
Object.extend(Layout.LiquidBase.prototype, {
  enabled: true,
  elems: [],
  offset: null,
  // Constructor is (offset, **array_of_elements)
  initialize: function() {
    args = $A(arguments)
    this.offset = args.shift();
    this.elems = args.select( function(elem){ return ($(elem) != null) } );
    if( this.elems.length > 0 ) {
      this.on_resize(); // Initial size
      Event.observe(window, 'resize', this.on_resize.bind(this) );
      Event.observe(window, 'load', this.on_resize.bind(this) );
    }
  },
  resize_in: function(timeout) {
    setTimeout( this.on_resize.bind(this), timeout );
  },
  on_resize: function() {
    // Need to override!
    alert('Override on_resize, please!');
  }
});

// Liquid vertical layout
Layout.LiquidVert = Class.create();
Object.extend(Layout.LiquidVert.prototype, Object.extend(Layout.LiquidBase.prototype, {
  on_resize: function() {
    if( this.offset != null && this.enabled ) {
      var new_height = ((window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) - this.offset) +"px";
      this.elems.each(function(e){ $(e).style.height = new_height; })
    }
  }
}) );

// Liquid horizontal layout
Layout.LiquidHoriz = Class.create();
Object.extend(Layout.LiquidHoriz.prototype, Object.extend(Layout.LiquidBase.prototype, {
  on_resize: function() {
    if( this.offset != null && this.enabled ) {
      var new_width = ((window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth) - this.offset) +"px";
      this.elems.each( function(e){ $(e).style.width = new_width; })
    }
  }
}) );

// String Extensions... Yes, these are from Radiant! ;-)
Object.extend(String.prototype, {
  upcase: function() {
    return this.toUpperCase();
  },
  downcase: function() {
    return this.toLowerCase();
  },
  strip: function() {
    return this.replace(/^\s+/, '').replace(/\s+$/, '');
  },
  toInteger: function() {
    return parseInt(this);
  },
  toSlug: function() {
    // M@: Modified from Radiant's version, removes multple --'s next to each other
    // This is the same RegExp as the one on the page model...
    return this.strip().transliterate().downcase().replace(/[^-a-z0-9~\s\.:;+=_]/g, '').replace(/[\s\.:;=_+]+/g, '-').replace(/[\-]{2,}/g, '-');
  },
  transliterate: function() {
    var substitutions = [
      { from: [ 'Á', 'À', 'À', 'Â', 'Ã', 'Ä' ], to: 'A' },
      { from: [ 'á', 'à', 'à', 'â', 'ã', 'ä', 'ª' ], to: 'a' },
      { from: [ 'É', 'È', 'Ê', 'Ë' ], to: 'E' },
      { from: [ 'é', 'è', 'ê', 'ë' ], to: 'e' },
      { from: [ 'Í', 'Ì', 'Î', 'Ï' ], to: 'I' },
      { from: [ 'í', 'ì', 'î', 'ï' ], to: 'i' },
      { from: [ 'Ó', 'Ò', 'Ô', 'Ö', 'Õ', 'º' ], to: 'O' },
      { from: [ 'ó', 'ò', 'ô', 'ö', 'õ', 'º' ], to: 'o' },
      { from: [ 'Ú', 'Ù', 'Û', 'Ü' ], to: 'U' },
      { from: [ 'ú', 'ù', 'û', 'ü' ], to: 'u' },
      { from: [ 'Ç' ], to: 'C' },
      { from: [ 'ç' ], to: 'c' },
      { from: [ 'Ñ' ], to: 'N' },
      { from: [ 'ñ' ], to: 'n' },
      { from: [ 'Ÿ' ], to: 'Y' },
      { from: [ 'ÿ' ], to: 'y' },
    ]
    var res = this;
    for (i in substitutions) { for (from in substitutions[i].from) { res = res.replace(substitutions[i].from[from], substitutions[i].to); } };
    return res;
  }
});

// Run a spinner when an AJAX request in running...
var ComatoseAJAXSpinner = {
  busy : function () {
    if($('spinner') && Ajax.activeRequestCount > 0) {
      Effect.Appear('spinner',{duration:0.5,queue:'end'});
    }
  },

  notBusy: function() {
    if($('spinner') && Ajax.activeRequestCount == 0) {
      Effect.Fade('spinner',{duration:0.5,queue:'end'});
    }
  }
}
// Register it with Prototype...
Ajax.Responders.register({
  onCreate: ComatoseAJAXSpinner.busy,
  onComplete: ComatoseAJAXSpinner.notBusy
});

if(!window.Cookie)
  (function (){
    // From Mephisto!
    window.Cookie = {
      version: '0.7',
      cookies: {},
      _each: function(iterator) {
        $H(this.cookies).each(iterator);
      },

      getAll: function() {
        this.cookies = {};
        $A(document.cookie.split('; ')).each(function(cookie) {
          var seperator = cookie.indexOf('=');
          this.cookies[cookie.substring(0, seperator)] =
              unescape(cookie.substring(seperator + 1, cookie.length));
        }.bind(this));
        return this.cookies;
      },

      read: function() {
        var cookies = $A(arguments), results = [];
        this.getAll();
        cookies.each(function(name) {
          if (this.cookies[name]) results.push(this.cookies[name]);
          else results.push(null);
        }.bind(this));
        return results.length > 1 ? results : results[0];
      },

      write: function(cookies, options) {
        if (cookies.constructor == Object && cookies.name) cookies = [cookies];
        if (cookies.constructor == Array) {
          $A(cookies).each(function(cookie) {
            this._write(cookie.name, cookie.value, cookie.expires,
                        cookie.path, cookie.domain);
          }.bind(this));
        } else {
          options = options || {expires: false, path: '', domain: ''};
          for (name in cookies){
            this._write(name, cookies[name],
                        options.expires, options.path, options.domain);
          }
        }
      },

      _write: function(name, value, expires, path, domain) {
        if (name.indexOf('=') != -1) return;
        var cookieString = name + '=' + escape(value);
        if (expires) cookieString += '; expires=' + expires.toGMTString();
        if (path) cookieString += '; path=' + path;
        if (domain) cookieString += '; domain=' + domain;
        document.cookie = cookieString;
      },

      erase: function(cookies) {
        var cookiesToErase = {};
        $A(arguments).each(function(cookie) {
          cookiesToErase[cookie] = '';
        });

        this.write(cookiesToErase, {expires: (new Date((new Date()).getTime() - 1e11))});
        this.getAll();
      },

      eraseAll: function() {
        this.erase.apply(this, $H(this.getAll()).keys());
      }
    };

    Object.extend(Cookie, {
      get: Cookie.read,
      set: Cookie.write,

      add: Cookie.read,
      remove: Cookie.erase,
      removeAll: Cookie.eraseAll,

      wipe: Cookie.erase,
      wipeAll: Cookie.eraseAll,
      destroy: Cookie.erase,
      destroyAll: Cookie.eraseAll
    });
  })();
<|endoftext|>


<commit_before>$(document).ready(function() {

	$("#screener-full").submit(function() {
		window.scrollTo(0, 0);
		Pace.restart();
		$("#screener-full :input").attr("readonly", true);
		$("#screener-full :input").attr("disabled", true);
		submitScreen("#"+this.id);
		return false;
	});

	$(".table-sorted").tablesorter({sortInitialOrder: "desc"});
	$(".table-sorted").oneSimpleTablePagination({rowsPerPage: 25});
	$(".table-sorted .hovertip").popover({placement: "bottom", trigger: "hover"});

	updateScreenerInputs();

	$("body").keydown(function(e) {
		var focusTag = document.activeElement.tagName;
		if (focusTag!="INPUT" && focusTag!="BUTTON") {
			var key = e.keyCode;
			if (key==37 || key==74) {
				$("#tablePaginationPrevPage").click();
			} else if (key==39 || key==75) {
				$("#tablePaginationNextPage").click();
			}
		}
	});

});

function submitScreen(formId) {
	var strat = $(formId + " #strat").val();
	var sym = $(formId + " #sym").val();
	var href = "/screener/";
	href += (sym && sym.length > 0) ?  strat + "/" + sym : strat;
	href += screenQueryString(formId);
	window.location = href;
}

function screenQueryString(formId) {
	var queryString = "";
	var moneyness = $(formId + " #moneyness").val();
	var numParams = {
		mindays: $(formId + " #mindays").val(),
		maxdays: $(formId + " #maxdays").val(),
		minprofitpercent: $(formId + " #minprofitpercent").val(),
		minprofitamount: $(formId + " #minprofitamount").val(),
		maxlossamount: $(formId + " #maxlossamount").val(),
	};

	var isFirstParam = true;

	if (moneyness != "any") {
		queryString += (isFirstParam) ? "?" : "&";
		queryString += "moneyness=" + moneyness;
		isFirstParam = false;
	}

	for (var param in numParams) {
		var value = numParams[param];
		if (value) {
			queryString += (isFirstParam) ? "?" : "&";
			queryString += param + "=" + value;
			isFirstParam = false;
		}
	}

	return queryString;
}

function updateScreenerInputs() {
	var ck = $.cookie();
	var alphaParams = ["strat", "sym", "moneyness"];
	for (var i in alphaParams) {
		var key = alphaParams[i];
		if (ck[key]) {
			$("#"+key.toLowerCase()).val(ck[key]);
		}
	}

	var numericParams = [
         "minDays",
         "maxDays",
         "minProfitAmount",
         "minProfitPercent",
         "maxLossAmount",
    ];
	for (var i in numericParams) {
		var key = numericParams[i];
		if (ck[key] && ck[key] > 0) {
			$("#"+key.toLowerCase()).val(ck[key]);
		}
	}

}

function mailto() {
	ML="f>o@eh=p./rcl nti\"m:<a";
	MI="DE=5:406ABE@<?2C@>02327?@2>2B4?4:8;2BA1@>02327?@2>2B4?4:8;2BD9E1";
	OT="";
	for (j=0;j<MI.length;j++) {
		OT+=ML.charAt(MI.charCodeAt(j)-48);
	} document.write(OT);
}<commit_msg>ignore keyboard shortcuts on INPUT focus only<commit_after>$(document).ready(function() {

	$("#screener-full").submit(function() {
		window.scrollTo(0, 0);
		Pace.restart();
		$("#screener-full :input").attr("readonly", true);
		$("#screener-full :input").attr("disabled", true);
		submitScreen("#"+this.id);
		return false;
	});

	$(".table-sorted").tablesorter({sortInitialOrder: "desc"});
	$(".table-sorted").oneSimpleTablePagination({rowsPerPage: 25});
	$(".table-sorted .hovertip").popover({placement: "bottom", trigger: "hover"});

	updateScreenerInputs();

	$("body").keydown(function(e) {
		var focusTag = document.activeElement.tagName;
		if (focusTag != "INPUT") {
			var key = e.keyCode;
			if (key==37 || key==74) {
				$("#tablePaginationPrevPage").click();
			} else if (key==39 || key==75) {
				$("#tablePaginationNextPage").click();
			}
		}
	});

});

function submitScreen(formId) {
	var strat = $(formId + " #strat").val();
	var sym = $(formId + " #sym").val();
	var href = "/screener/";
	href += (sym && sym.length > 0) ?  strat + "/" + sym : strat;
	href += screenQueryString(formId);
	window.location = href;
}

function screenQueryString(formId) {
	var queryString = "";
	var moneyness = $(formId + " #moneyness").val();
	var numParams = {
		mindays: $(formId + " #mindays").val(),
		maxdays: $(formId + " #maxdays").val(),
		minprofitpercent: $(formId + " #minprofitpercent").val(),
		minprofitamount: $(formId + " #minprofitamount").val(),
		maxlossamount: $(formId + " #maxlossamount").val(),
	};

	var isFirstParam = true;

	if (moneyness != "any") {
		queryString += (isFirstParam) ? "?" : "&";
		queryString += "moneyness=" + moneyness;
		isFirstParam = false;
	}

	for (var param in numParams) {
		var value = numParams[param];
		if (value) {
			queryString += (isFirstParam) ? "?" : "&";
			queryString += param + "=" + value;
			isFirstParam = false;
		}
	}

	return queryString;
}

function updateScreenerInputs() {
	var ck = $.cookie();
	var alphaParams = ["strat", "sym", "moneyness"];
	for (var i in alphaParams) {
		var key = alphaParams[i];
		if (ck[key]) {
			$("#"+key.toLowerCase()).val(ck[key]);
		}
	}

	var numericParams = [
         "minDays",
         "maxDays",
         "minProfitAmount",
         "minProfitPercent",
         "maxLossAmount",
    ];
	for (var i in numericParams) {
		var key = numericParams[i];
		if (ck[key] && ck[key] > 0) {
			$("#"+key.toLowerCase()).val(ck[key]);
		}
	}

}

function mailto() {
	ML="f>o@eh=p./rcl nti\"m:<a";
	MI="DE=5:406ABE@<?2C@>02327?@2>2B4?4:8;2BA1@>02327?@2>2B4?4:8;2BD9E1";
	OT="";
	for (j=0;j<MI.length;j++) {
		OT+=ML.charAt(MI.charCodeAt(j)-48);
	} document.write(OT);
}<|endoftext|>


<commit_before>/* global Affiliation, Authorship, ResearchEntity, Document, TagLabel, SqlService, DocumentOrigins */
'use strict';

/**
 * ResearchEntity.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/#!documentation/models
 */

const exec = require('child_process').exec;
const Promise = require("bluebird");
const _ = require("lodash");
const BaseModel = require("./BaseModel.js");

module.exports = _.merge({}, BaseModel, {
    attributes: {},
    createDraft: function (ResearchEntityModel, researchEntityId, draftData) {
        const selectedDraftData = Document.selectData(draftData);
        selectedDraftData.kind = DocumentKinds.DRAFT;
        return Promise.all([
            ResearchEntityModel.findOneById(researchEntityId).populate('drafts'),
            Document.create(selectedDraftData)
        ])
            .spread(function (researchEntity, draft) {
                researchEntity.drafts.add(draft);
                return Promise.all([
                    draft.id,
                    researchEntity.savePromise()
                ]);
            })
            .spread(function (draftId) {
                return Promise.all([
                    draftId,
                    Authorship.createEmptyAuthorships(draftId, draftData)
                ]);
            })
            .spread(function (draftId) {
                return Document.findOneById(draftId)
                    .populate('authorships')
                    .populate('affiliations')
                    .populate('authors')
                    .populate('source');
            });
    },
    unverifyDocument: async function (ResearchEntityModel, researchEntityId, documentId) {
        await this.doUnverifyDocument(ResearchEntityModel, researchEntityId, documentId);
        return await Document.deleteIfNotVerified(documentId);
    },
    doUnverifyDocument: async function (ResearchEntityModel, researchEntityId, documentId) {
        const authorshipModel = getAuthorshipModel(ResearchEntityModel);
        const authorship = await authorshipModel.findOne({researchEntity: researchEntityId, document: documentId});
        if (!authorship)
            return;
        return authorship.unverify();
    },
    createDrafts: function (Model, researchEntityId, documents) {
        return Promise.all(documents.map(function (document) {
            return Model.createDraft(Model, researchEntityId, document);
        }));
    },
    undiscardDocument: async function (Model, researchEntityId, documentId) {
        const DiscardedModel = getDiscardedModel(Model);
        await DiscardedModel.destroy({document: documentId, researchEntity: researchEntityId});
        return await Document.deleteIfNotVerified(documentId);
    },
    discardDocument: async function (Model, researchEntityId, documentId) {
        const DiscardedModel = getDiscardedModel(Model);
        const AuthorshipModel = getAuthorshipModel(Model);
        const authorships = await AuthorshipModel.find({document: documentId, researchEntity: researchEntityId});
        if (authorships.length > 0)
            return null;
        return await DiscardedModel.findOrCreate({researchEntity: researchEntityId, document: documentId});
    },
    discardDocuments: function (Model, researchEntityId, documentIds) {
        return Promise.all(documentIds.map(function (documentId) {
            return Model.discardDocument(Model, researchEntityId, documentId);
        }));
    },
    verifyDrafts: function (ResearchEntityModel, researchEntityId, draftIds) {
        return Promise.all(
            draftIds.map(draftId => ResearchEntityModel.verifyDraft(ResearchEntityModel, researchEntityId, draftId))
        );
    },
    verifyDraft: async function (ResearchEntityModel, researchEntityId, draftId, verificationData) {
        const draft = await Document.findOneById(draftId)
            .populate('authorships')
            .populate('affiliations');

        if (!draft || draft.kind !== DocumentKinds.DRAFT)
            return {
                error: 'Draft not found',
                item: draftId
            };
        if (!draft.isValid())
            return {
                error: 'Draft not valid for verification',
                item: draft
            };

        const authorshipData = await ResearchEntityModel.getAuthorshipsData(draft, researchEntityId, verificationData);

        if (!authorshipData.isVerifiable)
            return {
                error: authorshipData.error,
                item: authorshipData.document
            };

        const documentCopies = await Document.findCopies(draft, authorshipData.position);

        const n = documentCopies.length;
        let docToVerify;
        if (n === 0) {
            docToVerify = await draft.draftToDocument();
        }
        else {
            if (n > 1)
                sails.log.debug('Too many similar documents to ' + draft.id + ' ( ' + n + ')');
            docToVerify = documentCopies[0];

            if (docToVerify.isPositionVerified(authorshipData.position)) {
                const authorName = docToVerify.authorsStr.split(', ')[authorshipData.position];
                return {
                    error: `You cannot verify this document as ${authorName} because someone else already claimed to be that author`,
                    item: docToVerify
                };
            }
            sails.log.debug('Draft ' + draft.id + ' will be deleted and substituted by ' + docToVerify.id);
            await Document.destroy({id: draft.id});
        }

        return await ResearchEntityModel.doVerifyDocument(docToVerify, researchEntityId, authorshipData);
    },
    verifyDocuments: function (Model, researchEntityId, documentIds) {
        return Promise.all(documentIds.map(documentId => Model.verifyDocument(Model, researchEntityId, documentId)));
    },
    verifyDocument: async function (Model, researchEntityId, documentId, verificationData) {
        const DiscardedModel = getDiscardedModel(Model);
        await  DiscardedModel.destroy({document: documentId, researchEntity: researchEntityId});
        const document = await Document.findOneById(documentId)
            .populate('affiliations')
            .populate('authorships');
        if (!document || document.kind !== DocumentKinds.VERIFIED)
            return {
                error: 'Document not found',
                item: researchEntityId
            };
        const authorshipData = await Model.getAuthorshipsData(document, researchEntityId, verificationData);

        if (!authorshipData.isVerifiable)
            return {
                error: authorshipData.error,
                item: authorshipData.document
            };

        if (authorshipData.document.isPositionVerified(authorshipData.position))
            return {
                error: "The position is already verified",
                item: authorshipData.document
            };

        return await Model.doVerifyDocument(authorshipData.document, researchEntityId, authorshipData);
    },
    updateDraft: async function (ResearchEntityModel, draftId, draftData) {
        const documentFields = Document.getFields();
        const selectedDraftData = _.pick(draftData, documentFields);
        selectedDraftData.kind = DocumentKinds.DRAFT;
        selectedDraftData.synchronized = false;
        const updatedDraft = await Document.update({id: draftId}, selectedDraftData);
        return updatedDraft[0];
    },
    deleteDrafts: function (Model, draftIds) {
        return Promise.all(draftIds.map(function (draftId) {
            return Document.destroy({id: draftId});
        }));
    },
    addTags: function (TagModel, userId, documentId, tags) {
        return TagModel.destroy({researchEntity: userId, document: documentId})
            .then(() =>
                tags.forEach(t =>
                    TagLabel
                        .findOrCreate({value: t})
                        .then(tl => TagModel.create({
                            document: documentId,
                            researchEntity: userId,
                            tagLabel: tl.id
                        }))
                )
            )
    },
    updateProfile: async function (ResearchEntityModel, researchEntityId, researchEntityData) {
        const oldResearchEntity = await ResearchEntityModel.findOne({id: researchEntityId});
        const res = await ResearchEntityModel.update({id: researchEntityId}, researchEntityData);
        const newResearchEntity = res[0];
        const researchEntityType = newResearchEntity.getType();
        const command = 'external:import:' + researchEntityType + ':' + newResearchEntity.id;
        if (newResearchEntity.scopusId !== oldResearchEntity.scopusId)
            GruntTaskRunner.run(command + ':' + DocumentOrigins.SCOPUS);
        if (newResearchEntity.username !== oldResearchEntity.username
            || newResearchEntity.publicationsAcronym !== oldResearchEntity.publicationsAcronym)
            GruntTaskRunner.run(command + ':' + DocumentOrigins.PUBLICATIONS);

        return newResearchEntity;
    },
    _config: {
        actions: false,
        shortcuts: false,
        rest: false
    }
});

function getThroughModel(ResearchEntityModel, fieldName) {
    const throughModelName = ResearchEntityModel._attributes[fieldName].through;
    return sails.models[throughModelName];
}

function getAuthorshipModel(ResearchEntityModel) {
    return getThroughModel(ResearchEntityModel, 'documents');
}

function getDiscardedModel(ResearchEntityModel) {
    return getThroughModel(ResearchEntityModel, 'discardedDocuments');
}<commit_msg>Disabled editing to non-draft documents<commit_after>/* global Affiliation, Authorship, ResearchEntity, Document, TagLabel, SqlService, DocumentOrigins */
'use strict';

/**
 * ResearchEntity.js
 *
 * @description :: TODO: You might write a short summary of how this model works and what it represents here.
 * @docs        :: http://sailsjs.org/#!documentation/models
 */

const exec = require('child_process').exec;
const Promise = require("bluebird");
const _ = require("lodash");
const BaseModel = require("./BaseModel.js");

module.exports = _.merge({}, BaseModel, {
    attributes: {},
    createDraft: function (ResearchEntityModel, researchEntityId, draftData) {
        const selectedDraftData = Document.selectData(draftData);
        selectedDraftData.kind = DocumentKinds.DRAFT;
        return Promise.all([
            ResearchEntityModel.findOneById(researchEntityId).populate('drafts'),
            Document.create(selectedDraftData)
        ])
            .spread(function (researchEntity, draft) {
                researchEntity.drafts.add(draft);
                return Promise.all([
                    draft.id,
                    researchEntity.savePromise()
                ]);
            })
            .spread(function (draftId) {
                return Promise.all([
                    draftId,
                    Authorship.createEmptyAuthorships(draftId, draftData)
                ]);
            })
            .spread(function (draftId) {
                return Document.findOneById(draftId)
                    .populate('authorships')
                    .populate('affiliations')
                    .populate('authors')
                    .populate('source');
            });
    },
    unverifyDocument: async function (ResearchEntityModel, researchEntityId, documentId) {
        await this.doUnverifyDocument(ResearchEntityModel, researchEntityId, documentId);
        return await Document.deleteIfNotVerified(documentId);
    },
    doUnverifyDocument: async function (ResearchEntityModel, researchEntityId, documentId) {
        const authorshipModel = getAuthorshipModel(ResearchEntityModel);
        const authorship = await authorshipModel.findOne({researchEntity: researchEntityId, document: documentId});
        if (!authorship)
            return;
        return authorship.unverify();
    },
    createDrafts: function (Model, researchEntityId, documents) {
        return Promise.all(documents.map(function (document) {
            return Model.createDraft(Model, researchEntityId, document);
        }));
    },
    undiscardDocument: async function (Model, researchEntityId, documentId) {
        const DiscardedModel = getDiscardedModel(Model);
        await DiscardedModel.destroy({document: documentId, researchEntity: researchEntityId});
        return await Document.deleteIfNotVerified(documentId);
    },
    discardDocument: async function (Model, researchEntityId, documentId) {
        const DiscardedModel = getDiscardedModel(Model);
        const AuthorshipModel = getAuthorshipModel(Model);
        const authorships = await AuthorshipModel.find({document: documentId, researchEntity: researchEntityId});
        if (authorships.length > 0)
            return null;
        return await DiscardedModel.findOrCreate({researchEntity: researchEntityId, document: documentId});
    },
    discardDocuments: function (Model, researchEntityId, documentIds) {
        return Promise.all(documentIds.map(function (documentId) {
            return Model.discardDocument(Model, researchEntityId, documentId);
        }));
    },
    verifyDrafts: function (ResearchEntityModel, researchEntityId, draftIds) {
        return Promise.all(
            draftIds.map(draftId => ResearchEntityModel.verifyDraft(ResearchEntityModel, researchEntityId, draftId))
        );
    },
    verifyDraft: async function (ResearchEntityModel, researchEntityId, draftId, verificationData) {
        const draft = await Document.findOneById(draftId)
            .populate('authorships')
            .populate('affiliations');

        if (!draft || draft.kind !== DocumentKinds.DRAFT)
            return {
                error: 'Draft not found',
                item: draftId
            };
        if (!draft.isValid())
            return {
                error: 'Draft not valid for verification',
                item: draft
            };

        const authorshipData = await ResearchEntityModel.getAuthorshipsData(draft, researchEntityId, verificationData);

        if (!authorshipData.isVerifiable)
            return {
                error: authorshipData.error,
                item: authorshipData.document
            };

        const documentCopies = await Document.findCopies(draft, authorshipData.position);

        const n = documentCopies.length;
        let docToVerify;
        if (n === 0) {
            docToVerify = await draft.draftToDocument();
        }
        else {
            if (n > 1)
                sails.log.debug('Too many similar documents to ' + draft.id + ' ( ' + n + ')');
            docToVerify = documentCopies[0];

            if (docToVerify.isPositionVerified(authorshipData.position)) {
                const authorName = docToVerify.authorsStr.split(', ')[authorshipData.position];
                return {
                    error: `You cannot verify this document as ${authorName} because someone else already claimed to be that author`,
                    item: docToVerify
                };
            }
            sails.log.debug('Draft ' + draft.id + ' will be deleted and substituted by ' + docToVerify.id);
            await Document.destroy({id: draft.id});
        }

        return await ResearchEntityModel.doVerifyDocument(docToVerify, researchEntityId, authorshipData);
    },
    verifyDocuments: function (Model, researchEntityId, documentIds) {
        return Promise.all(documentIds.map(documentId => Model.verifyDocument(Model, researchEntityId, documentId)));
    },
    verifyDocument: async function (Model, researchEntityId, documentId, verificationData) {
        const DiscardedModel = getDiscardedModel(Model);
        await  DiscardedModel.destroy({document: documentId, researchEntity: researchEntityId});
        const document = await Document.findOneById(documentId)
            .populate('affiliations')
            .populate('authorships');
        if (!document || document.kind !== DocumentKinds.VERIFIED)
            return {
                error: 'Document not found',
                item: researchEntityId
            };
        const authorshipData = await Model.getAuthorshipsData(document, researchEntityId, verificationData);

        if (!authorshipData.isVerifiable)
            return {
                error: authorshipData.error,
                item: authorshipData.document
            };

        if (authorshipData.document.isPositionVerified(authorshipData.position))
            return {
                error: "The position is already verified",
                item: authorshipData.document
            };

        return await Model.doVerifyDocument(authorshipData.document, researchEntityId, authorshipData);
    },
    updateDraft: async function (ResearchEntityModel, draftId, draftData) {
        const d = await Document.findOneById(draftId);
        if(!d.kind || d.kind !== DocumentKinds.DRAFT)
            throw "Draft not found";
        const documentFields = Document.getFields();
        const selectedDraftData = _.pick(draftData, documentFields);
        selectedDraftData.kind = DocumentKinds.DRAFT;
        selectedDraftData.synchronized = false;
        const updatedDraft = await Document.update({id: draftId}, selectedDraftData);
        return updatedDraft[0];
    },
    deleteDrafts: function (Model, draftIds) {
        return Promise.all(draftIds.map(function (draftId) {
            return Document.destroy({id: draftId});
        }));
    },
    addTags: function (TagModel, userId, documentId, tags) {
        return TagModel.destroy({researchEntity: userId, document: documentId})
            .then(() =>
                tags.forEach(t =>
                    TagLabel
                        .findOrCreate({value: t})
                        .then(tl => TagModel.create({
                            document: documentId,
                            researchEntity: userId,
                            tagLabel: tl.id
                        }))
                )
            )
    },
    updateProfile: async function (ResearchEntityModel, researchEntityId, researchEntityData) {
        const oldResearchEntity = await ResearchEntityModel.findOne({id: researchEntityId});
        const res = await ResearchEntityModel.update({id: researchEntityId}, researchEntityData);
        const newResearchEntity = res[0];
        const researchEntityType = newResearchEntity.getType();
        const command = 'external:import:' + researchEntityType + ':' + newResearchEntity.id;
        if (newResearchEntity.scopusId !== oldResearchEntity.scopusId)
            GruntTaskRunner.run(command + ':' + DocumentOrigins.SCOPUS);
        if (newResearchEntity.username !== oldResearchEntity.username
            || newResearchEntity.publicationsAcronym !== oldResearchEntity.publicationsAcronym)
            GruntTaskRunner.run(command + ':' + DocumentOrigins.PUBLICATIONS);

        return newResearchEntity;
    },
    _config: {
        actions: false,
        shortcuts: false,
        rest: false
    }
});

function getThroughModel(ResearchEntityModel, fieldName) {
    const throughModelName = ResearchEntityModel._attributes[fieldName].through;
    return sails.models[throughModelName];
}

function getAuthorshipModel(ResearchEntityModel) {
    return getThroughModel(ResearchEntityModel, 'documents');
}

function getDiscardedModel(ResearchEntityModel) {
    return getThroughModel(ResearchEntityModel, 'discardedDocuments');
}<|endoftext|>


<commit_before>/* global $ */

$('form.login').submit(function (event) {
  'use strict';
  event.preventDefault();

  var form = $(this),
      name = form.find('input[name=username]').val(),
      key = form.find('input[name=password]').val(),
      email = form.find('input[name=email]').val(),
      $loginFeedback = form.find('.loginFeedback');

  // jsbin.settings.home = name; // will save later
  $loginFeedback.show().text('Checking...');

  $.ajax({
    url: 'http://' + window.location.host + form.attr('action'),
    data: { username: name, key: key, email: email },
    type: 'POST',
    dataType: 'json',
    complete: function (jqXHR) {
      var data = $.parseJSON(jqXHR.responseText) || {};
      // cookie is required to share with the server so we can do a redirect on new bin
      if (jqXHR.status === 200) {
        if (data.user.avatar) {
          $('#avatar').find('img').remove().end().prepend('');
        }
        if (data.message) {
          $loginFeedback.text(data.message);
        } else {
          window.location = window.location.pathname + window.location.search;
        }
      } else {
        analytics.login(false);
        $loginFeedback.text(data.message || ('"' + name + '" has already been taken. Please either double check the password, or choose another username.'));
      }
    }
  });
});<commit_msg>Using form action instead of hard coding url<commit_after>/* global $ */

$('form.login').submit(function (event) {
  'use strict';
  event.preventDefault();

  var form = $(this),
      name = form.find('input[name=username]').val(),
      key = form.find('input[name=password]').val(),
      email = form.find('input[name=email]').val(),
      $loginFeedback = form.find('.loginFeedback');

  // jsbin.settings.home = name; // will save later
  $loginFeedback.show().text('Checking...');

  $.ajax({
    url: form.attr('action'),
    data: { username: name, key: key, email: email },
    type: 'POST',
    dataType: 'json',
    complete: function (jqXHR) {
      var data = $.parseJSON(jqXHR.responseText) || {};
      // cookie is required to share with the server so we can do a redirect on new bin
      if (jqXHR.status === 200) {
        if (data.user.avatar) {
          $('#avatar').find('img').remove().end().prepend('');
        }
        if (data.message) {
          $loginFeedback.text(data.message);
        } else {
          window.location = window.location.pathname + window.location.search;
        }
      } else {
        analytics.login(false);
        $loginFeedback.text(data.message || ('"' + name + '" has already been taken. Please either double check the password, or choose another username.'));
      }
    }
  });
});
<|endoftext|>


<commit_before>/* globals chatMessages cordova */

import _ from 'underscore';
import moment from 'moment';
import toastr from 'toastr';

RocketChat.MessageAction = new class {
	/*
  	config expects the following keys (only id is mandatory):
  		id (mandatory)
  		icon: string
  		label: string
  		action: function(event, instance)
  		condition: function(message)
			order: integer
			group: string (message or menu)
   */

	constructor() {
		this.buttons = new ReactiveVar({});
	}

	addButton(config) {
		if (!config || !config.id) {
			return false;
		}

		if (!config.group) {
			config.group = 'menu';
		}

		return Tracker.nonreactive(() => {
			const btns = this.buttons.get();
			btns[config.id] = config;
			return this.buttons.set(btns);
		});
	}

	removeButton(id) {
		return Tracker.nonreactive(() => {
			const btns = this.buttons.get();
			delete btns[id];
			return this.buttons.set(btns);
		});
	}

	updateButton(id, config) {
		return Tracker.nonreactive(() => {
			const btns = this.buttons.get();
			if (btns[id]) {
				btns[id] = _.extend(btns[id], config);
				return this.buttons.set(btns);
			}
		});
	}

	getButtonById(id) {
		const allButtons = this.buttons.get();
		return allButtons[id];
	}

	getButtons(message, context, group) {
		let allButtons = _.toArray(this.buttons.get());

		if (group) {
			allButtons = allButtons.filter(button => button.group === group);
		}

		if (message) {
			allButtons = _.compact(_.map(allButtons, function(button) {
				if (button.context == null || button.context.includes(context)) {
					if (button.condition == null || button.condition(message, context)) {
						return button;
					}
				}
			}));
		}
		return _.sortBy(allButtons, 'order');
	}

	resetButtons() {
		return this.buttons.set({});
	}

	getPermaLink(msgId) {
		const roomData = ChatSubscription.findOne({
			rid: Session.get('openedRoom')
		});
		let routePath = document.location.pathname;
		if (roomData) {
			routePath = RocketChat.roomTypes.getRouteLink(roomData.t, roomData);
		}
		return `${ Meteor.absoluteUrl().replace(/\/$/, '') + routePath }?msg=${ msgId }`;
	}
};

Meteor.startup(function() {
	RocketChat.MessageAction.addButton({
		id: 'reply-message',
		icon: 'message',
		label: 'Reply',
		context: ['message', 'message-mobile'],
		action() {
			const message = this._arguments[1];
			const {input} = chatMessages[message.rid];
			$(input)
				.focus()
				.data('reply', message)
				.trigger('dataChange');
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}

			return true;
		},
		order: 1,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'edit-message',
		icon: 'edit',
		label: 'Edit',
		context: ['message', 'message-mobile'],
		action() {
			const messageId = this._arguments[1]._id;
			chatMessages[Session.get('openedRoom')].edit(document.getElementById(messageId));
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({
				rid: message.rid
			}) == null) {
				return false;
			}
			const hasPermission = RocketChat.authz.hasAtLeastOnePermission('edit-message', message.rid);
			const isEditAllowed = RocketChat.settings.get('Message_AllowEditing');
			const editOwn = message.u && message.u._id === Meteor.userId();
			if (!(hasPermission || (isEditAllowed && editOwn))) {
				return;
			}
			const blockEditInMinutes = RocketChat.settings.get('Message_AllowEditing_BlockEditInMinutes');
			if (blockEditInMinutes) {
				let msgTs;
				if (message.ts != null) {
					msgTs = moment(message.ts);
				}
				let currentTsDiff;
				if (msgTs != null) {
					currentTsDiff = moment().diff(msgTs, 'minutes');
				}
				return currentTsDiff < blockEditInMinutes;
			} else {
				return true;
			}
		},
		order: 2,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'delete-message',
		icon: 'trash',
		label: 'Delete',
		context: ['message', 'message-mobile'],
		color: 'alert',
		action() {
			const message = this._arguments[1];
			chatMessages[Session.get('openedRoom')].confirmDeleteMsg(message);
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}
			const forceDelete = RocketChat.authz.hasAtLeastOnePermission('force-delete-message', message.rid);
			const hasPermission = RocketChat.authz.hasAtLeastOnePermission('delete-message', message.rid);
			const isDeleteAllowed = RocketChat.settings.get('Message_AllowDeleting');
			const deleteOwn = message.u && message.u._id === Meteor.userId();
			if (!(hasPermission || (isDeleteAllowed && deleteOwn) || forceDelete)) {
				return;
			}
			const blockDeleteInMinutes = RocketChat.settings.get('Message_AllowDeleting_BlockDeleteInMinutes');
			if (forceDelete) {
				return true;
			}
			if (blockDeleteInMinutes != null && blockDeleteInMinutes !== 0) {
				let msgTs;
				if (message.ts != null) {
					msgTs = moment(message.ts);
				}
				let currentTsDiff;
				if (msgTs != null) {
					currentTsDiff = moment().diff(msgTs, 'minutes');
				}
				return currentTsDiff < blockDeleteInMinutes;
			} else {
				return true;
			}
		},
		order: 3,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'permalink',
		icon: 'permalink',
		label: 'Permalink',
		classes: 'clipboard',
		context: ['message', 'message-mobile'],
		action(event) {
			const message = this._arguments[1];
			const permalink = RocketChat.MessageAction.getPermaLink(message._id);
			if (Meteor.isCordova) {
				cordova.plugins.clipboard.copy(permalink);
			} else {
				$(event.currentTarget).attr('data-clipboard-text', permalink);
			}
			toastr.success(TAPi18n.__('Copied'));
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}

			return true;
		},
		order: 4,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'copy',
		icon: 'copy',
		label: 'Copy',
		classes: 'clipboard',
		context: ['message', 'message-mobile'],
		action(event) {
			const message = this._arguments[1].msg;
			if (Meteor.isCordova) {
				cordova.plugins.clipboard.copy(message);
			} else {
				$(event.currentTarget).attr('data-clipboard-text', message);
			}
			toastr.success(TAPi18n.__('Copied'));
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}

			return true;
		},
		order: 5,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'quote-message',
		icon: 'quote',
		label: 'Quote',
		context: ['message', 'message-mobile'],
		action() {
			const message = this._arguments[1];
			const {input} = chatMessages[message.rid];
			const url = RocketChat.MessageAction.getPermaLink(message._id);
			const text = `[ ](${ url }) `;
			if (input.value) {
				input.value += input.value.endsWith(' ') ? '' : ' ';
			}
			input.value += text;
			input.focus();
			$(input).trigger('change').trigger('input');
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}

			return true;
		},
		order: 6,
		group: 'menu'
	});
});
<commit_msg>Add reply box to quote and add quote on reply (#10437)<commit_after>/* globals chatMessages cordova */

import _ from 'underscore';
import moment from 'moment';
import toastr from 'toastr';

RocketChat.MessageAction = new class {
	/*
  	config expects the following keys (only id is mandatory):
  		id (mandatory)
  		icon: string
  		label: string
  		action: function(event, instance)
  		condition: function(message)
			order: integer
			group: string (message or menu)
   */

	constructor() {
		this.buttons = new ReactiveVar({});
	}

	addButton(config) {
		if (!config || !config.id) {
			return false;
		}

		if (!config.group) {
			config.group = 'menu';
		}

		return Tracker.nonreactive(() => {
			const btns = this.buttons.get();
			btns[config.id] = config;
			return this.buttons.set(btns);
		});
	}

	removeButton(id) {
		return Tracker.nonreactive(() => {
			const btns = this.buttons.get();
			delete btns[id];
			return this.buttons.set(btns);
		});
	}

	updateButton(id, config) {
		return Tracker.nonreactive(() => {
			const btns = this.buttons.get();
			if (btns[id]) {
				btns[id] = _.extend(btns[id], config);
				return this.buttons.set(btns);
			}
		});
	}

	getButtonById(id) {
		const allButtons = this.buttons.get();
		return allButtons[id];
	}

	getButtons(message, context, group) {
		let allButtons = _.toArray(this.buttons.get());

		if (group) {
			allButtons = allButtons.filter(button => button.group === group);
		}

		if (message) {
			allButtons = _.compact(_.map(allButtons, function(button) {
				if (button.context == null || button.context.includes(context)) {
					if (button.condition == null || button.condition(message, context)) {
						return button;
					}
				}
			}));
		}
		return _.sortBy(allButtons, 'order');
	}

	resetButtons() {
		return this.buttons.set({});
	}

	getPermaLink(msgId) {
		const roomData = ChatSubscription.findOne({
			rid: Session.get('openedRoom')
		});
		let routePath = document.location.pathname;
		if (roomData) {
			routePath = RocketChat.roomTypes.getRouteLink(roomData.t, roomData);
		}
		return `${ Meteor.absoluteUrl().replace(/\/$/, '') + routePath }?msg=${ msgId }`;
	}
};

Meteor.startup(function() {
	RocketChat.MessageAction.addButton({
		id: 'reply-message',
		icon: 'message',
		label: 'Reply',
		context: ['message', 'message-mobile'],
		action() {
			const message = this._arguments[1];
			const {input} = chatMessages[message.rid];
			input.value = `@${ message.u.username } `;
			$(input)
				.focus()
				.data('reply', message)
				.trigger('dataChange');
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}

			return true;
		},
		order: 1,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'edit-message',
		icon: 'edit',
		label: 'Edit',
		context: ['message', 'message-mobile'],
		action() {
			const messageId = this._arguments[1]._id;
			chatMessages[Session.get('openedRoom')].edit(document.getElementById(messageId));
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({
				rid: message.rid
			}) == null) {
				return false;
			}
			const hasPermission = RocketChat.authz.hasAtLeastOnePermission('edit-message', message.rid);
			const isEditAllowed = RocketChat.settings.get('Message_AllowEditing');
			const editOwn = message.u && message.u._id === Meteor.userId();
			if (!(hasPermission || (isEditAllowed && editOwn))) {
				return;
			}
			const blockEditInMinutes = RocketChat.settings.get('Message_AllowEditing_BlockEditInMinutes');
			if (blockEditInMinutes) {
				let msgTs;
				if (message.ts != null) {
					msgTs = moment(message.ts);
				}
				let currentTsDiff;
				if (msgTs != null) {
					currentTsDiff = moment().diff(msgTs, 'minutes');
				}
				return currentTsDiff < blockEditInMinutes;
			} else {
				return true;
			}
		},
		order: 2,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'delete-message',
		icon: 'trash',
		label: 'Delete',
		context: ['message', 'message-mobile'],
		color: 'alert',
		action() {
			const message = this._arguments[1];
			chatMessages[Session.get('openedRoom')].confirmDeleteMsg(message);
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}
			const forceDelete = RocketChat.authz.hasAtLeastOnePermission('force-delete-message', message.rid);
			const hasPermission = RocketChat.authz.hasAtLeastOnePermission('delete-message', message.rid);
			const isDeleteAllowed = RocketChat.settings.get('Message_AllowDeleting');
			const deleteOwn = message.u && message.u._id === Meteor.userId();
			if (!(hasPermission || (isDeleteAllowed && deleteOwn) || forceDelete)) {
				return;
			}
			const blockDeleteInMinutes = RocketChat.settings.get('Message_AllowDeleting_BlockDeleteInMinutes');
			if (forceDelete) {
				return true;
			}
			if (blockDeleteInMinutes != null && blockDeleteInMinutes !== 0) {
				let msgTs;
				if (message.ts != null) {
					msgTs = moment(message.ts);
				}
				let currentTsDiff;
				if (msgTs != null) {
					currentTsDiff = moment().diff(msgTs, 'minutes');
				}
				return currentTsDiff < blockDeleteInMinutes;
			} else {
				return true;
			}
		},
		order: 3,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'permalink',
		icon: 'permalink',
		label: 'Permalink',
		classes: 'clipboard',
		context: ['message', 'message-mobile'],
		action(event) {
			const message = this._arguments[1];
			const permalink = RocketChat.MessageAction.getPermaLink(message._id);
			if (Meteor.isCordova) {
				cordova.plugins.clipboard.copy(permalink);
			} else {
				$(event.currentTarget).attr('data-clipboard-text', permalink);
			}
			toastr.success(TAPi18n.__('Copied'));
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}

			return true;
		},
		order: 4,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'copy',
		icon: 'copy',
		label: 'Copy',
		classes: 'clipboard',
		context: ['message', 'message-mobile'],
		action(event) {
			const message = this._arguments[1].msg;
			if (Meteor.isCordova) {
				cordova.plugins.clipboard.copy(message);
			} else {
				$(event.currentTarget).attr('data-clipboard-text', message);
			}
			toastr.success(TAPi18n.__('Copied'));
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}

			return true;
		},
		order: 5,
		group: 'menu'
	});

	RocketChat.MessageAction.addButton({
		id: 'quote-message',
		icon: 'quote',
		label: 'Quote',
		context: ['message', 'message-mobile'],
		action() {
			const message = this._arguments[1];
			const {input} = chatMessages[message.rid];
			$(input)
				.focus()
				.data('reply', message)
				.trigger('dataChange');
		},
		condition(message) {
			if (RocketChat.models.Subscriptions.findOne({rid: message.rid}) == null) {
				return false;
			}

			return true;
		},
		order: 6,
		group: 'menu'
	});
});
<|endoftext|>


<commit_before>'use strict';

exports.init = function(req, res){
  if (req.isAuthenticated()) {
    res.redirect(req.user.defaultReturnUrl());
  }
  else {
    res.render('login/forgot/index');
  }
};

exports.send = function(req, res, next){
  console.log('in send');

  //res.send('test');

  var workflow = req.app.utility.workflow(req, res);

  workflow.on('validate', function() {
    console.log('in validate');
    if (!req.body.email) {
      workflow.outcome.errfor.email = 'required';
      return workflow.emit('response');
    }

    workflow.emit('generateToken');
  });

  workflow.on('generateToken', function() {
    console.log('in generateToken');
    var crypto = require('crypto');
    crypto.randomBytes(21, function(err, buf) {
      if (err) {
        return next(err);
      }

      var token = buf.toString('hex');
      console.log('before encryptPW');
      req.app.db.models.User.encryptPassword(token, function(err, hash) {
        if (err) {
          return next(err);
        }
        console.log('after encryptPW');
        workflow.emit('patchUser', token, hash);
      });
    });
  });

  workflow.on('patchUser', function(token, hash) {
    var starttime = new Date();
    starttime.setHours(starttime.getHours() + 4);
    // Get the iso time (GMT 0 == UTC 0)
    var isotime = new Date((new Date(starttime)).toISOString() );
    //isotime += 10000000;
    console.log('patchUser: resetPasswordExpires is ' + isotime);
    var conditions = { email: req.body.email.toLowerCase() };
    var fieldsToSet = {
      resetPasswordToken: hash,
      resetPasswordExpires: isotime //Date.now() + 10000000
    };

     req.app.db.models.User.findOne({
          where: conditions
       })
      .error(function(err) {
        // error callback
        console.log('Couldnt find user: ' + err);
        return workflow.emit('exception', err);
      })
      .success(function(user) {
          console.log('User returned.');
          //console.dir(user);

          if (!user) {
            return workflow.emit('exception', 'couldn\'t find user');
          }

          user.updateAttributes({
            resetPasswordToken: fieldsToSet.resetPasswordToken,
            resetPasswordExpires: fieldsToSet.resetPasswordExpires
          }).then(function(){
              if (err) {
                return workflow.emit('exception', err);
              }

              console.log('Saved user');

              workflow.emit('sendEmail', token, user);

            });

          //create Resetpassword record
          /*var resetPW = req.app.db.models.ResetPassword.build({
            userId: userId,
            resetPasswordToken: fieldsToSet.resetPasswordToken,
            resetPasswordExpires: fieldsToSet.resetPasswordExpires,
            isUsed: 'N',
          });*/

          // persist an instance
          //user.save()

          });
      });
  };

  workflow.on('sendEmail', function(token, user) {
    console.log('reached sendEmail: ');
    console.log(req.app.config.smtp.from.name +' <'+ req.app.config.smtp.from.address +'>');
    console.log(req.app.config.smtp.credentials.user + ', ' + req.app.config.smtp.credentials.host);
    req.app.utility.sendmail(req, res, {
      from: req.app.config.smtp.from.name +' <'+ req.app.config.smtp.from.address +'>',
      to: user.email,
      subject: 'Reset your '+ req.app.config.projectName +' password',
      textPath: 'login/forgot/email-text',
      htmlPath: 'login/forgot/email-html',
      locals: {
        username: user.username,
        resetLink: req.protocol +'://'+ req.headers.host +'?u=' + user.email + '&t=' + token, ///api/v1/login/reset/'+ user.email +'/'+ token +'/',
        projectName: req.app.config.projectName
      },
      success: function(message) {
        workflow.emit('response');
      },
      error: function(err) {
        workflow.outcome.errors.push('Error Sending: '+ err);
        workflow.emit('response');
      }
    });
  });

  workflow.emit('validate');

};

/*

*/<commit_msg>bracket issues<commit_after>'use strict';

exports.init = function(req, res){
  if (req.isAuthenticated()) {
    res.redirect(req.user.defaultReturnUrl());
  }
  else {
    res.render('login/forgot/index');
  }
};

exports.send = function(req, res, next){
  console.log('in send');

  //res.send('test');

  var workflow = req.app.utility.workflow(req, res);

  workflow.on('validate', function() {
    console.log('in validate');
    if (!req.body.email) {
      workflow.outcome.errfor.email = 'required';
      return workflow.emit('response');
    }

    workflow.emit('generateToken');
  });

  workflow.on('generateToken', function() {
    console.log('in generateToken');
    var crypto = require('crypto');
    crypto.randomBytes(21, function(err, buf) {
      if (err) {
        return next(err);
      }

      var token = buf.toString('hex');
      console.log('before encryptPW');
      req.app.db.models.User.encryptPassword(token, function(err, hash) {
        if (err) {
          return next(err);
        }
        console.log('after encryptPW');
        workflow.emit('patchUser', token, hash);
      });
    });
  });

  workflow.on('patchUser', function(token, hash) {
    var starttime = new Date();
    starttime.setHours(starttime.getHours() + 4);
    // Get the iso time (GMT 0 == UTC 0)
    var isotime = new Date((new Date(starttime)).toISOString() );
    //isotime += 10000000;
    console.log('patchUser: resetPasswordExpires is ' + isotime);
    var conditions = { email: req.body.email.toLowerCase() };
    var fieldsToSet = {
      resetPasswordToken: hash,
      resetPasswordExpires: isotime //Date.now() + 10000000
    };

     req.app.db.models.User.findOne({
          where: conditions
       })
      .error(function(err) {
        // error callback
        console.log('Couldnt find user: ' + err);
        return workflow.emit('exception', err);
      })
      .success(function(user) {
          console.log('User returned.');
          //console.dir(user);

          if (!user) {
            return workflow.emit('exception', 'couldn\'t find user');
          }

          user.updateAttributes({
            resetPasswordToken: fieldsToSet.resetPasswordToken,
            resetPasswordExpires: fieldsToSet.resetPasswordExpires
          }).then(function(){
              if (err) {
                return workflow.emit('exception', err);
              }

              console.log('Saved user');

              workflow.emit('sendEmail', token, user);

            });

          //create Resetpassword record
          /*var resetPW = req.app.db.models.ResetPassword.build({
            userId: userId,
            resetPasswordToken: fieldsToSet.resetPasswordToken,
            resetPasswordExpires: fieldsToSet.resetPasswordExpires,
            isUsed: 'N',
          });*/

          // persist an instance
          //user.save()

          });
      });

  workflow.on('sendEmail', function(token, user) {
    console.log('reached sendEmail: ');
    console.log(req.app.config.smtp.from.name +' <'+ req.app.config.smtp.from.address +'>');
    console.log(req.app.config.smtp.credentials.user + ', ' + req.app.config.smtp.credentials.host);
    req.app.utility.sendmail(req, res, {
      from: req.app.config.smtp.from.name +' <'+ req.app.config.smtp.from.address +'>',
      to: user.email,
      subject: 'Reset your '+ req.app.config.projectName +' password',
      textPath: 'login/forgot/email-text',
      htmlPath: 'login/forgot/email-html',
      locals: {
        username: user.username,
        resetLink: req.protocol +'://'+ req.headers.host +'?u=' + user.email + '&t=' + token, ///api/v1/login/reset/'+ user.email +'/'+ token +'/',
        projectName: req.app.config.projectName
      },
      success: function(message) {
        workflow.emit('response');
      },
      error: function(err) {
        workflow.outcome.errors.push('Error Sending: '+ err);
        workflow.emit('response');
      }
    });
  });

  workflow.emit('validate');

};

/*

*/<|endoftext|>


<commit_before>Ext.ns('Garp.dataTypes');
Garp.dataTypes.Image.on('init', function(){

	this.iconCls = 'icon-img';

	// Thumbnail column:
	this.insertColumn(0, {
		header: '' + __('Image') + '',
		dataIndex: 'id',
		width: 84,
		fixed: true,
		renderer: Garp.renderers.imageRelationRenderer,
		hidden: false
	});

	this.addListener('loaddata', function(rec, formPanel){
		function updateUI(){
			formPanel.preview.update(Garp.renderers.imagePreviewRenderer(rec.get('filename'), null, rec));
			formPanel.download.update({
				filename: rec.get('filename')
			});
		}
		if (formPanel.rendered) {
			updateUI();
		} else {
			formPanel.on('show', updateUI, null, {
				single: true
			});
		}
		// if we're in a relateCreateWindow, set it's height again, otherwise it might not fit.
		if (typeof formPanel.center == 'function' && rec.get('filename')) {
			formPanel.setHeight(440);
			formPanel.center();
		}
	}, true);

	// Remove these fields, cause we are about to change the order and appearance of them...
	this.removeField('caption');
	this.removeField('filename');
	this.removeField('id');

	// ...include them again:
	this.insertField(0, {
		xtype: 'fieldset',
		style: 'margin: 0;padding:0;',
		items: [{
			name: 'id',
			hideFieldLabel: true,
			disabled: true,
			xtype: 'numberfield',
			hidden: true,
			ref: '../../../../_id'
		}, {
			name: 'filename',
			fieldLabel: __('Filename'),
			xtype: 'uploadfield',
			allowBlank: false,
			emptyText: __('Drag image here, or click browse button'),
			uploadURL: BASE + 'g/content/upload/type/image',
			ref: '../../../../filename',

			listeners: {
				'change': function(field, val){
					if (this.refOwner._id.getValue()) {
						var url = BASE + 'admin?' +
						Ext.urlEncode({
							model: Garp.currentModel,
							id: this.refOwner._id.getValue()
						});
						if (DEBUG) {
							url += '#DEBUG';
						}

						// because images won't reload if their ID is
						// still the same, we need to reload the page
						this.refOwner.formcontent.on('loaddata', function(){
							var lm = new Ext.LoadMask(Ext.getBody());
							lm.show();
							document.location.href = url;
						});
						this.refOwner.fireEvent('save-all');
					} else {
						field.originalValue = ''; // mark dirty
						this.refOwner.preview.update(Garp.renderers.uploadedImagePreviewRenderer(val));
						this.refOwner.download.update({
							filename: val
						});
						this.refOwner.fireEvent('save-all');
					}
					return true;
				}
			}
		}, {
			xtype: 'box',
			cls: 'garp-notification-boxcomponent',
			html: __('Only {1} and {2} files with a maximum of {3} MB are accepted', 'jpg, png', 'gif', '20'),
			fieldLabel: ' '
		}, {
			name: 'caption',
			xtype: 'textfield',
			fieldLabel: __('Caption')
		}, {
			xtype: 'box',
			ref: '../../../../preview',
			fieldLabel: __('Preview'),
			cls: 'preview',
			html: ''
		}, {
			xtype: 'box',
			hidden: false,
			ref: '../../../../download',
			fieldLabel: ' ',
			hideFieldLabel: false,
			tpl: new Ext.XTemplate('<tpl if="filename">', '' + __('View original file') + '', '</tpl>')
		}]
	});

	// Wysiwyg Editor
	this.Wysiwyg = Ext.extend(Garp.WysiwygAbstract, {

		model: 'Image',

		idProperty: 'id',

		settingsMenu: true,

		margin: 0,

		getData: function(){
			return {
				id: this._data.id,
				caption: this._data.caption
			};
		},

		// override: we don't need filtering for images:
		filterHtml: function(){
			return true;
		},

		setCaption: function(text){
			this._data.caption = text;
			this.el.child('.caption').update(text);
			this.el.child('.caption').setDisplayed( text ? true : false);
		},

		showCaptionEditor: function(setPosition){
			if (!this.captionEditor) {
				this.captionEditor = new Ext.Editor({
					alignment: 'tl',
					autoSize: true,
					field: {
						selectOnFocus: true,
						xtype: 'textfield',
						width: '100%',
						anchor: '99%'
					}
				});
			}
			this.el.child('.caption').setDisplayed(true);
			// if the user clicks the upper 'settings' menu; the caption el should be displayed there,
			//... not below the image, where it might be "below the fold"
			if (!setPosition) {
				this.el.child('.caption').setStyle('position', 'static');
			}
			this.captionEditor.startEdit(this.el.child('.caption'), this._data.caption);
			this.captionEditor.on('complete', function(f, v){
				this.setCaption(v);
				this.el.child('.caption').setStyle('position','absolute');
			}, this);
		},

		getMenuOptions: function(){
			return [{
				group: '',
				text: 'Add / remove caption',
				handler: this.showCaptionEditor.createDelegate(this, [false])
			}];
		},

		/**
		 * After pick:
		 */
		pickerHandler: function(sel, afterInitCb){
			this._data = {
				id: sel.data.id,
				caption: sel.data.caption
			};
			var args = Array.prototype.slice.call(arguments);
			args.shift();
			afterInitCb.call(this, args);
		},

		/**
		 *
		 * @param {Object} afterInitCb
		 */
		beforeInit: function(afterInitCb){
			var args = arguments;
			// Do we need to present a dialog or not?
			if(this._data && this._data[this.idProperty]){
				afterInitCb.call(this, args);
				return;
			}
			var picker = new Garp.ModelPickerWindow({
				model: this.model,
				listeners: {
					select: function(sel){
						if (sel.selected) {
							this.pickerHandler(sel.selected, afterInitCb);
						} else {
							this.destroy();
						}
						picker.close();
					},
					scope: this
				}
			});
			picker.show();
		},

		/**
		 * Sets content height based on width (maintains aspect ratio)
		 * @param {Number} new width
		 * @returns height
		 */
		resizeContent: function(nw){
			var i = this._data;
			var aspct = i.height / i.width;
			var nHeight = (nw * aspct) - this.margin;
			this.contentEditableEl.setHeight(nHeight);
			this.contentEditableEl.child('.img').setHeight(nHeight);
			return nHeight;
		},

		setContent: function(){
			this.contentEditableEl = this.el.child('.contenteditable');
			this.contentEditableEl.update('');
			this.contentEditableEl.dom.setAttribute('contenteditable', false);

			var i = new Image();
			var scope = this;
			var path = IMAGES_CDN + 'scaled/cms_preview/' + this._data[this.idProperty];
			i.onerror = function(){
				scope.contentEditableEl.setStyle({
					position: 'relative',
					padding: 0
				});
				scope.contentEditableEl.update('
' + __('Image not found') + '
');
			};
			i.onload = function(){

				Ext.apply(scope._data, {
					width: i.width,
					height: i.height
				});

				scope.contentEditableEl.setStyle({
					position: 'relative',
					padding: 0
				});

				scope.contentEditableEl.update('

');
				scope.contentEditableEl.child('.img').setStyle({
					backgroundImage: 'url("' + path + '")'
				});
				var captionEl = scope.contentEditableEl.child('.caption');
				if (scope._data.caption) {
					captionEl.update(scope._data.caption);
					captionEl.show();
					captionEl.on('click', scope.showCaptionEditor, scope);
				} else {
					captionEl.hide();
				}

				scope.resizeContent(scope.contentEditableEl.getWidth());
				if (scope.ownerCt) {
					scope.ownerCt.doLayout();
				}
			};
			i.src = path;
			if (i.complete) {
				i.onload();
			}
		},

		/**
		 * init!
		 * @param {Object} ct
		 */
		initComponent: function(ct){

			this.html += '

';

			this.addClass('wysiwyg-image');
			this.addClass('wysiwyg-box');
			if (this.col) {
				this.addClass(this.col);
			}
			this.on('user-resize', function(w, nw){
				this.setHeight(this.resizeContent(nw));
			});
			this.on('afterrender', this.setContent, this);
			Garp.dataTypes.Image.Wysiwyg.superclass.initComponent.call(this, arguments);
		}
	});
});
<commit_msg>Image filename-notification dynamic<commit_after>Ext.ns('Garp.dataTypes');
Garp.dataTypes.Image.on('init', function(){

	this.iconCls = 'icon-img';

	// Thumbnail column:
	this.insertColumn(0, {
		header: '' + __('Image') + '',
		dataIndex: 'id',
		width: 84,
		fixed: true,
		renderer: Garp.renderers.imageRelationRenderer,
		hidden: false
	});

	this.addListener('loaddata', function(rec, formPanel){
		function updateUI(){
			formPanel.preview.update(Garp.renderers.imagePreviewRenderer(rec.get('filename'), null, rec));
			formPanel.download.update({
				filename: rec.get('filename')
			});
		}
		if (formPanel.rendered) {
			updateUI();
		} else {
			formPanel.on('show', updateUI, null, {
				single: true
			});
		}
		// if we're in a relateCreateWindow, set it's height again, otherwise it might not fit.
		if (typeof formPanel.center == 'function' && rec.get('filename')) {
			formPanel.setHeight(440);
			formPanel.center();
		}
	}, true);

	// Remove these fields, cause we are about to change the order and appearance of them...
	this.removeField('caption');
	this.removeField('filename');
	this.removeField('id');

	var fDesc = this.getField('filename-notification');
	if (fDesc) {
		this.removeField('filename-notification');
	} else {
		fDesc = {
			xtype: 'box',
			cls: 'garp-notification-boxcomponent',
			name: 'filename-notification',
			html: __('Only {1} and {2} files with a maximum of {3} MB are accepted', 'jpg, png', 'gif', '20'),
			fieldLabel: ' '
		};
	}

	// ...include them again:
	this.insertField(0, {
		xtype: 'fieldset',
		style: 'margin: 0;padding:0;',
		items: [{
			name: 'id',
			hideFieldLabel: true,
			disabled: true,
			xtype: 'numberfield',
			hidden: true,
			ref: '../../../../_id'
		}, {
			name: 'filename',
			fieldLabel: __('Filename'),
			xtype: 'uploadfield',
			allowBlank: false,
			emptyText: __('Drag image here, or click browse button'),
			uploadURL: BASE + 'g/content/upload/type/image',
			ref: '../../../../filename',

			listeners: {
				'change': function(field, val){
					if (this.refOwner._id.getValue()) {
						var url = BASE + 'admin?' +
						Ext.urlEncode({
							model: Garp.currentModel,
							id: this.refOwner._id.getValue()
						});
						if (DEBUG) {
							url += '#DEBUG';
						}

						// because images won't reload if their ID is
						// still the same, we need to reload the page
						this.refOwner.formcontent.on('loaddata', function(){
							var lm = new Ext.LoadMask(Ext.getBody());
							lm.show();
							document.location.href = url;
						});
						this.refOwner.fireEvent('save-all');
					} else {
						field.originalValue = ''; // mark dirty
						this.refOwner.preview.update(Garp.renderers.uploadedImagePreviewRenderer(val));
						this.refOwner.download.update({
							filename: val
						});
						this.refOwner.fireEvent('save-all');
					}
					return true;
				}
			}
		}, fDesc, {
			name: 'caption',
			xtype: 'textfield',
			fieldLabel: __('Caption')
		}, {
			xtype: 'box',
			ref: '../../../../preview',
			fieldLabel: __('Preview'),
			cls: 'preview',
			html: ''
		}, {
			xtype: 'box',
			hidden: false,
			ref: '../../../../download',
			fieldLabel: ' ',
			hideFieldLabel: false,
			tpl: new Ext.XTemplate('<tpl if="filename">', '' + __('View original file') + '', '</tpl>')
		}]
	});

	// Wysiwyg Editor
	this.Wysiwyg = Ext.extend(Garp.WysiwygAbstract, {

		model: 'Image',

		idProperty: 'id',

		settingsMenu: true,

		margin: 0,

		getData: function(){
			return {
				id: this._data.id,
				caption: this._data.caption
			};
		},

		// override: we don't need filtering for images:
		filterHtml: function(){
			return true;
		},

		setCaption: function(text){
			this._data.caption = text;
			this.el.child('.caption').update(text);
			this.el.child('.caption').setDisplayed( text ? true : false);
		},

		showCaptionEditor: function(setPosition){
			if (!this.captionEditor) {
				this.captionEditor = new Ext.Editor({
					alignment: 'tl',
					autoSize: true,
					field: {
						selectOnFocus: true,
						xtype: 'textfield',
						width: '100%',
						anchor: '99%'
					}
				});
			}
			this.el.child('.caption').setDisplayed(true);
			// if the user clicks the upper 'settings' menu; the caption el should be displayed there,
			//... not below the image, where it might be "below the fold"
			if (!setPosition) {
				this.el.child('.caption').setStyle('position', 'static');
			}
			this.captionEditor.startEdit(this.el.child('.caption'), this._data.caption);
			this.captionEditor.on('complete', function(f, v){
				this.setCaption(v);
				this.el.child('.caption').setStyle('position','absolute');
			}, this);
		},

		getMenuOptions: function(){
			return [{
				group: '',
				text: 'Add / remove caption',
				handler: this.showCaptionEditor.createDelegate(this, [false])
			}];
		},

		/**
		 * After pick:
		 */
		pickerHandler: function(sel, afterInitCb){
			this._data = {
				id: sel.data.id,
				caption: sel.data.caption
			};
			var args = Array.prototype.slice.call(arguments);
			args.shift();
			afterInitCb.call(this, args);
		},

		/**
		 *
		 * @param {Object} afterInitCb
		 */
		beforeInit: function(afterInitCb){
			var args = arguments;
			// Do we need to present a dialog or not?
			if(this._data && this._data[this.idProperty]){
				afterInitCb.call(this, args);
				return;
			}
			var picker = new Garp.ModelPickerWindow({
				model: this.model,
				listeners: {
					select: function(sel){
						if (sel.selected) {
							this.pickerHandler(sel.selected, afterInitCb);
						} else {
							this.destroy();
						}
						picker.close();
					},
					scope: this
				}
			});
			picker.show();
		},

		/**
		 * Sets content height based on width (maintains aspect ratio)
		 * @param {Number} new width
		 * @returns height
		 */
		resizeContent: function(nw){
			var i = this._data;
			var aspct = i.height / i.width;
			var nHeight = (nw * aspct) - this.margin;
			this.contentEditableEl.setHeight(nHeight);
			this.contentEditableEl.child('.img').setHeight(nHeight);
			return nHeight;
		},

		setContent: function(){
			this.contentEditableEl = this.el.child('.contenteditable');
			this.contentEditableEl.update('');
			this.contentEditableEl.dom.setAttribute('contenteditable', false);

			var i = new Image();
			var scope = this;
			var path = IMAGES_CDN + 'scaled/cms_preview/' + this._data[this.idProperty];
			i.onerror = function(){
				scope.contentEditableEl.setStyle({
					position: 'relative',
					padding: 0
				});
				scope.contentEditableEl.update('
' + __('Image not found') + '
');
			};
			i.onload = function(){

				Ext.apply(scope._data, {
					width: i.width,
					height: i.height
				});

				scope.contentEditableEl.setStyle({
					position: 'relative',
					padding: 0
				});

				scope.contentEditableEl.update('

');
				scope.contentEditableEl.child('.img').setStyle({
					backgroundImage: 'url("' + path + '")'
				});
				var captionEl = scope.contentEditableEl.child('.caption');
				if (scope._data.caption) {
					captionEl.update(scope._data.caption);
					captionEl.show();
					captionEl.on('click', scope.showCaptionEditor, scope);
				} else {
					captionEl.hide();
				}

				scope.resizeContent(scope.contentEditableEl.getWidth());
				if (scope.ownerCt) {
					scope.ownerCt.doLayout();
				}
			};
			i.src = path;
			if (i.complete) {
				i.onload();
			}
		},

		/**
		 * init!
		 * @param {Object} ct
		 */
		initComponent: function(ct){

			this.html += '

';

			this.addClass('wysiwyg-image');
			this.addClass('wysiwyg-box');
			if (this.col) {
				this.addClass(this.col);
			}
			this.on('user-resize', function(w, nw){
				this.setHeight(this.resizeContent(nw));
			});
			this.on('afterrender', this.setContent, this);
			Garp.dataTypes.Image.Wysiwyg.superclass.initComponent.call(this, arguments);
		}
	});
});
<|endoftext|>