<commit_before>/** @jsx m */

"use strict";
require("./ArticlePage.scss");

var _ = require("underscore");
var m = require("mithril");

var Layout = require("../layouts/DefaultLayout.js");
var OnUnload = require("../utils/OnUnload.js");

var Spinner = require("../components/Spinner.js");
var PillList = require("../components/PillList.js");
var StudiesTable = require("../components/StudiesTable.js");
var CommentBox = require("../components/CommentBox.js");
var Popover = require("../components/Popover.js");

var ArticleModel = require("../models/ArticleModel.js");
var AuthorModel = require("../models/AuthorModel.js");

var ArticlePage = {};

ArticlePage.controller = function(options) {
  OnUnload(this);
  var _this = this;
  this.user = options.user;

  if (m.route.param("articleId") === "new" && this.user.canEdit()) {
    this.article = new ArticleModel({});
    this.editing = m.prop(true);
  } else {
    this.article = new ArticleModel({id: m.route.param("articleId")});
    this.article.fetch();
    this.article.get("studies").fetch({data: {replications: true}});
    this.article.get("comments").fetch();
    this.editing = m.prop(false);
  }
  this.saving = m.prop(false);
  window.article = this.article;

  options = _.extend({id: "ArticlePage"}, options);
  this.controllers.layout = new Layout.controller(options);
  this.controllers.commentBox = new CommentBox.controller({comments: this.article.get("comments"), user: this.user});
  this.controllers.studiesTable = new StudiesTable.controller({article: article, user: this.user});
  this.controllers.tagsList = new PillList.controller({
    editable: this.editing,
    model: this.article
  });

  this.controllers.authorsList = new PillList.controller({
    editable: this.editing,
    collection: this.article.get("authors")
  });

  this.editClick = function() {
    if (_this.user.canEdit()) {
      _this.editing(true);
    }
  };

  this.saveClick = function() {
    _this.saving(true);
    var res = _this.article.save();
    res.then(function() {
      _this.saving(false);
    }, function() {
      _this.saving(false);
      if (!_this.article.hasErrors()) {
        _this.editing(false);
      }
    });
    if (_this.article.isNew()) {
      res.then(function() {
        if (!_this.article.isNew()) {
          m.route("/articles/" + _this.article.get("id"));
        }
      });
    }
  };

  this.discardClick = function() {
    if (_this.article.isNew()) {
      m.route("/articles/new");
    } else {
      _this.article.set(_this.article._serverState);
      _this.editing(false);
      _this.saving(false);
    }
  };

  this.updateReviewerNum = function(num) {
    return function(val) {
      var reviewers = _this.article.get("reviewers");
      reviewers[num] = val;
      _this.article.set("reviewers", _.compact(reviewers));
    };
  };
};

ArticlePage.view = function(ctrl) {
  var article = ctrl.article;
  var content;

  if (article) {
    document.title = _.compact([
      article.get("authors").etAl(1),
      article.get("journal"),
      article.get("year")
    ]).join(", ");

    var peerReviewers = _.map(article.get("reviewers").filter(function(reviewer) { return !_.isEmpty(reviewer); }).concat([""]), function(reviewer, i) {
      return
{reviewer}
;
    });

    var tags = new PillList.view(ctrl.controllers.tagsList);
    var authors = new PillList.view(ctrl.controllers.authorsList, { pillView: authorPillView });

    if (ctrl.user.canEdit()) {
      var editButtons;
      if (ctrl.editing()) {
        editButtons = [
          <button type="button" className="btn" key="save" onclick={ctrl.saveClick} disabled={ctrl.saving()}>{ctrl.saving() ? "Saving..." : "Save"}</button>,
          <button type="button" className="btn" key="discard" onclick={ctrl.discardClick} disabled={ctrl.saving()}>Discard</button>,
        ];
      } else {
        editButtons = <button type="button" className="btn" key="edit" onclick={ctrl.editClick}>Edit</button>;
      }
    }

    if (!_.isEmpty(article.get("year"))) {
      var year = "(" + article.get("year") + ")";
    }

    var studiesTable, commentsList;
    if (article.isNew()) {
      studiesTable =
You must save this new article before you can add studies.
;
      commentsList =
You must save this new article before you can leave comments.
;
    } else {
      studiesTable = new StudiesTable.view(ctrl.controllers.studiesTable);
      commentsList = new CommentBox.view(ctrl.controllers.commentBox);
    }

    if (article.hasErrors()) {
      var errors = _.map(article.errors(), function(error) {
        return
{error}
;
      });

      var errorMessage =

        {errors}

;
    }

    content = (

        {errorMessage}

{article.get("title")}

{article.get("year")}

{authors}

Abstract

{article.get("abstract")}

              {editButtons}
              <button type="button" key="bookmark" title="Bookmark article" className={"btn bookmark " + (ctrl.user.hasArticleBookmarked(article) ? "active" : "")} onclick={toggleBookmark(article, ctrl.user)}></button>

Journal

{article.get("journal")}

DOI

{article.get("doi")}

Tags

{tags}

Studies and replications

          {studiesTable}

Comments

              {commentsList}

Peer Review

Action Editor

{article.get("action_editor")}

Reviewers

                {peerReviewers}

    );
  } else {
    content =
Article not found
;
  }

  return new Layout.view(ctrl.controllers.layout, content);
};

// helpers
function toggleBookmark(article, user) {
  return function(e) {
    return user.toggleArticleBookmark(article);
  };
};

function authorPillView(pill, options) {
  options = options || {};

  if (options.onRemoveClick) {
    var removeIcon =
  }
  return
{pill.label} {removeIcon}

}

module.exports = ArticlePage;
<commit_msg>ArticlePage: set publication_date and don't show edit mode after save<commit_after>/** @jsx m */

"use strict";
require("./ArticlePage.scss");

var _ = require("underscore");
var m = require("mithril");

var Layout = require("../layouts/DefaultLayout.js");
var OnUnload = require("../utils/OnUnload.js");

var Spinner = require("../components/Spinner.js");
var PillList = require("../components/PillList.js");
var StudiesTable = require("../components/StudiesTable.js");
var CommentBox = require("../components/CommentBox.js");
var Popover = require("../components/Popover.js");

var ArticleModel = require("../models/ArticleModel.js");
var AuthorModel = require("../models/AuthorModel.js");

var ArticlePage = {};

ArticlePage.controller = function(options) {
  OnUnload(this);
  var _this = this;
  this.user = options.user;

  if (m.route.param("articleId") === "new" && this.user.canEdit()) {
    this.article = new ArticleModel({});
    this.editing = m.prop(true);
  } else {
    this.article = new ArticleModel({id: m.route.param("articleId")});
    this.article.fetch();
    this.article.get("studies").fetch({data: {replications: true}});
    this.article.get("comments").fetch();
    this.editing = m.prop(false);
  }
  this.saving = m.prop(false);
  window.article = this.article;

  options = _.extend({id: "ArticlePage"}, options);
  this.controllers.layout = new Layout.controller(options);
  this.controllers.commentBox = new CommentBox.controller({comments: this.article.get("comments"), user: this.user});
  this.controllers.studiesTable = new StudiesTable.controller({article: article, user: this.user});
  this.controllers.tagsList = new PillList.controller({
    editable: this.editing,
    model: this.article
  });

  this.controllers.authorsList = new PillList.controller({
    editable: this.editing,
    collection: this.article.get("authors")
  });

  this.editClick = function() {
    if (_this.user.canEdit()) {
      _this.editing(true);
    }
  };

  this.saveClick = function() {
    _this.saving(true);
    var res = _this.article.save();
    res.then(function() {
      _this.saving(false);
      _this.editing(false);
      m.redraw();
    }, function() {
      _this.saving(false);
      if (!_this.article.hasErrors()) {
        _this.editing(false);
      }
      m.redraw();
    });
    if (_this.article.isNew()) {
      res.then(function() {
        if (!_this.article.isNew()) {
          m.route("/articles/" + _this.article.get("id"));
        }
      });
    }
  };

  this.discardClick = function() {
    if (_this.article.isNew()) {
      m.route("/articles/new");
    } else {
      _this.article.set(_this.article._serverState);
      _this.editing(false);
      _this.saving(false);
    }
  };

  this.updateReviewerNum = function(num) {
    return function(val) {
      var reviewers = _this.article.get("reviewers");
      reviewers[num] = val;
      _this.article.set("reviewers", _.compact(reviewers));
    };
  };
};

ArticlePage.view = function(ctrl) {
  var article = ctrl.article;
  var content;

  if (article) {
    document.title = _.compact([
      article.get("authors").etAl(1),
      article.get("journal"),
      article.get("year")
    ]).join(", ");

    var peerReviewers = _.map(article.get("reviewers").filter(function(reviewer) { return !_.isEmpty(reviewer); }).concat([""]), function(reviewer, i) {
      return
{reviewer}
;
    });

    var tags = new PillList.view(ctrl.controllers.tagsList);
    var authors = new PillList.view(ctrl.controllers.authorsList, { pillView: authorPillView });

    if (ctrl.user.canEdit()) {
      var editButtons;
      if (ctrl.editing()) {
        editButtons = [
          <button type="button" className="btn" key="save" onclick={ctrl.saveClick} disabled={ctrl.saving()}>{ctrl.saving() ? "Saving..." : "Save"}</button>,
          <button type="button" className="btn" key="discard" onclick={ctrl.discardClick} disabled={ctrl.saving()}>Discard</button>,
        ];
      } else {
        editButtons = <button type="button" className="btn" key="edit" onclick={ctrl.editClick}>Edit</button>;
      }
    }

    var studiesTable, commentsList;
    if (article.isNew()) {
      studiesTable =
You must save this new article before you can add studies.
;
      commentsList =
You must save this new article before you can leave comments.
;
    } else {
      studiesTable = new StudiesTable.view(ctrl.controllers.studiesTable);
      commentsList = new CommentBox.view(ctrl.controllers.commentBox);
    }

    if (article.hasErrors()) {
      var errors = _.map(article.errors(), function(error) {
        return
{error}
;
      });

      var errorMessage =

        {errors}

;
    }

    content = (

        {errorMessage}

{article.get("title")}

{article.get("publication_date")}

{authors}

Abstract

{article.get("abstract")}

              {editButtons}
              <button type="button" key="bookmark" title="Bookmark article" className={"btn bookmark " + (ctrl.user.hasArticleBookmarked(article) ? "active" : "")} onclick={toggleBookmark(article, ctrl.user)}></button>

Journal

{article.get("journal")}

DOI

{article.get("doi")}

Tags

{tags}

Studies and replications

          {studiesTable}

Comments

              {commentsList}

Peer Review

Action Editor

{article.get("action_editor")}

Reviewers

                {peerReviewers}

    );
  } else {
    content =
Article not found
;
  }

  return new Layout.view(ctrl.controllers.layout, content);
};

// helpers
function toggleBookmark(article, user) {
  return function(e) {
    return user.toggleArticleBookmark(article);
  };
};

function authorPillView(pill, options) {
  options = options || {};

  if (options.onRemoveClick) {
    var removeIcon =
  }
  return
{pill.label} {removeIcon}

}

module.exports = ArticlePage;
<|endoftext|>


<commit_before>import api from '../api'
import axios from 'axios'
import config from '@/config'
import modelUser from '@/api/user'
// import _ from 'lodash'
export default {
  getSchema ({ commit }) {
    commit('SET_SCHEMA', [])
    api.request('get', '/schema')
      .then(response => {
        // console.log('hdhd::', _.reject(response.data, { 'isdeleted': true }))
        commit('SET_SCHEMA', response.data)
      })
      .catch(error => {
        console.log(error)
        commit('SET_SCHEMA', [])
      })
  },
  getSettings ({ commit }) {
    commit('SET_SETTINGS', [])
    api.request('get', '/settings')
      .then(response => {
        commit('SET_SETTINGS', response.data)
      })
      .catch(error => {
        console.log(error)
        // commit('SET_SETTINGS', [])
      })
  },
  getTabdata ({commit}, text) {
    commit('SET_TABDATA', text)
    // console.log('getTabdata', text)
  },
  getFlowzdata ({ commit }) {
    commit('SET_FLOWZDATA', [])
    api.request('get', '/flowz')
      .then(response => {
        commit('SET_FLOWZDATA', response.data.data)
      })
      .catch(error => {
        console.log(error)
        commit('SET_FLOWZDATA', [])
      })
  },
  setMapTemp ({commit}, text) {
    // console.log('getMapTempflag', text)
    commit('SET_MAPTEMP', text)
  },
  delThisMapTemp ({commit}, text) {
    // console.log('getMapTempflag', text)
    commit('SET_DELTHISMAPTEMP', text)
  },
  setXMLtoLocalStorage ({commit}, text) {
    commit('SET_XML', text)
  },
  removeXMLtoLocalStorage ({commit}) {
    commit('REMOVE_XML')
  },
  authenticate ({ commit }, authToken) {
    return axios({
      method: 'get',
      url: config.loginURL + '/userdetails',
      headers: {
        'authorization': authToken
      }
    })
    .then(response => {
      if (response) {
        return response.data.data
      } else {
        return
      }
    })
  },
  getUser ({ commit }, email) {
    return modelUser.getByParam(email).then((response) => {
      if (response && response.data.data.length > 0) {
        return response.data.data[0]
      }
    })
  }
}
<commit_msg>action.js modified<commit_after>import api from '../api'
import axios from 'axios'
import config from '@/config'
import modelUser from '@/api/user'
// import _ from 'lodash'
export default {
  getSchema ({ commit }) {
    commit('SET_SCHEMA', [])
    api.request('get', '/schema')
      .then(response => {
        // console.log('hdhd::', _.reject(response.data, { 'isdeleted': true }))
        commit('SET_SCHEMA', response.data)
      })
      .catch(error => {
        console.log(error)
        commit('SET_SCHEMA', [])
      })
  },
  getSettings ({ commit }) {
    commit('SET_SETTINGS', [])
    api.request('get', '/settings')
      .then(response => {
        commit('SET_SETTINGS', response.data)
      })
      .catch(error => {
        console.log(error)
        // commit('SET_SETTINGS', [])
      })
  },
  getTabdata ({commit}, text) {
    commit('SET_TABDATA', text)
    // console.log('getTabdata', text)
  },
  getFlowzdata ({ commit }) {
    // commit('SET_FLOWZDATA', [])
    api.request('get', '/flowz')
      .then(response => {
        commit('SET_FLOWZDATA', response.data.data)
      })
      .catch(error => {
        console.log(error)
        commit('SET_FLOWZDATA', [])
      })
  },
  setMapTemp ({commit}, text) {
    // console.log('getMapTempflag', text)
    commit('SET_MAPTEMP', text)
  },
  delThisMapTemp ({commit}, text) {
    // console.log('getMapTempflag', text)
    commit('SET_DELTHISMAPTEMP', text)
  },
  setXMLtoLocalStorage ({commit}, text) {
    commit('SET_XML', text)
  },
  removeXMLtoLocalStorage ({commit}) {
    commit('REMOVE_XML')
  },
  authenticate ({ commit }, authToken) {
    return axios({
      method: 'get',
      url: config.loginURL + '/userdetails',
      headers: {
        'authorization': authToken
      }
    })
    .then(response => {
      if (response) {
        return response.data.data
      } else {
        return
      }
    })
  },
  getUser ({ commit }, email) {
    return modelUser.getByParam(email).then((response) => {
      if (response && response.data.data.length > 0) {
        return response.data.data[0]
      }
    })
  }
}
<|endoftext|>


<commit_before>import axios from 'axios';

//
//INITIAL STATE
//

export const initialAuthState = {
  isLoggedIn: null,
};

/**
 * ACTION TYPES
 */

const AUTH_SUCCESS = 'AUTH_SUCCESS';
const AUTH_FAIL = 'AUTH_FAIL';
const DEAUTHENTICATE_USER = 'DEAUTHENTICATE_USER';
const SET_CURRENT_USER = 'SET_CURRENT_USER';

/**
 * ACTION CREATORS
 */

const authSuccess = user => {
  return {type: AUTH_SUCCESS, user};
};

const authFail = () => {
  return {type: AUTH_FAIL };
};

export const deAuthenticateUser = () => {
  return {type: DEAUTHENTICATE_USER};
};

const setCurrentUser = user => {
  return {type: SET_CURRENT_USER, user};
};

/**
 * THUNK CREATORS
 */

 export const checkIfLoggedIn = () => dispatch => {
   const jwtOptions = {
    	strategy: "jwt",
      accessToken: localStorage.getItem('jwt')
    };
   const userEmail = localStorage.getItem('email');
   axios.post('/authentication', jwtOptions)
     .then(result => result.data)
     .then((response) => dispatch(authSuccess()))
     .then(() => axios.get(`/api/users?email=${userEmail}`))
     .then((res) => res.data)
     .then(userArray => {
       dispatch(setCurrentUser(userArray[0]));
     })
     .catch((err) => {
     console.log('not logged in currently');
   });
 };

 export const attemptAuth = (user, history) => dispatch => {
   axios.post('/authentication', user)
     .then(result => result.data)
     .then(payload => {
       localStorage.setItem('jwt', payload.accessToken);
       localStorage.setItem('email', user.email);
       return dispatch(authSuccess());
     })
     .then(() => axios.get(`/api/users?email=${user.email}`))
     .then((res) => res.data)
     .then(userArray => {
       dispatch(setCurrentUser(userArray[0]));
       history.push('/dashboard')
     })
     .catch((err) => dispatch(authFail()));
 };

 // export const deauthUser = user => dispatch => {
 //   axios.post('/api/authentiction', user)
 //     .then(result => result.data)
 //     .then(newUser => {
 //       dispatch(de(newUser));
 //     })
 //     .catch(console.error);
 // };

 /**
  * REDUCER
  */

 export default function reducer (state = initialAuthState, action) {
   switch (action.type) {
   case AUTH_SUCCESS:
     return Object.assign({}, state, {isLoggedIn: true});
   case AUTH_FAIL:
     return Object.assign({}, state, {isLoggedIn: false});
   case DEAUTHENTICATE_USER:
     return initialAuthState;
   case SET_CURRENT_USER:
     return Object.assign({}, state, action.user);
   default:
     return state;
   }
 }
<commit_msg>user routes to their artist page instead of dashboard<commit_after>import axios from 'axios';

//
//INITIAL STATE
//

export const initialAuthState = {
  isLoggedIn: null,
};

/**
 * ACTION TYPES
 */

const AUTH_SUCCESS = 'AUTH_SUCCESS';
const AUTH_FAIL = 'AUTH_FAIL';
const DEAUTHENTICATE_USER = 'DEAUTHENTICATE_USER';
const SET_CURRENT_USER = 'SET_CURRENT_USER';

/**
 * ACTION CREATORS
 */

const authSuccess = user => {
  return {type: AUTH_SUCCESS, user};
};

const authFail = () => {
  return {type: AUTH_FAIL };
};

export const deAuthenticateUser = () => {
  return {type: DEAUTHENTICATE_USER};
};

const setCurrentUser = user => {
  return {type: SET_CURRENT_USER, user};
};

/**
 * THUNK CREATORS
 */

 export const checkIfLoggedIn = () => dispatch => {
   const jwtOptions = {
    	strategy: "jwt",
      accessToken: localStorage.getItem('jwt')
    };
   const userEmail = localStorage.getItem('email');
   axios.post('/authentication', jwtOptions)
     .then(result => result.data)
     .then((response) => dispatch(authSuccess()))
     .then(() => axios.get(`/api/users?email=${userEmail}`))
     .then((res) => res.data)
     .then(userArray => {
       dispatch(setCurrentUser(userArray[0]));
     })
     .catch((err) => {
     console.log('not logged in currently');
   });
 };

 export const attemptAuth = (user, history) => dispatch => {
   axios.post('/authentication', user)
     .then(result => result.data)
     .then(payload => {
       localStorage.setItem('jwt', payload.accessToken);
       localStorage.setItem('email', user.email);
       return dispatch(authSuccess());
     })
     .then(() => axios.get(`/api/users?email=${user.email}`))
     .then((res) => res.data)
     .then(userArray => {
       dispatch(setCurrentUser(userArray[0]));
       history.push(`/artists/${userArray[0].id}`);
     })
     .catch((err) => dispatch(authFail()));
 };

 // export const deauthUser = user => dispatch => {
 //   axios.post('/api/authentiction', user)
 //     .then(result => result.data)
 //     .then(newUser => {
 //       dispatch(de(newUser));
 //     })
 //     .catch(console.error);
 // };

 /**
  * REDUCER
  */

 export default function reducer (state = initialAuthState, action) {
   switch (action.type) {
   case AUTH_SUCCESS:
     return Object.assign({}, state, {isLoggedIn: true});
   case AUTH_FAIL:
     return Object.assign({}, state, {isLoggedIn: false});
   case DEAUTHENTICATE_USER:
     return initialAuthState;
   case SET_CURRENT_USER:
     return Object.assign({}, state, action.user);
   default:
     return state;
   }
 }
<|endoftext|>


<commit_before>var app = require('ampersand-app');
var ContentView = require('./widget-content');
var filterItemView = require('./filteritem.js');
var templates = require('../templates');
var d3 = require('d3');
var util = require('../util');
var chroma = require('chroma-js');

var margin = {top: 10, bottom: 20, left: 30, right: 50}; // default margins from DC

var setupPlot = function (view) {
    // get data
    var _dx = app.filters.get(view.model.filter).get('_dx');
    var records = _dx.top(Infinity);

    var el = view.queryByHook('scatter-plot');
    var height = 250;
    var width = parseInt(el.offsetWidth);

    // add the graph canvas to the body of the webpage
    var svg = d3.select(el).append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var colorscale = chroma.scale(["#022A08", "#35FE57"]);

    var xrange = window.app.filters.get(view.model.filter)._range;
    var yrange = window.app.filters.get(view.model.secondary)._range;

    // Allow a no-color plot
    var zrange = [0,1];
    if(window.app.filters.get(view.model.color)) {
        zrange = window.app.filters.get(view.model.color)._range;
    }

    var xScale = d3.scale.linear().domain(xrange).range([0,width - margin.left - margin.right]);
    var yScale = d3.scale.linear().domain(yrange).range([height - margin.top - margin.bottom,0]);
    var zScale = d3.scale.linear().domain(zrange).range([0,1]);

    var xMap = function (d) {
        var v = util.validateFloat(d[view.model.filter.toLowerCase()]);
        if(isNaN(v)) return -99999; else return xScale(v);
    };
    var yMap = function (d) {
        var v = util.validateFloat(d[view.model.secondary.toLowerCase()]);
        if(isNaN(v)) return -99999; else return yScale(v);
    };
    var zMap = function (d) {
        var v = util.validateFloat(d[view.model.color.toLowerCase()]);
        if(isNaN(v)) return 'red'; else return colorscale(zScale(v));
    };

    var xAxis = d3.svg.axis().scale(xScale).orient("bottom"),
        yAxis = d3.svg.axis().scale(yScale).orient("left");

    view._svg = svg;
    view._xMap = xMap;
    view._yMap = yMap;
    view._zMap = zMap;

    view._width = width;
    view._height = height;
    view._xAxis = xAxis;
    view._yAxis = yAxis;
};

var plotPoints = function (view) {
    // get data
    var _dx = app.filters.get(view.model.filter).get('_dx');
    var records = _dx.top(Infinity);

    // remove all
    var svg = view._svg;
    svg.selectAll(".dot").remove();

    // draw dots
    svg.selectAll(".dot")
        .data(records)
        .enter().append("circle")
        .attr("class", "dot")
        .attr("r", 3.5)
        .attr("cx", view._xMap)
        .attr("cy", view._yMap)
        .style("fill", view._zMap);

    // x-axis
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + (view._height - margin.bottom - margin.top) + ")")
        .call(view._xAxis)
        .append("text")
        .attr("class", "label")
        .attr("x", view._width - margin.left - margin.right)
        .attr("y", -6)
        .style("text-anchor", "end")
        .text(view.model.filter);

    // y-axis
    svg.append("g")
        .attr("class", "y axis")
        .call(view._yAxis)
        .append("text")
        .attr("class", "label")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text(view.model.secondary);
};

module.exports = ContentView.extend({
    template: templates.includes.correlation,

    render: function() {
        var select;

        this.renderWithTemplate(this);

        // initialize secondary and color filter selector
        this.renderCollection(app.filters,
                              filterItemView,
                              this.queryByHook('filter-selector'),
                              {filter: function (f) {return f.active;}});
        select = this.el.querySelector('select[data-hook~="filter-selector"]');
        select.value = this.model.secondary;

        this.renderCollection(app.filters,
                              filterItemView,
                              this.queryByHook('color-selector'),
                              {filter: function (f) {return f.active;}});
        select = this.el.querySelector('select[data-hook~="color-selector"]');
        select.value = this.model.secondary;

        return this;
    },

    renderContent: function (view) {
        if(! view.model.isReady) {
            return;
        }

        // Tear down old plot
        if(view._svg) {
            var el = view.queryByHook('scatter-plot');
            d3.selectAll(el.childNodes).remove();
            delete view._svg;
        }

        // Set up and plot
        setupPlot(view);
        plotPoints(view);
    },

    // function called by dc on filter events.
    redraw: function () {
        if(! this.model.isReady) {
            return;
        }
        plotPoints(this);
    },

    // Respond to secondary filter changes
    events: {
        'change': 'changeFilter',
    },

    changeFilter:  function (e) {
        var select;

        select = this.el.querySelector('[data-hook~="filter-selector"]');
        this.model.secondary = select.options[select.selectedIndex].value;

        select = this.el.querySelector('[data-hook~="color-selector"]');
        this.model.color = select.options[select.selectedIndex].value;

        this.renderContent(this);
    },
});
<commit_msg>Correlation chart: use canvas to speed up the widget<commit_after>var app = require('ampersand-app');
var ContentView = require('./widget-content');
var filterItemView = require('./filteritem.js');
var templates = require('../templates');
var d3 = require('d3');
var util = require('../util');
var chroma = require('chroma-js');

var margin = {top: 10, bottom: 20, left: 30, right: 50}; // default margins from DC

var setupPlot = function (view) {
    // get data
    var _dx = app.filters.get(view.model.filter).get('_dx');
    var records = _dx.top(Infinity);

    var el = view.queryByHook('scatter-plot');
    var height = 250;
    var width = parseInt(el.offsetWidth);

    // add the graph to the body of the webpage
    var main = d3.select(el).append("div")
        .attr("width", width)
        .attr("height", height)
        .style('position', 'relative');

    var canvas = main.append("canvas")
        .attr("width", width)
        .attr("height", height)
        .node().getContext('2d');

    var svg = main.append("svg")
        .style("position", "absolute")
        .style("top", "0px")
        .style("left", "0px")
        .style("z-index", 2)
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var colorscale = chroma.scale(["#022A08", "#35FE57"]);

    var xrange = window.app.filters.get(view.model.filter)._range;
    var yrange = window.app.filters.get(view.model.secondary)._range;

    // Allow a no-color plot
    var zrange = [0,1];
    if(window.app.filters.get(view.model.color)) {
        zrange = window.app.filters.get(view.model.color)._range;
    }

    var xScale = d3.scale.linear().domain(xrange).range([0,width - margin.left - margin.right]);
    var yScale = d3.scale.linear().domain(yrange).range([height - margin.top - margin.bottom,0]);
    var zScale = d3.scale.linear().domain(zrange).range([0,1]);

    var xMap = function (d) {
        var v = util.validateFloat(d[view.model.filter.toLowerCase()]);
        if(isNaN(v) || v == Infinity) return -99999;
        return xScale(v);
    };
    var yMap = function (d) {
        var v = util.validateFloat(d[view.model.secondary.toLowerCase()]);
        if(isNaN(v) || v == Infinity) return -99999;
        return yScale(v);
    };
    var zMap = function (d) {
        var v = util.validateFloat(d[view.model.color.toLowerCase()]);
        if(isNaN(v) || v == Infinity) return 'gray';
        return colorscale(zScale(v));
    };

    // x-axis
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + (height - margin.bottom - margin.top) + ")")
        .call(d3.svg.axis().scale(xScale).orient("bottom"))
        .append("text")
        .attr("class", "label")
        .attr("x", width - margin.left - margin.right)
        .attr("y", -6)
        .style("text-anchor", "end")
        .text(view.model.filter);

    // y-axis
    svg.append("g")
        .attr("class", "y axis")
        .call(d3.svg.axis().scale(yScale).orient("left"))
        .append("text")
        .attr("class", "label")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text(view.model.secondary);

    view._width = width;
    view._height = height;
    view._svg = svg;
    view._canvas = canvas;
    view._xMap = xMap;
    view._yMap = yMap;
    view._zMap = zMap;
    view._yScale = yScale;
};

var plotPointsCanvas = function (view) {

    // get data
    var id = app.filters.get(view.model.filter).get('id').toLowerCase();
    var _dx = app.filters.get(view.model.filter).get('_dx');
    var records = _dx.top(Infinity);

    // Clear the canvas
    var canvas = view._canvas;

    canvas.clearRect(0,0,view._width,view._height);

    canvas.beginPath();

    var i = 0, cx, cy;
    for(i=0; i < records.length; i++) {
        cx = view._xMap( records[i] ) + margin.left;
        cy = view._yMap( records[i] ) + margin.top;

        canvas.moveTo(cx, cy);
        canvas.arc( cx, cy, 3.5, 0, 2 * Math.PI);
    }

    canvas.fill();
};

module.exports = ContentView.extend({
    template: templates.includes.correlation,

    render: function() {
        var select;

        this.renderWithTemplate(this);

        // initialize secondary and color filter selector
        this.renderCollection(app.filters,
                              filterItemView,
                              this.queryByHook('filter-selector'),
                              {filter: function (f) {return f.active;}});
        select = this.el.querySelector('select[data-hook~="filter-selector"]');
        select.value = this.model.secondary;

        this.renderCollection(app.filters,
                              filterItemView,
                              this.queryByHook('color-selector'),
                              {filter: function (f) {return f.active;}});
        select = this.el.querySelector('select[data-hook~="color-selector"]');
        select.value = this.model.secondary;

        return this;
    },

    renderContent: function (view) {
        if(! view.model.isReady) {
            return;
        }

        // Tear down old plot
        var el = view.queryByHook('scatter-plot');
        while (el.firstChild) {
            el.removeChild(el.firstChild);
        }

        if(view._svg) {
            delete view._svg;
        }
        if(view._canvas) {
            delete view._canvas;
        }

        // Set up and plot
        setupPlot(view);
        this.redraw();
    },

    // function called by dc on filter events.
    redraw: function () {
        if(! this.model.isReady) {
            return;
        }

        plotPointsCanvas(this);
        plotLine(this);
    },

    // Respond to secondary filter changes
    events: {
        'change': 'changeFilter',
    },

    changeFilter:  function (e) {
        var select;

        select = this.el.querySelector('[data-hook~="filter-selector"]');
        this.model.secondary = select.options[select.selectedIndex].value;

        select = this.el.querySelector('[data-hook~="color-selector"]');
        this.model.color = select.options[select.selectedIndex].value;

        this.renderContent(this);
    },
});
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
// Control Base
//
// 'Abstract' base class for UI controls.
////////////////////////////////////////////////////////////////////////////////
var controls = function (controls, $) {
	var LAST_ID = 0;

	// generates a new, unique control id
	controls.id = function () {
		return LAST_ID++;
	};

	// control lookup (id -> control)
	controls.lookup = {};

	// base control class
	controls.base = function () {
		var id = controls.id(),

		self =  {
			// properties
			id: id,							// id is automatically assigned
			parent: null,				// parent control
			children: [],				// child controls
			data: [],						// data associated with control

			// adds a child to the control
			append: function (child) {
				this.children.push(child);
				child.parent = this;
			},

			// removes child controls
			clear: function () {
				this.children.length = 0;
			},

			// initializes the control
			// e.g. adding data, events, etc.
			init: null,

			// produces the control's html
			html: function () {
				throw "Abstract";
			},

			// renders the control
			// -parent: parent jQuery object
			render: function (parent) {
				// initializing jQuery object from full control markup
				var elem = $(this.html()),
						target = $('#' + this.id);

				// adding control to dom
				if (target.length) {
					target.replaceWith(elem);
				} else if (parent) {
					parent.append(elem);
				}

				// walking control tree initializing nodes
				(function inner(control) {
					var selector = '#' + control.id,
							i,
					// initializing current control
					result = control.init ? control.init(elem.is(selector) ? elem : elem.find(selector)) : true;
					// initializing children
					if (control.children.length) {
						for (i = 0; i < control.children.length; i++) {
							// when a control.init() returns false, it breaks the init cycle
							// for its siblings -> useful in conj. with jQuery.live()
							if (inner(control.children[i]) === false) {
								break;
							}
						}
					}
					return result;
				}(this));

				return parent || elem;
			}
		};

		// adding control to lookup
		controls.lookup[id] = self;

		return self;
	};

	return controls;
}(controls || {},
	jQuery);
<commit_msg>.appendTo() added to control base<commit_after>////////////////////////////////////////////////////////////////////////////////
// Control Base
//
// 'Abstract' base class for UI controls.
////////////////////////////////////////////////////////////////////////////////
var controls = function (controls, $) {
	var LAST_ID = 0;

	// generates a new, unique control id
	controls.id = function () {
		return LAST_ID++;
	};

	// control lookup (id -> control)
	controls.lookup = {};

	// base control class
	controls.base = function () {
		var id = controls.id(),

		self =  {
			// properties
			id: id,							// id is automatically assigned
			parent: null,				// parent control
			children: [],				// child controls
			data: [],						// data associated with control

			// adds a child to the control
			append: function (child) {
				this.children.push(child);
				child.parent = this;
				return this;
			},

			// adds control to parent as child
			appendTo: function (parent) {
				this.parent = parent;
				parent.children.push(this);
				return this;
			},

			// removes child controls
			clear: function () {
				this.children.length = 0;
				return this;
			},

			// initializes the control
			// e.g. adding data, events, etc.
			init: null,

			// produces the control's html
			html: function () {
				throw "Abstract";
			},

			// renders the control
			// -parent: parent jQuery object
			render: function (parent) {
				// initializing jQuery object from full control markup
				var elem = $(this.html()),
						target = $('#' + this.id);

				// adding control to dom
				if (target.length) {
					target.replaceWith(elem);
				} else if (parent) {
					parent.append(elem);
				}

				// walking control tree initializing nodes
				(function inner(control) {
					var selector = '#' + control.id,
							i,
					// initializing current control
					result = control.init ? control.init(elem.is(selector) ? elem : elem.find(selector)) : true;
					// initializing children
					if (control.children.length) {
						for (i = 0; i < control.children.length; i++) {
							// when a control.init() returns false, it breaks the init cycle
							// for its siblings -> useful in conj. with jQuery.live()
							if (inner(control.children[i]) === false) {
								break;
							}
						}
					}
					return result;
				}(this));

				return parent || elem;
			}
		};

		// adding control to lookup
		controls.lookup[id] = self;

		return self;
	};

	return controls;
}(controls || {},
	jQuery);
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2000 - 2019 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception. You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

(function() {

  /**
   * SilverKeyboard version 1.0.0.
   * It is currently based upon Simple Keyboard
   */
  if (window.top.SilverKeyboard) {
    return;
  }

  window.top.SilverKeyboard = new function() {

    var inputTypes = ['date', 'datetime-local', 'email', 'month', 'number', 'password', 'search',
      'tel', 'text', 'time', 'url', 'week'];

    var kbdClassName = 'kbd-textual-input';

    var _keyboard = undefined;

    var _initialized = false;

    var _inputs = [];

    /**
     * Initializes the virtual keyboard with the specified locale and for the specified selectors of
     * input HTML element.
     * @param locale an ISO 639-1 code of a locale (currently only 'en', 'de' and 'fr' are
     * supported)
     * @param selectors a list of selectors of HTML elements for which the keyboard has to be used.
     * If not set, by default selects all the inputs and the textarea: ["input", "textarea"].
     * Nevertheless, among all the scanned HTML inputs, only textual ones are taken in charge by
     * the virtual keyboard.
     * @return {Window.SilverKeyboard} instance.
     */
    this.init = function(locale, selectors = []) {
      if (_initialized) {
        console.log('[Virtual Keyboard] Already initialized!');
        return this;
      }

      if (!('ontouchstart' in window) || (navigator.maxTouchPoints > 0) ||
          (navigator.msMaxTouchPoints > 0)) {
        console.log(
            "[Virtual Keyboard] The screen isn't a touch one. Disable the virtual keyboard!");
        //return;
      }

      console.log("[Virtual Keyboard] The screen is a touch one. Enable the virtual keyboard");

      let kbdWidget = window.top.document.querySelector('#virtual-keyboard');
      if (kbdWidget === null) {
        throw new Error("No virtual keyboard widget's location defined in the DOM!");
      }

      if (selectors === undefined || selectors === null || selectors.length === 0) {
        _inputs = ["input", "textarea"];
      } else {
        _inputs = selectors;
      }

      console.log('[Virtual Keyboard] Inputs to support:', _inputs);

      let layout = '';
      if (locale === 'fr')
        layout = french;
      else if (locale === 'en')
        layout = english;
      else if (locale === 'de')
        layout = german;

      _keyboard = new window.SimpleKeyboard.default({
        onChange : onChange,
        onKeyPress : onKeyPress,
        onInit : function() {
          window.top.document.querySelector('#virtual-keyboard').style.display = 'none';
        },
        newLineOnEnter : true,
        tabCharOnTab : false,
        preventMouseDownDefault : true,
        layout : layout
      });

      _initialized = true;
      return this;
    };

    /**
     * Enables the Silverpeas virtual keyboard for the specified node. It will look for the all the
     * supported textual inputs to interact with them.
     * @param node an HTML node. By default the current document.
     * @return {Window.top.SilverKeyboard} itself.
     */
    this.enableFor = function(node) {
      if (!_initialized) {
        throw new Error('SilverKeyboard not initialized!');
      }

      if (!node) {
        throw new Error('No HTML node specified for which SilverKeyboard is enabled!');
      }

      if (!window.MutationObserver) {
        console.log(
            "The web browser doesn't support MutationObserver. The virtual keyboard cannot be enabled");
        return this;
      }

      let observer = new MutationObserver(function(mutations) {
        mutations.filter(function(aMutation) {
          return aMutation.target.querySelectorAll;
        }).forEach(function(aMutation) {
          scanInputs(aMutation.target);
        })
      }.bind(this));
      observer.observe(node.body, {
        childList : true, subtree : true
      });

      return this;
    };

    function isATextualInput(node) {
      let nodeName = node.nodeName.toLowerCase();
      return nodeName === 'textarea' || (nodeName === 'input' && inputTypes.includes(node.type))
    }

    function processTextualInput($input) {
      if (isATextualInput($input)) {
        if (!$input.classList.contains(kbdClassName)) {
          console.log('[Virtual Keyboard] Input found: ', $input);
          $input.classList.add(kbdClassName);
          attachEventsListening($input);
        }
      }
    }

    function scanInputs(doc) {
      doc.querySelectorAll(_inputs.join(',')).forEach(function($input) {
        processTextualInput($input);
      });
    }

    function attachEventsListening($input) {
      $input.addEventListener("focus", function(event) {
        window.top.document.querySelector('#virtual-keyboard').style.display = '';
        switchToInput(event.target);
      });
      $input.addEventListener("focusout", function(event) {
        event.target.style.border = '';
        window.top.document.querySelector('#virtual-keyboard').style.display = 'none';
      });
      $input.addEventListener("input", function(event) {
        _keyboard.setInput(event.target.value, event.target.id);
      });
    }

    function switchToInput($input) {
      if (_keyboard.options.inputName && _keyboard.options.inputName !== 'default') {
        _keyboard.focusedInput.style.border = '';
      }
      $input.style.border = 'thin solid green';
      _keyboard.setOptions({
        inputName : $input.name
      });
      _keyboard.focusedInput = $input;
    }

    function isVisible($input) {
      return $input.type.toLowerCase() !== 'hidden' && $input.offsetHeight > 0 &&
          $input.offsetWidth > 0;
    }

    function nextValidInput($allInputs, $currentInput) {
      let $nextInput;
      for (let i = 0; i < $allInputs.length; i++) {
        if ($allInputs[i] === $currentInput) {
          let next = i;
          do {
            next = (next + 1 < $allInputs.length) ? next + 1 : 0;
          } while (!isVisible($allInputs[next]));
          $nextInput = $allInputs[next];
          break;
        }
      }
      return $nextInput;
    }

    function onChange(input) {
      let $input = _keyboard.focusedInput;
      $input.value = input;
      let caretPosition = _keyboard.caretPosition;
      if (caretPosition !== null) {
        if ($input.setSelectionRange) {
          $input.focus();
          $input.setSelectionRange(caretPosition, caretPosition);
        }
      }
    }

    function onKeyPress(button) {

      /**
       * Cycle the keyboard focus over the different inputs
       */
      if (button === "{tab}") {
        let $input = _keyboard.focusedInput;
        let doc = $input.ownerDocument;
        if (doc) {
          let $allInputs = doc.querySelectorAll(`.${kbdClassName}`);
          let $nextInput = nextValidInput($allInputs, $input);
          if ($nextInput)
            $nextInput.focus();
        }
      }

      /**
       * If you want to handle the shift and caps lock buttons
       */
      if (button === "{shift}" || button === "{lock}") {
        let currentLayout = _keyboard.options.layoutName;
        let shiftToggle = currentLayout === "default" ? "shift" : "default";

        _keyboard.setOptions({
          layoutName : shiftToggle
        });
      }
    }
  };
})();
<commit_msg>Feature #Virtual-Keyboard<commit_after>/*
 * Copyright (C) 2000 - 2019 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of
 * the GPL, you may redistribute this Program in connection with Free/Libre
 * Open Source Software ("FLOSS") applications as described in Silverpeas's
 * FLOSS exception. You should have received a copy of the text describing
 * the FLOSS exception, and it is also available here:
 * "https://www.silverpeas.org/legal/floss_exception.html"
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

(function() {

  /**
   * SilverKeyboard version 1.0.0.
   * It is currently based upon Simple Keyboard
   */
  if (window.top.SilverKeyboard) {
    return;
  }

  window.top.SilverKeyboard = new function() {

    var inputTypes = ['date', 'datetime-local', 'email', 'month', 'number', 'password', 'search',
      'tel', 'text', 'time', 'url', 'week'];

    var kbdClassName = 'kbd-textual-input';

    var _keyboard = undefined;

    var _initialized = false;

    var _inputs = [];

    /**
     * Initializes the virtual keyboard with the specified locale and for the specified selectors of
     * input HTML element.
     * @param locale an ISO 639-1 code of a locale (currently only 'en', 'de' and 'fr' are
     * supported)
     * @param selectors a list of selectors of HTML elements for which the keyboard has to be used.
     * If not set, by default selects all the inputs and the textarea: ["input", "textarea"].
     * Nevertheless, among all the scanned HTML inputs, only textual ones are taken in charge by
     * the virtual keyboard.
     * @return {Window.SilverKeyboard} instance.
     */
    this.init = function(locale, selectors = []) {
      if (_initialized) {
        console.log('[Virtual Keyboard] Already initialized!');
        return this;
      }

      if (!('ontouchstart' in window) || (navigator.maxTouchPoints > 0) ||
          (navigator.msMaxTouchPoints > 0)) {
        console.log(
            "[Virtual Keyboard] The screen isn't a touch one. Disable the virtual keyboard!");
        //return;
      }

      console.log("[Virtual Keyboard] The screen is a touch one. Enable the virtual keyboard");

      let kbdWidget = window.top.document.querySelector('#virtual-keyboard');
      if (kbdWidget === null) {
        throw new Error("No virtual keyboard widget's location defined in the DOM!");
      }

      if (selectors === undefined || selectors === null || selectors.length === 0) {
        _inputs = ["input", "textarea"];
      } else {
        _inputs = selectors;
      }

      console.log('[Virtual Keyboard] Inputs to support:', _inputs);

      let layout = '';
      if (locale === 'fr')
        layout = french;
      else if (locale === 'en')
        layout = english;
      else if (locale === 'de')
        layout = german;

      _keyboard = new window.SimpleKeyboard.default({
        onChange : onChange,
        onKeyPress : onKeyPress,
        onInit : function() {
          window.top.document.querySelector('#virtual-keyboard').style.display = 'none';
        },
        newLineOnEnter : true,
        tabCharOnTab : false,
        preventMouseDownDefault : true,
        layout : layout
      });

      _initialized = true;
      return this;
    };

    /**
     * Enables the Silverpeas virtual keyboard for the specified node. It will look for the all the
     * supported textual inputs to interact with them.
     * @param node an HTML node. By default the current document.
     * @return {Window.top.SilverKeyboard} itself.
     */
    this.enableFor = function(node) {
      if (!_initialized) {
        throw new Error('SilverKeyboard not initialized!');
      }

      if (!node) {
        throw new Error('No HTML node specified for which SilverKeyboard is enabled!');
      }

      if (!window.MutationObserver) {
        console.log(
            "The web browser doesn't support MutationObserver. The virtual keyboard cannot be enabled");
        return this;
      }

      let observer = new MutationObserver(function(mutations) {
        mutations.filter(function(aMutation) {
          return aMutation.target.querySelectorAll;
        }).forEach(function(aMutation) {
          scanInputs(aMutation.target);
        })
      }.bind(this));
      observer.observe(node.body, {
        childList : true, subtree : true
      });

      return this;
    };

    function isATextualInput(node) {
      let nodeName = node.nodeName.toLowerCase();
      return nodeName === 'textarea' || (nodeName === 'input' && inputTypes.includes(node.type))
    }

    function processTextualInput($input) {
      if (isATextualInput($input)) {
        if (!$input.classList.contains(kbdClassName)) {
          console.log('[Virtual Keyboard] Input found: ', $input);
          $input.classList.add(kbdClassName);
          attachEventsListening($input);
        }
      }
    }

    function scanInputs(doc) {
      doc.querySelectorAll(_inputs.join(',')).forEach(function($input) {
        processTextualInput($input);
      });
    }

    function attachEventsListening($input) {
      $input.addEventListener("focus", function(event) {
        window.top.document.querySelector('#virtual-keyboard').style.display = '';
        _keyboard.setInput(event.target.value, event.target.name);
        if (event.target.setSelectionRange) {
          event.target.setSelectionRange(event.target.value.length, event.target.value.length);
          _keyboard.caretPosition = event.target.value.length;
        }
        switchToInput(event.target);
      });
      $input.addEventListener("focusout", function(event) {
        event.target.style.border = '';
        window.top.document.querySelector('#virtual-keyboard').style.display = 'none';
      });
      $input.addEventListener("input", function(event) {
        _keyboard.setInput(event.target.value, event.target.name);
      });
    }

    function switchToInput($input) {
      if (_keyboard.options.inputName && _keyboard.options.inputName !== 'default') {
        _keyboard.focusedInput.style.border = '';
      }
      $input.style.border = 'thin solid green';
      _keyboard.setOptions({
        inputName : $input.name
      });
      _keyboard.focusedInput = $input;
    }

    function isVisible($input) {
      return $input.type.toLowerCase() !== 'hidden' && $input.offsetHeight > 0 &&
          $input.offsetWidth > 0;
    }

    function nextValidInput($allInputs, $currentInput) {
      let $nextInput;
      for (let i = 0; i < $allInputs.length; i++) {
        if ($allInputs[i] === $currentInput) {
          let next = i;
          do {
            next = (next + 1 < $allInputs.length) ? next + 1 : 0;
          } while (!isVisible($allInputs[next]));
          $nextInput = $allInputs[next];
          break;
        }
      }
      return $nextInput;
    }

    function onChange(input) {
      let $input = _keyboard.focusedInput;
      $input.value = input;
      let caretPosition = _keyboard.caretPosition;
      if (caretPosition !== null) {
        if ($input.setSelectionRange) {
          $input.focus();
          $input.setSelectionRange(caretPosition, caretPosition);
        }
      }
    }

    function onKeyPress(button) {

      /**
       * Cycle the keyboard focus over the different inputs
       */
      if (button === "{tab}") {
        let $input = _keyboard.focusedInput;
        let doc = $input.ownerDocument;
        if (doc) {
          let $allInputs = doc.querySelectorAll(`.${kbdClassName}`);
          let $nextInput = nextValidInput($allInputs, $input);
          if ($nextInput)
            $nextInput.focus();
        }
      }

      /**
       * If you want to handle the shift and caps lock buttons
       */
      if (button === "{shift}" || button === "{lock}") {
        let currentLayout = _keyboard.options.layoutName;
        let shiftToggle = currentLayout === "default" ? "shift" : "default";

        _keyboard.setOptions({
          layoutName : shiftToggle
        });
      }
    }
  };
})();
<|endoftext|>


<commit_before>var usersModel = require('../model/users-model')
  , standupModel = require('../model/standup-model')
  , readyForNextStatus = false
  , standupHappening = false
  , standupChannel
  , currentUser
  , userIterator;

module.exports.use = function(controller) {
  controller.hears('start', 'direct_mention', function (bot, message) {

    if (standupHappening) {
      return bot.reply(message, 'Standup has already started!');
    }

    if (!standupModel.getSummaryChannel()) {
      standupModel.setSummaryChannel(message.channel);
    }

    standupChannel = message.channel; // save this for later

    if (usersModel.list().length === 0) {
      return bot.reply(message, 'Looks like no one is in the team! Get a few people to join and then we\'ll have some fun!');
    }

    standupHappening = true;
    bot.reply(message, 'Alright! Let\'s get this party started!');

    // notify first user and start a conversation
    userIterator = usersModel.iterator();
    currentUser = userIterator.next();
    promptUser(bot);
  });

  controller.hears('end', 'direct_mention', function (bot, message) {
    if (!standupHappening) {
      return bot.reply(message, 'Standup is already over! Start another one with `start`');
    }

    readyForNextStatus = false;
    standupHappening = false;

    bot.reply(message, 'Standup is over!');

    bot.startConversation(message, function (err, convo) {
      convo.ask('<@' + message.user + '> do you want a summary of this standup?', [{
          pattern: bot.utterances.yes,
          callback: function() {
            summarizeStandup(bot);
          }
        }, {
          pattern: bot.utterances.no,
          callback: function() {
            bot.say({channel: message.channel, text: 'Ok :hear_no_evil: Come again soon!'})
          }
        }]);
    });
  });

  /*******************
   * Running Standup *
   *******************/

  controller.hears(['yes', 'yea', 'yup', 'yep', 'ya', 'sure', 'ok', 'y', 'yeah', 'yah'], 'direct_mention,ambient', function (bot, message) {
    if (readyForNextStatus && message.user === currentUser.id) {
      readyForNextStatus = false;
      gatherStatus(bot, message);
    }
  });

  controller.hears(['skip', 'no', 'nope', 'nah', 'n'], 'direct_mention,ambient', function (bot, message) {
    if (readyForNextStatus) {
      bot.reply(message, 'Skipping ' + currentUser.name);
      afterStatus(bot);
    }
  });

  function promptUser(bot) {
    readyForNextStatus = true;
    bot.say({
      text: '<@' + currentUser.id + '> are you ready?',
      channel: standupChannel
    });
  }

  function gatherStatus(bot, message) {
    bot.startConversation(message, function (err, convo) {

      convo.ask('What did you do yesterday?', convoCallback, {
        key: 'yesterday',
        multiple: true
      });

      convo.ask('What are you doing today?', convoCallback, {
        key: 'today',
        multiple: true
      });

      convo.ask('Anything in your way?', convoCallback, {
        key: 'obstacles',
        multiple: true
      });

      convo.say('Great! Thanks ' + currentUser.name + '!');

      convo.on('end', function(convo) {
        if (convo.status === 'completed') {
          standupModel.addStatus(currentUser.id, {
            yesterday: convo.extractResponse('yesterday'),
            today: convo.extractResponse('today'),
            obstacles: convo.extractResponse('obstacles'),
            user: currentUser
          });
        }

        afterStatus(bot);
      });

      function convoCallback(response, convo) {
        convo.next();
      }
    });
  }

  function afterStatus(bot) {
    // get next user, if null summarize
    if (userIterator.hasNext()) {
      currentUser = userIterator.next();
      promptUser(bot);
    } else {
      summarizeStandup(bot);
      standupHappening = false;
      readyForNextStatus = false;
    }
  }

  function summarizeStandup(bot) {
    var sayConfig = {channel: standupChannel, text: 'Great job everyone! :tada:'};

    standupModel.summarize(bot)
      .then(function () {
        sayConfig.text += ' You can find a summary in <#' + standupModel.getSummaryChannel() + '>';
        bot.say(sayConfig);
      })
      .fail(function (err) {
        console.log(err);
        sayConfig.text += ' I had a problem saving the summary though, sorry about that :grimacing:';
        return bot.say(sayConfig);
      });
  }
};<commit_msg>fixed bug in ending conversation<commit_after>var usersModel = require('../model/users-model')
  , standupModel = require('../model/standup-model')
  , readyForNextStatus = false
  , standupHappening = false
  , standupChannel
  , currentUser
  , userIterator;

module.exports.use = function(controller) {
  controller.hears('start', 'direct_mention', function (bot, message) {

    if (standupHappening) {
      return bot.reply(message, 'Standup has already started!');
    }

    if (!standupModel.getSummaryChannel()) {
      standupModel.setSummaryChannel(message.channel);
    }

    standupChannel = message.channel; // save this for later

    if (usersModel.list().length === 0) {
      return bot.reply(message, 'Looks like no one is in the team! Get a few people to join and then we\'ll have some fun!');
    }

    standupHappening = true;
    bot.reply(message, 'Alright! Let\'s get this party started!');

    // notify first user and start a conversation
    userIterator = usersModel.iterator();
    currentUser = userIterator.next();
    promptUser(bot);
  });

  controller.hears('end', 'direct_mention', function (bot, message) {
    if (!standupHappening) {
      return bot.reply(message, 'Standup is already over! Start another one with `start`');
    }

    readyForNextStatus = false;
    standupHappening = false;

    bot.reply(message, 'Standup is over!');

    bot.startConversation(message, function (err, convo) {
      convo.ask('<@' + message.user + '> do you want a summary of this standup?', [{
          pattern: bot.utterances.yes,
          callback: function(response, convo) {
            summarizeStandup(bot);
            convo.next();
          }
        }, {
          pattern: bot.utterances.no,
          callback: function(response, convo) {
            bot.say({channel: message.channel, text: 'Ok :hear_no_evil: Come again soon!'});
            convo.next();
          }
        }]);
    });
  });

  /*******************
   * Running Standup *
   *******************/

  controller.hears(['yes', 'yea', 'yup', 'yep', 'ya', 'sure', 'ok', 'y', 'yeah', 'yah'], 'direct_mention,ambient', function (bot, message) {
    if (readyForNextStatus && message.user === currentUser.id) {
      readyForNextStatus = false;
      gatherStatus(bot, message);
    }
  });

  controller.hears(['skip', 'no', 'nope', 'nah', 'n'], 'direct_mention,ambient', function (bot, message) {
    if (readyForNextStatus) {
      bot.reply(message, 'Skipping ' + currentUser.name);
      afterStatus(bot);
    }
  });

  function promptUser(bot) {
    readyForNextStatus = true;
    bot.say({
      text: '<@' + currentUser.id + '> are you ready?',
      channel: standupChannel
    });
  }

  function gatherStatus(bot, message) {
    bot.startConversation(message, function (err, convo) {

      convo.ask('What did you do yesterday?', convoCallback, {
        key: 'yesterday',
        multiple: true
      });

      convo.ask('What are you doing today?', convoCallback, {
        key: 'today',
        multiple: true
      });

      convo.ask('Anything in your way?', convoCallback, {
        key: 'obstacles',
        multiple: true
      });

      convo.say('Great! Thanks ' + currentUser.name + '!');

      convo.on('end', function(convo) {
        if (convo.status === 'completed') {
          standupModel.addStatus(currentUser.id, {
            yesterday: convo.extractResponse('yesterday'),
            today: convo.extractResponse('today'),
            obstacles: convo.extractResponse('obstacles'),
            user: currentUser
          });
        }

        afterStatus(bot);
      });

      function convoCallback(response, convo) {
        convo.next();
      }
    });
  }

  function afterStatus(bot) {
    // get next user, if null summarize
    if (userIterator.hasNext()) {
      currentUser = userIterator.next();
      promptUser(bot);
    } else {
      summarizeStandup(bot);
      standupHappening = false;
      readyForNextStatus = false;
    }
  }

  function summarizeStandup(bot) {
    var sayConfig = {channel: standupChannel, text: 'Great job everyone! :tada:'};

    standupModel.summarize(bot)
      .then(function () {
        sayConfig.text += ' You can find a summary in <#' + standupModel.getSummaryChannel() + '>';
        bot.say(sayConfig);
      })
      .fail(function (err) {
        console.log(err);
        sayConfig.text += ' I had a problem saving the summary though, sorry about that :grimacing:';
        return bot.say(sayConfig);
      });
  }
};<|endoftext|>


<commit_before>// Meteor package definition.
Package.describe({
  name: 'aramk:notifications',
  version: '1.0.0_1',
  summary: 'User notification messages',
  git: 'https://github.com/aramk/meteor-notifications.git'
});

Package.onUse(function (api) {
  api.versionsFrom('METEOR@1.6.1');
  api.use([
    'coffeescript@2.2.1_1',
    'email',
    'underscore',
    'templating@1.3.2',
    'less',
    'reactive-var@1.0.4',
    'tracker@1.0.5',
    'aldeed:simple-schema@1.3.2',
    'aldeed:collection2@2.3.3',
    'aramk:events@1.0.0',
    'momentjs:moment@2.10.3',
    'urbanetic:utility@2.0.0',
    'urbanetic:accounts-ui@1.0.0',
    'aramk:user-status@1.0.0'
  ], ['client', 'server']);
  api.use([
    'semantic:ui-css@2.1.2'
  ], {weak: true});
  api.export('Notifications', ['client', 'server']);
  api.addFiles([
    'src/Notifications.coffee'
  ], ['client', 'server']);
  api.addFiles([
    'src/client.coffee',
    'src/notificationBar.html',
    'src/notificationBar.coffee',
    'src/notificationList.html',
    'src/notificationList.coffee',
    'src/notificationListItem.html',
    'src/notificationListItem.coffee',
    'src/notificationMessage.html',
    'src/notificationMessage.coffee',
    'src/notifications.less',
    'src/notificationUserMenu.html',
    'src/notificationUserMenu.coffee',
    'src/unreadNotificationLabel.html',
    'src/unreadNotificationLabel.coffee',
    'src/userMenuButtons.html'
  ], 'client');
  api.addAssets([
    'assets/notificationEmailTemplate.html'
  ], 'server');
  api.addFiles([
    'src/server.coffee'
  ], 'server');
});
<commit_msg>Update versions<commit_after>// Meteor package definition.
Package.describe({
  name: 'aramk:notifications',
  version: '1.0.1',
  summary: 'User notification messages',
  git: 'https://github.com/aramk/meteor-notifications.git'
});

Package.onUse(function (api) {
  api.versionsFrom('METEOR@1.6.1');
  api.use([
    'coffeescript@2.2.1_1',
    'email',
    'underscore',
    'templating@1.3.2',
    'less',
    'reactive-var@1.0.4',
    'tracker@1.0.5',
    'aldeed:simple-schema@1.3.2',
    'aldeed:collection2@2.3.3',
    'aramk:events@1.0.0_1',
    'momentjs:moment@2.10.3',
    'urbanetic:utility@2.0.1',
    'urbanetic:accounts-ui@1.0.0_1',
    'aramk:user-status@1.0.0'
  ], ['client', 'server']);
  api.use([
    'semantic:ui-css@2.1.2'
  ], {weak: true});
  api.export('Notifications', ['client', 'server']);
  api.addFiles([
    'src/Notifications.coffee'
  ], ['client', 'server']);
  api.addFiles([
    'src/client.coffee',
    'src/notificationBar.html',
    'src/notificationBar.coffee',
    'src/notificationList.html',
    'src/notificationList.coffee',
    'src/notificationListItem.html',
    'src/notificationListItem.coffee',
    'src/notificationMessage.html',
    'src/notificationMessage.coffee',
    'src/notifications.less',
    'src/notificationUserMenu.html',
    'src/notificationUserMenu.coffee',
    'src/unreadNotificationLabel.html',
    'src/unreadNotificationLabel.coffee',
    'src/userMenuButtons.html'
  ], 'client');
  api.addAssets([
    'assets/notificationEmailTemplate.html'
  ], 'server');
  api.addFiles([
    'src/server.coffee'
  ], 'server');
});
<|endoftext|>


<commit_before>require('../../../dist/tags/alert/su-alert.js')

riot.mixin('semantic-ui', {})

describe('su-alert', function () {
  let tag
  let app
  let mount = () => {
    tag = riot.mount('su-alert')[0]
    app = riot.mount('app')[0]
  }

  beforeEach(function () {
    this.clock = sinon.useFakeTimers()
    riot.mixin('semantic-ui', { observable: riot.observable() })
    riot.tag('app')
    $('body').append(`
      <su-alert></su-alert>
      <app></app>
    `)
  })

  afterEach(function () {
    this.clock.restore()
    tag.unmount()
    app.unmount()
  })

  it('is mounted', function () {
    mount()
    tag.isMounted.should.be.true
  })

  it('opens/closes alert', function () {
    mount()
    $('su-alert su-modal > .dimmer').is(':visible').should.equal(false)

    app.suAlert('hello')
    tag.messages[0].should.equal('hello')
    this.clock.tick(510)
    $('su-alert').is(':visible').should.equal(true)

    const btn_one = $('su-alert su-modal .ui.button:first')
    btn_one.text().trim().should.equal('Close')
    btn_one.is(':focus').should.equal(false)

    $('su-modal').click()
    this.clock.tick(310)
    $('su-modal').is(':visible').should.equal(true)

    btn_one.click()
    this.clock.tick(310)
    $('su-alert su-modal > .dimmer').is(':visible').should.equal(false)
  })

  it('title and messages', function () {
    mount()
    app.suAlert({ title: 'riot', message: ['hello!', 'hello!!'] })
    tag.title.should.equal('riot')
    tag.messages[0].should.equal('hello!')
    tag.messages[1].should.equal('hello!!')
  })

  it('title only', function () {
    mount()
    app.suAlert({ title: 'riot' })
    tag.title.should.equal('riot')
    expect(tag.messages[0]).to.be.null
  })

  it('messages only', function () {
    mount()
    app.suAlert({ message: ['hello!', 'hello!!'] })
    expect(tag.title).to.be.null
    tag.messages[0].should.equal('hello!')
    tag.messages[1].should.equal('hello!!')
  })

  it('none message', function () {
    mount()
    app.suAlert()
    expect(tag.title).to.be.null
    expect(tag.messages[0]).to.be.null
  })

  it('button name by opts', function () {
    mount()
    app.suAlert({
      button: {
        text: 'ok',
        default: true,
      }
    })
    const btn_one = $('su-alert su-modal .ui.button:first')
    btn_one.text().trim().should.equal('ok')
    btn_one.is(':focus').should.equal(true)
  })

  it('button name by defaultOptions', function () {
    riot.mixin('semantic-ui', {
      defaultOptions: {
        alert: {
          button: {
            text: 'Yes',
            default: true,
          }
        }
      }
    })
    mount()
    app.suAlert()
    const btn_one = $('su-alert su-modal .ui.button:first')
    btn_one.text().trim().should.equal('Yes')
    btn_one.is(':focus').should.equal(true)
  })

})
<commit_msg>Fix broken su-alert tests<commit_after>require('../../../dist/tags/alert/su-alert.js')

riot.mixin('semantic-ui', {})

describe('su-alert', function () {
  const Q = require('q')
  let tag
  let app
  let mount = () => {
    tag = riot.mount('su-alert')[0]
    app = riot.mount('app')[0]
  }

  beforeEach(function () {
    this.clock = sinon.useFakeTimers()
    riot.mixin('semantic-ui',
      {
        observable: riot.observable(),
        Q: {
          Promise: Q.Promise
        }
      })
    riot.tag('app')
    $('body').append(`
      <su-alert></su-alert>
      <app></app>
    `)
  })

  afterEach(function () {
    this.clock.restore()
    tag.unmount()
    app.unmount()
  })

  it('is mounted', function () {
    mount()
    tag.isMounted.should.be.true
  })

  it('opens/closes alert', function () {
    mount()
    $('su-alert su-modal > .dimmer').is(':visible').should.equal(false)

    app.suAlert('hello')
    tag.messages[0].should.equal('hello')
    this.clock.tick(510)
    $('su-alert').is(':visible').should.equal(true)

    const btn_one = $('su-alert su-modal .ui.button:first')
    btn_one.text().trim().should.equal('Close')
    btn_one.is(':focus').should.equal(false)

    $('su-modal').click()
    this.clock.tick(310)
    $('su-modal').is(':visible').should.equal(true)

    btn_one.click()
    this.clock.tick(310)
    $('su-alert su-modal > .dimmer').is(':visible').should.equal(false)
  })

  it('title and messages', function () {
    mount()
    app.suAlert({ title: 'riot', message: ['hello!', 'hello!!'] })
    tag.title.should.equal('riot')
    tag.messages[0].should.equal('hello!')
    tag.messages[1].should.equal('hello!!')
  })

  it('title only', function () {
    mount()
    app.suAlert({ title: 'riot' })
    tag.title.should.equal('riot')
    expect(tag.messages[0]).to.be.null
  })

  it('messages only', function () {
    mount()
    app.suAlert({ message: ['hello!', 'hello!!'] })
    expect(tag.title).to.be.null
    tag.messages[0].should.equal('hello!')
    tag.messages[1].should.equal('hello!!')
  })

  it('none message', function () {
    mount()
    app.suAlert()
    expect(tag.title).to.be.null
    expect(tag.messages[0]).to.be.null
  })

  it('button name by opts', function () {
    mount()
    app.suAlert({
      button: {
        text: 'ok',
        default: true,
      }
    })
    const btn_one = $('su-alert su-modal .ui.button:first')
    btn_one.text().trim().should.equal('ok')
    btn_one.is(':focus').should.equal(true)
  })

  it('button name by defaultOptions', function () {
    riot.mixin('semantic-ui', {
      defaultOptions: {
        alert: {
          button: {
            text: 'Yes',
            default: true,
          }
        }
      }
    })
    mount()
    app.suAlert()
    const btn_one = $('su-alert su-modal .ui.button:first')
    btn_one.text().trim().should.equal('Yes')
    btn_one.is(':focus').should.equal(true)
  })

})
<|endoftext|>


<commit_before>'use strict'
var path = require('path')
var fs = require('fs')
var diskBufferObjectCache = {}
var os = require('os')
var mkpath = require('mkpath')
var util = require('util')
var events = require('events')

function log (message) {
  if (process.env.DEBUG_LOGSENE_DISK_BUFFER) {
    console.log(new Date().toISOString() + ': ' + message)
  }
}

function DiskBuffer (options) {
  this.storedRequestCount = 0
  this.fileId = 0
  this.options = options || {tmpDir: os.tmpDir(), maxStoredRequests: 1000}
  this.tmpDir = options.tmpDir
  this.maxStoredRequests = options.maxStoredRequests || 1000
  this.storedFiles = []
  this.retransmitIndex = 0
  var self = this
  this.tid = setInterval(function () {
    self.retransmitNext.call(self)
  }, options.interval || 5000)
  mkpath(this.tmpDir, function (err) {
    if (err) {
      log('Error: can not activate disk buffer for logsene-js: ' + err)
    }
    self.syncFileListFromDir()
  })
}
util.inherits(DiskBuffer, events.EventEmitter)

DiskBuffer.prototype.unlock = function (fileName) {
  try {
    fs.renameSync(fileName, fileName.replace('.lock', ''))
  } catch (err) {
  }
}
DiskBuffer.prototype.retransmitNext = function () {
  if (this.storedFiles.length === 0) {
    this.retransmitIndex = 0
    return
  }
  this.retransmitIndex = this.retransmitIndex + 1
  if (this.retransmitIndex >= this.storedFiles.length) {
    this.retransmitIndex = 0
  }
  var index = this.retransmitIndex
  log('# of files: ' + this.storedFiles.length + ' current file:' + index)
  if (this.storedFiles.length >= index) {
    try {
      var fileName = this.storedFiles[index]
      if (!fileName) {
        log('filename not in list:' + fileName + ' ' + this.storedFiles.length)
        return
      }
      log('retransmitNext: ' + fileName)
      try {
        fs.statSync(fileName)
      } catch (fsStatErr) {
        // this.rmFile(fileName)
        return
      }
      var lockedFileName = fileName + '.lock'
      fs.renameSync(fileName, lockedFileName)
      var buffer = fs.readFileSync(lockedFileName)
      var self = this
      setImmediate(function () {
        self.emit('retransmit-req', {fileName: lockedFileName, buffer: buffer})
      })
    } catch (err) {
      // console.error('retransmitNext error: ' + err.message)
    }
  }
}

DiskBuffer.prototype.syncFileListFromDir = function () {
  try {
    this.cleanUp()
    this.storedFiles = fs.readdirSync(this.tmpDir)
    this.storedFiles = this.storedFiles.filter(function (fileName) {
      var rv = false
      if (/\.bulk$/.test(fileName)) {
        rv = true
      } else {
        try {
          var fName = path.join(this.tmpDir, fileName)
          var fStat = fs.statSync(fName)
          var now = Date.now()
          if (now - fStat.atime.getTime() > 1000 * 60 * 5) {
            // a bulk req. should no take longer than 5 min
            log('rename 5 min old .lock file to .bulk: ' + fName)
            fs.renameSync(fName, fName.substring(0, fName.length - 5))
            fs.unlinkSync(fName)
          }
        } catch (fsErr) {
          log('syncFileListFromDir error: ' + fsErr.message)
        }
      }
      return rv
    }.bind(this))
    this.storedFiles = this.storedFiles.map(function (fileName) {
      return path.join(this.tmpDir, fileName)
    }.bind(this))
    this.storedRequestCount = this.storedFiles.length
  } catch (err) {
    log('error syncFileListFromDir', err)
    this.storedFiles = []
  }
  log('syncFileListFromDir: ' + this.storedFiles)
}

DiskBuffer.prototype.cleanUp = function () {
  var self = this
  log('cleanUp DiskBuffer ' + this.tmpDir)
  log('stored req: ' + this.storedRequestCount + ', maxStoredReq: ' + this.maxStoredRequests)
  if (this.storedRequestCount > this.maxStoredRequests) {
    if (this.storedFiles.length > this.maxStoredRequests) {
      for (var i = 0; this.storedFiles.length >= this.maxStoredRequests; i++) {
        if (i === this.storedFiles.length) {
          break
        }
        log('disk buffer limit reached, drop old file:' + this.storedFiles[i])
        self.rmFile.call(self, this.storedFiles[i])
      }
    }
  }
}

DiskBuffer.prototype.addFile = function (fileName) {
  this.storedFiles.push(fileName)
}

DiskBuffer.prototype.rmFile = function (fileName) {
  if (!fileName) {
    return
  }
  var index = this.storedFiles.indexOf(fileName.replace('.lock', ''))
  if (index < 0) {
    // already done before
    // this.emit('removed', {fileName: fileName})
    return
  }
  try {
    fs.unlinkSync(fileName)
    log('rm file:' + fileName)
    this.emit('removed', {fileName: fileName})
  } catch (err) {
    log('rmFile: could not delete file:' + err.message)
    // ignore when file was already deleted
    this.emit('removed', {fileName: fileName})
  } finally {
    if (index > -1) {
      this.storedFiles.splice(index, 1)
      this.storedRequestCount = this.storedFiles.length
      return true
    } else {
      return false
    }
  }
}

DiskBuffer.prototype.getFileName = function () {
  this.fileId += 1
  return path.join(this.tmpDir, this.fileId + '_' + new Date().getTime() + '.bulk')
}

DiskBuffer.prototype.store = function (data, cb) {
  var self = this
  this.storedRequestCount++
  this.checkTmpDir = true
  var fn = this.getFileName()
  log('stored req: ' + this.storedRequestCount + ', maxStoredReq: ' + this.maxStoredRequests)
  this.cleanUp()
  fs.writeFile(fn, JSON.stringify(data), function (err) {
    if (cb & err) {
      return cb(err)
    }
    self.addFile.call(self, fn)
    if (cb) {
      return cb(null, fn)
    }
  })
}

function createDiskBuffer (options) {
  if (!diskBufferObjectCache[options.tmpDir]) {
    diskBufferObjectCache[options.tmpDir] = new DiskBuffer(options)
    diskBufferObjectCache[options.tmpDir].isCached = false
  } else {
    diskBufferObjectCache[options.tmpDir].isCached = true
  }
  return diskBufferObjectCache[options.tmpDir]
}
module.exports.createDiskBuffer = createDiskBuffer
module.exports.DiskBuffer = DiskBuffer
<commit_msg>increase interval to sync diskBuffer with fs<commit_after>'use strict'
var path = require('path')
var fs = require('fs')
var diskBufferObjectCache = {}
var os = require('os')
var mkpath = require('mkpath')
var util = require('util')
var events = require('events')

function log (message) {
  if (process.env.DEBUG_LOGSENE_DISK_BUFFER) {
    console.log(new Date().toISOString() + ': ' + message)
  }
}

function DiskBuffer (options) {
  this.storedRequestCount = 0
  this.fileId = 0
  this.options = options || {tmpDir: os.tmpDir(), maxStoredRequests: 1000}
  this.tmpDir = options.tmpDir
  this.maxStoredRequests = options.maxStoredRequests || 1000
  this.storedFiles = []
  this.retransmitIndex = 0
  var self = this
  this.tid = setInterval(function () {
    self.retransmitNext.call(self)
  }, options.interval || 60000)
  mkpath(this.tmpDir, function (err) {
    if (err) {
      log('Error: can not activate disk buffer for logsene-js: ' + err)
    }
    self.syncFileListFromDir()
  })
}
util.inherits(DiskBuffer, events.EventEmitter)

DiskBuffer.prototype.unlock = function (fileName) {
  try {
    fs.renameSync(fileName, fileName.replace('.lock', ''))
  } catch (err) {
  }
}
DiskBuffer.prototype.retransmitNext = function () {
  if (this.storedFiles.length === 0) {
    this.retransmitIndex = 0
    return
  }
  this.retransmitIndex = this.retransmitIndex + 1
  if (this.retransmitIndex >= this.storedFiles.length) {
    this.retransmitIndex = 0
  }
  var index = this.retransmitIndex
  log('# of files: ' + this.storedFiles.length + ' current file:' + index)
  if (this.storedFiles.length >= index) {
    try {
      var fileName = this.storedFiles[index]
      if (!fileName) {
        log('filename not in list:' + fileName + ' ' + this.storedFiles.length)
        return
      }
      log('retransmitNext: ' + fileName)
      try {
        fs.statSync(fileName)
      } catch (fsStatErr) {
        // this.rmFile(fileName)
        return
      }
      var lockedFileName = fileName + '.lock'
      fs.renameSync(fileName, lockedFileName)
      var buffer = fs.readFileSync(lockedFileName)
      var self = this
      setImmediate(function () {
        self.emit('retransmit-req', {fileName: lockedFileName, buffer: buffer})
      })
    } catch (err) {
      // console.error('retransmitNext error: ' + err.message)
    }
  }
}

DiskBuffer.prototype.syncFileListFromDir = function () {
  try {
    this.cleanUp()
    this.storedFiles = fs.readdirSync(this.tmpDir)
    this.storedFiles = this.storedFiles.filter(function (fileName) {
      var rv = false
      if (/\.bulk$/.test(fileName)) {
        rv = true
      } else {
        try {
          var fName = path.join(this.tmpDir, fileName)
          var fStat = fs.statSync(fName)
          var now = Date.now()
          if (now - fStat.atime.getTime() > 1000 * 60 * 5) {
            // a bulk req. should no take longer than 5 min
            log('rename 5 min old .lock file to .bulk: ' + fName)
            fs.renameSync(fName, fName.substring(0, fName.length - 5))
            fs.unlinkSync(fName)
          }
        } catch (fsErr) {
          log('syncFileListFromDir error: ' + fsErr.message)
        }
      }
      return rv
    }.bind(this))
    this.storedFiles = this.storedFiles.map(function (fileName) {
      return path.join(this.tmpDir, fileName)
    }.bind(this))
    this.storedRequestCount = this.storedFiles.length
  } catch (err) {
    log('error syncFileListFromDir', err)
    this.storedFiles = []
  }
  log('syncFileListFromDir: ' + this.storedFiles)
}

DiskBuffer.prototype.cleanUp = function () {
  var self = this
  log('cleanUp DiskBuffer ' + this.tmpDir)
  log('stored req: ' + this.storedRequestCount + ', maxStoredReq: ' + this.maxStoredRequests)
  if (this.storedRequestCount > this.maxStoredRequests) {
    if (this.storedFiles.length > this.maxStoredRequests) {
      for (var i = 0; this.storedFiles.length >= this.maxStoredRequests; i++) {
        if (i === this.storedFiles.length) {
          break
        }
        log('disk buffer limit reached, drop old file:' + this.storedFiles[i])
        self.rmFile.call(self, this.storedFiles[i])
      }
    }
  }
}

DiskBuffer.prototype.addFile = function (fileName) {
  this.storedFiles.push(fileName)
}

DiskBuffer.prototype.rmFile = function (fileName) {
  if (!fileName) {
    return
  }
  var index = this.storedFiles.indexOf(fileName.replace('.lock', ''))
  if (index < 0) {
    // already done before
    // this.emit('removed', {fileName: fileName})
    return
  }
  try {
    fs.unlinkSync(fileName)
    log('rm file:' + fileName)
    this.emit('removed', {fileName: fileName})
  } catch (err) {
    log('rmFile: could not delete file:' + err.message)
    // ignore when file was already deleted
    this.emit('removed', {fileName: fileName})
  } finally {
    if (index > -1) {
      this.storedFiles.splice(index, 1)
      this.storedRequestCount = this.storedFiles.length
      return true
    } else {
      return false
    }
  }
}

DiskBuffer.prototype.getFileName = function () {
  this.fileId += 1
  return path.join(this.tmpDir, this.fileId + '_' + new Date().getTime() + '.bulk')
}

DiskBuffer.prototype.store = function (data, cb) {
  var self = this
  this.storedRequestCount++
  this.checkTmpDir = true
  var fn = this.getFileName()
  log('stored req: ' + this.storedRequestCount + ', maxStoredReq: ' + this.maxStoredRequests)
  this.cleanUp()
  fs.writeFile(fn, JSON.stringify(data), function (err) {
    if (cb & err) {
      return cb(err)
    }
    self.addFile.call(self, fn)
    if (cb) {
      return cb(null, fn)
    }
  })
}

function createDiskBuffer (options) {
  if (!diskBufferObjectCache[options.tmpDir]) {
    diskBufferObjectCache[options.tmpDir] = new DiskBuffer(options)
    diskBufferObjectCache[options.tmpDir].isCached = false
  } else {
    diskBufferObjectCache[options.tmpDir].isCached = true
  }
  return diskBufferObjectCache[options.tmpDir]
}
module.exports.createDiskBuffer = createDiskBuffer
module.exports.DiskBuffer = DiskBuffer
<|endoftext|>


<commit_before>/**
  This mixin adds support for being notified every time the browser window
  is scrolled.

  @class Scrolling
  @extends Ember.Mixin
  @namespace Discourse
  @module Discourse
**/

Discourse.Scrolling = Em.Mixin.create({

  /**
    Begin watching for scroll events. By default they will be called at max every 100ms.
    call with {debounce: N} for a diff time

    @method bindScrolling
  */
  bindScrolling: function(opts) {
    opts = opts || {debounce: 100};

    // So we can not call the scrolled event while transitioning
    var router = Discourse.__container__.lookup('router:main').router;

    var self = this,
        onScrollMethod = function() {
          if (router.activeTransition) { return; }
          return Em.run.scheduleOnce('afterRender', self, 'scrolled');
        };

    if (opts.debounce) {
      onScrollMethod = Discourse.debounce(onScrollMethod, opts.debounce);
    }

    Discourse.ScrollingDOMMethods.bindOnScroll(onScrollMethod, opts.name);
  },

  /**
    Stop watching for scroll events.

    @method unbindScrolling
  */
  unbindScrolling: function(name) {
    Discourse.ScrollingDOMMethods.unbindOnScroll(name);
  }

});

/**
  This object provides the DOM methods we need for our Mixin to bind to scrolling
  methods in the browser. By removing them from the Mixin we can test them
  easier.

  @class ScrollingDOMMethods
  @module Discourse
**/
Discourse.ScrollingDOMMethods = {

  bindOnScroll: function(onScrollMethod, name) {
    name = name || 'default';
    $(document).bind('touchmove.discourse-' + name, onScrollMethod);
    $(window).bind('scroll.discourse-' + name, onScrollMethod);
  },

  unbindOnScroll: function(name) {
    name = name || 'default';
    $(window).unbind('scroll.discourse-' + name);
    $(document).unbind('touchmove.discourse-' + name);
  }

};
<commit_msg>FIX: Issue scroll event after rendering<commit_after>/**
  This mixin adds support for being notified every time the browser window
  is scrolled.

  @class Scrolling
  @extends Ember.Mixin
  @namespace Discourse
  @module Discourse
**/

Discourse.Scrolling = Em.Mixin.create({

  /**
    Begin watching for scroll events. By default they will be called at max every 100ms.
    call with {debounce: N} for a diff time

    @method bindScrolling
  */
  bindScrolling: function(opts) {
    opts = opts || {debounce: 100};

    // So we can not call the scrolled event while transitioning
    var router = Discourse.__container__.lookup('router:main').router;

    var self = this,
        onScrollMethod = function() {
          if (router.activeTransition) { return; }
          return Em.run.scheduleOnce('afterRender', self, 'scrolled');
        };

    if (opts.debounce) {
      onScrollMethod = Discourse.debounce(onScrollMethod, opts.debounce);
    }

    Discourse.ScrollingDOMMethods.bindOnScroll(onScrollMethod, opts.name);
    Em.run.scheduleOnce('afterRender', onScrollMethod);
  },

  /**
    Stop watching for scroll events.

    @method unbindScrolling
  */
  unbindScrolling: function(name) {
    Discourse.ScrollingDOMMethods.unbindOnScroll(name);
  }

});

/**
  This object provides the DOM methods we need for our Mixin to bind to scrolling
  methods in the browser. By removing them from the Mixin we can test them
  easier.

  @class ScrollingDOMMethods
  @module Discourse
**/
Discourse.ScrollingDOMMethods = {

  bindOnScroll: function(onScrollMethod, name) {
    name = name || 'default';
    $(document).bind('touchmove.discourse-' + name, onScrollMethod);
    $(window).bind('scroll.discourse-' + name, onScrollMethod);
  },

  unbindOnScroll: function(name) {
    name = name || 'default';
    $(window).unbind('scroll.discourse-' + name);
    $(document).unbind('touchmove.discourse-' + name);
  }

};
<|endoftext|>


<commit_before>function initBarChart() {

  d3.json("/tally", function(error, json) {
    if (error) return console.warn(error);

    var data = [];
    var max = Number.NEGATIVE_INFINITY;
    for (var k in json) {
      var v = parseInt(json[k]);
      data.push({
        t: k,
        v: v
      });
      max = Math.max(max, v);
    }

    // for(var key in data){
    //   createNodes(data[key], frameworks[key]);
    // }
    // var merged = [];
    // merged = merged.concat.apply(merged, nodes);
    // console.log(merged);
    // visualize(merged);

    // var data = [4, 8, 15, 16, 23, 42];

    var x = d3.scale.linear()
      .domain([0, max])
      .range([0, 420]);

    d3.select("#bar-chart")
      .selectAll("div")
      .data(data)
      .enter().append("div")
      .style("width", function(d) {
        console.log(x(d.v) + "px");
        return x(d.v) + "px"; //x(parseInt(d.v)) + "px";
      })
      .style("background-color", function(d){
        var color = frameworks[d.t];
        color = color || '#333';
        return color;
      })
      .text(function(d) {
        return d.t;
      });

  });

};
<commit_msg>refining bar chart; removing bar labels in preparation for legend parameterizing width and height of chart making use of globaly defined framework colors<commit_after>function initBarChart() {

  d3.json("/tally", function(error, json) {
    if (error) return console.warn(error);

    var WIDTH = 420;
    var HEIGHT = 420;
    var BARHEIGHT;
    var data = [];
    var max = Number.NEGATIVE_INFINITY;

    for (var k in json) {
      var v = parseInt(json[k]);
      data.push({
        t: k,
        v: v
      });
      max = Math.max(max, v);
    }

    BARHEIGHT = Math.round(HEIGHT / data. length);

    var x = d3.scale.linear()
          .domain([0, max])
          .range([0, WIDTH]);

    d3.select("#bar-chart")
      .selectAll("div")
      .data(data)
      .enter().append("div")
      .style("width", function(d) {
        console.log(x(d.v) + "px");
        return x(d.v) + "px"; //x(parseInt(d.v)) + "px";
      })
      .style("height", BARHEIGHT + "px")
      .style("background-color", function(d) {
        var color = frameworkColor[d.t];
        color = color || '#333';
        return color;
      });
    // .text(function(d) {
    //   return d.t;
    // });

  });
}
<|endoftext|>


<commit_before>// GridManager
// -----------
// Generally one GridManager will be used per map.
//
// It takes one options object, which current accepts a single option:
// `resolution` determines the number of pixels per grid element in the grid.
// The default is 4.
wax.gm = function() {

    var resolution = 4,
        grid_tiles = {},
        manager = {},
        tilejson,
        formatter;

    var gridUrl = function(url) {
        if (url) {
            return url.replace(/(\.png|\.jpg|\.jpeg)(\d*)/, '.grid.json');
        }
    };

    function templatedGridUrl(template) {
        if (typeof template === 'string') template = [template];
        return function templatedGridFinder(url) {
            if (!url) return;
            var rx = new RegExp(manager.tileRegexp())
            var xyz = rx.exec(url);
            if (!xyz) return;
            return template[parseInt(xyz[2], 10) % template.length]
                .replace(/\{z\}/g, xyz[1])
                .replace(/\{x\}/g, xyz[2])
                .replace(/\{y\}/g, xyz[3]);
        };
    }

    // return the regexp to catch the tile number given the url
    manager.tileRegexp = function() {
      var tileTemplate = tilejson.tiles[0];
      // remove params
      var p = tileTemplate.indexOf('?');
      if(p !== -1) {
        tileTemplate = tileTemplate.substr(0, p);
      }
      // remove from the url all the special characters
      // replacing them by a dot (dont mind the character)
      tileTemplate = tileTemplate.
                        replace(/[\(\)\?\$\*\+\^]/g,'.')

      // the browser removes the port in the case it matchs with
      // the default port of the protocol
      if(tileTemplate.indexOf('https') === 0) {
        tileTemplate = tileTemplate.replace(':443', '[:0-9]*')
      } else if(tileTemplate.indexOf('http') === 0) {
        tileTemplate = tileTemplate.replace(':80', '[:0-9]*')
      }

      var r = '';
      if(tilejson.tiles.length > 1) {
        var t0 = tilejson.tiles[0];
        var t1 = tilejson.tiles[1];
        //search characters where differs
        for(var i = 0; i < t0.length; ++i) {
          if(t0.charAt(i) != t1.charAt(i)) {
            r += '.';
          } else {
            r += tileTemplate.charAt(i) || '';
          }
        }
      } else {
        r = tileTemplate;
      }

      // replace the first {x}{y}{z} by (\\d+)
      return r
        .replace(/\{x\}/,'(\\d+)')
        .replace(/\{y\}/,'(\\d+)')
        .replace(/\{z\}/,'(\\d+)')
    }

    manager.formatter = function(x) {
        if (!arguments.length) return formatter;
        formatter =  wax.formatter(x);
        return manager;
    };

    manager.template = function(x) {
        if (!arguments.length) return formatter;
        formatter = wax.template(x);
        return manager;
    };

    manager.gridUrl = function(x) {
        // Getter-setter
        if (!arguments.length) return gridUrl;

        // Handle tilesets that don't support grids
        if (!x) {
            gridUrl = function() { return null; };
        } else {
            gridUrl = typeof x === 'function' ?
                x : templatedGridUrl(x);
        }
        return manager;
    };

    manager.getGrid = function(url, callback) {
        var gurl = gridUrl(url);
        if (!formatter || !gurl) return callback(null, null);

        wax.request.get(gurl, function(err, t) {
            if (err) return callback(err, null);
            callback(null, wax.gi(t, {
                formatter: formatter,
                resolution: resolution
            }));
        });
        return manager;
    };

    manager.tilejson = function(x) {
        if (!arguments.length) return tilejson;
        // prefer templates over formatters
        if (x.template) {
            manager.template(x.template);
        } else if (x.formatter) {
            manager.formatter(x.formatter);
        } else {
            // In this case, we cannot support grids
            formatter = undefined;
        }
        manager.gridUrl(x.grids);
        if (x.resolution) resolution = x.resolution;
        tilejson = x;
        return manager;
    };

    return manager;
};
<commit_msg>Catch errors from tiler.<commit_after>// GridManager
// -----------
// Generally one GridManager will be used per map.
//
// It takes one options object, which current accepts a single option:
// `resolution` determines the number of pixels per grid element in the grid.
// The default is 4.
wax.gm = function() {

    var resolution = 4,
        grid_tiles = {},
        manager = {},
        tilejson,
        formatter;

    var gridUrl = function(url) {
        if (url) {
            return url.replace(/(\.png|\.jpg|\.jpeg)(\d*)/, '.grid.json');
        }
    };

    function templatedGridUrl(template) {
        if (typeof template === 'string') template = [template];
        return function templatedGridFinder(url) {
            if (!url) return;
            var rx = new RegExp(manager.tileRegexp())
            var xyz = rx.exec(url);
            if (!xyz) return;
            return template[parseInt(xyz[2], 10) % template.length]
                .replace(/\{z\}/g, xyz[1])
                .replace(/\{x\}/g, xyz[2])
                .replace(/\{y\}/g, xyz[3]);
        };
    }

    // return the regexp to catch the tile number given the url
    manager.tileRegexp = function() {
      var tileTemplate = tilejson.tiles[0];
      // remove params
      var p = tileTemplate.indexOf('?');
      if(p !== -1) {
        tileTemplate = tileTemplate.substr(0, p);
      }
      // remove from the url all the special characters
      // replacing them by a dot (dont mind the character)
      tileTemplate = tileTemplate.
                        replace(/[\(\)\?\$\*\+\^]/g,'.')

      // the browser removes the port in the case it matchs with
      // the default port of the protocol
      if(tileTemplate.indexOf('https') === 0) {
        tileTemplate = tileTemplate.replace(':443', '[:0-9]*')
      } else if(tileTemplate.indexOf('http') === 0) {
        tileTemplate = tileTemplate.replace(':80', '[:0-9]*')
      }

      var r = '';
      if(tilejson.tiles.length > 1) {
        var t0 = tilejson.tiles[0];
        var t1 = tilejson.tiles[1];
        //search characters where differs
        for(var i = 0; i < t0.length; ++i) {
          if(t0.charAt(i) != t1.charAt(i)) {
            r += '.';
          } else {
            r += tileTemplate.charAt(i) || '';
          }
        }
      } else {
        r = tileTemplate;
      }

      // replace the first {x}{y}{z} by (\\d+)
      return r
        .replace(/\{x\}/,'(\\d+)')
        .replace(/\{y\}/,'(\\d+)')
        .replace(/\{z\}/,'(\\d+)')
    }

    manager.formatter = function(x) {
        if (!arguments.length) return formatter;
        formatter =  wax.formatter(x);
        return manager;
    };

    manager.template = function(x) {
        if (!arguments.length) return formatter;
        formatter = wax.template(x);
        return manager;
    };

    manager.gridUrl = function(x) {
        // Getter-setter
        if (!arguments.length) return gridUrl;

        // Handle tilesets that don't support grids
        if (!x) {
            gridUrl = function() { return null; };
        } else {
            gridUrl = typeof x === 'function' ?
                x : templatedGridUrl(x);
        }
        return manager;
    };

    manager.getGrid = function(url, callback) {
        var gurl = gridUrl(url);
        if (!formatter || !gurl) return callback(null, null);

        wax.request.get(gurl, function(err, t) {
            if (err) return callback(err, null);
            if (t.errors_with_context) return callback(t.errors_with_context, null);
            callback(null, wax.gi(t, {
                formatter: formatter,
                resolution: resolution
            }));
        });
        return manager;
    };

    manager.tilejson = function(x) {
        if (!arguments.length) return tilejson;
        // prefer templates over formatters
        if (x.template) {
            manager.template(x.template);
        } else if (x.formatter) {
            manager.formatter(x.formatter);
        } else {
            // In this case, we cannot support grids
            formatter = undefined;
        }
        manager.gridUrl(x.grids);
        if (x.resolution) resolution = x.resolution;
        tilejson = x;
        return manager;
    };

    return manager;
};
<|endoftext|>