<commit_before>/* ************************************ */
/* Define helper functions */
/* ************************************ */
var randomDraw = function(lst) {
    var index = Math.floor(Math.random()*(lst.length))
    return lst[index]
}

var makeTrialList = function(len, stim, data) {
	//choice array: numeric key codes for the numbers 1-4
	var choice_array = [49,50,51,52]
	// 1 is a switch trial: ensure half the trials are switch trials
	var switch_trials = jsPsych.randomization.repeat([0,1],len/2,false)
	//create test array
	output_list = {image:[],data:[]}
	//randomize first trial
	var trial_index = jsPsych.randomization.shuffle(['global','local'])[0]
	if (trial_index == 'global') {
		tmpi = Math.floor(Math.random()*(stim.length/2))
	} else {
		tmpi = Math.floor(Math.random()*(stim.length/2))+stim.length/2
	}
	output_list['image'].push(stim[tmpi])
	var tmp_data = $.extend({},data[tmpi])
	tmp_data["switch"] = 0
	tmp_data["correct_response"] = choice_array[task_shapes.indexOf(data[tmpi][trial_index + '_shape'])]
	output_list['data'].push(tmp_data)

	/* randomly sample from either the global or local stimuli lists (first and half part of the stim/data arrays)
	On stay trials randomly select an additional stimulus from that array. On switch trials choose from the other list. */
	for (i=1; i<switch_trials.length;i++) {
		if (switch_trials[i] == 1) {
			if (trial_index == 'global') {trial_index = 'local'}
			else {trial_index = 'global'}
		}
		if (trial_index == 'global') {
			tmpi = Math.floor(Math.random()*(stim.length/2))
		} else {
			tmpi = Math.floor(Math.random()*(stim.length/2))+stim.length/2
		}
		output_list['image'].push(stim[tmpi])
		tmp_data = $.extend({},data[tmpi])
		tmp_data["switch"] = switch_trials[i]
		tmp_data["correct_response"] = choice_array[task_shapes.indexOf(data[tmpi][trial_index + '_shape'])]
		output_list['data'].push(tmp_data)
	}
	return output_list
}

/* ************************************ */
/* Define experimental variables */
/* ************************************ */
var task_colors = jsPsych.randomization.shuffle(['blue','black'])
var task_shapes = ['circle','X','triangle','square']
var path = 'static/images/local-global/'
prefix = '
<img src = "images/'
postfix = '"
'
stim = []
data = []
for (c=0; c<task_colors.length; c++) {
	if (c==0) {condition = 'global'} else { condition = 'local'}
	for (g=0; g<task_shapes.length; g++) {
		for (l=0; l<task_shapes.length; l++) {
			stim.push(prefix + path + task_colors[c] + '_' + task_shapes[g] + 'of' + task_shapes[l] +'s.png' + postfix)
			data.push({exp_id: 'local_global', condition: condition, global_shape: task_shapes[g], local_shape: task_shapes[l]})
		}
	}
}

//Set up experiment stimulus order
var practice_trials = makeTrialList(36,stim,data)
var practice_response_array = []
for (i=0; i<practice_trials.data.length; i++) {
	practice_response_array.push(practice_trials['data'][i].correct_response)
}
var test_trials = makeTrialList(96,stim,data)

/* ************************************ */
/* Set up jsPsych blocks */
/* ************************************ */
/* define static blocks */
var welcome_block = {
  type: 'text',
  text: '

Welcome to the local-global experiment. Press enter to begin.

',
  cont_key: 13,
  timing_post_trial: 0
};

var end_block = {
  type: 'text',
  text: '

Finished with this task.

Press enter to continue.

',
  cont_key: 13,
  timing_post_trial: 0
};

var instructions_block = {
  type: 'instructions',
  pages: [
	'

In this experiment you will see blue or black shapes made up of smaller shapes, like the image below. All of the smaller shapes will always be the same shape. Both the large shape and the smaller shapes can either be a circle, X, triangle or square.

',
	'

Your task is to respond based on how many lines either the large or small shapes have, depending on the color. If the shape is ' + task_colors[0] + ' respond based on how many lines the large shape has. If the shape is ' + task_colors[1] + ' respond based on how many lines the small shape has.

Use the number keys to respond 1 for a circle, 2 for an X, 3 for a triangle and 4 for a square.

',
	'

For instance, for the shape below you would press 3 because it is ' + task_colors[1] + ' which means you should respond based on the smaller shapes. If the shape was instead ' + task_colors[0] + ' you would press 2.

'
	],
  allow_keys: false,
  show_clickable_nav: true,
  timing_post_trial: 1000
};

var start_practice_block = {
  type: 'text',
  text: '

We will start with some practice. Press enter to begin.

',
  cont_key: 13,
  timing_post_trial: 1000
};

var start_test_block = {
  type: 'text',
  text: '

We will now start the test. Press enter to begin.

',
  cont_key: 13,
  timing_post_trial: 1000
};

/* define practice block */
var practice_block = {
  type: 'categorize',
  stimuli: practice_trials.image,
  is_html: true,
  key_answer: practice_response_array,
  correct_text: '

Correct

',
  incorrect_text: '

Incorrect

',
  timeout_message: '

Response faster!

',
  choices: [49,50,51,52],
  data: practice_trials.data,
  timing_feedback_duration: 1000,
  show_stim_with_feedback: false,
  timing_post_trial: 500
}

/* define test block */
var test_block = {
  type: 'single-stim',
  stimuli: test_trials.image,
  is_html: true,
  data: test_trials.data,
  choices: [49,50,51,52],
  timing_post_trial: 500
};

/* create experiment definition array */
var local_global_experiment = [];
local_global_experiment.push(welcome_block);
local_global_experiment.push(instructions_block);
local_global_experiment.push(start_practice_block);
local_global_experiment.push(practice_block);
local_global_experiment.push(start_test_block);
local_global_experiment.push(test_block);
local_global_experiment.push(end_block);
<commit_msg>fixing local global image paths<commit_after>/* ************************************ */
/* Define helper functions */
/* ************************************ */
var randomDraw = function(lst) {
    var index = Math.floor(Math.random()*(lst.length))
    return lst[index]
}

var makeTrialList = function(len, stim, data) {
	//choice array: numeric key codes for the numbers 1-4
	var choice_array = [49,50,51,52]
	// 1 is a switch trial: ensure half the trials are switch trials
	var switch_trials = jsPsych.randomization.repeat([0,1],len/2,false)
	//create test array
	output_list = {image:[],data:[]}
	//randomize first trial
	var trial_index = jsPsych.randomization.shuffle(['global','local'])[0]
	if (trial_index == 'global') {
		tmpi = Math.floor(Math.random()*(stim.length/2))
	} else {
		tmpi = Math.floor(Math.random()*(stim.length/2))+stim.length/2
	}
	output_list['image'].push(stim[tmpi])
	var tmp_data = $.extend({},data[tmpi])
	tmp_data["switch"] = 0
	tmp_data["correct_response"] = choice_array[task_shapes.indexOf(data[tmpi][trial_index + '_shape'])]
	output_list['data'].push(tmp_data)

	/* randomly sample from either the global or local stimuli lists (first and half part of the stim/data arrays)
	On stay trials randomly select an additional stimulus from that array. On switch trials choose from the other list. */
	for (i=1; i<switch_trials.length;i++) {
		if (switch_trials[i] == 1) {
			if (trial_index == 'global') {trial_index = 'local'}
			else {trial_index = 'global'}
		}
		if (trial_index == 'global') {
			tmpi = Math.floor(Math.random()*(stim.length/2))
		} else {
			tmpi = Math.floor(Math.random()*(stim.length/2))+stim.length/2
		}
		output_list['image'].push(stim[tmpi])
		tmp_data = $.extend({},data[tmpi])
		tmp_data["switch"] = switch_trials[i]
		tmp_data["correct_response"] = choice_array[task_shapes.indexOf(data[tmpi][trial_index + '_shape'])]
		output_list['data'].push(tmp_data)
	}
	return output_list
}

/* ************************************ */
/* Define experimental variables */
/* ************************************ */
var task_colors = jsPsych.randomization.shuffle(['blue','black'])
var task_shapes = ['circle','X','triangle','square']
var path = 'static/experiments/local_global/images/'
prefix = '
<img src = "'
postfix = '"
'
stim = []
data = []
for (c=0; c<task_colors.length; c++) {
	if (c==0) {condition = 'global'} else { condition = 'local'}
	for (g=0; g<task_shapes.length; g++) {
		for (l=0; l<task_shapes.length; l++) {
			stim.push(prefix + path + task_colors[c] + '_' + task_shapes[g] + 'of' + task_shapes[l] +'s.png' + postfix)
			data.push({exp_id: 'local_global', condition: condition, global_shape: task_shapes[g], local_shape: task_shapes[l]})
		}
	}
}

//Set up experiment stimulus order
var practice_trials = makeTrialList(36,stim,data)
var practice_response_array = []
for (i=0; i<practice_trials.data.length; i++) {
	practice_response_array.push(practice_trials['data'][i].correct_response)
}
var test_trials = makeTrialList(96,stim,data)

/* ************************************ */
/* Set up jsPsych blocks */
/* ************************************ */
/* define static blocks */
var welcome_block = {
  type: 'text',
  text: '

Welcome to the local-global experiment. Press enter to begin.

',
  cont_key: 13,
  timing_post_trial: 0
};

var end_block = {
  type: 'text',
  text: '

Finished with this task.

Press enter to continue.

',
  cont_key: 13,
  timing_post_trial: 0
};

var instructions_block = {
  type: 'instructions',
  pages: [
	'

In this experiment you will see blue or black shapes made up of smaller shapes, like the image below. All of the smaller shapes will always be the same shape. Both the large shape and the smaller shapes can either be a circle, X, triangle or square.

',
	'

Your task is to respond based on how many lines either the large or small shapes have, depending on the color. If the shape is ' + task_colors[0] + ' respond based on how many lines the large shape has. If the shape is ' + task_colors[1] + ' respond based on how many lines the small shape has.

Use the number keys to respond 1 for a circle, 2 for an X, 3 for a triangle and 4 for a square.

',
	'

For instance, for the shape below you would press 3 because it is ' + task_colors[1] + ' which means you should respond based on the smaller shapes. If the shape was instead ' + task_colors[0] + ' you would press 2.

'
	],
  allow_keys: false,
  show_clickable_nav: true,
  timing_post_trial: 1000
};

var start_practice_block = {
  type: 'text',
  text: '

We will start with some practice. Press enter to begin.

',
  cont_key: 13,
  timing_post_trial: 1000
};

var start_test_block = {
  type: 'text',
  text: '

We will now start the test. Press enter to begin.

',
  cont_key: 13,
  timing_post_trial: 1000
};

/* define practice block */
var practice_block = {
  type: 'categorize',
  stimuli: practice_trials.image,
  is_html: true,
  key_answer: practice_response_array,
  correct_text: '

Correct

',
  incorrect_text: '

Incorrect

',
  timeout_message: '

Response faster!

',
  choices: [49,50,51,52],
  data: practice_trials.data,
  timing_feedback_duration: 1000,
  show_stim_with_feedback: false,
  timing_post_trial: 500
}

/* define test block */
var test_block = {
  type: 'single-stim',
  stimuli: test_trials.image,
  is_html: true,
  data: test_trials.data,
  choices: [49,50,51,52],
  timing_post_trial: 500
};

/* create experiment definition array */
var local_global_experiment = [];
local_global_experiment.push(welcome_block);
local_global_experiment.push(instructions_block);
local_global_experiment.push(start_practice_block);
local_global_experiment.push(practice_block);
local_global_experiment.push(start_test_block);
local_global_experiment.push(test_block);
local_global_experiment.push(end_block);
<|endoftext|>


<commit_before>import OsfAdapter from './osf-adapter';

export default OsfAdapter.extend({
    fileManager: Ember.inject.service(),

    ajaxOptions() {
        // When reloading file models after a file action, bypass the cache
        let options = this._super(...arguments);
        if (this.get('fileManager.isReloading')) {
            let prefix = options.url.indexOf('?') === -1 ? '?' : '&';
            let nonce = Date.now();
            options.url += `${prefix}bypasscache=${nonce}`;
        }
        return options;
    }
});
<commit_msg>Add forgotten import<commit_after>import Ember from 'ember';
import OsfAdapter from './osf-adapter';

export default OsfAdapter.extend({
    fileManager: Ember.inject.service(),

    ajaxOptions() {
        // When reloading file models after a file action, bypass the cache
        let options = this._super(...arguments);
        if (this.get('fileManager.isReloading')) {
            let prefix = options.url.indexOf('?') === -1 ? '?' : '&';
            let nonce = Date.now();
            options.url += `${prefix}bypasscache=${nonce}`;
        }
        return options;
    }
});
<|endoftext|>


<commit_before>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// Define search commands. Depends on dialog.js or another
// implementation of the openDialog method.

// Replace works a little oddly -- it will do the replace on the next
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
// replace by making sure the match is no longer selected when hitting
// Ctrl-G.

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  function searchOverlay(query, caseInsensitive) {
    if (typeof query == "string")
      query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
    else if (!query.global)
      query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");

    return {token: function(stream) {
      query.lastIndex = stream.pos;
      var match = query.exec(stream.string);
      if (match && match.index == stream.pos) {
        stream.pos += match[0].length || 1;
        return "searching";
      } else if (match) {
        stream.pos = match.index;
      } else {
        stream.skipToEnd();
      }
    }};
  }

  function SearchState() {
    this.posFrom = this.posTo = this.lastQuery = this.query = null;
    this.overlay = null;
  }

  function getSearchState(cm) {
    return cm.state.search || (cm.state.search = new SearchState());
  }

  function queryCaseInsensitive(query) {
    return typeof query == "string" && query == query.toLowerCase();
  }

  function getSearchCursor(cm, query, pos) {
    // Heuristic: if the query string is all lowercase, do a case insensitive search.
    return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
  }

  function persistentDialog(cm, text, deflt, f) {
    cm.openDialog(text, f, {
      value: deflt,
      selectValueOnOpen: true,
      closeOnEnter: false,
      onClose: function() { clearSearch(cm); }
    });
  }

  function dialog(cm, text, shortText, deflt, f) {
    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
    else f(prompt(shortText, deflt));
  }

  function confirmDialog(cm, text, shortText, fs) {
    if (cm.openConfirm) cm.openConfirm(text, fs);
    else if (confirm(shortText)) fs[0]();
  }

  function parseString(string) {
    return string.replace(/\\(.)/g, function(_, ch) {
      if (ch == "n") return "\n"
      if (ch == "r") return "\r"
      return ch
    })
  }

  function parseQuery(query) {
    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
    if (isRE) {
      try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
      catch(e) {} // Not a regular expression after all, do a string search
    } else {
      query = parseString(query)
    }
    if (typeof query == "string" ? query == "" : query.test(""))
      query = /x^/;
    return query;
  }

  var queryDialog =
    'Search: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> (Use /re/ syntax for regexp search)';

  function startSearch(cm, state, query) {
    state.queryText = query;
    state.query = parseQuery(query);
    cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
    state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
    cm.addOverlay(state.overlay);
    if (cm.showMatchesOnScrollbar) {
      if (state.annotate) { state.annotate.clear(); state.annotate = null; }
      state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
    }
  }

  function doSearch(cm, rev, persistent) {
    var state = getSearchState(cm);
    if (state.query) return findNext(cm, rev);
    var q = cm.getSelection() || state.lastQuery;
    if (persistent && cm.openDialog) {
      var hiding = null
      persistentDialog(cm, queryDialog, q, function(query, event) {
        CodeMirror.e_stop(event);
        if (!query) return;
        if (query != state.queryText) {
          startSearch(cm, state, query);
          state.posFrom = state.posTo = cm.getCursor();
        }
        if (hiding) hiding.style.opacity = 1
        findNext(cm, event.shiftKey, function(_, to) {
          var dialog
          if (to.line < 3 && document.querySelector &&
              (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
              dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
            (hiding = dialog).style.opacity = .4
        })
      });
    } else {
      dialog(cm, queryDialog, "Search for:", q, function(query) {
        if (query && !state.query) cm.operation(function() {
          startSearch(cm, state, query);
          state.posFrom = state.posTo = cm.getCursor();
          findNext(cm, rev);
        });
      });
    }
  }

  function findNext(cm, rev, callback) {cm.operation(function() {
    var state = getSearchState(cm);
    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
    if (!cursor.find(rev)) {
      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
      if (!cursor.find(rev)) return;
    }
    cm.setSelection(cursor.from(), cursor.to());
    cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
    state.posFrom = cursor.from(); state.posTo = cursor.to();
    if (callback) callback(cursor.from(), cursor.to())
  });}

  function clearSearch(cm) {cm.operation(function() {
    var state = getSearchState(cm);
    state.lastQuery = state.query;
    if (!state.query) return;
    state.query = state.queryText = null;
    cm.removeOverlay(state.overlay);
    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
  });}

  var replaceQueryDialog =
    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> (Use /re/ syntax for regexp search)';
  var replacementQueryDialog = 'With: <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
  var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>";

  function replaceAll(cm, query, text) {
    cm.operation(function() {
      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
        if (typeof query != "string") {
          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
        } else cursor.replace(text);
      }
    });
  }

  function replace(cm, all) {
    if (cm.getOption("readOnly")) return;
    var query = cm.getSelection() || getSearchState(cm).lastQuery;
    var dialogText = all ? "Replace all:" : "Replace:"
    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
      if (!query) return;
      query = parseQuery(query);
      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
        text = parseString(text)
        if (all) {
          replaceAll(cm, query, text)
        } else {
          clearSearch(cm);
          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
          var advance = function() {
            var start = cursor.from(), match;
            if (!(match = cursor.findNext())) {
              cursor = getSearchCursor(cm, query);
              if (!(match = cursor.findNext()) ||
                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
            }
            cm.setSelection(cursor.from(), cursor.to());
            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
            confirmDialog(cm, doReplaceConfirm, "Replace?",
                          [function() {doReplace(match);}, advance,
                           function() {replaceAll(cm, query, text)}]);
          };
          var doReplace = function(match) {
            cursor.replace(typeof query == "string" ? text :
                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
            advance();
          };
          advance();
        }
      });
    });
  }

  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
  CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
  CodeMirror.commands.findNext = doSearch;
  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
  CodeMirror.commands.clearSearch = clearSearch;
  CodeMirror.commands.replace = replace;
  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
});
<commit_msg>[search addon] Capture keys for search-related commands in persistent dialog<commit_after>// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: http://codemirror.net/LICENSE

// Define search commands. Depends on dialog.js or another
// implementation of the openDialog method.

// Replace works a little oddly -- it will do the replace on the next
// Ctrl-G (or whatever is bound to findNext) press. You prevent a
// replace by making sure the match is no longer selected when hitting
// Ctrl-G.

(function(mod) {
  if (typeof exports == "object" && typeof module == "object") // CommonJS
    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
  else if (typeof define == "function" && define.amd) // AMD
    define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
  else // Plain browser env
    mod(CodeMirror);
})(function(CodeMirror) {
  "use strict";

  function searchOverlay(query, caseInsensitive) {
    if (typeof query == "string")
      query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
    else if (!query.global)
      query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");

    return {token: function(stream) {
      query.lastIndex = stream.pos;
      var match = query.exec(stream.string);
      if (match && match.index == stream.pos) {
        stream.pos += match[0].length || 1;
        return "searching";
      } else if (match) {
        stream.pos = match.index;
      } else {
        stream.skipToEnd();
      }
    }};
  }

  function SearchState() {
    this.posFrom = this.posTo = this.lastQuery = this.query = null;
    this.overlay = null;
  }

  function getSearchState(cm) {
    return cm.state.search || (cm.state.search = new SearchState());
  }

  function queryCaseInsensitive(query) {
    return typeof query == "string" && query == query.toLowerCase();
  }

  function getSearchCursor(cm, query, pos) {
    // Heuristic: if the query string is all lowercase, do a case insensitive search.
    return cm.getSearchCursor(query, pos, queryCaseInsensitive(query));
  }

  function persistentDialog(cm, text, deflt, f) {
    cm.openDialog(text, f, {
      value: deflt,
      selectValueOnOpen: true,
      closeOnEnter: false,
      onClose: function() { clearSearch(cm); },
      onKeyDown: function(ev, query) {
        var cmd = CodeMirror.keyMap['default'][CodeMirror.keyName(ev)];
        if (cmd) {
          var nextSearchCmds = ['findNext', 'findPrev'];
          var searchCmds = ['find', 'findPersistent'];
          if (nextSearchCmds.indexOf(cmd) !== -1) {
            startSearch(cm, getSearchState(cm), query);
            CodeMirror.commands[cmd](cm);
            CodeMirror.e_stop(ev);
          } else if (searchCmds.indexOf(cmd) !== -1) {
            f(query, ev);
          }
        }
      }
    });
  }

  function dialog(cm, text, shortText, deflt, f) {
    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
    else f(prompt(shortText, deflt));
  }

  function confirmDialog(cm, text, shortText, fs) {
    if (cm.openConfirm) cm.openConfirm(text, fs);
    else if (confirm(shortText)) fs[0]();
  }

  function parseString(string) {
    return string.replace(/\\(.)/g, function(_, ch) {
      if (ch == "n") return "\n"
      if (ch == "r") return "\r"
      return ch
    })
  }

  function parseQuery(query) {
    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
    if (isRE) {
      try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
      catch(e) {} // Not a regular expression after all, do a string search
    } else {
      query = parseString(query)
    }
    if (typeof query == "string" ? query == "" : query.test(""))
      query = /x^/;
    return query;
  }

  var queryDialog =
    'Search: <input type="text" style="width: 10em" class="CodeMirror-search-field"/> (Use /re/ syntax for regexp search)';

  function startSearch(cm, state, query) {
    state.queryText = query;
    state.query = parseQuery(query);
    cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
    state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
    cm.addOverlay(state.overlay);
    if (cm.showMatchesOnScrollbar) {
      if (state.annotate) { state.annotate.clear(); state.annotate = null; }
      state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
    }
  }

  function doSearch(cm, rev, persistent) {
    var state = getSearchState(cm);
    if (state.query) return findNext(cm, rev);
    var q = cm.getSelection() || state.lastQuery;
    if (persistent && cm.openDialog) {
      var hiding = null
      persistentDialog(cm, queryDialog, q, function(query, event) {
        CodeMirror.e_stop(event);
        if (!query) return;
        if (query != state.queryText) {
          startSearch(cm, state, query);
          state.posFrom = state.posTo = cm.getCursor();
        }
        if (hiding) hiding.style.opacity = 1
        findNext(cm, event.shiftKey, function(_, to) {
          var dialog
          if (to.line < 3 && document.querySelector &&
              (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
              dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
            (hiding = dialog).style.opacity = .4
        })
      });
    } else {
      dialog(cm, queryDialog, "Search for:", q, function(query) {
        if (query && !state.query) cm.operation(function() {
          startSearch(cm, state, query);
          state.posFrom = state.posTo = cm.getCursor();
          findNext(cm, rev);
        });
      });
    }
  }

  function findNext(cm, rev, callback) {cm.operation(function() {
    var state = getSearchState(cm);
    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
    if (!cursor.find(rev)) {
      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
      if (!cursor.find(rev)) return;
    }
    cm.setSelection(cursor.from(), cursor.to());
    cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
    state.posFrom = cursor.from(); state.posTo = cursor.to();
    if (callback) callback(cursor.from(), cursor.to())
  });}

  function clearSearch(cm) {cm.operation(function() {
    var state = getSearchState(cm);
    state.lastQuery = state.query;
    if (!state.query) return;
    state.query = state.queryText = null;
    cm.removeOverlay(state.overlay);
    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
  });}

  var replaceQueryDialog =
    ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> (Use /re/ syntax for regexp search)';
  var replacementQueryDialog = 'With: <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
  var doReplaceConfirm = "Replace? <button>Yes</button> <button>No</button> <button>All</button> <button>Stop</button>";

  function replaceAll(cm, query, text) {
    cm.operation(function() {
      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
        if (typeof query != "string") {
          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
        } else cursor.replace(text);
      }
    });
  }

  function replace(cm, all) {
    if (cm.getOption("readOnly")) return;
    var query = cm.getSelection() || getSearchState(cm).lastQuery;
    var dialogText = all ? "Replace all:" : "Replace:"
    dialog(cm, dialogText + replaceQueryDialog, dialogText, query, function(query) {
      if (!query) return;
      query = parseQuery(query);
      dialog(cm, replacementQueryDialog, "Replace with:", "", function(text) {
        text = parseString(text)
        if (all) {
          replaceAll(cm, query, text)
        } else {
          clearSearch(cm);
          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
          var advance = function() {
            var start = cursor.from(), match;
            if (!(match = cursor.findNext())) {
              cursor = getSearchCursor(cm, query);
              if (!(match = cursor.findNext()) ||
                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
            }
            cm.setSelection(cursor.from(), cursor.to());
            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
            confirmDialog(cm, doReplaceConfirm, "Replace?",
                          [function() {doReplace(match);}, advance,
                           function() {replaceAll(cm, query, text)}]);
          };
          var doReplace = function(match) {
            cursor.replace(typeof query == "string" ? text :
                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
            advance();
          };
          advance();
        }
      });
    });
  }

  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
  CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
  CodeMirror.commands.findNext = doSearch;
  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
  CodeMirror.commands.clearSearch = clearSearch;
  CodeMirror.commands.replace = replace;
  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
});
<|endoftext|>


<commit_before>import Ember from 'ember';

var Poll = Ember.Service.extend({
  setup: function (route, record, interval_info) {
    if (typeof(Ember.$.idle) !== "function") {
      this.setIdleListener();
    }

    if (!interval_info) {
      interval_info = Ember.Object.create({
        repititions_per_itteration: 5,
        muliplier: 2,
        max_interval_time: 60000,
        current_interval_delay: 1000,
        current_run_count: 0,
      });
    }

    route.set('interval_info', interval_info);

    var reset = function () {
      route.set('interval_info.current_run_count', 1);
      route.set('interval_info.current_interval_delay', 1000);
      Ember.run.cancel(route.get('current_poll'));
      this.run(record, route);
    };

    Ember.$(document).idle({
      onIdle: function(){
        Ember.run.cancel(route.get('current_poll'));
      },
      onActive: function(){
        Ember.run.throttle(this, reset, 1000);
      },
      idle: 10000
    });

    this.run(record, route);
  },
  reloadable: function (record) {
    return (
        record.get('isLoaded')  &&
      ! record.get('isDirty')   &&
      ! record.get('isSaving')  &&
      ! record.get('isDeleted') &&
      ! record.get('isError')   &&
      ! record.get('isNew')     &&
        record.get('isValid')
    );
  },
  setIdleListener: function () {
    Ember.$.fn.idle = function (options) {

      var defaults = {
          idle: 60000, //idle time in ms
          events: 'mousemove keypress mousedown touchstart', //events that will trigger the idle resetter
          onIdle: function () {}, //callback function to be executed after idle time
          onActive: function () {}, //callback function to be executed after back from idleness
          onHide: function () {}, //callback function to be executed when window is hidden
          onShow: function () {}, //callback function to be executed when window is visible
          keepTracking: false //if you want to keep tracking user even after the first time, set this to true
        },
        idle = false,
        visible = true,
        settings = Ember.$.extend({}, defaults, options),
        resetTimeout,
        timeout;

      resetTimeout = function (id, settings) {
        if (idle) {
          settings.onActive.call();
          idle = false;
        }
        (settings.keepTracking ? clearInterval : clearTimeout)(id);

        return timeout(settings);
      };

      timeout = function (settings) {
        var timer = (settings.keepTracking ? setInterval : setTimeout),
          id;

        id = timer(function () {
          idle = true;
          settings.onIdle.call();
        }, settings.idle);
        return id;
      };

      return this.each(function () {
        var id = timeout(settings);
        Ember.$(this).on(settings.events, function (e) {
          id = resetTimeout(id, settings);
        });
        if (options.onShow || options.onHide) {
          Ember.$(document).on("visibilitychange webkitvisibilitychange mozvisibilitychange msvisibilitychange", function () {
            if (document.hidden || document.webkitHidden || document.mozHidden || document.msHidden) {
              if (visible) {
                visible = false;
                settings.onHide.call();
              }
            } else {
              if (!visible) {
                visible = true;
                settings.onShow.call();
              }
            }
          });
        }
      });

    };
  },
  run: function (record, route) {
    var interval_info = route.get('interval_info');
    var current_run_count = interval_info.current_run_count % (interval_info.repititions_per_itteration + 1);
    var current_interval_delay = interval_info.current_interval_delay;

    var current_model = route.modelFor(route.routeName).contact;
    var id = current_model.id;
    if (this.reloadable(record) && id === record.id) {
      if (current_run_count >= interval_info.repititions_per_itteration) {
        current_interval_delay = interval_info.current_interval_delay * interval_info.muliplier;
        route.set('interval_info.current_interval_delay', current_interval_delay);
        route.set('interval_info.current_run_count', 1);
      }

      route.set('interval_info.current_run_count', current_run_count + 1);

      var poll = Ember.run.later(() => {
        record.reload();
        this.rerun(record, route);
      }, current_interval_delay);

      this.set('current_poll', poll);
    }
  },
  rerun: function (record, route) {
    this.run(record, route);
  }
});

export default Poll;
<commit_msg>Ensure this has the correct context<commit_after>import Ember from 'ember';

var Poll = Ember.Service.extend({
  setup: function (route, record, interval_info) {
    if (typeof(Ember.$.idle) !== "function") {
      this.setIdleListener();
    }

    if (!interval_info) {
      interval_info = Ember.Object.create({
        repititions_per_itteration: 5,
        muliplier: 2,
        max_interval_time: 60000,
        current_interval_delay: 1000,
        current_run_count: 0,
      });
    }

    route.set('interval_info', interval_info);

    var reset = () => {
      route.set('interval_info.current_run_count', 1);
      route.set('interval_info.current_interval_delay', 1000);
      Ember.run.cancel(route.get('current_poll'));
      this.run(record, route);
    };

    Ember.$(document).idle({
      onIdle: function(){
        Ember.run.cancel(route.get('current_poll'));
      },
      onActive: function(){
        Ember.run.throttle(this, reset, 1000);
      },
      idle: 10000
    });

    this.run(record, route);
  },
  reloadable: function (record) {
    return (
        record.get('isLoaded')  &&
      ! record.get('isDirty')   &&
      ! record.get('isSaving')  &&
      ! record.get('isDeleted') &&
      ! record.get('isError')   &&
      ! record.get('isNew')     &&
        record.get('isValid')
    );
  },
  setIdleListener: function () {
    Ember.$.fn.idle = function (options) {

      var defaults = {
          idle: 60000, //idle time in ms
          events: 'mousemove keypress mousedown touchstart', //events that will trigger the idle resetter
          onIdle: function () {}, //callback function to be executed after idle time
          onActive: function () {}, //callback function to be executed after back from idleness
          onHide: function () {}, //callback function to be executed when window is hidden
          onShow: function () {}, //callback function to be executed when window is visible
          keepTracking: false //if you want to keep tracking user even after the first time, set this to true
        },
        idle = false,
        visible = true,
        settings = Ember.$.extend({}, defaults, options),
        resetTimeout,
        timeout;

      resetTimeout = function (id, settings) {
        if (idle) {
          settings.onActive.call();
          idle = false;
        }
        (settings.keepTracking ? clearInterval : clearTimeout)(id);

        return timeout(settings);
      };

      timeout = function (settings) {
        var timer = (settings.keepTracking ? setInterval : setTimeout),
          id;

        id = timer(function () {
          idle = true;
          settings.onIdle.call();
        }, settings.idle);
        return id;
      };

      return this.each(function () {
        var id = timeout(settings);
        Ember.$(this).on(settings.events, function (e) {
          id = resetTimeout(id, settings);
        });
        if (options.onShow || options.onHide) {
          Ember.$(document).on("visibilitychange webkitvisibilitychange mozvisibilitychange msvisibilitychange", function () {
            if (document.hidden || document.webkitHidden || document.mozHidden || document.msHidden) {
              if (visible) {
                visible = false;
                settings.onHide.call();
              }
            } else {
              if (!visible) {
                visible = true;
                settings.onShow.call();
              }
            }
          });
        }
      });

    };
  },
  run: function (record, route) {
    var interval_info = route.get('interval_info');
    var current_run_count = interval_info.current_run_count % (interval_info.repititions_per_itteration + 1);
    var current_interval_delay = interval_info.current_interval_delay;

    var current_model = route.modelFor(route.routeName).contact;
    var id = current_model.id;
    if (this.reloadable(record) && id === record.id) {
      if (current_run_count >= interval_info.repititions_per_itteration) {
        current_interval_delay = interval_info.current_interval_delay * interval_info.muliplier;
        route.set('interval_info.current_interval_delay', current_interval_delay);
        route.set('interval_info.current_run_count', 1);
      }

      route.set('interval_info.current_run_count', current_run_count + 1);

      var poll = Ember.run.later(() => {
        record.reload();
        this.rerun(record, route);
      }, current_interval_delay);

      this.set('current_poll', poll);
    }
  },
  rerun: function (record, route) {
    this.run(record, route);
  }
});

export default Poll;
<|endoftext|>


<commit_before>var Wheel = (function() {
  function Wheel(fact, params) {
    this.fact = fact;
    this.opinions = $(fact).find(".opinion");
    this.authority = $(fact).find(".authority").first();
    this.params = $.extend(params, {
      "dim": 24,
      "radius": 16,
      "default_stroke": {
        "opacity": 0.2,
        "stroke": 9
      },
      "hover_stroke": {
        "opacity": 0.5,
        "stroke": 11
      }
    });
  }

  function arc(wheel, opinion, p) {
    var opacity = $(opinion).data("user-opinion") ? 1.0 : wheel.params.default_stroke.opacity;
    var the_arc = [opinion.display_value, p.offset, p.radius];

    if (!opinion.raphael) {
      // set new path
      opinion.raphael = wheel.raphael.path().attr({
        arc: the_arc,
        stroke: $(opinion).data("color"),
        "stroke-width": wheel.params.default_stroke.stroke,
        opacity: opacity
      });
    } else {
      opinion.raphael.animate({
        arc: the_arc,
        opacity: opacity
      }, 200, '<>');
    }

    return opinion.raphael;
  }

  function update_authority(wheel, authority_element) {
    var auth = authority_element.data("authority");
    var pos = wheel.params.dim + (wheel.params.dim * 0.25);

    if (!authority_element.raphael) {
      authority_element.raphael = wheel.raphael.text().attr({
        "font-size": "13pt",
        "fill": "#999"
      });
    }

    authority_element.raphael.attr({
      "text": auth,
      "x": pos,
      "y": pos
    });
    $(authority_element.raphael[0]).tooltip({title: 'This number represents the amount of thinking people have spend on this Factlink'});
  }

  Wheel.prototype.calc_display = function(opinions) {
    var too_much = 0;
    var large_ones = 0;
    $(opinions).each(function() {
      var percentage = $(this).data("value");
      if (percentage < 15) {
        too_much += 15 - percentage;
      } else if (percentage > 40) {
        large_ones += percentage;
      }
    }).each(function() {
      var percentage = $(this).data("value");
      if (percentage < 15) {
        percentage = 15;
      } else if (percentage > 40) {
        percentage = percentage - percentage / large_ones * too_much;
      }
      this.display_value = percentage;
    });

  };

  Wheel.prototype.update = function() {
    var wheel = this;
    update_authority(wheel, wheel.authority);

    wheel.calc_display(this.opinions);
    var offset = 0;
    $(this.opinions).each(function() {
      arc(wheel, this, {
        offset: offset,
        val: this.display_value,
        radius: wheel.params.radius
      });
      offset = offset + this.display_value;
    });
  };

  Wheel.prototype.bind_events = function() {
    var wheel = this;
    $(this.opinions).each(function() {
      // bind events
      var $t = $(this);
      this.raphael.mouseover(function() {
        if (!$t.data("user-opinion")) {
          this.animate({
            'stroke-width': wheel.params.hover_stroke.stroke,
            opacity: wheel.params.hover_stroke.opacity
          }, 200, '<>');
        } else {
          this.animate({
            'stroke-width': wheel.params.hover_stroke.stroke
          }, 200, '<>');
        }
      });
      this.raphael.mouseout(function() {
        if (!$t.data("user-opinion")) {
          this.stop().animate({
            'stroke-width': wheel.params.default_stroke.stroke,
            opacity: wheel.params.default_stroke.opacity
          }, 200, '<>');
        } else {
          this.animate({
            'stroke-width': wheel.params.default_stroke.stroke
          }, 200, '<>');
        }
      });
      $(this.raphael.node).bind("click", function() {
        $(wheel.fact).factlink("switch_opinion", $t);
      });
      // Bootstap popver
      $(this.raphael.node).attr("rel", "twipsy").tooltip({
        title: function() { return $t.data("name") + ": " + $t.data("value") + "%"; },
        offset: 0,
        placement:"left"
      });
    });
  };

  Wheel.prototype.init = function(canvas) {
    var wheel = this;
    wheel.raphael = Raphael(canvas, wheel.params.dim * 2 + 17, wheel.params.dim * 2 + 17);
    wheel.raphael.customAttributes.arc = function(percentage, percentage_offset, radius) {
      percentage = percentage - 2; // add padding after arc
      var large_angle = percentage > 50,
          box_dim = wheel.params.dim + 6,
          start_angle = percentage_offset * 2 * Math.PI / 100,
          end_angle = (percentage_offset + percentage) * 2 * Math.PI / 100,
          start_x = box_dim + radius * Math.cos(start_angle),
          start_y = box_dim - radius * Math.sin(start_angle),
          end_x = box_dim + radius * Math.cos(end_angle),
          end_y = box_dim - radius * Math.sin(end_angle);
      return {
        path: [
          ['M', start_x, start_y],
          ['A', radius, radius, 0, (large_angle ? 1 : 0), 0, end_x, end_y]
        ]
      };
    };
    this.update();
    this.bind_events();
  };
  return Wheel;
})();

<commit_msg>Fixed wheel authority position<commit_after>var Wheel = (function() {
  function Wheel(fact, params) {
    this.fact = fact;
    this.opinions = $(fact).find(".opinion");
    this.authority = $(fact).find(".authority").first();
    this.params = $.extend(params, {
      "dim": 24,
      "radius": 16,
      "default_stroke": {
        "opacity": 0.2,
        "stroke": 9
      },
      "hover_stroke": {
        "opacity": 0.5,
        "stroke": 11
      }
    });
  }

  function arc(wheel, opinion, p) {
    var opacity = $(opinion).data("user-opinion") ? 1.0 : wheel.params.default_stroke.opacity;
    var the_arc = [opinion.display_value, p.offset, p.radius];

    if (!opinion.raphael) {
      // set new path
      opinion.raphael = wheel.raphael.path().attr({
        arc: the_arc,
        stroke: $(opinion).data("color"),
        "stroke-width": wheel.params.default_stroke.stroke,
        opacity: opacity
      });
    } else {
      opinion.raphael.animate({
        arc: the_arc,
        opacity: opacity
      }, 200, '<>');
    }

    return opinion.raphael;
  }

  function update_authority(wheel, authority_element) {
    var auth = authority_element.data("authority");
    var pos = wheel.params.dim + (wheel.params.dim * 0.25);

    if (!authority_element.raphael) {
      authority_element.raphael = wheel.raphael.text(pos, pos, auth).attr({
        "font-size": "13pt",
        "fill": "#999"
      });
      authority_element.raphael.node.childNodes[0].setAttribute("dy","5");
    } else {
      authority_element.raphael.attr({
        "text": auth,
        "x": pos,
        "y": pos
      });
    }

    $(authority_element.raphael[0]).tooltip({title: 'This number represents the amount of thinking people have spend on this Factlink'});
  }

  Wheel.prototype.calc_display = function(opinions) {
    var too_much = 0;
    var large_ones = 0;
    $(opinions).each(function() {
      var percentage = $(this).data("value");
      if (percentage < 15) {
        too_much += 15 - percentage;
      } else if (percentage > 40) {
        large_ones += percentage;
      }
    }).each(function() {
      var percentage = $(this).data("value");
      if (percentage < 15) {
        percentage = 15;
      } else if (percentage > 40) {
        percentage = percentage - percentage / large_ones * too_much;
      }
      this.display_value = percentage;
    });

  };

  Wheel.prototype.update = function() {
    var wheel = this;

    wheel.calc_display(this.opinions);
    var offset = 0;
    $(this.opinions).each(function() {
      arc(wheel, this, {
        offset: offset,
        val: this.display_value,
        radius: wheel.params.radius
      });
      offset = offset + this.display_value;
    });

    update_authority(wheel, wheel.authority);
  };

  Wheel.prototype.bind_events = function() {
    var wheel = this;
    $(this.opinions).each(function() {
      // bind events
      var $t = $(this);
      this.raphael.mouseover(function() {
        if (!$t.data("user-opinion")) {
          this.animate({
            'stroke-width': wheel.params.hover_stroke.stroke,
            opacity: wheel.params.hover_stroke.opacity
          }, 200, '<>');
        } else {
          this.animate({
            'stroke-width': wheel.params.hover_stroke.stroke
          }, 200, '<>');
        }
      });
      this.raphael.mouseout(function() {
        if (!$t.data("user-opinion")) {
          this.stop().animate({
            'stroke-width': wheel.params.default_stroke.stroke,
            opacity: wheel.params.default_stroke.opacity
          }, 200, '<>');
        } else {
          this.animate({
            'stroke-width': wheel.params.default_stroke.stroke
          }, 200, '<>');
        }
      });
      $(this.raphael.node).bind("click", function() {
        $(wheel.fact).factlink("switch_opinion", $t);
      });
      // Bootstap popver
      $(this.raphael.node).attr("rel", "twipsy").tooltip({
        title: function() { return $t.data("name") + ": " + $t.data("value") + "%"; },
        offset: 0,
        placement:"left"
      });
    });
  };

  Wheel.prototype.init = function(canvas) {
    var wheel = this;
    wheel.raphael = Raphael(canvas, wheel.params.dim * 2 + 17, wheel.params.dim * 2 + 17);
    wheel.raphael.customAttributes.arc = function(percentage, percentage_offset, radius) {
      percentage = percentage - 2; // add padding after arc
      var large_angle = percentage > 50,
          box_dim = wheel.params.dim + 6,
          start_angle = percentage_offset * 2 * Math.PI / 100,
          end_angle = (percentage_offset + percentage) * 2 * Math.PI / 100,
          start_x = box_dim + radius * Math.cos(start_angle),
          start_y = box_dim - radius * Math.sin(start_angle),
          end_x = box_dim + radius * Math.cos(end_angle),
          end_y = box_dim - radius * Math.sin(end_angle);
      return {
        path: [
          ['M', start_x, start_y],
          ['A', radius, radius, 0, (large_angle ? 1 : 0), 0, end_x, end_y]
        ]
      };
    };
    this.update();
    this.bind_events();
  };
  return Wheel;
})();

<|endoftext|>


<commit_before>/*
 * NchanSubscriber
 * usage: var sub = NchanSubscriber(url, opt);
 *
 * opt = {
 *   subscriber: 'longpoll', 'eventsource', or 'websocket'
 *     //or an array of the above indicating subscriber type preference
 * }
 *
 * sub.on("message", function(message, message_metadata) {
 *   // message is a string
 *   // message_metadata may contain 'id' and 'content-type'
 * });
 *
 * sub.on('connect', function(evt) {
 *   //fired when first connected.
 * }
 *
 * sub.on('disconnect', function(evt) {
 *   // when disconnected.
 * }
 *
 * sub.on('error', function(code, message) {
 *   //error callback. not sure about the parameters yet
 * }
 *
 * sub.reconnect; // should subscriber try to reconnect? true by default.
 * sub.reconnectTimeout; //how long to wait to reconnect? does not apply to EventSource
 * sub.lastMessageId; //last message id. useful for resuming a connection without loss or repetition.
 *
 * sub.start(); // begin (or resume) subscribing
 * sub.stop(); // stop subscriber. do not reconnect.
 */

//Thanks Darren Whitlen ( @prawnsalad ) for your feedback

(function(global, undefined) {

// https://github.com/yanatan16/nanoajax
!function(t,e){function n(t){return t&&e.XDomainRequest&&!/MSIE 1/.test(navigator.userAgent)?new XDomainRequest:e.XMLHttpRequest?new XMLHttpRequest:void 0}function o(t,e,n){t[e]=t[e]||n}var r=["responseType","withCredentials","timeout","onprogress"];t.ajax=function(t,a){function s(t,e){return function(){c||(a(void 0===f.status?t:f.status,0===f.status?"Error":f.response||f.responseText||e,f),c=!0)}}var u=t.headers||{},i=t.body,d=t.method||(i?"POST":"GET"),c=!1,f=n(t.cors);f.open(d,t.url,!0);var l=f.onload=s(200);f.onreadystatechange=function(){4===f.readyState&&l()},f.onerror=s(null,"Error"),f.ontimeout=s(null,"Timeout"),f.onabort=s(null,"Abort"),i&&(o(u,"X-Requested-With","XMLHttpRequest"),e.FormData&&i instanceof e.FormData||o(u,"Content-Type","application/x-www-form-urlencoded"));for(var p,m=0,v=r.length;v>m;m++)p=r[m],void 0!==t[p]&&(f[p]=t[p]);for(var p in u)f.setRequestHeader(p,u[p]);return f.send(i),f},e.nanoajax=t}({},function(){return this}());

// https://github.com/component/emitter
function Emitter(t){return t?mixin(t):void 0}function mixin(t){for(var e in Emitter.prototype)t[e]=Emitter.prototype[e];return t}Emitter.prototype.on=Emitter.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},Emitter.prototype.once=function(t,e){function i(){this.off(t,i),e.apply(this,arguments)}return i.fn=e,this.on(t,i),this},Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var i=this._callbacks["$"+t];if(!i)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var r,s=0;s<i.length;s++)if(r=i[s],r===e||r.fn===e){i.splice(s,1);break}return this},Emitter.prototype.emit=function(t){this._callbacks=this._callbacks||{};var e=[].slice.call(arguments,1),i=this._callbacks["$"+t];if(i){i=i.slice(0);for(var r=0,s=i.length;s>r;++r)i[r].apply(this,e)}return this},Emitter.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks["$"+t]||[]},Emitter.prototype.hasListeners=function(t){return!!this.listeners(t).length};

var ughbind = (Function.prototype.bind
  ? function ughbind(fn, thisObj) {
    return fn.bind(thisObj);
  }
  : function ughbind(fn, thisObj) {
    return function() {
      fn.apply(thisObj, arguments);
    };
  }
);

"use strict";
function NchanSubscriber(url, opt) {
  this.url = url;

  //which transport should i use?
  if(typeof opt === "string") {
    opt = {subscriber: opt};
  }
  if(opt.transport && !opt.subscriber) {
    opt.subscriber = opt.transport;
  }
  if(typeof opt.subscriber === "string") {
    opt.subscriber = [ opt.subscriber ];
  }
  var tryInitializeTransport = ughbind(function(name) {
    if(!this.SubscriberClass[name]) {
      throw "unknown subscriber type " + name;
    }
    try {
      this.transport = new this.SubscriberClass[name](ughbind(this.emit, this)); //try it
      return this.transport;
    } catch(err) { /*meh...*/ }
  }, this);

  var i;
  if(opt.subscriber) {
    for(i in opt.subscriber) {
      if(tryInitializeTransport(opt.subscriber[i])) {
        break;
      }
    }
  }
  else {
    for(i in this.SubscriberClass) {
      if(tryInitializeTransport(i)) {
        break;
      }
    }
  }
  if(! this.transport) {
    throw "can't use any transport type";
  }

  this.msgId = opt.id || opt.msgId;
  this.reconnect = typeof opt.reconnect == "undefined" ? true : opt.reconnect;
  this.reconnectTimeout = opt.reconnectTimeout || 10;

  var restartTimeoutIndex;
  var stopHandler = ughbind(function() {
    if(!restartTimeoutIndex && this.running && this.reconnect && !this.transport.reconnecting) {
      restartTimeoutIndex = setTimeout(ughbind(function() {
        restartTimeoutIndex = null;
        this.start();
      }, this), this.reconnectTimeout);
    }
  }, this);

  this.on("message", function msg(msg, meta) {
    this.lastMessageId=meta.id;
    //console.log(msg, meta);
  });
  this.on("error", function fail(code, text) {
    stopHandler(code, text);
    //console.log("failure", code, text);
  });
  this.on("connect", function() {
    this.connected = true;
  });
  this.on("__disconnect", function fail(code, text) {
    this.connected = false;
    this.emit("disconnect", code, text);
    stopHandler(code, text);
    //console.log("__disconnect", code, text);
  });

  /*
  //explicitly stop just before leaving the page
  ['unload', 'beforeunload'].each(function(ev) {
    window.addEvent(ev, this.stop.bind(this));
  }.bind(this));
  */
}

Emitter(NchanSubscriber.prototype);

NchanSubscriber.prototype.start = function() {
  this.transport.listen(this.url, this.lastMessageId);
  this.running = true;
  return this;
};

NchanSubscriber.prototype.stop = function() {
  this.running = false;
  this.transport.cancel();
  return this;
};

NchanSubscriber.prototype.saveState = function() {
  throw "not implemented";
};

function addLastMsgIdToQueryString(url, msgid) {
  if(msgid) {
    var m = url.match(/(\?.*)$/);
    url += (m ? "&" : "?") + "last_event_id=" + encodeURIComponent(msgid);
  }
  return url;
}

NchanSubscriber.prototype.SubscriberClass = {
  'longpoll': (function () {
    function Longpoll(emit) {
      this.headers = {};
      this.longPollStartTime = null;
      this.maxLongPollTime = 5*60*1000; //5 minutes
      this.emit = emit;
    }

    Longpoll.prototype.listen = function(url, msgid) {
      if(this.req) {
        throw "already listening";
      }
      if(url) { this.url=url; }
      var setHeader = ughbind(function(incoming, name) {
        if(incoming) { this.headers[name]= incoming; }
      }, this);

      if(headers) {
        this.headers = {"Etag": msgid};
      }

      this.reqStartTime = new Date().getTime();

      var  requestCallback;
      requestCallback = ughbind(function (code, response_text, req) {
        setHeader(req.getResponseHeader('Last-Modified'), 'If-Modified-Since');
        setHeader(req.getResponseHeader('Etag'), 'If-None-Match');

        if(code >= 200 && code <= 210) {
          //legit reply
          var content_type = req.getResponseHeader('Content-Type');
          if (!this.parseMultipartMixedMessage(content_type, response_text, req)) {
            this.emit("message", response_text || "", {'content-type': content_type, 'id': this.msgIdFromResponseHeaders(req)});
          }

          this.reqStartTime = new Date().getTime();
          this.req = nanoajax.ajax({url: this.url, headers: this.headers}, requestCallback);
        }
        else if((code == 0 && response_text == "Error" && req.readyState == 4) || (code === null && response_text != "Abort")) {
          //console.log("abort!!!");
          this.emit("__disconnect", code || 0, response_text);
          delete this.req;
        }
        else if(code !== null) {
          //HTTP error
          this.emit("error", code, response_text);
          delete this.req;
        }
        else {
          //don't care about abortions
          delete this.req;
          this.emit("__disconnect");
          //console.log("abort!");
        }
      }, this);

      this.reqStartTime = new Date().getTime();
      this.req = nanoajax.ajax({url: this.url, headers: this.headers}, requestCallback);
      this.emit("connect");

      return this;
    };

    Longpoll.prototype.parseMultipartMixedMessage = function(content_type, text, req) {
      var m = content_type && content_type.match(/^multipart\/mixed;\s+boundary=(.*)$/);
      if(!m) {
        return false;
      }
      var boundary = m[1];

      var msgs = text.split("--" + boundary);
      if(msgs[0] != "" || !msgs[msgs.length-1].match(/--\r?\n/)) { throw "weird multipart/mixed split"; }

      msgs = msgs.slice(1, -1);
      for(var i in msgs) {
        m = msgs[i].match(/^(.*)\r?\n\r?\n([\s\S]*)\r?\n$/m);
        var hdrs = m[1].split("\n");

        var meta = {};
        for(var j in hdrs) {
          var hdr = hdrs[j].match(/^([^:]+):\s+(.*)/);
          if(hdr && hdr[1] == "Content-Type") {
            meta["content-type"] = hdr[2];
          }
        }

        if(i == msgs.length - 1) {
          meta["id"] = this.msgIdFromResponseHeaders(req);
        }
        this.emit('message', m[2], meta);
      }
      return true;
    };

    Longpoll.prototype.msgIdFromResponseHeaders = function(req) {
      var lastModified, etag;
      lastModified = req.getResponseHeader('Last-Modified');
      etag = req.getResponseHeader('Etag');
      if(lastModified) {
        return "" + Date.parse(lastModified)/1000 + ":" + (etag || "0");
      }
      else if(etag) {
        return etag;
      }
      else {
        return null;
      }
    };

    Longpoll.prototype.cancel = function() {
      if(this.req) {
        this.req.abort();
        delete this.req;
      }
      return this;
    };

    return Longpoll;
  })(),

  'eventsource': (function() {
    function ESWrapper(emit) {
      EventSource;
      this.emit = emit;
    }

    ESWrapper.prototype.listen= function(url, msgid) {
      url = addLastMsgIdToQueryString(url, msgid);
      if(this.listener) {
        throw "there's a ES listener running already";
      }
      this.listener = new EventSource(url);
      var l = this.listener;
      l.onmessage = ughbind(function(evt){
        //console.log("message", evt);
        this.emit('message', evt.data, {id: evt.lastEventId});
      }, this);

      l.onopen = ughbind(function(evt) {
        this.reconnecting = false;
        //console.log("connect", evt);
        this.emit('connect', evt);
      }, this);

      l.onerror = ughbind(function(evt) {
        //EventSource will try to reconnect by itself
        //console.log("onerror", this.listener.readyState, evt);
        if(this.listener.readyState == EventSource.CONNECTING && !this.reconnecting) {
          if(!this.reconnecting) {
            this.reconnecting = true;
            this.emit('__disconnect', evt);
          }
        }
        else {
          this.emit('__disconnect', evt);
          //console.log('other __disconnect', evt);
        }
      }, this);
    };

    ESWrapper.prototype.cancel= function() {
      if(this.listener) {
        this.listener.close();
        delete this.listener;
      }
    };

    return ESWrapper;
  })(),

  'websocket': (function() {
    function WSWrapper(emit) {
      WebSocket;
      this.emit = emit;
    }

    WSWrapper.prototype.websocketizeURL = function(url) {
      var m = url.match(/^((\w+:)?\/\/([^\/]+))?(\/)?(.*)/);
      var protocol = m[2];
      var host = m[3];
      var absolute = m[4];
      var path = m[5];

      var loc;
      if(typeof window == "object") {
        loc = window.location;
      }

      if(!protocol && loc) {
        protocol = loc.protocol;
      }
      if(protocol == "https:") {
        protocol = "wss:";
      }
      else if(protocol == "http:") {
        protocol = "ws:";
      }
      else {
        protocol = "wss:"; //default setting: secure
      }

      if(!host && loc) {
        host = loc.host;
      }

      if(!absolute) {
        path = loc ? loc.pathname.match(/(.*\/)[^/]*/)[1] + path : "/" + path;
      }
      else {
        path = "/" + path;
      }

      return protocol + "//" + host + path;
    };

    WSWrapper.prototype.listen = function(url, msgid) {
      url = this.websocketizeURL(url);
      url = addLastMsgIdToQueryString(url, msgid);
      //console.log(url);
      if(this.listener) {
        throw "websocket already listening";
      }
      this.listener = new WebSocket(url, 'ws+meta.nchan');
      var l = this.listener;
      l.onmessage = ughbind(function(evt) {
        var m = evt.data.match(/^id: (.*)\n(content-type: (.*)\n)?\n/m);
        this.emit('message', evt.data.substr(m[0].length), {'id': m[1], 'content-type': m[3]});
      }, this);

      l.onopen = ughbind(function(evt) {
        this.emit('connect', evt);
        //console.log("connect", evt);
      }, this);

      l.onerror = ughbind(function(evt) {
        //console.log("error", evt);
        this.emit('error', evt, l);
        delete this.listener;
      }, this);

      l.onclose = ughbind(function(evt) {
        this.emit('__disconnect', evt);
        delete this.listener;
      }, this);
    };

    WSWrapper.prototype.cancel = function() {
      if(this.listener) {
        this.listener.close();
        delete this.listener;
      }
    };

    return WSWrapper;
  })()
};

global.NchanSubscriber = NchanSubscriber;
})(window);

<commit_msg>"new NchanSubscriber"<commit_after>/*
 * NchanSubscriber
 * usage: var sub = new NchanSubscriber(url, opt);
 *
 * opt = {
 *   subscriber: 'longpoll', 'eventsource', or 'websocket'
 *     //or an array of the above indicating subscriber type preference
 * }
 *
 * sub.on("message", function(message, message_metadata) {
 *   // message is a string
 *   // message_metadata may contain 'id' and 'content-type'
 * });
 *
 * sub.on('connect', function(evt) {
 *   //fired when first connected.
 * }
 *
 * sub.on('disconnect', function(evt) {
 *   // when disconnected.
 * }
 *
 * sub.on('error', function(code, message) {
 *   //error callback. not sure about the parameters yet
 * }
 *
 * sub.reconnect; // should subscriber try to reconnect? true by default.
 * sub.reconnectTimeout; //how long to wait to reconnect? does not apply to EventSource
 * sub.lastMessageId; //last message id. useful for resuming a connection without loss or repetition.
 *
 * sub.start(); // begin (or resume) subscribing
 * sub.stop(); // stop subscriber. do not reconnect.
 */

//Thanks Darren Whitlen ( @prawnsalad ) for your feedback

(function(global, undefined) {

// https://github.com/yanatan16/nanoajax
!function(t,e){function n(t){return t&&e.XDomainRequest&&!/MSIE 1/.test(navigator.userAgent)?new XDomainRequest:e.XMLHttpRequest?new XMLHttpRequest:void 0}function o(t,e,n){t[e]=t[e]||n}var r=["responseType","withCredentials","timeout","onprogress"];t.ajax=function(t,a){function s(t,e){return function(){c||(a(void 0===f.status?t:f.status,0===f.status?"Error":f.response||f.responseText||e,f),c=!0)}}var u=t.headers||{},i=t.body,d=t.method||(i?"POST":"GET"),c=!1,f=n(t.cors);f.open(d,t.url,!0);var l=f.onload=s(200);f.onreadystatechange=function(){4===f.readyState&&l()},f.onerror=s(null,"Error"),f.ontimeout=s(null,"Timeout"),f.onabort=s(null,"Abort"),i&&(o(u,"X-Requested-With","XMLHttpRequest"),e.FormData&&i instanceof e.FormData||o(u,"Content-Type","application/x-www-form-urlencoded"));for(var p,m=0,v=r.length;v>m;m++)p=r[m],void 0!==t[p]&&(f[p]=t[p]);for(var p in u)f.setRequestHeader(p,u[p]);return f.send(i),f},e.nanoajax=t}({},function(){return this}());

// https://github.com/component/emitter
function Emitter(t){return t?mixin(t):void 0}function mixin(t){for(var e in Emitter.prototype)t[e]=Emitter.prototype[e];return t}Emitter.prototype.on=Emitter.prototype.addEventListener=function(t,e){return this._callbacks=this._callbacks||{},(this._callbacks["$"+t]=this._callbacks["$"+t]||[]).push(e),this},Emitter.prototype.once=function(t,e){function i(){this.off(t,i),e.apply(this,arguments)}return i.fn=e,this.on(t,i),this},Emitter.prototype.off=Emitter.prototype.removeListener=Emitter.prototype.removeAllListeners=Emitter.prototype.removeEventListener=function(t,e){if(this._callbacks=this._callbacks||{},0==arguments.length)return this._callbacks={},this;var i=this._callbacks["$"+t];if(!i)return this;if(1==arguments.length)return delete this._callbacks["$"+t],this;for(var r,s=0;s<i.length;s++)if(r=i[s],r===e||r.fn===e){i.splice(s,1);break}return this},Emitter.prototype.emit=function(t){this._callbacks=this._callbacks||{};var e=[].slice.call(arguments,1),i=this._callbacks["$"+t];if(i){i=i.slice(0);for(var r=0,s=i.length;s>r;++r)i[r].apply(this,e)}return this},Emitter.prototype.listeners=function(t){return this._callbacks=this._callbacks||{},this._callbacks["$"+t]||[]},Emitter.prototype.hasListeners=function(t){return!!this.listeners(t).length};

var ughbind = (Function.prototype.bind
  ? function ughbind(fn, thisObj) {
    return fn.bind(thisObj);
  }
  : function ughbind(fn, thisObj) {
    return function() {
      fn.apply(thisObj, arguments);
    };
  }
);

"use strict";
function NchanSubscriber(url, opt) {
  if(this === window) {
    throw "use 'new NchanSubscriber(...)' to initialize";
  }

  this.url = url;

  //which transport should i use?
  if(typeof opt === "string") {
    opt = {subscriber: opt};
  }
  if(opt.transport && !opt.subscriber) {
    opt.subscriber = opt.transport;
  }
  if(typeof opt.subscriber === "string") {
    opt.subscriber = [ opt.subscriber ];
  }
  var tryInitializeTransport = ughbind(function(name) {
    if(!this.SubscriberClass[name]) {
      throw "unknown subscriber type " + name;
    }
    try {
      this.transport = new this.SubscriberClass[name](ughbind(this.emit, this)); //try it
      return this.transport;
    } catch(err) { /*meh...*/ }
  }, this);

  var i;
  if(opt.subscriber) {
    for(i in opt.subscriber) {
      if(tryInitializeTransport(opt.subscriber[i])) {
        break;
      }
    }
  }
  else {
    for(i in this.SubscriberClass) {
      if(tryInitializeTransport(i)) {
        break;
      }
    }
  }
  if(! this.transport) {
    throw "can't use any transport type";
  }

  this.msgId = opt.id || opt.msgId;
  this.reconnect = typeof opt.reconnect == "undefined" ? true : opt.reconnect;
  this.reconnectTimeout = opt.reconnectTimeout || 10;

  var restartTimeoutIndex;
  var stopHandler = ughbind(function() {
    if(!restartTimeoutIndex && this.running && this.reconnect && !this.transport.reconnecting) {
      restartTimeoutIndex = setTimeout(ughbind(function() {
        restartTimeoutIndex = null;
        this.start();
      }, this), this.reconnectTimeout);
    }
  }, this);

  this.on("message", function msg(msg, meta) {
    this.lastMessageId=meta.id;
    //console.log(msg, meta);
  });
  this.on("error", function fail(code, text) {
    stopHandler(code, text);
    //console.log("failure", code, text);
  });
  this.on("connect", function() {
    this.connected = true;
  });
  this.on("__disconnect", function fail(code, text) {
    this.connected = false;
    this.emit("disconnect", code, text);
    stopHandler(code, text);
    //console.log("__disconnect", code, text);
  });

  /*
  //explicitly stop just before leaving the page
  ['unload', 'beforeunload'].each(function(ev) {
    window.addEvent(ev, this.stop.bind(this));
  }.bind(this));
  */
}

Emitter(NchanSubscriber.prototype);

NchanSubscriber.prototype.start = function() {
  this.transport.listen(this.url, this.lastMessageId);
  this.running = true;
  return this;
};

NchanSubscriber.prototype.stop = function() {
  this.running = false;
  this.transport.cancel();
  return this;
};

NchanSubscriber.prototype.saveState = function() {
  throw "not implemented";
};

function addLastMsgIdToQueryString(url, msgid) {
  if(msgid) {
    var m = url.match(/(\?.*)$/);
    url += (m ? "&" : "?") + "last_event_id=" + encodeURIComponent(msgid);
  }
  return url;
}

NchanSubscriber.prototype.SubscriberClass = {
  'longpoll': (function () {
    function Longpoll(emit) {
      this.headers = {};
      this.longPollStartTime = null;
      this.maxLongPollTime = 5*60*1000; //5 minutes
      this.emit = emit;
    }

    Longpoll.prototype.listen = function(url, msgid) {
      if(this.req) {
        throw "already listening";
      }
      if(url) { this.url=url; }
      var setHeader = ughbind(function(incoming, name) {
        if(incoming) { this.headers[name]= incoming; }
      }, this);

      if(headers) {
        this.headers = {"Etag": msgid};
      }

      this.reqStartTime = new Date().getTime();

      var  requestCallback;
      requestCallback = ughbind(function (code, response_text, req) {
        setHeader(req.getResponseHeader('Last-Modified'), 'If-Modified-Since');
        setHeader(req.getResponseHeader('Etag'), 'If-None-Match');

        if(code >= 200 && code <= 210) {
          //legit reply
          var content_type = req.getResponseHeader('Content-Type');
          if (!this.parseMultipartMixedMessage(content_type, response_text, req)) {
            this.emit("message", response_text || "", {'content-type': content_type, 'id': this.msgIdFromResponseHeaders(req)});
          }

          this.reqStartTime = new Date().getTime();
          this.req = nanoajax.ajax({url: this.url, headers: this.headers}, requestCallback);
        }
        else if((code == 0 && response_text == "Error" && req.readyState == 4) || (code === null && response_text != "Abort")) {
          //console.log("abort!!!");
          this.emit("__disconnect", code || 0, response_text);
          delete this.req;
        }
        else if(code !== null) {
          //HTTP error
          this.emit("error", code, response_text);
          delete this.req;
        }
        else {
          //don't care about abortions
          delete this.req;
          this.emit("__disconnect");
          //console.log("abort!");
        }
      }, this);

      this.reqStartTime = new Date().getTime();
      this.req = nanoajax.ajax({url: this.url, headers: this.headers}, requestCallback);
      this.emit("connect");

      return this;
    };

    Longpoll.prototype.parseMultipartMixedMessage = function(content_type, text, req) {
      var m = content_type && content_type.match(/^multipart\/mixed;\s+boundary=(.*)$/);
      if(!m) {
        return false;
      }
      var boundary = m[1];

      var msgs = text.split("--" + boundary);
      if(msgs[0] != "" || !msgs[msgs.length-1].match(/--\r?\n/)) { throw "weird multipart/mixed split"; }

      msgs = msgs.slice(1, -1);
      for(var i in msgs) {
        m = msgs[i].match(/^(.*)\r?\n\r?\n([\s\S]*)\r?\n$/m);
        var hdrs = m[1].split("\n");

        var meta = {};
        for(var j in hdrs) {
          var hdr = hdrs[j].match(/^([^:]+):\s+(.*)/);
          if(hdr && hdr[1] == "Content-Type") {
            meta["content-type"] = hdr[2];
          }
        }

        if(i == msgs.length - 1) {
          meta["id"] = this.msgIdFromResponseHeaders(req);
        }
        this.emit('message', m[2], meta);
      }
      return true;
    };

    Longpoll.prototype.msgIdFromResponseHeaders = function(req) {
      var lastModified, etag;
      lastModified = req.getResponseHeader('Last-Modified');
      etag = req.getResponseHeader('Etag');
      if(lastModified) {
        return "" + Date.parse(lastModified)/1000 + ":" + (etag || "0");
      }
      else if(etag) {
        return etag;
      }
      else {
        return null;
      }
    };

    Longpoll.prototype.cancel = function() {
      if(this.req) {
        this.req.abort();
        delete this.req;
      }
      return this;
    };

    return Longpoll;
  })(),

  'eventsource': (function() {
    function ESWrapper(emit) {
      EventSource;
      this.emit = emit;
    }

    ESWrapper.prototype.listen= function(url, msgid) {
      url = addLastMsgIdToQueryString(url, msgid);
      if(this.listener) {
        throw "there's a ES listener running already";
      }
      this.listener = new EventSource(url);
      var l = this.listener;
      l.onmessage = ughbind(function(evt){
        //console.log("message", evt);
        this.emit('message', evt.data, {id: evt.lastEventId});
      }, this);

      l.onopen = ughbind(function(evt) {
        this.reconnecting = false;
        //console.log("connect", evt);
        this.emit('connect', evt);
      }, this);

      l.onerror = ughbind(function(evt) {
        //EventSource will try to reconnect by itself
        //console.log("onerror", this.listener.readyState, evt);
        if(this.listener.readyState == EventSource.CONNECTING && !this.reconnecting) {
          if(!this.reconnecting) {
            this.reconnecting = true;
            this.emit('__disconnect', evt);
          }
        }
        else {
          this.emit('__disconnect', evt);
          //console.log('other __disconnect', evt);
        }
      }, this);
    };

    ESWrapper.prototype.cancel= function() {
      if(this.listener) {
        this.listener.close();
        delete this.listener;
      }
    };

    return ESWrapper;
  })(),

  'websocket': (function() {
    function WSWrapper(emit) {
      WebSocket;
      this.emit = emit;
    }

    WSWrapper.prototype.websocketizeURL = function(url) {
      var m = url.match(/^((\w+:)?\/\/([^\/]+))?(\/)?(.*)/);
      var protocol = m[2];
      var host = m[3];
      var absolute = m[4];
      var path = m[5];

      var loc;
      if(typeof window == "object") {
        loc = window.location;
      }

      if(!protocol && loc) {
        protocol = loc.protocol;
      }
      if(protocol == "https:") {
        protocol = "wss:";
      }
      else if(protocol == "http:") {
        protocol = "ws:";
      }
      else {
        protocol = "wss:"; //default setting: secure
      }

      if(!host && loc) {
        host = loc.host;
      }

      if(!absolute) {
        path = loc ? loc.pathname.match(/(.*\/)[^/]*/)[1] + path : "/" + path;
      }
      else {
        path = "/" + path;
      }

      return protocol + "//" + host + path;
    };

    WSWrapper.prototype.listen = function(url, msgid) {
      url = this.websocketizeURL(url);
      url = addLastMsgIdToQueryString(url, msgid);
      //console.log(url);
      if(this.listener) {
        throw "websocket already listening";
      }
      this.listener = new WebSocket(url, 'ws+meta.nchan');
      var l = this.listener;
      l.onmessage = ughbind(function(evt) {
        var m = evt.data.match(/^id: (.*)\n(content-type: (.*)\n)?\n/m);
        this.emit('message', evt.data.substr(m[0].length), {'id': m[1], 'content-type': m[3]});
      }, this);

      l.onopen = ughbind(function(evt) {
        this.emit('connect', evt);
        //console.log("connect", evt);
      }, this);

      l.onerror = ughbind(function(evt) {
        //console.log("error", evt);
        this.emit('error', evt, l);
        delete this.listener;
      }, this);

      l.onclose = ughbind(function(evt) {
        this.emit('__disconnect', evt);
        delete this.listener;
      }, this);
    };

    WSWrapper.prototype.cancel = function() {
      if(this.listener) {
        this.listener.close();
        delete this.listener;
      }
    };

    return WSWrapper;
  })()
};

global.NchanSubscriber = NchanSubscriber;
})(window);

<|endoftext|>


<commit_before>import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import * as actions from '../datafiles';
import * as types from '../../constants/actionTypes';
import { API } from '../../constants/api';
import nock from 'nock';
import expect from 'expect';

const middlewares = [ thunk ];
const mockStore = configureMockStore(middlewares);

import { datafile } from './fixtures';

describe('Actions::Datafiles', () => {
  afterEach(() => {
    nock.cleanAll();
  });

  it('fetches data files successfully', () => {
    nock(API)
      .get('/data')
      .reply(200, [datafile]);

    const expectedActions = [
      { type: types.FETCH_DATAFILES_REQUEST },
      { type: types.FETCH_DATAFILES_SUCCESS, files: [datafile] }
    ];

    const store = mockStore({ files: [] });

    return store.dispatch(actions.fetchDataFiles())
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedActions);
      });
  });

  it('creates FETCH_DATAFILES_FAILURE when fetching datafiles failed', () => {
    nock(API)
      .get('/data')
      .replyWithError('Something gone wrong');

    const expectedActions = [
      { type: types.FETCH_DATAFILES_REQUEST },
      { type: types.FETCH_DATAFILES_FAILURE }
    ];

    const store = mockStore({ files: [] });

    return store.dispatch(actions.fetchDataFiles())
      .then(() => { // return of async actions
        expect(store.getActions()[1].type).toEqual(expectedActions[1].type);
      });
  });

  it('fetches a data file successfully', () => {
    nock(API)
      .get('/data/data_file.yml')
      .reply(200, datafile);

    const expectedActions = [
      { type: types.FETCH_DATAFILE_REQUEST },
      { type: types.FETCH_DATAFILE_SUCCESS, file: datafile }
    ];

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.fetchDataFile('data_file.yml'))
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedActions);
      });
  });

  it('creates FETCH_DATAFILE_FAILURE when fetching a datafile failed', () => {
    nock(API)
      .get('/data/data_file.yml')
      .replyWithError('Something gone wrong');

    const expectedActions = [
      { type: types.FETCH_DATAFILE_REQUEST },
      { type: types.FETCH_DATAFILE_FAILURE }
    ];

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.fetchDataFile('data_file.yml'))
      .then(() => { // return of async actions
        expect(store.getActions()[1].type).toEqual(expectedActions[1].type);
      });
  });

  it('updates a data file successfully', () => {
    nock(API)
      .put('/data/data_file.yml', { raw_content: 'foo: bar' } )
      .reply(200, datafile);

    const expectedAction = [
      { type: types.CLEAR_ERRORS },
      { type: types.PUT_DATAFILE_SUCCESS, file: datafile }
    ];

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.putDataFile('data_file.yml', 'foo: bar'))
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedAction);
      });
  });

  it('creates PUT_DATAFILE_FAILURE when updating a datafile failed', () => {
    nock(API)
      .put('/data/data_file.yml', datafile)
      .replyWithError('something awful happened');

    const expectedActions = [
      { type: types.CLEAR_ERRORS },
      { type: types.PUT_DATAFILE_FAILURE }
    ];

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.putDataFile('data_file.yml', datafile))
      .then(() => { // return of async actions
        expect(store.getActions()[1].type).toEqual(expectedActions[1].type);
      });
  });

  it('deletes a data file successfully', () => {
    nock(API)
      .delete('/data/data_file.yml')
      .reply(200);

    const expectedAction = [
      { type: types.DELETE_DATAFILE_SUCCESS, id: 'data_file.yml' }
    ];

    const store = mockStore({ files: [] });

    return store.dispatch(actions.deleteDataFile('data_file.yml'))
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedAction);
      });
  });

  it('creates DELETE_DATAFILE_FAILURE when deleting a datafile failed', () => {
    nock(API)
      .delete('/data/data_file.yml')
      .replyWithError('something awful happened');

    const expectedAction = {
      type: types.DELETE_DATAFILE_FAILURE
    };

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.deleteDataFile('data_file.yml'))
      .then(() => { // return of async actions
        expect(store.getActions()[0].type).toEqual(expectedAction.type);
      });
  });

  it('creates VALIDATION_ERROR if required field is not provided.', () => {
    const expectedActions = [
      {
        type: types.VALIDATION_ERROR,
        errors: [
          'The filename is required.',
          'The content is required.'
        ]
      }
    ];

    const store = mockStore({currentFile: {}});

    store.dispatch(actions.putDataFile());
    expect(store.getActions()).toEqual(expectedActions);
  });
});
<commit_msg>fix data file test<commit_after>import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import * as actions from '../datafiles';
import * as types from '../../constants/actionTypes';
import { API } from '../../constants/api';
import nock from 'nock';
import expect from 'expect';

const middlewares = [ thunk ];
const mockStore = configureMockStore(middlewares);

import { datafile } from './fixtures';

describe('Actions::Datafiles', () => {
  afterEach(() => {
    nock.cleanAll();
  });

  it('fetches data files successfully', () => {
    nock(API)
      .get('/data')
      .reply(200, [datafile]);

    const expectedActions = [
      { type: types.FETCH_DATAFILES_REQUEST },
      { type: types.FETCH_DATAFILES_SUCCESS, files: [datafile] }
    ];

    const store = mockStore({ files: [] });

    return store.dispatch(actions.fetchDataFiles())
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedActions);
      });
  });

  it('creates FETCH_DATAFILES_FAILURE when fetching datafiles failed', () => {
    nock(API)
      .get('/data')
      .replyWithError('Something gone wrong');

    const expectedActions = [
      { type: types.FETCH_DATAFILES_REQUEST },
      { type: types.FETCH_DATAFILES_FAILURE }
    ];

    const store = mockStore({ files: [] });

    return store.dispatch(actions.fetchDataFiles())
      .then(() => { // return of async actions
        expect(store.getActions()[1].type).toEqual(expectedActions[1].type);
      });
  });

  it('fetches a data file successfully', () => {
    nock(API)
      .get('/data/data_file.yml')
      .reply(200, datafile);

    const expectedActions = [
      { type: types.FETCH_DATAFILE_REQUEST },
      { type: types.FETCH_DATAFILE_SUCCESS, file: datafile }
    ];

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.fetchDataFile('data_file.yml'))
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedActions);
      });
  });

  it('creates FETCH_DATAFILE_FAILURE when fetching a datafile failed', () => {
    nock(API)
      .get('/data/data_file.yml')
      .replyWithError('Something gone wrong');

    const expectedActions = [
      { type: types.FETCH_DATAFILE_REQUEST },
      { type: types.FETCH_DATAFILE_FAILURE }
    ];

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.fetchDataFile('data_file.yml'))
      .then(() => { // return of async actions
        expect(store.getActions()[1].type).toEqual(expectedActions[1].type);
      });
  });

  it('updates a data file successfully', () => {
    nock(API)
      .put('/data/data_file.yml', { content: { foo: "bar" } } )
      .reply(200, datafile);

    const expectedAction = [
      { type: types.CLEAR_ERRORS },
      { type: types.PUT_DATAFILE_SUCCESS, file: datafile }
    ];

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.putDataFile('data_file.yml', 'foo: bar'))
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedAction);
      });
  });

  it('creates PUT_DATAFILE_FAILURE when updating a datafile failed', () => {
    nock(API)
      .put('/data/data_file.yml', datafile)
      .replyWithError('something awful happened');

    const expectedActions = [
      { type: types.CLEAR_ERRORS },
      { type: types.PUT_DATAFILE_FAILURE }
    ];

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.putDataFile('data_file.yml', datafile))
      .then(() => { // return of async actions
        expect(store.getActions()[1].type).toEqual(expectedActions[1].type);
      });
  });

  it('deletes a data file successfully', () => {
    nock(API)
      .delete('/data/data_file.yml')
      .reply(200);

    const expectedAction = [
      { type: types.DELETE_DATAFILE_SUCCESS, id: 'data_file.yml' }
    ];

    const store = mockStore({ files: [] });

    return store.dispatch(actions.deleteDataFile('data_file.yml'))
      .then(() => { // return of async actions
        expect(store.getActions()).toEqual(expectedAction);
      });
  });

  it('creates DELETE_DATAFILE_FAILURE when deleting a datafile failed', () => {
    nock(API)
      .delete('/data/data_file.yml')
      .replyWithError('something awful happened');

    const expectedAction = {
      type: types.DELETE_DATAFILE_FAILURE
    };

    const store = mockStore({ currentFile: {} });

    return store.dispatch(actions.deleteDataFile('data_file.yml'))
      .then(() => { // return of async actions
        expect(store.getActions()[0].type).toEqual(expectedAction.type);
      });
  });

  it('creates VALIDATION_ERROR if required field is not provided.', () => {
    const expectedActions = [
      {
        type: types.VALIDATION_ERROR,
        errors: [
          'The filename is required.',
          'The content is required.'
        ]
      }
    ];

    const store = mockStore({currentFile: {}});

    store.dispatch(actions.putDataFile());
    expect(store.getActions()).toEqual(expectedActions);
  });
});
<|endoftext|>


<commit_before>import {Image} from 'test/common';

describe('we check ROI.getMask', function () {
    it('should yield the right mask', function () {
        let image = new Image(5, 5, {kind: 'GREY'});

        let points = [[1,1],[3,2],[4,4],[5,0]];

        let roiManager = image.getROIManager();
        roiManager.fromPoints(points, {kind: 'smallCross'});

        Array.from(roiManager.getPixels()).should.eql([
            0, 1, 0, 0, 4,
            1, 1, 1, 2, 0,
            0, 1, 2, 2, 2,
            0, 0, 0, 2, 3,
            0, 0, 0, 3, 3
        ]);

        let mask = roiManager.getMask({minSurface:5, maxSurface:5});

        // only 2 ROI will be selected !

        // should be
        // 01000
        // 11110
        // 01111
        // 00010
        // 00000

        Array.from(mask.data).should.eql([0b01000111,0b10011110,0b00100000,0]);
    });

    it.only('should yield the right mask, position and resize', function () {
        let image = new Image(5, 5, {kind: 'GREY'});
        image.data = [
            0, 0, 0, 0, 0,
            0, 1, 1, 1, 0,
            0, 1, 1, 1, 0,
            0, 1, 1, 1, 0,
            0, 0, 0, 0, 0
        ];

        let mask = image.mask({threshold: 1, algorithm:'threshold'});

        Array.from(mask.data).should.eql([3, 156, 224, 0]);

        let roiManager = image.getROIManager();
        roiManager.fromMask(mask, {positive: true, negative: false});

        let rois = roiManager.getROI().sort(function (a,b) {return a.surface - b.surface;});
        rois[0].surface.should.equal(9);
        rois[1].surface.should.equal(16);
        rois[0].getMask().position.should.eql([1,1]);
        rois[1].getMask().position.should.eql([0,0]);

        rois[0].getMask().parent.size.should.equal(25); // the mask image
        rois[0].getMask().parent.parent.size.should.equal(25); // the grey image
        (rois[0].getMask().parent.parent.parent === undefined).should.be.true();  // no parent to grey image

        let roi0Mask=rois[0].getMask({scale: 0.34});
        roi0Mask.position.should.eql([2,2]);
        roi0Mask.parent.size.should.equal(25); // the mask image
        roi0Mask.parent.parent.size.should.equal(25); // the grey image
        (roi0Mask.parent.parent.parent === undefined).should.be.true();  // no parent to grey image

        let roi1Mask=rois[1].getMask({scale: 0.2});
        roi1Mask.position.should.eql([2,2]);

        let painted = roiManager.paint({scale: 0.34, positive: true, negative: false});

        Array.from(painted.getChannel(0).data).should.eql(
            [
                0, 0, 0, 0, 0,
                0, 1, 1, 1, 0,
                0, 1, 255, 1, 0,
                0, 1, 1, 1, 0,
                0, 0, 0, 0, 0
            ]
        );
    });
});

<commit_msg>Fix eslint<commit_after>import {Image} from 'test/common';

describe('we check ROI.getMask', function () {
    it('should yield the right mask', function () {
        let image = new Image(5, 5, {kind: 'GREY'});

        let points = [[1,1],[3,2],[4,4],[5,0]];

        let roiManager = image.getROIManager();
        roiManager.fromPoints(points, {kind: 'smallCross'});

        Array.from(roiManager.getPixels()).should.eql([
            0, 1, 0, 0, 4,
            1, 1, 1, 2, 0,
            0, 1, 2, 2, 2,
            0, 0, 0, 2, 3,
            0, 0, 0, 3, 3
        ]);

        let mask = roiManager.getMask({minSurface:5, maxSurface:5});

        // only 2 ROI will be selected !

        // should be
        // 01000
        // 11110
        // 01111
        // 00010
        // 00000

        Array.from(mask.data).should.eql([0b01000111,0b10011110,0b00100000,0]);
    });

    it('should yield the right mask, position and resize', function () {
        let image = new Image(5, 5, {kind: 'GREY'});
        image.data = [
            0, 0, 0, 0, 0,
            0, 1, 1, 1, 0,
            0, 1, 1, 1, 0,
            0, 1, 1, 1, 0,
            0, 0, 0, 0, 0
        ];

        let mask = image.mask({threshold: 1, algorithm:'threshold'});

        Array.from(mask.data).should.eql([3, 156, 224, 0]);

        let roiManager = image.getROIManager();
        roiManager.fromMask(mask, {positive: true, negative: false});

        let rois = roiManager.getROI().sort(function (a,b) {return a.surface - b.surface;});
        rois[0].surface.should.equal(9);
        rois[1].surface.should.equal(16);
        rois[0].getMask().position.should.eql([1,1]);
        rois[1].getMask().position.should.eql([0,0]);

        rois[0].getMask().parent.size.should.equal(25); // the mask image
        rois[0].getMask().parent.parent.size.should.equal(25); // the grey image
        (rois[0].getMask().parent.parent.parent === undefined).should.be.true();  // no parent to grey image

        let roi0Mask = rois[0].getMask({scale: 0.34});
        roi0Mask.position.should.eql([2,2]);
        roi0Mask.parent.size.should.equal(25); // the mask image
        roi0Mask.parent.parent.size.should.equal(25); // the grey image
        (roi0Mask.parent.parent.parent === undefined).should.be.true();  // no parent to grey image

        let roi1Mask = rois[1].getMask({scale: 0.2});
        roi1Mask.position.should.eql([2,2]);

        let painted = roiManager.paint({scale: 0.34, positive: true, negative: false});

        Array.from(painted.getChannel(0).data).should.eql(
            [
                0, 0, 0, 0, 0,
                0, 1, 1, 1, 0,
                0, 1, 255, 1, 0,
                0, 1, 1, 1, 0,
                0, 0, 0, 0, 0
            ]
        );
    });
});

<|endoftext|>


<commit_before>var logger = require('../lib/logger')
var Trace = require('./trace')

var TraceBuffer = function (name, options) {
  this.traces = []
  this.activetraces = []
  this.traceTransactionReference = this
  this._isLocked = false
  this._options = options
}

TraceBuffer.prototype.startTrace = function (signature, type) {
  logger.log('opbeat.instrumentation.TraceBuffer.startTrace', signature)

  var trace = new Trace(this.traceTransactionReference, signature, type, this._options)

  if (this._isLocked) {
    trace.setParent(this.traceTransactionReference._rootTrace)
  }

  this.activetraces.push(trace)

  return trace
}

TraceBuffer.prototype._onTraceEnd = function (trace) {
  logger.log('opbeat.instrumentation.TraceBuffer._endTrace', trace.signature)
  this.traces.push(trace)

  var index = this.activetraces.indexOf(trace)
  if (index > -1) {
    this.activetraces.splice(index, 1)
  }
  // TODO: Buffer should probably be flushed at somepoint to save memory
}

TraceBuffer.prototype.setTransactionReference = function (transaction) {
  logger.log('opbeat.instrumentation.TraceBuffer.setTransactionReference', transaction)

  if (this._isLocked) {
    return
  }

  this.traceTransactionReference = transaction

  this.activetraces.forEach(function (trace) {
    trace.transaction = this.traceTransactionReference
    trace.setParent(this.traceTransactionReference._rootTrace)
  }.bind(this))

  this.traces.forEach(function (trace) {
    trace.transaction = this.traceTransactionReference
    trace.setParent(this.traceTransactionReference._rootTrace)
  }.bind(this))

  this.traces = []
}

TraceBuffer.prototype.lock = function () {
  this._isLocked = true
}

TraceBuffer.prototype.isLocked = function () {
  return this._isLocked
}

module.exports = TraceBuffer
<commit_msg>Add flush method to TraceBuffer<commit_after>var logger = require('../lib/logger')
var Trace = require('./trace')

var TraceBuffer = function (name, options) {
  this.traces = []
  this.activetraces = []
  this.traceTransactionReference = this
  this._isLocked = false
  this._options = options
}

TraceBuffer.prototype.startTrace = function (signature, type) {
  logger.log('opbeat.instrumentation.TraceBuffer.startTrace', signature)

  var trace = new Trace(this.traceTransactionReference, signature, type, this._options)

  if (this._isLocked) {
    trace.setParent(this.traceTransactionReference._rootTrace)
  }

  this.activetraces.push(trace)

  return trace
}

TraceBuffer.prototype._onTraceEnd = function (trace) {
  logger.log('opbeat.instrumentation.TraceBuffer._endTrace', trace.signature)
  this.traces.push(trace)

  var index = this.activetraces.indexOf(trace)
  if (index > -1) {
    this.activetraces.splice(index, 1)
  }
}

TraceBuffer.prototype.setTransactionReference = function (transaction) {
  logger.log('opbeat.instrumentation.TraceBuffer.setTransactionReference', transaction)

  if (this._isLocked) {
    return
  }

  this.traceTransactionReference = transaction

  this.activetraces.forEach(function (trace) {
    trace.transaction = this.traceTransactionReference
    trace.setParent(this.traceTransactionReference._rootTrace)
  }.bind(this))

  this.traces.forEach(function (trace) {
    trace.transaction = this.traceTransactionReference
    trace.setParent(this.traceTransactionReference._rootTrace)
  }.bind(this))
}

TraceBuffer.prototype.flush = function () {
  this.traces = []
  this.activetraces = []
}

TraceBuffer.prototype.lock = function () {
  this._isLocked = true
}

TraceBuffer.prototype.isLocked = function () {
  return this._isLocked
}

module.exports = TraceBuffer
<|endoftext|>