<commit_before>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

// find maximum number of deltas that can be applied
var SimpleDebug = (function SimpleDebugClosure() {
  var log = getLog('simpledebug');

  function SimpleDebug(orig, deltas, grouped, accumulate, test, callback) {
    this.orig = orig;
    this.deltas = deltas;
    this.grouped = grouped;
    this.accumulate = accumulate;
    this.test = test;
    this.callback = callback;
  }

  SimpleDebug.prototype = {
    run: function _run() {
      this.enabled = [];
      this.disabled = [];

      if (this.grouped) {
        this.index = [0, 0];
      } else {
        this.index = 0;
      }

      this.runTest();
    },
    runTest: function _runTest() {
      if (this.isFinished()) {
        log.log('finished minimizing:', this.enabled, this.disabled);
        var finished = jQuery.extend(true, [], this.orig);
        var enabled = this.enabled;
        for (var i = 0, ii = enabled.length; i < ii; ++i) {
          finished = enabled[i].delta.apply(finished);
        }
        this.finished = finished;
        if (this.callback)
          this.callback(this);
        return;
      }

      var cur = jQuery.extend(true, [], this.orig);
      var delta = this.getNextDelta();
      cur = delta.apply(cur);

      var enabled = this.enabled;
      if (this.accumulate) {
        for (var i = 0, ii = enabled.length; i < ii; ++i) {
          cur = enabled[i].delta.apply(cur);
        }
      }

      var test = this.test;
      var simpleDebug = this;

      test(cur, function(result, data) {
        var info = {delta: delta, misc: data};
        if (!result) {
          simpleDebug.disabled.push(info);
        } else {
          simpleDebug.enabled.push(info);
        }
        simpleDebug.incrementIndex(result);
        setTimeout(function() {
          simpleDebug.runTest();
        }, 0);
      });
    },
    incrementIndex: function _incrementIndex(curDeltaSuccess) {
      if (this.grouped) {
        var index = this.index;
        var idxGroup = index[0];
        var idxDelta = index[1];

        var deltas = this.deltas;
        var deltaGroup = deltas[idxGroup];

        if (!curDeltaSuccess && idxDelta + 1 < deltaGroup.length) {
          index[1]++;
        } else {
          index[0]++;
          index[1] = 0;
        }
      } else {
        this.index++;
      }
    },
    getNextDelta: function _getNextDelta() {
      var deltas = this.deltas;
      var index = this.index;

      if (this.grouped) {
        return deltas[index[0]][index[1]];
      } else {
        return deltas[index];
      }
    },
    isFinished: function _isFinished() {
      var deltas = this.deltas;
      var index = this.index;
      var grouped = this.grouped;

      if (grouped) {
        return grouped && index[0] >= deltas.length;
      } else {
       return !grouped && index >= deltas.length;
      }
    }
  };

  return SimpleDebug;
})();

function runScript(id, events, numRuns, timeout, callback) {
  var runs = [];
  function runOnce() {
    if (runs.length < numRuns) {
      var r = controller.replayScript(id, events, function(replay) {
        clearTimeout(timeoutId);

        var run = {
          index: replay.index,
          events: replay.record.events,
          captures: replay.captures
        };
        runs.push(run);

        setTimeout(function() {runOnce();});
      });

      // kill script after timeout period
      var timeoutId = setTimeout(function() {
        r.finish();
      }, timeout);
    } else {
      callback(runs);
    }
  }
  runOnce();
}

function collectCaptures(events) {
  var expCaptures = [];
  for (var i = 0, ii = events.length; i < ii; ++i) {
    var e = events[i];
    if (e.type == 'capture') {
      expCaptures.push(e);
    }
  }
  return expCaptures;
}

function checkReplaySuccess(captureEvents, events, replay) {
  // if (replay.index != events.length) {
  //   return false;
  // }

  var captures = replay.captures;
  for (var i = 0, ii = captureEvents.length; i < ii; ++i) {
    if (i >= captures.length) {
      return false;
    }

    var c = captures[i];
    var e = captureEvents[i];

    var eText = e.value.data.target.snapshot.prop.innerText;
    var cText = c.innerText;
    if (eText != cText) {
      return false;
    }
  }
  return true;
}

function saveScript(scriptName, debug) {
  var finalEvents = debug.finished;
  scriptServer.saveScript(scriptName, finalEvents, params, null);
}

function runRemoveEvents(scriptName) {
  params.replay.eventTimeout = 40;
  params.replay.defaultUser = true;
  params.panel.enableEdit = false;
  controller.updateParams();

  scriptServer.getScript(scriptName, true, function(id, events) {

    var removeEvents = [];
    for (var i = 0, ii = events.length; i < ii; ++i) {
      var e = events[i];
      if (e.type == 'dom') {
        (function() {
          var eventId = e.value.meta.id;
          removeEvents.push({
            id: 'remove event:' + eventId,
            apply: function(origEvents) {
              for (var j = 0, jj = origEvents.length; j < jj; ++j) {
                if (origEvents[j].value.meta.id == eventId) {
                  origEvents.splice(j, 1);
                  break;
                }
              }
              return origEvents;
            }
          });
        })();
      }
    }

    var captureEvents = collectCaptures(events);

    function testScript(scriptEvents, callback) {
      runScript(id, scriptEvents, 1, 300 * 1000, function(replays) {
        var replay = replays[0];
        callback(checkReplaySuccess(captureEvents, scriptEvents, replay),
                 replay);
      });
    }

    console.log('trying to remove events:', removeEvents);
    var debug = new SimpleDebug(events, removeEvents, false, true, testScript,
        function(finalEvents) {
          saveScript(scriptName + '_remove', finalEvents);
        });
    debug.run();
  });
}

function runMinWait(scriptName) {
  params.replay.eventTimeout = 15;
  params.replay.defaultUser = true;
  params.panel.enableEdit = false;
  controller.updateParams();

  scriptServer.getScript(scriptName, true, function(id, events) {

    var removeWaits = [];
    for (var i = 0, ii = events.length; i < ii; ++i) {
      var e = events[i];
      if (e.type == 'dom') {
        (function() {
          var eventId = e.value.meta.id;
          var origWait = e.value.timing.waitTime;
          removeWaits.push({
            id: 'remove wait:' + eventId + ',' + origWait,
            apply: function(origEvents) {
              for (var j = 0, jj = origEvents.length; j < jj; ++j) {
                if (origEvents[j].value.meta.id == eventId) {
                  origEvents[j].value.timing.waitTime = 0;
                  break;
                }
              }
              return origEvents;
            }
          });
        })();
      }
    }

    var captureEvents = collectCaptures(events);

    function testScript(scriptEvents, callback) {
      runScript(id, scriptEvents, 1, 300 * 1000, function(replays) {
        /*
        var replay = replays[0];
        callback(checkReplaySuccess(captureEvents, scriptEvents, replay),
                 replay);
        */
        for (var i = 0, ii = replays.length; i < ii; ++i) {
          if (!checkReplaySuccess(captureEvents, scriptEvents, replay[i])) {
            callback(false);
          }
        }
        callback(true);
      });
    }

    console.log('trying to remove waits:', removeWaits);
    var debug = new SimpleDebug(events, removeWaits, false, true, testScript,
        function(debug) {
          saveScript(scriptName + '_waits', debug);
        });
    debug.run();
  });
}

function runSynthWait(scriptName) {
  params = jQuery.extend(true, {}, defaultParams);
  params.replay.eventTimeout = 40;
  //params.replay.defaultUser = true;
  params.replay.timingStrategy = TimingStrategy.SLOWER;
  params.panel.enableEdit = false;
  controller.updateParams();

  scriptServer.getScript(scriptName, true, function(scriptId, events) {
    runScript(scriptId, events, 2, 300 * 1000, function(replays) {
      var triggers = mapPossibleTriggerToEvent(events, replays);
      console.log(triggers);

      var triggerChanges = [];
      for (var i = 0, ii = events.length; i < ii; ++i) {
        var e = events[i];
        var id = e.value.meta.id;
        if (id in triggers) {
          var eventTriggers = triggers[id];
          var triggerGroup = [];
          for (var j = 0, jj = eventTriggers.length; j < jj; ++j) {
            var triggerEvent = eventTriggers[j];
            if (triggerEvent != 'nowait') {
              (function() {
                var eventId = id;
                var triggerEventId = triggerEvent;
                triggerGroup.push({
                  id: 'add wait:' + eventId + ',' + triggerEventId,
                  apply: function(origEvents) {
                    for (var j = 0, jj = origEvents.length; j < jj; ++j) {
                      if (origEvents[j].value.meta.id == eventId) {
                        origEvents[j].value.timing.waitEvent = triggerEventId;
                        origEvents[j].value.timing.waitTime = 0;
                        break;
                      }
                    }
                    return origEvents;
                  }
                });
              })();
            } else {
              (function() {
                var eventId = id;
                triggerGroup.push({
                  id: 'remove wait:' + eventId,
                  apply: function(origEvents) {
                    for (var j = 0, jj = origEvents.length; j < jj; ++j) {
                      if (origEvents[j].value.meta.id == eventId) {
                        origEvents[j].value.timing.waitTime = 0;
                        break;
                      }
                    }
                    return origEvents;
                  }
                });
              })();
            }
          }
          triggerChanges.push(triggerGroup);
        }
      }

      var captureEvents = collectCaptures(events);

      var count = 0;
      function testScript(scriptEvents, callback) {
        // lets make it replay a bit harder
        // params.replay.defaultWaitNewTab = 100;
        // params.replay.targetTimeout = 1;
        controller.updateParams();

        runScript(null, scriptEvents, 2, 300 * 1000,
            function(replays) {
          for (var i = 0, ii = replays.length; i < ii; ++i) {
            if (!checkReplaySuccess(captureEvents, scriptEvents, replays[i])) {
              callback(false);
              return;
            }
          }
          callback(true);
        });
      }

      console.log('trying to synthesize waits:');
      var debug = new SimpleDebug(events, triggerChanges, true, true, testScript,
          function(debug) {
            console.log(debug);
            saveScript(scriptName + '_trigger', debug);
          });
      debug.run();
    });
  });
}

function getCompletedUrls(replay) {
  var events = replay.events;
  var completed = events.filter(function(e) {return e.type == 'completed'});
  return completed.map(function(e) {return e.value.data.url});
}

function getPossibleTriggerUrls(replays) {
  var urlLists = replays.map(getCompletedUrls);
  var intersectList = urlLists[0];
  for (var i = 1, ii = urlLists.length; i < ii; ++i) {
    var l = urlLists[i];
    intersectList = intersectList.filter(function(url) {
      return l.indexOf(url) != -1;
    });
  }
  return intersectList;
}

function mapPossibleTriggerToEvent(orig, replays) {
  var triggerUrls = getPossibleTriggerUrls(replays);

  var mapping = {};
  var completedEvents = [];
  completedEvents.push('nowait');

  for (var i = 0, ii = orig.length; i < ii; ++i) {
    var e = orig[i];
    if (e.type == 'dom' || e.type == 'capture') {
      mapping[e.value.meta.id] = completedEvents;
      completedEvents = [];
      completedEvents.push('nowait');
    } else if (e.type == 'completed') {
      if (triggerUrls.indexOf(e.value.data.url) != -1) {
        completedEvents.push(e.value.meta.id);
      }
    }
  }
  return mapping;
}
<commit_msg>worked on saving scripts when learning<commit_after>/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */

'use strict';

// find maximum number of deltas that can be applied
var SimpleDebug = (function SimpleDebugClosure() {
  var log = getLog('simpledebug');

  function SimpleDebug(orig, deltas, grouped, accumulate, test, callback) {
    this.orig = orig;
    this.deltas = deltas;
    this.grouped = grouped;
    this.accumulate = accumulate;
    this.test = test;
    this.callback = callback;
  }

  SimpleDebug.prototype = {
    run: function _run() {
      this.enabled = [];
      this.disabled = [];

      if (this.grouped) {
        this.index = [0, 0];
      } else {
        this.index = 0;
      }

      this.runTest();
    },
    runTest: function _runTest() {
      if (this.isFinished()) {
        log.log('finished minimizing:', this.enabled, this.disabled);
        var finished = jQuery.extend(true, [], this.orig);
        var enabled = this.enabled;
        for (var i = 0, ii = enabled.length; i < ii; ++i) {
          finished = enabled[i].delta.apply(finished);
        }
        this.finished = finished;
        if (this.callback)
          this.callback(this);
        return;
      }

      var cur = jQuery.extend(true, [], this.orig);
      var delta = this.getNextDelta();
      cur = delta.apply(cur);

      var enabled = this.enabled;
      if (this.accumulate) {
        for (var i = 0, ii = enabled.length; i < ii; ++i) {
          cur = enabled[i].delta.apply(cur);
        }
      }

      var test = this.test;
      var simpleDebug = this;

      test(cur, enabled, delta, function(result, data) {
        var info = {delta: delta, misc: data};
        if (!result) {
          simpleDebug.disabled.push(info);
        } else {
          simpleDebug.enabled.push(info);
        }
        simpleDebug.incrementIndex(result);
        setTimeout(function() {
          simpleDebug.runTest();
        }, 0);
      });
    },
    incrementIndex: function _incrementIndex(curDeltaSuccess) {
      if (this.grouped) {
        var index = this.index;
        var idxGroup = index[0];
        var idxDelta = index[1];

        var deltas = this.deltas;
        var deltaGroup = deltas[idxGroup];

        if (!curDeltaSuccess && idxDelta + 1 < deltaGroup.length) {
          index[1]++;
        } else {
          index[0]++;
          index[1] = 0;
        }
      } else {
        this.index++;
      }
    },
    getNextDelta: function _getNextDelta() {
      var deltas = this.deltas;
      var index = this.index;

      if (this.grouped) {
        return deltas[index[0]][index[1]];
      } else {
        return deltas[index];
      }
    },
    isFinished: function _isFinished() {
      var deltas = this.deltas;
      var index = this.index;
      var grouped = this.grouped;

      if (grouped) {
        return grouped && index[0] >= deltas.length;
      } else {
       return !grouped && index >= deltas.length;
      }
    }
  };

  return SimpleDebug;
})();

function runScript(id, events, numRuns, timeout, callback) {
  var runs = [];
  function runOnce() {
    if (runs.length < numRuns) {
      var r = controller.replayScript(id, events, function(replay) {
        clearTimeout(timeoutId);

        var run = {
          index: replay.index,
          events: replay.record.events,
          captures: replay.captures
        };
        runs.push(run);

        setTimeout(function() {runOnce();});
      });

      // kill script after timeout period
      var timeoutId = setTimeout(function() {
        r.finish();
      }, timeout);
    } else {
      callback(runs);
    }
  }
  runOnce();
}

function collectCaptures(events) {
  var expCaptures = [];
  for (var i = 0, ii = events.length; i < ii; ++i) {
    var e = events[i];
    if (e.type == 'capture') {
      expCaptures.push(e);
    }
  }
  return expCaptures;
}

function checkReplaySuccess(captureEvents, events, replay) {
  // if (replay.index != events.length) {
  //   return false;
  // }

  var captures = replay.captures;
  for (var i = 0, ii = captureEvents.length; i < ii; ++i) {
    if (i >= captures.length) {
      return false;
    }

    var c = captures[i];
    var e = captureEvents[i];

    var eText = e.target.snapshot.prop.innerText;
    var cText = c.innerText;
    if (eText != cText) {
      return false;
    }
  }
  return true;
}

// function saveScript(scriptName, debug) {
//   var finalEvents = debug.finished;
//   scriptServer.saveScript(scriptName, finalEvents, params, null);
// }

// function runRemoveEvents(scriptName) {
//   params.replay.eventTimeout = 40;
//   params.replay.defaultUser = true;
//   params.panel.enableEdit = false;
//   controller.updateParams();
//
//   scriptServer.getScript(scriptName, function(id, events) {
//
//     var removeEvents = [];
//     for (var i = 0, ii = events.length; i < ii; ++i) {
//       var e = events[i];
//       if (e.type == 'dom') {
//         (function() {
//           var eventId = e.value.meta.id;
//           removeEvents.push({
//             id: 'remove event:' + eventId,
//             apply: function(origEvents) {
//               for (var j = 0, jj = origEvents.length; j < jj; ++j) {
//                 if (origEvents[j].value.meta.id == eventId) {
//                   origEvents.splice(j, 1);
//                   break;
//                 }
//               }
//               return origEvents;
//             }
//           });
//         })();
//       }
//     }
//
//     var captureEvents = collectCaptures(events);
//
//     function testScript(scriptEvents, callback) {
//       runScript(id, scriptEvents, 1, 300 * 1000, function(replays) {
//         var replay = replays[0];
//         callback(checkReplaySuccess(captureEvents, scriptEvents, replay),
//                  replay);
//       });
//     }
//
//     console.log('trying to remove events:', removeEvents);
//     var debug = new SimpleDebug(events, removeEvents, false, true, testScript,
//         function(finalEvents) {
//           saveScript(scriptName + '_remove', finalEvents);
//         });
//     debug.run();
//   });
// }
//
// function runMinWait(scriptName) {
//   params.replay.eventTimeout = 15;
//   params.replay.defaultUser = true;
//   params.panel.enableEdit = false;
//   controller.updateParams();
//
//   scriptServer.getScript(scriptName, function(id, events) {
//
//     var removeWaits = [];
//     for (var i = 0, ii = events.length; i < ii; ++i) {
//       var e = events[i];
//       if (e.type == 'dom') {
//         (function() {
//           var eventId = e.value.meta.id;
//           var origWait = e.value.timing.waitTime;
//           removeWaits.push({
//             id: 'remove wait:' + eventId + ',' + origWait,
//             apply: function(origEvents) {
//               for (var j = 0, jj = origEvents.length; j < jj; ++j) {
//                 if (origEvents[j].value.meta.id == eventId) {
//                   origEvents[j].value.timing.waitTime = 0;
//                   break;
//                 }
//               }
//               return origEvents;
//             }
//           });
//         })();
//       }
//     }
//
//     var captureEvents = collectCaptures(events);
//
//     function testScript(scriptEvents, callback) {
//       runScript(id, scriptEvents, 1, 300 * 1000, function(replays) {
//         /*
//         var replay = replays[0];
//         callback(checkReplaySuccess(captureEvents, scriptEvents, replay),
//                  replay);
//         */
//         for (var i = 0, ii = replays.length; i < ii; ++i) {
//           if (!checkReplaySuccess(captureEvents, scriptEvents, replay[i])) {
//             callback(false);
//           }
//         }
//         callback(true);
//       });
//     }
//
//     console.log('trying to remove waits:', removeWaits);
//     var debug = new SimpleDebug(events, removeWaits, false, true, testScript,
//         function(debug) {
//           saveScript(scriptName + '_waits', debug);
//         });
//     debug.run();
//   });
// }

function runSynthWait(scriptName) {
  var uniqueId = scriptName + ':' + (new Date()).getTime();

  params = jQuery.extend(true, {}, defaultParams);
  params.replay.eventTimeout = 40;
  //params.replay.defaultUser = true;
  params.replay.timingStrategy = TimingStrategy.SLOWER;
  params.panel.enableEdit = false;
  controller.updateParams();

  scriptServer.getScript(scriptName, function(item) {
    var scriptId = item.id;
    var events =  item.events;

    runScript(null, events, 2, 300 * 1000, function(replays) {

      for (var i = 0, ii = replays.length; i < ii; ++i) {
        var r = replays[i];
        scriptServer.saveScript(uniqueId, r.events, scriptId, 'testing');
      }

      var triggers = mapPossibleTriggerToEvent(events, replays);
      console.log(triggers);

      var triggerChanges = [];
      for (var i = 0, ii = events.length; i < ii; ++i) {
        var e = events[i];
        var id = e.meta.id;
        if (id in triggers) {
          var eventTriggers = triggers[id];
          var triggerGroup = [];
          for (var j = 0, jj = eventTriggers.length; j < jj; ++j) {
            var triggerEvent = eventTriggers[j];
            if (triggerEvent != 'nowait') {
              (function() {
                var eventId = id;
                var triggerEventId = triggerEvent;
                triggerGroup.push({
                  id: 'add wait:' + eventId + ',' + triggerEventId,
                  apply: function(origEvents) {
                    for (var j = 0, jj = origEvents.length; j < jj; ++j) {
                      if (origEvents[j].meta.id == eventId) {
                        origEvents[j].timing.waitEvent = triggerEventId;
                        origEvents[j].timing.waitTime = 0;
                        break;
                      }
                    }
                    return origEvents;
                  }
                });
              })();
            } else {
              (function() {
                var eventId = id;
                triggerGroup.push({
                  id: 'remove wait:' + eventId,
                  apply: function(origEvents) {
                    for (var j = 0, jj = origEvents.length; j < jj; ++j) {
                      if (origEvents[j].meta.id == eventId) {
                        origEvents[j].timing.waitTime = 0;
                        break;
                      }
                    }
                    return origEvents;
                  }
                });
              })();
            }
          }
          triggerChanges.push(triggerGroup);
        }
      }

      // test whether the modified script still passes
      var captureEvents = collectCaptures(events);
      function testScript(modifiedEvents, enabled, delta, callback) {
        // lets make it replay a bit harder
        // params.replay.defaultWaitNewTab = 100;
        // params.replay.targetTimeout = 1;
        controller.updateParams();

        scriptServer.saveScript(uniqueId + ':' + delta.id, modifiedEvents, scriptId, 'original');

        runScript(null, modifiedEvents, 2, 300 * 1000,
            function(replays) {
              var passed = true;

              for (var i = 0, ii = replays.length; i < ii; ++i) {
                var r = replays[i];
                var pass = checkReplaySuccess(captureEvents, modifiedEvents, r);

                scriptServer.saveScript(uniqueId + ':' + delta.id, r.events, scriptId, 'test:' + pass);
                passed = passed && pass;
              }
              callback(passed);
            });
      }

      console.log('trying to synthesize waits:');
      var debug = new SimpleDebug(events, triggerChanges, true, true, testScript,
          function(debug) {
            console.log(debug);
            scriptServer.saveScript(uniqueId + ':final', debug.finished, scriptId, 'trigger synthesized');
          });
      debug.run();
    });
  });
}

function getCompletedUrls(replay) {
  var events = replay.events;
  var completed = events.filter(function(e) {return e.type == 'completed'});
  return completed.map(function(e) {return e.data.url});
}

function getPossibleTriggerUrls(replays) {
  var urlLists = replays.map(getCompletedUrls);
  var intersectList = urlLists[0];
  for (var i = 1, ii = urlLists.length; i < ii; ++i) {
    var l = urlLists[i];
    intersectList = intersectList.filter(function(url) {
      return l.indexOf(url) != -1;
    });
  }
  return intersectList;
}

function mapPossibleTriggerToEvent(orig, replays) {
  var triggerUrls = getPossibleTriggerUrls(replays);

  var mapping = {};
  var completedEvents = [];
  completedEvents.push('nowait');

  for (var i = 0, ii = orig.length; i < ii; ++i) {
    var e = orig[i];
    if (e.type == 'dom' || e.type == 'capture') {
      mapping[e.meta.id] = completedEvents;
      completedEvents = [];
      completedEvents.push('nowait');
    } else if (e.type == 'completed') {
      if (triggerUrls.indexOf(e.data.url) != -1) {
        completedEvents.push(e.meta.id);
      }
    }
  }
  return mapping;
}
<|endoftext|>


<commit_before>var Fangorn = require('fangorn');
var m = require('mithril');

function FileViewTreebeard(data) {
    var fangornOpts = {
    divID: 'grid',
    filesData: data.data,
    uploads: false,
    showFilter: false,
    title: undefined,
    hideColumnTitles: true,
    ondataload: function () {
        var tb = this;
        tb.options.folderIndex = 0;
        if (window.contextVars.file.provider === 'figshare') {
            tb.options.folderArray = [window.contextVars.file.name]
        } else if (window.contextVars.file.path) {
            window.contextVars.file.path = decodeURIComponent(window.contextVars.file.path);
            tb.options.folderArray = window.contextVars.file.path.split("/");
            if (tb.options.folderArray.length > 1) {
                tb.options.folderArray.splice(0, 1);
            }
        } else {
            tb.options.folderArray = [''];
        }
        m.render($('#files-search').get(0), tb.options.filterTemplate.call(tb));
        $('#files-search input[placeholder=Search]').css('width', '95%');
        $('#files-search input[placeholder=Search]').removeClass('pull-right').addClass('pull-left');
        $('#toggle-icon').css('margin-top', '5px');
    },
    columnTitles: function () {
        return [{
            title: 'Name',
            width: '100%'
        }];
    },
    ontogglefolder : function (tree) {
        Fangorn.DefaultOptions.ontogglefolder.call(this, tree);
        var containerHeight = this.select('#tb-tbody').height();
        this.options.showTotal = Math.floor(containerHeight / this.options.rowHeight) + 1;
        this.redraw();
    },
    lazyLoadOnLoad: function(tree) {
        var tb = this;
        Fangorn.DefaultOptions.lazyLoadOnLoad.call(tb, tree);

        if (tb.options.folderIndex < tb.options.folderArray.length) {
            for (var i = 0; i < tree.children.length; i++) {
                var child = tree.children[i];
                if (window.contextVars.node.id === child.data.nodeId && child.data.provider === window.contextVars.file.provider && child.data.name === tb.options.folderArray[tb.options.folderIndex]) {
                    tb.options.folderIndex++;
                    if (child.data.kind === 'folder') {
                        tb.updateFolder(null, child);
                        tree = child;
                    }
                    else {
                        tb.currentFileID = child.id;
                    }
                }
            }
        }
        if (tb.currentFileID) {
            var index = tb.returnIndex(tb.currentFileID);
            var visibleIndex = tb.visibleIndexes.indexOf(index);
            if (visibleIndex !== -1 && visibleIndex > tb.showRange.length - 2) {
                var scrollTo = visibleIndex * tb.options.rowHeight;
                $('#tb-tbody').scrollTop(scrollTo);
            }
        }
    },
    resolveRows: function (item) {
        var selectClass = '';
        var tb = this;
        var node = item.parent().parent();
        if (item.data.kind === 'file' && tb.currentFileID === item.id) {
            selectClass = 'fangorn-hover';
        }

        var defaultColumns = [
            {
                data: 'name',
                folderIcons: true,
                filter: true,
                css: selectClass,
                custom: Fangorn.DefaultColumns._fangornTitleColumn
            }
        ];

        if (item.parentID) {
            item.data.permissions = item.data.permissions || item.parent().data.permissions;
            if (item.data.kind === 'folder') {
                item.data.accept = item.data.accept || item.parent().data.accept;
            }
        }

        if (item.data.tmpID) {
            defaultColumns = [
                {
                    data: 'name',  // Data field name
                    folderIcons: true,
                    filter: true,
                    custom: function () {
                        return m('span.text-muted', 'Uploading ' + item.data.name + '...');
                    }
                }
            ];
        }

        var configOption = Fangorn.Utils.resolveconfigOption.call(this, item, 'resolveRows', [item]);
        return configOption || defaultColumns;
    }
    };
    var filebrowser = new Fangorn(fangornOpts);
}

module.exports = FileViewTreebeard;<commit_msg>Show current github branch files, not default<commit_after>var Fangorn = require('fangorn');
var m = require('mithril');

function FileViewTreebeard(data) {

    // Set item.branch to show the branch of the rendered GitHub file instead of the default branch
    var addonRootFolders = data['data'][0].children;
    for (var i=0; i < addonRootFolders.length; i++) {
        var item = addonRootFolders[i];
        if (item.provider === 'github' && item.isAddonRoot && window.contextVars.file.extra.branch) {
            item.branch = window.contextVars.file.extra.branch;
        }
    }

    var fangornOpts = {
    divID: 'grid',
    filesData: data.data,
    uploads: false,
    showFilter: false,
    title: undefined,
    hideColumnTitles: true,
    ondataload: function () {
        var tb = this;
        tb.options.folderIndex = 0;
        if (window.contextVars.file.provider === 'figshare') {
            tb.options.folderArray = [window.contextVars.file.name]
        } else if (window.contextVars.file.path) {
            window.contextVars.file.path = decodeURIComponent(window.contextVars.file.path);
            tb.options.folderArray = window.contextVars.file.path.split("/");
            if (tb.options.folderArray.length > 1) {
                tb.options.folderArray.splice(0, 1);
            }
        } else {
            tb.options.folderArray = [''];
        }
        m.render($('#files-search').get(0), tb.options.filterTemplate.call(tb));
        $('#files-search input[placeholder=Search]').css('width', '95%');
        $('#files-search input[placeholder=Search]').removeClass('pull-right').addClass('pull-left');
        $('#toggle-icon').css('margin-top', '5px');
    },
    columnTitles: function () {
        return [{
            title: 'Name',
            width: '100%'
        }];
    },
    ontogglefolder : function (tree) {
        Fangorn.DefaultOptions.ontogglefolder.call(this, tree);
        var containerHeight = this.select('#tb-tbody').height();
        this.options.showTotal = Math.floor(containerHeight / this.options.rowHeight) + 1;
        this.redraw();
    },
    lazyLoadOnLoad: function(tree) {
        var tb = this;
        Fangorn.DefaultOptions.lazyLoadOnLoad.call(tb, tree);

        if (tb.options.folderIndex < tb.options.folderArray.length) {
            for (var i = 0; i < tree.children.length; i++) {
                var child = tree.children[i];
                if (window.contextVars.node.id === child.data.nodeId && child.data.provider === window.contextVars.file.provider && child.data.name === tb.options.folderArray[tb.options.folderIndex]) {
                    tb.options.folderIndex++;
                    if (child.data.kind === 'folder') {
                        tb.updateFolder(null, child);
                        tree = child;
                    }
                    else {
                        tb.currentFileID = child.id;
                    }
                }
            }
        }
        if (tb.currentFileID) {
            var index = tb.returnIndex(tb.currentFileID);
            var visibleIndex = tb.visibleIndexes.indexOf(index);
            if (visibleIndex !== -1 && visibleIndex > tb.showRange.length - 2) {
                var scrollTo = visibleIndex * tb.options.rowHeight;
                $('#tb-tbody').scrollTop(scrollTo);
            }
        }
    },
    resolveRows: function (item) {
        var selectClass = '';
        var tb = this;
        var node = item.parent().parent();
        if (item.data.kind === 'file' && tb.currentFileID === item.id) {
            selectClass = 'fangorn-hover';
        }

        var defaultColumns = [
            {
                data: 'name',
                folderIcons: true,
                filter: true,
                css: selectClass,
                custom: Fangorn.DefaultColumns._fangornTitleColumn
            }
        ];

        if (item.parentID) {
            item.data.permissions = item.data.permissions || item.parent().data.permissions;
            if (item.data.kind === 'folder') {
                item.data.accept = item.data.accept || item.parent().data.accept;
            }
        }

        if (item.data.tmpID) {
            defaultColumns = [
                {
                    data: 'name',  // Data field name
                    folderIcons: true,
                    filter: true,
                    custom: function () {
                        return m('span.text-muted', 'Uploading ' + item.data.name + '...');
                    }
                }
            ];
        }

        var configOption = Fangorn.Utils.resolveconfigOption.call(this, item, 'resolveRows', [item]);
        return configOption || defaultColumns;
    }
    };
    var filebrowser = new Fangorn(fangornOpts);
}

module.exports = FileViewTreebeard;<|endoftext|>


<commit_before>var config =  require('./config.js'),
    nano = require('nano')(config.couch_auth_db_url),
    serializer = require('serializer'),
    users = nano.use('_users');

function create_oauth_tokens(secret_base, user, callback) {
    var consumer_key = serializer.randomString(96);
    var token_key = serializer.randomString(96);
    user.oauth = {
        consumer_keys: {},
        tokens: {}
    };
    user.oauth.consumer_keys[consumer_key] = create_secret(secret_base);
    user.oauth.tokens[token_key] = create_secret(secret_base);
    users.insert(user, user._id, function(err, response) {
        if (err || !response.ok) {
            console.log("ERROR Updating user with credentials:");
            console.dir(response);
            callback(response);
        } else {
            callback(null, denormalize_oauth(user));
        }
    });
}

function create_secret(secret_base) {
    var encrypt_key = serializer.randomString(48);
    var validate_key = serializer.randomString(48);
    var secret_string = serializer.secureStringify(secret_base, encrypt_key, validate_key);
    if (secret_string.length > 80) {
        secret_string = secret_string.substr(30,50);
    }
    return secret_string;
}

function denormalize_oauth(user) {
    var key;
    for (key in user.oauth.consumer_keys) {
        user.consumer_key = key;
        user.consumer_secret = user.oauth.consumer_keys[key];
        break;
    }
    for (key in user.oauth.tokens) {
        user.token_key = key;
        user.token_secret = user.oauth.tokens[key];
        break;
    }
    return user;
}

function is_admin(user) {
    var isAdmin = false;
    if (user.roles) {
        user.roles.forEach(function(role) {
            if (role === 'admin') {
                isAdmin = true;
            }
        });
    }
    return isAdmin;
}

function validate_oauth(oauth) {
    try {
        if (Object.keys(oauth.consumer_keys).length > 0 && Object.keys(oauth.tokens).length > 0) {
            return true;
        }
    } catch (ex) {
        //oauth is bad, just let the false return;
    }
    return false;
}

module.exports = {
    delete_user: function(user, idToDelete, rev, res) {
        if(is_admin(user)) {
            users.destroy(idToDelete, rev, function(err, body) {
                if (err) {
                    res.json({error:true, errorResult: err});
                } else {
                    res.json(body);
                }
            });
        }
    },

    find_oauth_user: function(accessToken, refreshToken, profile, callback) {
        var user_key = 'org.couchdb.user:'+profile.emails[0].value;
        users.get(user_key, {}, function(err, body) {
            if (err) {
                if (err.status_code === 404) {
                    callback();
                } else {
                    callback(err);
                }
                return;
            }
            if (validate_oauth(body.oauth)) {
                return callback(null, denormalize_oauth(body));
            } else {
                create_oauth_tokens(accessToken, body, callback);
            }
        });
    },

    find_user: function(userName, callback) {
        var user_key = 'org.couchdb.user:'+userName;
        users.get(user_key, {}, function(err, body) {
            if (err) {
                callback(err);
                return;
            }
            callback(null, body);
        });
    },

    get_user: function(user, id, res) {
        if(is_admin(user)) {
            this.find_user(id, function(err, body) {
                if (err) {
                    res.json({error:true, errorResult: err});
                } else {
                    res.json(body);
                }
            });
        }
    },

    get_users: function(user, res) {
        if(is_admin(user)) {
            var options = {
                include_docs: true,
                startkey: 'org.couchdb.user'
            };
            users.list(options, function (err, body) {
                if (err) {
                    res.json({error:true, errorResult: err});
                } else {
                    res.json(body);
                }
            });
        } else {
            res.json({error:true, errorResult: 'Unauthorized'});
        }
    },

    get_primary_role: function(user) {
        var primaryRole = '';
        if (user.roles) {
            user.roles.forEach(function(role) {
                if (role !== 'user' && role !== 'admin') {
                    primaryRole = role;
                }
            });
        }
        return primaryRole;
    },

    update_user: function(user, userData, updateParams, res) {
        if(is_admin(user)) {
            users.insert(userData, updateParams, function(err, body) {
                if (err) {
                    res.json({error:true, errorResult: err});
                } else {
                    res.json(body);
                }
            });
        }
    },

};
<commit_msg>fix find_user to use proper key.<commit_after>var config =  require('./config.js'),
    nano = require('nano')(config.couch_auth_db_url),
    serializer = require('serializer'),
    users = nano.use('_users');

function create_oauth_tokens(secret_base, user, callback) {
    var consumer_key = serializer.randomString(96);
    var token_key = serializer.randomString(96);
    user.oauth = {
        consumer_keys: {},
        tokens: {}
    };
    user.oauth.consumer_keys[consumer_key] = create_secret(secret_base);
    user.oauth.tokens[token_key] = create_secret(secret_base);
    users.insert(user, user._id, function(err, response) {
        if (err || !response.ok) {
            console.log("ERROR Updating user with credentials:");
            console.dir(response);
            callback(response);
        } else {
            callback(null, denormalize_oauth(user));
        }
    });
}

function create_secret(secret_base) {
    var encrypt_key = serializer.randomString(48);
    var validate_key = serializer.randomString(48);
    var secret_string = serializer.secureStringify(secret_base, encrypt_key, validate_key);
    if (secret_string.length > 80) {
        secret_string = secret_string.substr(30,50);
    }
    return secret_string;
}

function denormalize_oauth(user) {
    var key;
    for (key in user.oauth.consumer_keys) {
        user.consumer_key = key;
        user.consumer_secret = user.oauth.consumer_keys[key];
        break;
    }
    for (key in user.oauth.tokens) {
        user.token_key = key;
        user.token_secret = user.oauth.tokens[key];
        break;
    }
    return user;
}

function is_admin(user) {
    var isAdmin = false;
    if (user.roles) {
        user.roles.forEach(function(role) {
            if (role === 'admin') {
                isAdmin = true;
            }
        });
    }
    return isAdmin;
}

function validate_oauth(oauth) {
    try {
        if (Object.keys(oauth.consumer_keys).length > 0 && Object.keys(oauth.tokens).length > 0) {
            return true;
        }
    } catch (ex) {
        //oauth is bad, just let the false return;
    }
    return false;
}

module.exports = {
    delete_user: function(user, idToDelete, rev, res) {
        if(is_admin(user)) {
            users.destroy(idToDelete, rev, function(err, body) {
                if (err) {
                    res.json({error:true, errorResult: err});
                } else {
                    res.json(body);
                }
            });
        }
    },

    find_oauth_user: function(accessToken, refreshToken, profile, callback) {
        var user_key = 'org.couchdb.user:'+profile.emails[0].value;
        users.get(user_key, {}, function(err, body) {
            if (err) {
                if (err.status_code === 404) {
                    callback();
                } else {
                    callback(err);
                }
                return;
            }
            if (validate_oauth(body.oauth)) {
                return callback(null, denormalize_oauth(body));
            } else {
                create_oauth_tokens(accessToken, body, callback);
            }
        });
    },

    find_user: function(userName, callback) {
        var user_key = userName;
        if (user_key.indexOf('org.couchdb.user:') !== 0) {
            user_key = 'org.couchdb.user:' + user_key;
        }
        users.get(user_key, {}, function(err, body) {
            if (err) {
                callback(err);
                return;
            }
            callback(null, body);
        });
    },

    get_user: function(user, id, res) {
        if(is_admin(user)) {
            this.find_user(id, function(err, body) {
                if (err) {
                    res.json({error:true, errorResult: err});
                } else {
                    res.json(body);
                }
            });
        }
    },

    get_users: function(user, res) {
        if(is_admin(user)) {
            var options = {
                include_docs: true,
                startkey: 'org.couchdb.user'
            };
            users.list(options, function (err, body) {
                if (err) {
                    res.json({error:true, errorResult: err});
                } else {
                    res.json(body);
                }
            });
        } else {
            res.json({error:true, errorResult: 'Unauthorized'});
        }
    },

    get_primary_role: function(user) {
        var primaryRole = '';
        if (user.roles) {
            user.roles.forEach(function(role) {
                if (role !== 'user' && role !== 'admin') {
                    primaryRole = role;
                }
            });
        }
        return primaryRole;
    },

    update_user: function(user, userData, updateParams, res) {
        if(is_admin(user)) {
            users.insert(userData, updateParams, function(err, body) {
                if (err) {
                    res.json({error:true, errorResult: err});
                } else {
                    res.json(body);
                }
            });
        }
    },

};
<|endoftext|>


<commit_before>export const UPDATE_STATUS = `media/UPDATE_STATUS`;
export const STORE_CALL = `media/STORE_CALL`;
export const UPDATE_CALL_STATE = `media/UPDATE_CALL_STATE`;
export const CONNECT_CALL = `media/CONNECT_CALL`;
export const REMOVE_CALL = `media/REMOVE_CALL`;
export const CHECKING_WEB_RTC_SUPPORT = `media/CHECKING_WEB_RTC_SUPPORT`;
export const UPDATE_WEBRTC_SUPPORT = `media/UPDATE_WEBRTC_SUPPORT`;

function updateCallState(call, locusUrl) {
  return {
    type: UPDATE_CALL_STATE,
    payload: {
      call,
      callState: constructCallState(call),
      locusUrl
    }
  };
}

function updateStatus(status) {
  return {
    type: UPDATE_STATUS,
    payload: {
      status
    }
  };
}

function storeCall(call, locusUrl) {
  return {
    type: STORE_CALL,
    payload: {
      call,
      callState: constructCallState(call),
      locusUrl
    }
  };
}

function connectCall(call) {
  return {
    type: CONNECT_CALL,
    payload: {
      call,
      callState: constructCallState(call)
    }
  };
}

function removeCall(call) {
  return {
    type: REMOVE_CALL,
    payload: {
      call
    }
  };
}

function checkingWebRTCSupport() {
  return {
    type: CHECKING_WEB_RTC_SUPPORT
  };
}

export function updateWebRTCSupport(supported) {
  return {
    type: UPDATE_WEBRTC_SUPPORT,
    payload: {
      supported
    }
  };
}

/**
 * Call a user with an email address or userId
 *
 * @export
 * @param {String} data.destination
 * @param {String} data.locusUrl Optional id we use in the store for a specific call
 * @param {Object} data.constraints
 * @param {Object} sparkInstance
 * @returns {Promise}
 */

export function placeCall({
  destination,
  locusUrl,
  constraints = {audio: true, video: true}
}, sparkInstance) {
  let localMediaStream;
  return (dispatch) =>
    sparkInstance.phone.createLocalMediaStream(constraints)
      .then((stream) => {
        localMediaStream = stream;
        const call = sparkInstance.phone.dial(
          destination,
          Object.assign({
            constraints,
            offerOptions: {offerToReceiveVideo: true, offerToReceiveAudio: true}
          }, {localMediaStream})
        );

        dispatch(storeCall(call, locusUrl));
        dispatch(updateCallState(call, locusUrl));
        bindEvents(dispatch, call, locusUrl);
        return Promise.resolve(call);
      })
      .catch((e) => {
        releaseCamera(localMediaStream);
        return Promise.reject(e);
      });
}

export function hangupCall(call) {
  return (dispatch) => {
    // Don't update call states after hangup
    call.off();
    return call.hangup().then(() => dispatch(removeCall(call)));
  };
}

/**
 * Listen for incoming calls and sets up call handling
 *
 * @export
 * @param {Object} sparkInstance
 * @param {String} locusUrl if limiting listener to single locusUrl
 * @returns {Promise}
 */
export function listenForIncomingCalls(sparkInstance, locusUrl) {
  return (dispatch) => {
    dispatch(updateStatus({isListening: true}));
    return sparkInstance.phone.on(`call:incoming`, (incomingCall) => {
      // If locusUrl is passed, only listen to specific locus
      if (locusUrl && incomingCall.locus.url !== locusUrl) {
        return false;
      }

      // If we make an outbound call via the widget, when the remote party answers,
      // we get a call:incoming notification when they connect
      if (incomingCall.direction === `out`) {
        return false;
      }

      return incomingCall.acknowledge()
        .then(() => {
          const removeIncoming = () => dispatch(removeCall(incomingCall));

          // If the remote party hangs up before we accept/decline
          incomingCall.once(`inactive`, removeIncoming);

          // Once we are connected, our connected call state will handle disconnects
          incomingCall.on(`membership:change`, () => {
            if (incomingCall.me.state === `connected`) {
              incomingCall.off(`inactive`, removeIncoming);
            }
          });
          return dispatch(storeCall(incomingCall));
        });
    });
  };
}

/**
 * Declines an incoming call
 *
 * @export
 * @param {object} incomingCall
 * @returns {Promise}
 */
export function declineIncomingCall(incomingCall) {
  return (dispatch) =>
    incomingCall.reject()
      .then(() => dispatch(removeCall(incomingCall)));
}

export function acceptIncomingCall(incomingCall, locusUrl) {
  return (dispatch) =>
    incomingCall.answer({offerOptions: {offerToReceiveVideo: true, offerToReceiveAudio: true}})
      .then(() => {
        bindEvents(dispatch, incomingCall, locusUrl);
        return dispatch(connectCall(incomingCall));
      });
}

/**
 * Check for browser webRTC support
 *
 *  @param {object} sparkInstance
 *  @returns {Promise}
 */
export function checkWebRTCSupport(sparkInstance) {
  return (dispatch) => {
    dispatch(checkingWebRTCSupport());
    return sparkInstance.phone.isCallingSupported().then((supported) => dispatch(updateWebRTCSupport(supported)));
  };
}

function constructCallState(call) {
  let connected,
    remoteAudioMuted,
    remoteAudioStream,
    remoteVideoMuted,
    remoteVideoStream,
    ringing;

  try {
    remoteAudioMuted = call.remoteAudioMuted;
    remoteVideoMuted = call.remoteVideoMuted;
    if (call.remoteMediaStream.getAudioTracks) {
      remoteAudioStream = new MediaStream(call.remoteMediaStream.getAudioTracks());
    }
    if (call.remoteMediaStream.getVideoTracks) {
      remoteVideoStream = new MediaStream(call.remoteMediaStream.getVideoTracks());
    }

    // Ringing means we are calling out to 1:1 and the party hasn't answered
    ringing = call.remoteMember && call.remoteMember.state === `notified`;

    // Connected means both parties are active on a 1:1 or joined in group
    if (call.isCall) {
      connected = !!call.remoteMember && call.remoteMember.state === `connected` && !!call.joinedOnThisDevice;
    }
    else {
      connected = !!call.joinedOnThisDevice;
    }
  }
  catch (e) {
    // Do nothing
  }
  return {
    activeParticipantsCount: call.activeParticipantsCount,
    state: call.state,
    isCall: call.isCall,
    direction: call.direction,
    joinedOnThisDevice: call.joinedOnThisDevice,
    connected,
    ringing,
    receivingAudio: call.receivingAudio,
    receivingVideo: call.receivingVideo,
    sendingAudio: call.sendingAudio,
    sendingVideo: call.sendingVideo,
    remoteMediaStream: call.remoteMediaStream,
    localMediaStream: call.localMediaStream,
    remoteAudioMuted,
    remoteVideoMuted,
    remoteAudioStream,
    remoteVideoStream
  };
}

function bindEvents(dispatch, call, locusUrl) {
  call.once(`active`, () => dispatch(connectCall(call)));
  call.once(`inactive`, () => dispatch(hangupCall(call)));
  call.on(`change:state`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:sendingAudio`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:receivingAudio`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:sendingVideo`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:receivingVideo`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:activeParticipantsCount`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`remoteAudioMuted:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`remoteVideoMuted:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`remoteMediaStream:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`localMediaStream:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:joinedOnThisDevice`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`membership:notified`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`membership:declined`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`membership:disconnected`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`membership:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`memberships:add`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`memberships:remove`, () => dispatch(updateCallState(call, locusUrl)));
}

function releaseCamera(mediaStream) {
  return mediaStream.getTracks().forEach((track) => track.stop());
}
<commit_msg>feat(redux-module-media): hang up call on remote decline<commit_after>export const UPDATE_STATUS = `media/UPDATE_STATUS`;
export const STORE_CALL = `media/STORE_CALL`;
export const UPDATE_CALL_STATE = `media/UPDATE_CALL_STATE`;
export const CONNECT_CALL = `media/CONNECT_CALL`;
export const REMOVE_CALL = `media/REMOVE_CALL`;
export const CHECKING_WEB_RTC_SUPPORT = `media/CHECKING_WEB_RTC_SUPPORT`;
export const UPDATE_WEBRTC_SUPPORT = `media/UPDATE_WEBRTC_SUPPORT`;

function updateCallState(call, locusUrl) {
  return {
    type: UPDATE_CALL_STATE,
    payload: {
      call,
      callState: constructCallState(call),
      locusUrl
    }
  };
}

function updateStatus(status) {
  return {
    type: UPDATE_STATUS,
    payload: {
      status
    }
  };
}

function storeCall(call, locusUrl) {
  return {
    type: STORE_CALL,
    payload: {
      call,
      callState: constructCallState(call),
      locusUrl
    }
  };
}

function connectCall(call) {
  return {
    type: CONNECT_CALL,
    payload: {
      call,
      callState: constructCallState(call)
    }
  };
}

function removeCall(call) {
  return {
    type: REMOVE_CALL,
    payload: {
      call
    }
  };
}

function checkingWebRTCSupport() {
  return {
    type: CHECKING_WEB_RTC_SUPPORT
  };
}

export function updateWebRTCSupport(supported) {
  return {
    type: UPDATE_WEBRTC_SUPPORT,
    payload: {
      supported
    }
  };
}

/**
 * Call a user with an email address or userId
 *
 * @export
 * @param {String} data.destination
 * @param {String} data.locusUrl Optional id we use in the store for a specific call
 * @param {Object} data.constraints
 * @param {Object} sparkInstance
 * @returns {Promise}
 */

export function placeCall({
  destination,
  locusUrl,
  constraints = {audio: true, video: true}
}, sparkInstance) {
  let localMediaStream;
  return (dispatch) =>
    sparkInstance.phone.createLocalMediaStream(constraints)
      .then((stream) => {
        localMediaStream = stream;
        const call = sparkInstance.phone.dial(
          destination,
          Object.assign({
            constraints,
            offerOptions: {offerToReceiveVideo: true, offerToReceiveAudio: true}
          }, {localMediaStream})
        );

        dispatch(storeCall(call, locusUrl));
        dispatch(updateCallState(call, locusUrl));
        bindEvents(dispatch, call, locusUrl);
        return Promise.resolve(call);
      })
      .catch((e) => {
        releaseCamera(localMediaStream);
        return Promise.reject(e);
      });
}

export function hangupCall(call) {
  return (dispatch) => {
    // Don't update call states after hangup
    call.off();
    return call.hangup().then(() => dispatch(removeCall(call)));
  };
}

/**
 * Listen for incoming calls and sets up call handling
 *
 * @export
 * @param {Object} sparkInstance
 * @param {String} locusUrl if limiting listener to single locusUrl
 * @returns {Promise}
 */
export function listenForIncomingCalls(sparkInstance, locusUrl) {
  return (dispatch) => {
    dispatch(updateStatus({isListening: true}));
    return sparkInstance.phone.on(`call:incoming`, (incomingCall) => {
      // If locusUrl is passed, only listen to specific locus
      if (locusUrl && incomingCall.locus.url !== locusUrl) {
        return false;
      }

      // If we make an outbound call via the widget, when the remote party answers,
      // we get a call:incoming notification when they connect
      if (incomingCall.direction === `out`) {
        return false;
      }

      return incomingCall.acknowledge()
        .then(() => {
          const removeIncoming = () => dispatch(removeCall(incomingCall));

          // If the remote party hangs up before we accept/decline
          incomingCall.once(`inactive`, removeIncoming);

          // Once we are connected, our connected call state will handle disconnects
          incomingCall.on(`membership:change`, () => {
            if (incomingCall.me.state === `connected`) {
              incomingCall.off(`inactive`, removeIncoming);
            }
          });
          return dispatch(storeCall(incomingCall));
        });
    });
  };
}

/**
 * Declines an incoming call
 *
 * @export
 * @param {object} incomingCall
 * @returns {Promise}
 */
export function declineIncomingCall(incomingCall) {
  return (dispatch) =>
    incomingCall.reject()
      .then(() => dispatch(removeCall(incomingCall)));
}

export function acceptIncomingCall(incomingCall, locusUrl) {
  return (dispatch) =>
    incomingCall.answer({offerOptions: {offerToReceiveVideo: true, offerToReceiveAudio: true}})
      .then(() => {
        bindEvents(dispatch, incomingCall, locusUrl);
        return dispatch(connectCall(incomingCall));
      });
}

/**
 * Check for browser webRTC support
 *
 *  @param {object} sparkInstance
 *  @returns {Promise}
 */
export function checkWebRTCSupport(sparkInstance) {
  return (dispatch) => {
    dispatch(checkingWebRTCSupport());
    return sparkInstance.phone.isCallingSupported().then((supported) => dispatch(updateWebRTCSupport(supported)));
  };
}

function constructCallState(call) {
  let connected,
    remoteAudioMuted,
    remoteAudioStream,
    remoteVideoMuted,
    remoteVideoStream,
    ringing;

  try {
    remoteAudioMuted = call.remoteAudioMuted;
    remoteVideoMuted = call.remoteVideoMuted;
    if (call.remoteMediaStream.getAudioTracks) {
      remoteAudioStream = new MediaStream(call.remoteMediaStream.getAudioTracks());
    }
    if (call.remoteMediaStream.getVideoTracks) {
      remoteVideoStream = new MediaStream(call.remoteMediaStream.getVideoTracks());
    }

    // Ringing means we are calling out to 1:1 and the party hasn't answered
    ringing = call.remoteMember && call.remoteMember.state === `notified`;

    // Connected means both parties are active on a 1:1 or joined in group
    if (call.isCall) {
      connected = !!call.remoteMember && call.remoteMember.state === `connected` && !!call.joinedOnThisDevice;
    }
    else {
      connected = !!call.joinedOnThisDevice;
    }
  }
  catch (e) {
    // Do nothing
  }
  return {
    activeParticipantsCount: call.activeParticipantsCount,
    state: call.state,
    isCall: call.isCall,
    direction: call.direction,
    joinedOnThisDevice: call.joinedOnThisDevice,
    connected,
    ringing,
    receivingAudio: call.receivingAudio,
    receivingVideo: call.receivingVideo,
    sendingAudio: call.sendingAudio,
    sendingVideo: call.sendingVideo,
    remoteMediaStream: call.remoteMediaStream,
    localMediaStream: call.localMediaStream,
    remoteAudioMuted,
    remoteVideoMuted,
    remoteAudioStream,
    remoteVideoStream
  };
}

function bindEvents(dispatch, call, locusUrl) {
  call.once(`active`, () => dispatch(connectCall(call)));
  call.once(`inactive`, () => dispatch(hangupCall(call)));
  call.on(`change:state`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:sendingAudio`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:receivingAudio`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:sendingVideo`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:receivingVideo`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:activeParticipantsCount`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`remoteAudioMuted:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`remoteVideoMuted:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`remoteMediaStream:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`localMediaStream:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`change:joinedOnThisDevice`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`membership:notified`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`membership:disconnected`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`membership:change`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`memberships:add`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`memberships:remove`, () => dispatch(updateCallState(call, locusUrl)));
  call.on(`membership:declined`, () => {
    if (call.isCall && call.remoteMember && call.remoteMember.state === `declined`) {
      // Hang up if the remote person declines the call
      return dispatch(hangupCall(call));
    }
    return dispatch(updateCallState(call, locusUrl));
  });
}

function releaseCamera(mediaStream) {
  return mediaStream.getTracks().forEach((track) => track.stop());
}
<|endoftext|>


<commit_before>import React from 'react';
import lbry from '../lbry.js';
import {Link} from '../component/link.js';
import {FileActions} from '../component/file-actions.js';
import {Thumbnail, TruncatedText, CreditAmount} from '../component/common.js';

let FilePrice = React.createClass({
  _isMounted: false,

  propTypes: {
    name: React.PropTypes.string
  },

  getInitialState: function() {
    return {
      cost: null,
      costIncludesData: null,
    }
  },

  componentDidMount: function() {
    this._isMounted = true;

    lbry.getCostInfoForName(this.props.name, ({cost, includesData}) => {
      if (this._isMounted) {
        this.setState({
          cost: cost,
          costIncludesData: includesData,
        });
      }
    }, () => {
      // If we get an error looking up cost information, do nothing
    });
  },

  componentWillUnmount: function() {
    this._isMounted = false;
  },

  render: function() {
    if (this.state.cost === null)
    {
      return null;
    }

    return (

        <CreditAmount amount={this.state.cost} isEstimate={!this.state.costIncludesData}/>

    );
  }
});

/*should be merged into FileTile once FileTile is refactored to take a single id*/
export let FileTileStream = React.createClass({
  _fileInfoSubscribeId: null,
  _isMounted: null,

  propTypes: {
    metadata: React.PropTypes.object,
    sdHash: React.PropTypes.string,
    hideOnRemove: React.PropTypes.bool,
    hidePrice: React.PropTypes.bool,
    obscureNsfw: React.PropTypes.bool
  },
  getInitialState: function() {
    return {
      showNsfwHelp: false,
      isHidden: false,
      available: null,
    }
  },
  getDefaultProps: function() {
    return {
      obscureNsfw: !lbry.getClientSetting('showNsfw'),
      hidePrice: false
    }
  },
  componentDidMount: function() {
    this._isMounted = true;
    if (this.props.hideOnRemove) {
      lbry.fileInfoSubscribe(this.props.sdHash, this.onFileInfoUpdate);
    }
  },
  componentWillUnmount: function() {
    if (this._fileInfoSubscribeId) {
      lbry.fileInfoUnsubscribe(this.props.sdHash, this._fileInfoSubscribeId);
    }
  },
  onFileInfoUpdate: function(fileInfo) {
    if (!fileInfo && this._isMounted && this.props.hideOnRemove) {
      this.setState({
        isHidden: true
      });
    }
  },
  handleMouseOver: function() {
    if (this.props.obscureNsfw && this.props.metadata && this.props.metadata.nsfw) {
      this.setState({
        showNsfwHelp: true,
      });
    }
  },
  handleMouseOut: function() {
    if (this.state.showNsfwHelp) {
      this.setState({
        showNsfwHelp: false,
      });
    }
  },
  render: function() {
    if (this.state.isHidden) {
      return null;
    }

    const metadata = this.props.metadata;
    const isConfirmed = typeof metadata == 'object';
    const title = isConfirmed ? metadata.title : ('lbry://' + this.props.name);
    const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
    return (

            <Thumbnail className="file-tile__thumbnail" src={metadata.thumbnail} alt={`Photo for ${title}`} />

            { !this.props.hidePrice
              ? <FilePrice name={this.props.name} />
              : null}

{'lbry://' + this.props.name}

                <TruncatedText lines={1}>
                  {title}
                </TruncatedText>

            <FileActions streamName={this.props.name} sdHash={this.props.sdHash} metadata={metadata} />

              <TruncatedText lines={3}>
                {isConfirmed
                   ? metadata.description
                   : This file is pending confirmation.}
              </TruncatedText>

        {this.state.showNsfwHelp
          ?

             This content is Not Safe For Work.
             To view adult content, please change your <Link className="button-text" href="?settings" label="Settings" />.

          : null}

    );
  }
});

export let FileTile = React.createClass({
  _isMounted: false,

  propTypes: {
    name: React.PropTypes.string.isRequired,
    available: React.PropTypes.bool,
  },

  getInitialState: function() {
    return {
      sdHash: null,
      metadata: null
    }
  },

  componentDidMount: function() {
    this._isMounted = true;

    lbry.resolveName(this.props.name, (metadata) => {
      if (this._isMounted && metadata) {
        // In case of a failed lookup, metadata will be null, in which case the component will never display
        this.setState({
          sdHash: metadata.sources.lbry_sd_hash,
          metadata: metadata,
        });
      }
    });
  },
  componentWillUnmount: function() {
    this._isMounted = false;
  },
  render: function() {
    if (!this.state.metadata || !this.state.sdHash) {
      return null;
    }

    return <FileTileStream sdHash={this.state.sdHash} metadata={this.state.metadata} {... this.props} />;
  }
});<commit_msg>need full link for electron<commit_after>import React from 'react';
import lbry from '../lbry.js';
import {Link} from '../component/link.js';
import {FileActions} from '../component/file-actions.js';
import {Thumbnail, TruncatedText, CreditAmount} from '../component/common.js';

let FilePrice = React.createClass({
  _isMounted: false,

  propTypes: {
    name: React.PropTypes.string
  },

  getInitialState: function() {
    return {
      cost: null,
      costIncludesData: null,
    }
  },

  componentDidMount: function() {
    this._isMounted = true;

    lbry.getCostInfoForName(this.props.name, ({cost, includesData}) => {
      if (this._isMounted) {
        this.setState({
          cost: cost,
          costIncludesData: includesData,
        });
      }
    }, () => {
      // If we get an error looking up cost information, do nothing
    });
  },

  componentWillUnmount: function() {
    this._isMounted = false;
  },

  render: function() {
    if (this.state.cost === null)
    {
      return null;
    }

    return (

        <CreditAmount amount={this.state.cost} isEstimate={!this.state.costIncludesData}/>

    );
  }
});

/*should be merged into FileTile once FileTile is refactored to take a single id*/
export let FileTileStream = React.createClass({
  _fileInfoSubscribeId: null,
  _isMounted: null,

  propTypes: {
    metadata: React.PropTypes.object,
    sdHash: React.PropTypes.string,
    hideOnRemove: React.PropTypes.bool,
    hidePrice: React.PropTypes.bool,
    obscureNsfw: React.PropTypes.bool
  },
  getInitialState: function() {
    return {
      showNsfwHelp: false,
      isHidden: false,
      available: null,
    }
  },
  getDefaultProps: function() {
    return {
      obscureNsfw: !lbry.getClientSetting('showNsfw'),
      hidePrice: false
    }
  },
  componentDidMount: function() {
    this._isMounted = true;
    if (this.props.hideOnRemove) {
      lbry.fileInfoSubscribe(this.props.sdHash, this.onFileInfoUpdate);
    }
  },
  componentWillUnmount: function() {
    if (this._fileInfoSubscribeId) {
      lbry.fileInfoUnsubscribe(this.props.sdHash, this._fileInfoSubscribeId);
    }
  },
  onFileInfoUpdate: function(fileInfo) {
    if (!fileInfo && this._isMounted && this.props.hideOnRemove) {
      this.setState({
        isHidden: true
      });
    }
  },
  handleMouseOver: function() {
    if (this.props.obscureNsfw && this.props.metadata && this.props.metadata.nsfw) {
      this.setState({
        showNsfwHelp: true,
      });
    }
  },
  handleMouseOut: function() {
    if (this.state.showNsfwHelp) {
      this.setState({
        showNsfwHelp: false,
      });
    }
  },
  render: function() {
    if (this.state.isHidden) {
      return null;
    }

    const metadata = this.props.metadata;
    const isConfirmed = typeof metadata == 'object';
    const title = isConfirmed ? metadata.title : ('lbry://' + this.props.name);
    const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
    return (

            <Thumbnail className="file-tile__thumbnail" src={metadata.thumbnail} alt={'Photo for ' + (title || this.props.name)} />

            { !this.props.hidePrice
              ? <FilePrice name={this.props.name} />
              : null}

{'lbry://' + this.props.name}

                <TruncatedText lines={1}>
                  {title}
                </TruncatedText>

            <FileActions streamName={this.props.name} sdHash={this.props.sdHash} metadata={metadata} />

              <TruncatedText lines={3}>
                {isConfirmed
                   ? metadata.description
                   : This file is pending confirmation.}
              </TruncatedText>

        {this.state.showNsfwHelp
          ?

             This content is Not Safe For Work.
             To view adult content, please change your <Link className="button-text" href="?settings" label="Settings" />.

          : null}

    );
  }
});

export let FileTile = React.createClass({
  _isMounted: false,

  propTypes: {
    name: React.PropTypes.string.isRequired,
    available: React.PropTypes.bool,
  },

  getInitialState: function() {
    return {
      sdHash: null,
      metadata: null
    }
  },

  componentDidMount: function() {
    this._isMounted = true;

    lbry.resolveName(this.props.name, (metadata) => {
      if (this._isMounted && metadata) {
        // In case of a failed lookup, metadata will be null, in which case the component will never display
        this.setState({
          sdHash: metadata.sources.lbry_sd_hash,
          metadata: metadata,
        });
      }
    });
  },
  componentWillUnmount: function() {
    this._isMounted = false;
  },
  render: function() {
    if (!this.state.metadata || !this.state.sdHash) {
      return null;
    }

    return <FileTileStream sdHash={this.state.sdHash} metadata={this.state.metadata} {... this.props} />;
  }
});
<|endoftext|>


<commit_before>import { h, Component }				from 'preact/preact';
import SVGIcon 						from 'com/svg-icon/icon';
import NavLink 						from 'com/nav-link/link';

export default class HeaderWhatsup extends Component {
	constructor( props ) {
		super(props);
	}

	render( props ) {
		var node = props.node;
		var user = props.user;
		var path = props.path;
		var extra = props.extra;
		var featured = props.featured;

		// NOTE: For Theme Voting, look at what's in 'available' returned by the theme list.
		// Then use that to check the name in 'names'.

		// TODO: Write code to convert an array to "1 and 2" or "1, 2, and 3"

		// TODO: The actual theme can be found in the the public metatag "event-theme" (private one before event)

//		return null;

//		return (
//

//				ON NOW: Ludum Dare 38 <NavLink href="/events/ludum-dare/38/theme"><SVGIcon baseline small gap>suggestion</SVGIcon>Theme Suggestions</NavLink> are open
//

//		);

//		return (
//

//				ON NOW: Ludum Dare 38 <NavLink href="/events/ludum-dare/38"><SVGIcon baseline small gap>fire</SVGIcon>Theme Slaughter</NavLink>
//

//		);
		if ( featured ) {
			if ( featured.meta ) {
				if ( featured.meta['event-theme'] ) {
					if ( featured.meta['can-grade'] ) {
						return (

								JUDGING NOW: <NavLink href={featured.path}><SVGIcon baseline small gap>trophy</SVGIcon>{featured.name}</NavLink> Theme: {featured.meta['event-theme']}

						);
					}
					else {
						return (

								ON NOW: <NavLink href={featured.path}><SVGIcon baseline small gap>trophy</SVGIcon>{featured.name}</NavLink> Theme: {featured.meta['event-theme']}

Judging will start Friday Sunday. See <NavLink href="/news">the News</NavLink>. Contact <NavLink href="/contact">Mike</NavLink> if you need help

						);
					}
				}
			}

//

Wow! Sorry about the site! Judging will start Wednesday Friday.
//
<NavLink href="https://twitter.com/mikekasprzak"><SVGIcon baseline small gap>twitter</SVGIcon>Mike</NavLink> wants to take Tuesday to be sure we got all the submissions.
//
<NavLink href="/contact">Contact Mike</NavLink> if you need help. PS: <NavLink href="https://twitter.com/mikekasprzak/status/856708287856746497">We set a record</NavLink>. Still going!

//

Game Publishing (Submission) is coming! Sorry for the <NavLink href="https://twitter.com/mikekasprzak"><SVGIcon baseline small gap>twitter</SVGIcon>delay</NavLink>!
//
We will make sure you get your compo games in the compo!

			return (

					ON NOW: <NavLink href={featured.path}><SVGIcon baseline small gap>trophy</SVGIcon>{featured.name}</NavLink> Theme Announcement Soon! <NavLink href="https://twitter.com/ludumdare"><SVGIcon baseline small gap>twitter</SVGIcon>Check Twitter</NavLink>

			);
		}
		return null;

//					ON NOW: <NavLink href={featured.path}><SVGIcon baseline small gap>trophy</SVGIcon>{featured.name}</NavLink> Final Round Theme Voting

//				ON NOW: <NavLink href="/events/ludum-dare/38"><SVGIcon baseline small gap>trophy</SVGIcon>Ludum Dare 38</NavLink> Final Round Theme Voting

//				ON NOW: <NavLink href="/events/ludum-dare/38"><SVGIcon baseline small gap>trophy</SVGIcon>Ludum Dare 38</NavLink> <SVGIcon baseline small gap>ticket</SVGIcon>Theme Voting

//				{"Theme:"}{" One room"}

//				<NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> has ended. The theme will be announced soon.

// Yes, if you're watching the source code, we're going to have to do this manually.
//				{"Theme:"}{" ???"}

//				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Final Round!

//				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Round 1, Round 2, and Round 3
	}
}
<commit_msg>hacked hide event fuff<commit_after>import { h, Component }				from 'preact/preact';
import SVGIcon 						from 'com/svg-icon/icon';
import NavLink 						from 'com/nav-link/link';

export default class HeaderWhatsup extends Component {
	constructor( props ) {
		super(props);
	}

	render( props ) {
		var node = props.node;
		var user = props.user;
		var path = props.path;
		var extra = props.extra;
		var featured = props.featured;

		// NOTE: For Theme Voting, look at what's in 'available' returned by the theme list.
		// Then use that to check the name in 'names'.

		// TODO: Write code to convert an array to "1 and 2" or "1, 2, and 3"

		// TODO: The actual theme can be found in the the public metatag "event-theme" (private one before event)

//		return null;

//		return (
//

//				ON NOW: Ludum Dare 38 <NavLink href="/events/ludum-dare/38/theme"><SVGIcon baseline small gap>suggestion</SVGIcon>Theme Suggestions</NavLink> are open
//

//		);

//		return (
//

//				ON NOW: Ludum Dare 38 <NavLink href="/events/ludum-dare/38"><SVGIcon baseline small gap>fire</SVGIcon>Theme Slaughter</NavLink>
//

//		);

		return null; // HACK HAAAAAAAAAAACK!

		if ( featured ) {
			if ( featured.meta ) {
				if ( featured.meta['event-theme'] ) {
					if ( featured.meta['can-grade'] ) {
						return (

								JUDGING NOW: <NavLink href={featured.path}><SVGIcon baseline small gap>trophy</SVGIcon>{featured.name}</NavLink> Theme: {featured.meta['event-theme']}

						);
					}
					else {
						return (

								ON NOW: <NavLink href={featured.path}><SVGIcon baseline small gap>trophy</SVGIcon>{featured.name}</NavLink> Theme: {featured.meta['event-theme']}

Judging will start Friday Sunday. See <NavLink href="/news">the News</NavLink>. Contact <NavLink href="/contact">Mike</NavLink> if you need help

						);
					}
				}
			}

//

Wow! Sorry about the site! Judging will start Wednesday Friday.
//
<NavLink href="https://twitter.com/mikekasprzak"><SVGIcon baseline small gap>twitter</SVGIcon>Mike</NavLink> wants to take Tuesday to be sure we got all the submissions.
//
<NavLink href="/contact">Contact Mike</NavLink> if you need help. PS: <NavLink href="https://twitter.com/mikekasprzak/status/856708287856746497">We set a record</NavLink>. Still going!

//

Game Publishing (Submission) is coming! Sorry for the <NavLink href="https://twitter.com/mikekasprzak"><SVGIcon baseline small gap>twitter</SVGIcon>delay</NavLink>!
//
We will make sure you get your compo games in the compo!

			return (

					ON NOW: <NavLink href={featured.path}><SVGIcon baseline small gap>trophy</SVGIcon>{featured.name}</NavLink> Theme Announcement Soon! <NavLink href="https://twitter.com/ludumdare"><SVGIcon baseline small gap>twitter</SVGIcon>Check Twitter</NavLink>

			);
		}
		return null;

//					ON NOW: <NavLink href={featured.path}><SVGIcon baseline small gap>trophy</SVGIcon>{featured.name}</NavLink> Final Round Theme Voting

//				ON NOW: <NavLink href="/events/ludum-dare/38"><SVGIcon baseline small gap>trophy</SVGIcon>Ludum Dare 38</NavLink> Final Round Theme Voting

//				ON NOW: <NavLink href="/events/ludum-dare/38"><SVGIcon baseline small gap>trophy</SVGIcon>Ludum Dare 38</NavLink> <SVGIcon baseline small gap>ticket</SVGIcon>Theme Voting

//				{"Theme:"}{" One room"}

//				<NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> has ended. The theme will be announced soon.

// Yes, if you're watching the source code, we're going to have to do this manually.
//				{"Theme:"}{" ???"}

//				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Final Round!

//				ON NOW: Ludum Dare 37 <NavLink href="/events/ludum-dare/37/theme"><SVGIcon baseline small gap>mallet</SVGIcon>Theme Voting</NavLink> Round 1, Round 2, and Round 3
	}
}
<|endoftext|>


<commit_before>class SelectionSort {
  constructor(data = [], compareFunc = (a, b) => a < b) {
    this._unsortedList = data;
    this._compareFunc = compareFunc;

    this._sortedList = this._sort(data.slice());
    this._length = data.length;
  }

  get size() {
    return this._length;
  }

  get unsortedList() {
    return this._unsortedList;
  }

  get sortedList() {
    return this._sortedList;
  }

  _sort(list) {
    const len = list.length;

    for (let i = 0; i < len - 1; i += 1) {
      let minIndex = i;

      for (let j = i + 1; j < len; j += 1) {
        if (this._compareFunc(list[j], list[minIndex])) {
          minIndex = j;
        }
      }

      [list[i], list[minIndex]] = [list[minIndex], list[i]];
    }

    return list;
  }

  toString() {
    return this._sortedList.join(', ');
  }
}

module.exports = SelectionSort;
<commit_msg>Selection Sort: Commented the code<commit_after>class SelectionSort {
  constructor(data = [], compareFunc = (a, b) => a < b) {
    this._unsortedList = data;
    this._compareFunc = compareFunc;

    this._sortedList = this._sort(data.slice());
    this._length = data.length;
  }

  /**
   * Get size of array
   * @return {Number} Size of array
   */
  get size() {
    return this._length;
  }

  /**
   * Get unsorted array
   * @return {Array} Unsorted/Initial array
   */
  get unsortedList() {
    return this._unsortedList;
  }

  /**
   * Get sorted array
   * @return {Array} Sorted array
   */
  get sortedList() {
    return this._sortedList;
  }

  /**
   * Selection Sorts the array
   * @param  {Array} list Array to be sorted
   * @return {Array}      Sorted array
   */
  _sort(list) {
    const len = list.length;

    for (let i = 0; i < len - 1; i += 1) {
      let minIndex = i;

      for (let j = i + 1; j < len; j += 1) {
        if (this._compareFunc(list[j], list[minIndex])) {
          minIndex = j;
        }
      }

      [list[i], list[minIndex]] = [list[minIndex], list[i]];
    }

    return list;
  }

  /**
   * Get string form of array
   * @return {String} Comma separated string array
   */
  toString() {
    return this._sortedList.join(', ');
  }
}

module.exports = SelectionSort;
<|endoftext|>


<commit_before>'use strict'
const path = require('path')

const Bluebird = require('bluebird')
const uuid = require('uuid')

const fs = use('fs-promises')
const Site = use('site')
const promisify = use('promisify')

const rtfToHTML = use('rtf-to-html')

const parseString = promisify(require('xml2js').parseString)

class Scrivener extends Site {
  static matches (siteUrlStr) {
    return /[.]scriv\/?/.test(siteUrlStr)
  }

  constructor (siteUrlStr) {
    super(siteUrlStr)
    this.publisher = 'scrivener'
    this.publisherName = 'Scrivener'
  }

  normalizeLink (href, base) {
    return href
  }

  getFicMetadata (fetch, fic) {
    fic.id = 'urn:uuid:' + uuid.v4()
    fic.publisher = this.publisherName
    fic.updateFrom = fic.link
    fic.link = null
    const basename = path.basename(fic.updateFrom)
    fic.title = basename
    const scrivname = path.join(fic.updateFrom, basename + 'x')
    return this.fromScrivener(fic, scrivname)
  }

  fromScrivener (fic, scrivx) {
    return parseString(fs.readFile(scrivx)).then(data => {
      const props = this.scrivMap(data.ScrivenerProject, 'ProjectProperties')
      fic.title = props.ProjectTitle
      fic.author = props.FullName
      const items = this.scrivBinder(data.ScrivenerProject, 'Binder')
      return this.recurseItems(fic, items)
    })
  }

  recurseItems (fic, items) {
    return Bluebird.each(items, item => {
      if (item.Type === 'TrashFolder' || item.Type === 'ResearchFolder') return
      if (item.Children) return this.recurseItems(fic, item.Children)
      fic.addChapter({
        name: item.Title,
        fetchFrom: path.join(fic.updateFrom, 'Files', 'Docs', item.ID + '.rtf'),
        created: new Date(item.Created),
        modified: new Date(item.Modified),
      })
    })
  }

  scrivValue (obj, prop) {
    return obj[prop][0]
  }

  scrivMap (obj, prop) {
    const item = this.scrivValue(obj, prop)
    const result = {}
    for (let prop in item) {
      result[prop] = this.scrivValue(item, prop)
    }
    return result
  }

  scrivBinder (obj, prop) {
    const item = this.scrivValue(obj, prop)
    return item.BinderItem.map(item => this.scrivBinderItem(item))
  }

  scrivBinderItem (item) {
    const result = {}
    for (let prop in item['$']) {
      result[prop] = item['$'][prop]
    }
    result.Title = this.scrivValue(item, 'Title')
    result.MetaData = this.scrivMap(item, 'MetaData')
    if (item.Children) {
      result.Children = this.scrivBinder(item, 'Children')
    }
    return result
  }

  scrapeFicMetadata (fetch, fic) {
    // There's never any reason to scrape scrivener content.
    return Bluebird.resolve()
  }

  getChapter (fetch, chapter) {
    return rtfToHTML(fs.readFile(chapter, 'ascii')).then(result => {
      return {
        'finalUrl': chapter,
        'content': result
      }
    }).catch(() => { return {finalUrl: chapter, content: ''} })
  }
}
module.exports = Scrivener
<commit_msg>site/scrivener: Better support stories without titles set<commit_after>'use strict'
const path = require('path')

const Bluebird = require('bluebird')
const uuid = require('uuid')

const fs = use('fs-promises')
const Site = use('site')
const promisify = use('promisify')

const rtfToHTML = use('rtf-to-html')

const parseString = promisify(require('xml2js').parseString)

class Scrivener extends Site {
  static matches (siteUrlStr) {
    return /[.]scriv\/?/.test(siteUrlStr)
  }

  constructor (siteUrlStr) {
    super(siteUrlStr)
    this.publisher = 'scrivener'
    this.publisherName = 'Scrivener'
  }

  normalizeLink (href, base) {
    return href
  }

  getFicMetadata (fetch, fic) {
    fic.id = 'urn:uuid:' + uuid.v4()
    fic.publisher = this.publisherName
    fic.updateFrom = fic.link.replace(/[/]$/, '')
    fic.link = null
    const basename = path.basename(fic.updateFrom)
    fic.title = basename.replace(/\.scriv$/, '')
    const scrivname = path.join(fic.updateFrom, basename + 'x')
    return this.fromScrivener(fic, scrivname)
  }

  fromScrivener (fic, scrivx) {
    return parseString(fs.readFile(scrivx)).then(data => {
      const props = this.scrivMap(data.ScrivenerProject, 'ProjectProperties')
      if (props.ProjectTitle) fic.title = props.ProjectTitle
      if (props.FullName) fic.author = props.FullName
      const items = this.scrivBinder(data.ScrivenerProject, 'Binder')
      return this.recurseItems(fic, items)
    })
  }

  recurseItems (fic, items) {
    return Bluebird.each(items, item => {
      if (item.Type === 'TrashFolder' || item.Type === 'ResearchFolder') return
      if (item.Children) return this.recurseItems(fic, item.Children)
      fic.addChapter({
        name: item.Title,
        fetchFrom: path.join(fic.updateFrom, 'Files', 'Docs', item.ID + '.rtf'),
        created: new Date(item.Created),
        modified: new Date(item.Modified),
      })
    })
  }

  scrivValue (obj, prop) {
    return obj[prop] && obj[prop][0]
  }

  scrivMap (obj, prop) {
    const item = this.scrivValue(obj, prop)
    const result = {}
    for (let prop in item) {
      result[prop] = this.scrivValue(item, prop)
    }
    return result
  }

  scrivBinder (obj, prop) {
    const item = this.scrivValue(obj, prop)
    return item.BinderItem.map(item => this.scrivBinderItem(item))
  }

  scrivBinderItem (item) {
    const result = {}
    for (let prop in item['$']) {
      result[prop] = item['$'][prop]
    }
    result.Title = this.scrivValue(item, 'Title')
    result.MetaData = this.scrivMap(item, 'MetaData')
    if (item.Children) {
      result.Children = this.scrivBinder(item, 'Children')
    }
    return result
  }

  scrapeFicMetadata (fetch, fic) {
    // There's never any reason to scrape scrivener content.
    return Bluebird.resolve()
  }

  getChapter (fetch, chapter) {
    return rtfToHTML(fs.readFile(chapter, 'ascii')).then(result => {
      return {
        'finalUrl': chapter,
        'content': result
      }
    }).catch(() => { return {finalUrl: chapter, content: ''} })
  }
}
module.exports = Scrivener
<|endoftext|>


<commit_before>import React from 'react';
import { QueryRenderer, graphql } from 'react-relay/compat';
import Relay from 'react-relay/classic';
import PropTypes from 'prop-types';
import { FormattedMessage, FormattedDate } from 'react-intl';
import { Box, Chip } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import WhatsAppIcon from '@material-ui/icons/WhatsApp';
import RequestCard from './RequestCard';
import RequestSubscription from './RequestSubscription';
import MediasLoading from '../media/MediasLoading';
import ErrorBoundary from '../error/ErrorBoundary';
import BulletSeparator from '../layout/BulletSeparator';
import { whatsappGreen } from '../../styles/js/shared';

const useStyles = makeStyles({
  requestsHeader: {
    height: '48px',
    fontWeight: 700,
    fontSize: '15px',
    display: 'flex',
    alignItems: 'center',
  },
  subscriptions: {
    color: '#E78A00',
  },
});

const RequestCards = ({ request, mediaDbid }) => {
  const isAllMedias = !mediaDbid;
  const isParentRequest = request.media.dbid === mediaDbid;
  const classes = useStyles();

  let requestsCount = request.similar_requests?.edges.length;
  if (isAllMedias || isParentRequest) { requestsCount += 1; }

  const whatsappIcon = (
    <WhatsAppIcon
      style={{
        backgroundColor: whatsappGreen,
        color: '#FFF',
        borderRadius: 4,
        padding: 2,
      }}
    />
  );

  const feedChip = <Chip label={request.feed.name} size="small" />;

  return (

      <Box pl={1}>

          {
            isAllMedias ? (
              <BulletSeparator
                icon={whatsappIcon}
                details={[
                  (<FormattedMessage
                    id="feedRequestedMedia.requestsForAllMedias"
                    defaultMessage="{requestsCount, plural, one {# request} other {# requests}}"
                    description="Header of requests list. Example: 26 requests"
                    values={{ requestsCount }}
                  />),
                  (

                      <FormattedMessage
                        id="feedRequestedMedia.subscriptionsForAllMedias"
                        defaultMessage="{subscriptionsCount, plural, one {# subscription} other {# subscriptions}}"
                        description="Part of the header of requests list. Example: 12 subscriptions"
                        values={{ subscriptionsCount: request.subscriptions_count }}
                      />

                  ),
                ]}
              />
            ) : (
              <FormattedMessage
                id="feedRequestedMedia.requestsForThisMedia"
                defaultMessage="{requestsCount, plural, one {# request for this media} other {# requests for this media}}"
                description="Header of requests list. Example: 3 requests for this media"
                values={{ requestsCount }}
              />
            )
          }

      </Box>
      { isAllMedias || isParentRequest ?
        <RequestCard
          icon={whatsappIcon}
          text={request.content}
          details={[
            (<FormattedDate
              value={request.last_submitted_at}
              year="numeric"
              month="short"
              day="2-digit"
            />),
            feedChip,
            ((request.subscribed || request.last_called_webhook_at) ?
              <RequestSubscription
                subscribed={request.subscribed}
                lastCalledAt={request.last_called_webhook_at}
              />
              : null
            ),
          ]}
        />
        : null
      }
      { request.similar_requests?.edges.map(r => (
        <RequestCard
          key={r.node.dbid}
          icon={whatsappIcon}
          text={r.node.content}
          details={[
            (<FormattedDate
              value={r.node.last_submitted_at}
              year="numeric"
              month="short"
              day="2-digit"
            />),
            feedChip,
            ((request.subscribed || request.last_called_webhook_at) ?
              <RequestSubscription
                subscribed={request.subscribed}
                lastCalledAt={request.last_called_webhook_at}
              />
              : null
            ),
          ]}
        />
      )) }

  );
};

RequestCards.propTypes = {
  request: PropTypes.object.isRequired,
};

const RequestCardsQuery = ({ requestDbid, mediaDbid }) => (
  <ErrorBoundary component="RequestCards">
    <QueryRenderer
      environment={Relay.Store}
      query={graphql`
        query RequestCardsQuery($requestId: ID!, $mediaId: Int!) {
          request(id: $requestId) {
            content
            subscriptions_count
            subscribed
            last_called_webhook_at
            feed {
              name
            }
            last_submitted_at
            media {
              dbid
            }
            similar_requests(first: 50, media_id: $mediaId) {
              edges {
                node {
                  dbid
                  content
                  subscribed
                  last_submitted_at
                  last_called_webhook_at
                }
              }
            }
          }
        }
      `}
      variables={{
        requestId: requestDbid,
        mediaId: mediaDbid,
      }}
      render={({ props, error }) => {
        if (!error && !props) {
          return <MediasLoading center />;
        }
        if (props && !error) {
          return (<RequestCards {...props} mediaDbid={mediaDbid} />);
        }
        return null;
      }}
    />
  </ErrorBoundary>
);

export default RequestCardsQuery;
<commit_msg>Fixing subscription information displayed on feed request cards.<commit_after>import React from 'react';
import { QueryRenderer, graphql } from 'react-relay/compat';
import Relay from 'react-relay/classic';
import PropTypes from 'prop-types';
import { FormattedMessage, FormattedDate } from 'react-intl';
import { Box, Chip } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import WhatsAppIcon from '@material-ui/icons/WhatsApp';
import RequestCard from './RequestCard';
import RequestSubscription from './RequestSubscription';
import MediasLoading from '../media/MediasLoading';
import ErrorBoundary from '../error/ErrorBoundary';
import BulletSeparator from '../layout/BulletSeparator';
import { whatsappGreen } from '../../styles/js/shared';

const useStyles = makeStyles({
  requestsHeader: {
    height: '48px',
    fontWeight: 700,
    fontSize: '15px',
    display: 'flex',
    alignItems: 'center',
  },
  subscriptions: {
    color: '#E78A00',
  },
});

const RequestCards = ({ request, mediaDbid }) => {
  const isAllMedias = !mediaDbid;
  const isParentRequest = request.media.dbid === mediaDbid;
  const classes = useStyles();

  let requestsCount = request.similar_requests?.edges.length;
  if (isAllMedias || isParentRequest) { requestsCount += 1; }

  const whatsappIcon = (
    <WhatsAppIcon
      style={{
        backgroundColor: whatsappGreen,
        color: '#FFF',
        borderRadius: 4,
        padding: 2,
      }}
    />
  );

  const feedChip = <Chip label={request.feed.name} size="small" />;

  return (

      <Box pl={1}>

          {
            isAllMedias ? (
              <BulletSeparator
                icon={whatsappIcon}
                details={[
                  (<FormattedMessage
                    id="feedRequestedMedia.requestsForAllMedias"
                    defaultMessage="{requestsCount, plural, one {# request} other {# requests}}"
                    description="Header of requests list. Example: 26 requests"
                    values={{ requestsCount }}
                  />),
                  (

                      <FormattedMessage
                        id="feedRequestedMedia.subscriptionsForAllMedias"
                        defaultMessage="{subscriptionsCount, plural, one {# subscription} other {# subscriptions}}"
                        description="Part of the header of requests list. Example: 12 subscriptions"
                        values={{ subscriptionsCount: request.subscriptions_count }}
                      />

                  ),
                ]}
              />
            ) : (
              <FormattedMessage
                id="feedRequestedMedia.requestsForThisMedia"
                defaultMessage="{requestsCount, plural, one {# request for this media} other {# requests for this media}}"
                description="Header of requests list. Example: 3 requests for this media"
                values={{ requestsCount }}
              />
            )
          }

      </Box>
      { isAllMedias || isParentRequest ?
        <RequestCard
          icon={whatsappIcon}
          text={request.content}
          details={[
            (<FormattedDate
              value={request.last_submitted_at}
              year="numeric"
              month="short"
              day="2-digit"
            />),
            feedChip,
            ((request.subscribed || request.last_called_webhook_at) ?
              <RequestSubscription
                subscribed={request.subscribed}
                lastCalledAt={request.last_called_webhook_at}
              />
              : null
            ),
          ]}
        />
        : null
      }
      { request.similar_requests?.edges.map(r => (
        <RequestCard
          key={r.node.dbid}
          icon={whatsappIcon}
          text={r.node.content}
          details={[
            (<FormattedDate
              value={r.node.last_submitted_at}
              year="numeric"
              month="short"
              day="2-digit"
            />),
            feedChip,
            ((r.node.subscribed || r.node.last_called_webhook_at) ?
              <RequestSubscription
                subscribed={r.node.subscribed}
                lastCalledAt={r.node.last_called_webhook_at}
              />
              : null
            ),
          ]}
        />
      )) }

  );
};

RequestCards.propTypes = {
  request: PropTypes.object.isRequired,
};

const RequestCardsQuery = ({ requestDbid, mediaDbid }) => (
  <ErrorBoundary component="RequestCards">
    <QueryRenderer
      environment={Relay.Store}
      query={graphql`
        query RequestCardsQuery($requestId: ID!, $mediaId: Int!) {
          request(id: $requestId) {
            content
            subscriptions_count
            subscribed
            last_called_webhook_at
            feed {
              name
            }
            last_submitted_at
            media {
              dbid
            }
            similar_requests(first: 50, media_id: $mediaId) {
              edges {
                node {
                  dbid
                  content
                  subscribed
                  last_submitted_at
                  last_called_webhook_at
                }
              }
            }
          }
        }
      `}
      variables={{
        requestId: requestDbid,
        mediaId: mediaDbid,
      }}
      render={({ props, error }) => {
        if (!error && !props) {
          return <MediasLoading center />;
        }
        if (props && !error) {
          return (<RequestCards {...props} mediaDbid={mediaDbid} />);
        }
        return null;
      }}
    />
  </ErrorBoundary>
);

export default RequestCardsQuery;
<|endoftext|>