<commit_before>'use strict';

var pd = require('pretty-data').pd;
var $ = require('jquery');
var m = require('mithril');
var $osf = require('js/osfHelpers');
var utils = require('./utils');
require('truncate');

var LoadingIcon = {
    view: function(ctrl) {
        return m('img', {src: '/static/img/loading.gif'});
    }
};

var Results = {
    view: function(ctrl, params) {
        var vm = params.vm;
        var resultViews = $.map(vm.results || [], function(result, i) {
            return m.component(Result, {result: result, vm: vm,});
        });

        var len = 0;
        if (vm.results){
            len = vm.results.length;
        }
        var maybeResults = function(results, loading) {
            if (results.length > 0) {
                return results;
            } else if (!loading && results.length === 0) {
                return m('p', {class: 'text-muted'}, 'No results for this query');
            } else {
                return m('', [m.component(LoadingIcon), 'loading...']);
            }
        };

        return m('', [
            m('.row', m('.col-md-12', maybeResults(resultViews, vm.resultsLoading()))),
            m('.row', m('.col-md-12', m('div', {style: {display: 'block', margin: 'auto', 'text-align': 'center'}},
                len > 0 && len < vm.count ?
                m('a.btn.btn-md.btn-default', {
                    onclick: function(){
                        utils.loadMore(vm)
                            .then(function(data) {
                                utils.updateVM(vm, data);
                            });
                    }
                }, 'More') : [])
            ))
        ]);

    }
};

var Result = {
    view: function(ctrl, params) {
        return m( '.animated.fadeInUp', [
            m('div', [
                m('h4', [
                    m.component(TitleBar, params)
                ]),
                m('.row', [
                    m('.col-md-7',
                      m('span.pull-left',
                        m.component(Contributors, params)
                      )
                    ),
                    m('.col-md-5',
                        m('.pull-right',
                          {style: {'text-align': 'right'}},
                          m.component(Subjects, params)
                        )
                    )
                ]),
                m('br'),
                m.component(Footer, params)
            ]),
            m('hr')
        ]);

    }
};

var TitleBar = {
    view: function(ctrl, params) {
        var result = params.result;
        return m('span', {}, [
            m('a[href=' + result.uris.canonicalUri + ']', ((result.title || 'No title provided'))),
            m('br'),
            m.component(Description, params)
        ]);
    }
};

/* Render the description of a single result. Will highlight the matched text */
var Description = {
    controller: function(vm) {
        var self = this;
        self.showAll = m.prop(false);
    },
    view: function(ctrl, params) {
        var result = params.result;
        var showOnClick = function() {
            ctrl.showAll(!ctrl.showAll());
        };
        if ((result.description || '').length > 350) {
            return m('', [
                m('p.readable.pointer', {onclick: showOnClick},
                    ctrl.showAll() ? result.description : $.truncate(result.description, {length: 350})
                ),
                m('a.sr-only', {href: '#', onclick: showOnClick}, ctrl.showAll() ? 'See less' : 'See more')]);
        } else {
            return m('p.readable', result.description);
        }
    }
};

var Contributors = {
    controller: function(vm) {
        var self = this;
        self.showAll = false;
    },
    view: function(ctrl, params) {
        var result = params.result;
        var contributorViews = $.map(result.contributors, function(contributor, i) {
                return m.component(Contributor, $.extend({contributor: contributor, index: i}, params));
            });

        return m('span.pull-left', {style: {'text-align': 'left'}},
            ctrl.showAll || result.contributors.length < 8 ?
                contributorViews :
                m('span', [
                    contributorViews.slice(0, 7),
                    m('br'),
                    m('a', {onclick: function(){ctrl.showAll = !ctrl.showAll;}}, 'See All')
                ])
        );

    }
};

var Contributor = {
    view: function(ctrl, params) {
        var contributor = params.contributor;
        var index = params.index;
        var vm = params.vm;
        return m('span', [
            m('span', index !== 0 ? ' · ' : ''),
            m('a', {
                href: '#',
                onclick: function() {
                    utils.updateFilter(vm, 'match:contributors.familyName:' + contributor.familyName, true);
                    utils.updateFilter(vm, 'match:contributors.givenName:' + contributor.givenName, true);
                }
            }, contributor.name)
        ]);
    }
};

var Subjects = {
    controller: function(vm) {
        var self = this;
        self.showAll = false;
    },
    view: function(ctrl, params){
        var result = params.result;
        var subjectViews = $.map(result.subjects || [], function(subject, i) {
            return m.component(Subject, $.extend({subject: subject}, params));
        });
        if (ctrl.showAll || (result.subjects || []).length <= 5) {
            return m('span', subjectViews);
        }
        return m('span', [
            subjectViews.slice(0, 5),
            m('br'),
            m('div', m('a', {href: '#', onclick: function() {ctrl.showAll = !ctrl.showAll;}},'See All'))
        ]);

    }
};

var Subject = {
    view: function(ctrl, params) {
        var subject = params.subject;
        var vm = params.vm;
        return m('span', m('a.badge.pointer', {href: '#', onclick: function(){
                utils.updateFilter(vm, 'match:subjects:"' + subject + '"', true);
            }}, $.truncate(subject, {length: 50}), ' '
        ));
    }
};

var Footer = {
    controller: function(params) {
        var self = this;
        this.result = params.result;
        this.cleanResult = m.prop(null);
        this.missingError = m.prop(null);
        this.showRawNormalized = m.prop(false);
        this.loadRawNormalized = function() {
            utils.loadRawNormalized(this.result).then(
                function(cleanData) {
                    self.cleanResult(cleanData);
                }, function(error) {
                    self.missingError(error);
                }
            );
        };
    },
    view: function(ctrl, params) {
        var result = params.result;
        var vm = params.vm;
        return m('div', [
            m('span',
                'Released on ' + new $osf.FormattableDate(result.providerUpdatedDateTime).local,
                vm.rawNormedLoaded() ?  m('span', [
                    m('span', {style: {'margin-right': '5px', 'margin-left': '5px'}}, ' | '),
                    m('a', {
                        onclick: function() {
                            ctrl.showRawNormalized(!ctrl.showRawNormalized());
                            ctrl.loadRawNormalized();
                        }
                    }, 'Data')
                ]) : m('span', [])
            ),
            m('span.pull-right', [
                m('img', {src: vm.ProviderMap[result.shareProperties.source].favicon, alt: 'favicon for ' + result.shareProperties.source, style: {width: '16px', height: '16px'}}),
                ' ',
                m('a', {onclick: function() {utils.updateFilter(vm, 'shareProperties.source:' + result.shareProperties.source);}}, vm.ProviderMap[result.shareProperties.source].long_name),
                m('br')
            ]),
            ctrl.showRawNormalized() ? m.component(RawNormalizedData, {result: ctrl.cleanResult(), missingError: ctrl.missingError()}) : '',
        ]);
    }
};

var RawNormalizedData = {
    view: function(ctrl, params) {
        var result = params.result || params.missingError;
        var divID = params.missingError ? '' : (result.normalized.shareProperties.docID + result.normalized.shareProperties.source).replace( /(:|\.|\[|\]|,)/g, '-' );
        return m('.row', [
            m('.col-md-12',
                m('div', [
                    m('ul', {className: 'nav nav-tabs'}, [
                        m('li', m('a', {href: '#raw' + divID, 'data-toggle': 'tab'}, 'Raw')),
                        m('li', m('a', {href: '#normalized' + divID, 'data-toggle': 'tab'}, 'Normalized'))
                    ]),
                    m('div', {className: 'tab-content'},
                        m('div',
                            {className: 'tab-pane active', id:'raw' + divID},
                            m('pre',
                                (function(){
                                    if (result.rawfiletype === 'xml') {
                                        return pd.xml(result.raw);
                                    }
                                    else {
                                        var rawjson = JSON.parse(result.raw);
                                        return JSON.stringify(rawjson, undefined, 2);
                                    }
                                }())
                            )
                        ),
                        m('div',
                            {className: 'tab-pane', id:'normalized' + divID},
                            m('pre',
                                (function(){
                                    return JSON.stringify(result.normalized, undefined, 2);
                                }())
                            )
                        )
                    )
                ])
            )
        ]);
    }
};

module.exports = Results;
<commit_msg>Mute footer text, prevent page jumping when clicking subjects<commit_after>'use strict';

var pd = require('pretty-data').pd;
var $ = require('jquery');
var m = require('mithril');
var $osf = require('js/osfHelpers');
var utils = require('./utils');
require('truncate');

var LoadingIcon = {
    view: function(ctrl) {
        return m('img', {src: '/static/img/loading.gif'});
    }
};

var Results = {
    view: function(ctrl, params) {
        var vm = params.vm;
        var resultViews = $.map(vm.results || [], function(result, i) {
            return m.component(Result, {result: result, vm: vm,});
        });

        var len = 0;
        if (vm.results){
            len = vm.results.length;
        }
        var maybeResults = function(results, loading) {
            if (results.length > 0) {
                return results;
            } else if (!loading && results.length === 0) {
                return m('p', {class: 'text-muted'}, 'No results for this query');
            } else {
                return m('', [m.component(LoadingIcon), 'loading...']);
            }
        };

        return m('', [
            m('.row', m('.col-md-12', maybeResults(resultViews, vm.resultsLoading()))),
            m('.row', m('.col-md-12', m('div', {style: {display: 'block', margin: 'auto', 'text-align': 'center'}},
                len > 0 && len < vm.count ?
                m('a.btn.btn-md.btn-default', {
                    onclick: function(){
                        utils.loadMore(vm)
                            .then(function(data) {
                                utils.updateVM(vm, data);
                            });
                    }
                }, 'More') : [])
            ))
        ]);

    }
};

var Result = {
    view: function(ctrl, params) {
        return m( '.animated.fadeInUp', [
            m('div', [
                m('h4', [
                    m.component(TitleBar, params)
                ]),
                m('.row', [
                    m('.col-md-7',
                      m('span.pull-left',
                        m.component(Contributors, params)
                      )
                    ),
                    m('.col-md-5',
                        m('.pull-right',
                          {style: {'text-align': 'right'}},
                          m.component(Subjects, params)
                        )
                    )
                ]),
                m('br'),
                m.component(Footer, params)
            ]),
            m('hr')
        ]);

    }
};

var TitleBar = {
    view: function(ctrl, params) {
        var result = params.result;
        return m('span', {}, [
            m('a[href=' + result.uris.canonicalUri + ']', ((result.title || 'No title provided'))),
            m('br'),
            m.component(Description, params)
        ]);
    }
};

/* Render the description of a single result. Will highlight the matched text */
var Description = {
    controller: function(vm) {
        var self = this;
        self.showAll = m.prop(false);
    },
    view: function(ctrl, params) {
        var result = params.result;
        var showOnClick = function() {
            ctrl.showAll(!ctrl.showAll());
        };
        if ((result.description || '').length > 350) {
            return m('', [
                m('p.readable.pointer', {onclick: showOnClick},
                    ctrl.showAll() ? result.description : $.truncate(result.description, {length: 350})
                ),
                m('a.sr-only', {href: '#', onclick: showOnClick}, ctrl.showAll() ? 'See less' : 'See more')]);
        } else {
            return m('p.readable', result.description);
        }
    }
};

var Contributors = {
    controller: function(vm) {
        var self = this;
        self.showAll = false;
    },
    view: function(ctrl, params) {
        var result = params.result;
        var contributorViews = $.map(result.contributors, function(contributor, i) {
                return m.component(Contributor, $.extend({contributor: contributor, index: i}, params));
            });

        return m('span.pull-left', {style: {'text-align': 'left'}},
            ctrl.showAll || result.contributors.length < 8 ?
                contributorViews :
                m('span', [
                    contributorViews.slice(0, 7),
                    m('br'),
                    m('a', {onclick: function(){ctrl.showAll = !ctrl.showAll;}}, 'See All')
                ])
        );

    }
};

var Contributor = {
    view: function(ctrl, params) {
        var contributor = params.contributor;
        var index = params.index;
        var vm = params.vm;
        return m('span', [
            m('span', index !== 0 ? ' · ' : ''),
            m('a', {
                href: '#',
                onclick: function() {
                    utils.updateFilter(vm, 'match:contributors.familyName:' + contributor.familyName, true);
                    utils.updateFilter(vm, 'match:contributors.givenName:' + contributor.givenName, true);
                }
            }, contributor.name)
        ]);
    }
};

var Subjects = {
    controller: function(vm) {
        var self = this;
        self.showAll = false;
    },
    view: function(ctrl, params){
        var result = params.result;
        var subjectViews = $.map(result.subjects || [], function(subject, i) {
            return m.component(Subject, $.extend({subject: subject}, params));
        });
        if (ctrl.showAll || (result.subjects || []).length <= 5) {
            return m('span', subjectViews);
        }
        return m('span', [
            subjectViews.slice(0, 5),
            m('br'),
            m('div', m('a', {href: '#', onclick: function() {ctrl.showAll = !ctrl.showAll;}},'See All'))
        ]);

    }
};

var Subject = {
    view: function(ctrl, params) {
        var subject = params.subject;
        var vm = params.vm;
        return m('span', m('a.badge.pointer', {onclick: function(){
                utils.updateFilter(vm, 'match:subjects:"' + subject + '"', true);
            }}, $.truncate(subject, {length: 50}), ' '
        ));
    }
};

var Footer = {
    controller: function(params) {
        var self = this;
        this.result = params.result;
        this.cleanResult = m.prop(null);
        this.missingError = m.prop(null);
        this.showRawNormalized = m.prop(false);
        this.loadRawNormalized = function() {
            utils.loadRawNormalized(this.result).then(
                function(cleanData) {
                    self.cleanResult(cleanData);
                }, function(error) {
                    self.missingError(error);
                }
            );
        };
    },
    view: function(ctrl, params) {
        var result = params.result;
        var vm = params.vm;
        return m('div', [
            m('span.text-muted',
                'Released on ' + new $osf.FormattableDate(result.providerUpdatedDateTime).local,
                vm.rawNormedLoaded() ?  m('span', [
                    m('span', {style: {'margin-right': '5px', 'margin-left': '5px'}}, ' | '),
                    m('a', {
                        onclick: function() {
                            ctrl.showRawNormalized(!ctrl.showRawNormalized());
                            ctrl.loadRawNormalized();
                        }
                    }, 'Data')
                ]) : m('span', [])
            ),
            m('span.pull-right', [
                m('img', {src: vm.ProviderMap[result.shareProperties.source].favicon, alt: 'favicon for ' + result.shareProperties.source, style: {width: '16px', height: '16px'}}),
                ' ',
                m('a', {onclick: function() {utils.updateFilter(vm, 'shareProperties.source:' + result.shareProperties.source);}}, vm.ProviderMap[result.shareProperties.source].long_name),
                m('br')
            ]),
            ctrl.showRawNormalized() ? m.component(RawNormalizedData, {result: ctrl.cleanResult(), missingError: ctrl.missingError()}) : '',
        ]);
    }
};

var RawNormalizedData = {
    view: function(ctrl, params) {
        var result = params.result || params.missingError;
        var divID = params.missingError ? '' : (result.normalized.shareProperties.docID + result.normalized.shareProperties.source).replace( /(:|\.|\[|\]|,)/g, '-' );
        return m('.row', [
            m('.col-md-12',
                m('div', [
                    m('ul', {className: 'nav nav-tabs'}, [
                        m('li', m('a', {href: '#raw' + divID, 'data-toggle': 'tab'}, 'Raw')),
                        m('li', m('a', {href: '#normalized' + divID, 'data-toggle': 'tab'}, 'Normalized'))
                    ]),
                    m('div', {className: 'tab-content'},
                        m('div',
                            {className: 'tab-pane active', id:'raw' + divID},
                            m('pre',
                                (function(){
                                    if (result.rawfiletype === 'xml') {
                                        return pd.xml(result.raw);
                                    }
                                    else {
                                        var rawjson = JSON.parse(result.raw);
                                        return JSON.stringify(rawjson, undefined, 2);
                                    }
                                }())
                            )
                        ),
                        m('div',
                            {className: 'tab-pane', id:'normalized' + divID},
                            m('pre',
                                (function(){
                                    return JSON.stringify(result.normalized, undefined, 2);
                                }())
                            )
                        )
                    )
                ])
            )
        ]);
    }
};

module.exports = Results;
<|endoftext|>


<commit_before>/**
  * @constructor
  * @extends BaseEditor
  */

var DOMMarkupEditor = function()
{
  this.base_init(this);
  this.type = "dom-markup-editor";
  this.domnodeserializer = new cls.DOMSerializer();
  // specific context
  this.context_enter =
  {
    rt_id: '',
    obj_id: '',
    parent_obj_id: '',
    outerHTML: '',
    host_target: ''
  }

  this.context_cur =
  {
    rt_id: '',
    obj_id: '',
    parent_obj_id: '',
    outerHTML: '',
    host_target: ''
  }

  const
  STATUS = 0,
  OBJECT_VALUE = 3,
  OBJECT_ID = 0,
  NODE_LIST = 0;

  this.edit = function(event, ref_ele)
  {
    var
    ele = ref_ele || event.target,
    rt_id = parseInt(ele.get_attr('parent-node-chain', 'rt-id')),
    obj_id = parseInt(ele.parentElement.getAttribute('ref-id')),
    model_id = ele.get_attr('parent-node-chain', 'data-model-id'),
    script = '',
    tag = '',
    prop = '',
    container = ele,
    model = null,
    dom = null,
    cb = null;

    while( container
            && !/container/i.test(container.nodeName)
            && ( container = container.parentElement ) );
    if(container && model_id)
    {
      model = window.dominspections[model_id];
      this.context_enter =
      {
        rt_id: rt_id,
        obj_id: obj_id,
        parent_obj_id: model.getParentElement(obj_id),
        outerHTML: '',
        host_target: '',
        model: model
      };
      this.context_cur = {};
      for( prop in this.context_enter )
      {
        this.context_cur[prop] = this.context_enter[prop];
      }
      script = this["return new Host_updater(target)"];
      tag = tagManager.set_callback(this, this.register_host_updater, [rt_id]);
      services['ecmascript-debugger'].requestEval(tag, [rt_id, 0, 0, script, [['target', obj_id]]]);
      dom = new cls.InspectableDOMNode(rt_id, obj_id);
      cb = this.handle_get_outer_html.bind(this, dom, rt_id, obj_id, ele, event);
      dom.expand(cb, obj_id, "subtree");
    }
  }

  this.oninput = function(event)
  {
    var
    script = "",
    state = this.context_cur;

    window.hostspotlighter.clearSpotlight();

    if( this.textarea_container.parentElement && state.host_target )
    {
      this.set_textarea_dimensions();
      state.outerHTML = this.textarea.value;
      var script = "host_target.outerHTML = '" + encode(state.outerHTML) + "';";
      services['ecmascript-debugger'].requestEval
      (
        0, [state.rt_id, 0, 0, script, [['host_target', state.host_target]]]
      );
    }
  }

  this.submit = function()
  {
    // return a valid navigation target or null
    var
    state = this.context_cur,
    nav_target = this.textarea_container.parentElement;

    if(nav_target)
    {
      var tag = tagManager.set_callback(this, this.on_exit_edit, [state, nav_target]);
      var script = "host_target.exit_edit()";
      services['ecmascript-debugger'].requestEval
      (
        tag, [state.rt_id, 0, 0, script, [['host_target', state.host_target]]]
      );
    }
    return nav_target;
  }

  this.cancel = function()
  {
    // return a valid navigation target or null.
    // this is aproblem. the view will be updated anyway.
    var
    script = "",
    state = this.context_enter,
    nav_target = this.textarea_container.parentElement;

    if( nav_target )
    {
      if(state.outerHTML != this.context_cur.outerHTML)
      {
        var tag = tagManager.set_callback(this, this.on_exit_edit, [state, nav_target]);
        var script = "host_target.cancel_edit()";
        services['ecmascript-debugger'].requestEval
        (
          tag, [state.rt_id, 0, 0, script, [['host_target', state.host_target]]]
        );
      }
      else
      {
        nav_target.textContent = "";
        this.context_cur = this.context_enter = null;
        views.dom.update();
      }
    }
    return nav_target;
  }

  this.nav_next = function()
  {
    var
    val = this.textarea.value,
    start = this.textarea.selectionStart,
    end = this.textarea.selectionEnd,
    indent = '  ';

    this.textarea.value = val.slice(0, start) + indent +  val.slice(end);
    this.textarea.selectionStart = start + indent.length;
    this.textarea.selectionEnd = start + indent.length;
  }

  this.set_textarea_dimensions = function()
  {
    this.textarea.style.height = this.textarea.scrollHeight + 'px';
  }

  this.__is_active = function()
  {
    return this.context_enter && this.context_enter.host_target && true || false;
  }

  // could be the default method?
  this.onclick = function(event)
  {
    if (!this.textarea_container.contains(event.target))
    {
      this.submit(true);
      return false;
    }
    return true;
  }

  // helpers

  this.on_exit_edit = function(status, message, state, nav_target)
  {
    if( message[STATUS] == 'completed' )
    {
      var scroll_position = new Element.ScrollPosition(nav_target);
      var cb = function()
      {
        window.views.dom.update();
        scroll_position.reset(document.getElementById('target-element'));
        nav_target.textContent = "";
      }
      this.context_enter.model.collapse(state.parent_obj_id);
      this.context_enter.model.expand(cb, state.parent_obj_id, 'children');
      this.context_cur = this.context_enter = null;
    }
    else
    {
      opera.postError(ui_strings.DRAGONFLY_INFO_MESSAGE +
        'exit markup edit failed in DOMMarkupEditor');
    }
  };

  var encode = function(str)
  {
    return str.replace(/\r\n/g, "\\n").replace(/'/g, "\\'");
  };

  // class on the host side to update the given DOM range
  var Host_updater = function(target)
  {
    var
    range_target = document.createRange(),
    timeout = 0,
    new_str = '',
    enter_node = null,
    namespace = target.namespaceURI,
    scripts = null,
    script = null,
    script_type_attr = '',
    i = 0,
    update = function(str)
    {
      if( enter_node )
      {
        range_target.deleteContents();
      }
      else
      {
        enter_node = range_target.extractContents();
      };
      var range_source = document.createRange();
      var temp = null;
      if( document.documentElement )
      {
        temp = namespace
                ? document.createElementNS(namespace, 'df-temp-element')
                : document.createElement('df-temp-element');
        ( document.body || document.documentElement ).appendChild(temp);
        if(typeof temp.innerHTML == 'string')
        {
          temp.innerHTML = new_str;
        }
        else
        {
          innerXML(temp, new_str)
        }
      }
      else
      {
        var new_source = lexer.lex(new_str), tag = new_source.length && new_source[0];

        if(tag)
        {
          var attrs = tag[2], i = 0, attr = null;
          for( ; ( attr = attrs[i] ) && attr[0] != 'xmlns'; i++);
          if( attr )
          {
            attrs.splice(i, 1);
            temp = document.createElementNS(attr[1], tag[1]);
          }
          else
          {
            temp = document.createElement(tag[1]);
          }
          for( i = 0 ; attr = attrs[i]; i++)
          {
            temp.setAttribute(attr[0], attr[1]);
          }
          document.appendChild(temp);
          if(typeof temp.innerHTML == 'string')
          {
            temp.innerHTML = new_source[1];
          }
          else
          {
            innerXML(temp, new_source[1].replace(new RegExp('</' + tag[1] + '>$'), ''))
          }
          range_target.selectNode(document.documentElement);
          temp = null;
        }
      };
      for( scripts = (temp || document).getElementsByTagName('script'), i = 0; script = scripts[i]; i++)
      {
        script_attr = script.getAttribute('type');
        script.setAttribute('type', ( script_attr ? script_attr + '/' : '' ) + 'edited');
      }
      var first =  temp && temp.firstChild;
      var last = temp && temp.lastChild;
      if(first)
      {
        range_source.selectNodeContents(temp);
        var fragment = range_source.extractContents();
        range_target.insertNode(fragment);
        range_target.setStartBefore(first);
        range_target.setEndAfter(last);

      };
      clean_up_temp();
      timeout = 0;
    },
    lexer = new function()
    {
      /* adjusted to get only the first tag */
      const TEXT = 'TEXT', TAG = 'TAG';
      var
      source = '',
      cur = '',
      pos = 0,
      text = function()
      {
        var ret = [], buffer = '', _tag = null;
        while( cur )
        {
          if( cur == '<' )
          {
            cur = source.charAt(pos++);
            if(buffer)
            {
              ret[ret.length] = [TEXT, buffer];
              buffer = '';
            }
            _tag = tag();
            if( _tag && !/[!?]/.test(_tag[1].charAt(0)) )
            {
              return [_tag, source.slice(pos-1)];
            }
            continue;
          }
          buffer += cur;
          cur = source.charAt(pos++);
        }
        return ret;
      },
      tag = function()
      {
        var buffer = '', attrs = [], has_attrs = false;
        while( cur )
        {
          if( cur == '>' )
          {
            cur = source.charAt(pos++);
            return [TAG, buffer, attrs];
          }
          if( cur == ' ' )
          {
            cur = source.charAt(pos++);
            has_attrs = true;
            continue;
          }
          if( has_attrs )
          {
            attrs = attr_key();
            continue;
          }
          buffer += cur;
          cur = source.charAt(pos++);
        }
      },
      attr_key = function()
      {
        var attrs = [], attr_key = '', buffer = '';
        while( cur )
        {
          if( cur == ' ' )
          {
            if(buffer)
            {
              attr_key = buffer;
              buffer = '';
            }
            cur = source.charAt(pos++);
            continue;
          }
          if( cur == '=' )
          {
            if(buffer)
            {
              attr_key = buffer;
              buffer = '';
            }
            cur = source.charAt(pos++);
            attrs[attrs.length] = [attr_key, set_attr_delimiter()];
            attr_key = '';
            continue;
          }
          if( cur == '>' )
          {
            if(buffer)
            {
              attr_key = buffer;
              buffer = '';
            }
            if(attr_key)
            {
              attrs[attrs.length] = [attr_key];
            }
            return attrs;
          }
          buffer += cur;
          cur = source.charAt(pos++);
        }
      },
      set_attr_delimiter = function()
      {
        var delimiter = '';
        while( cur )
        {
          if( cur == ' ' )
          {
            cur = source.charAt(pos++);
            continue;
          }
          if( cur == '\'' || cur == '"' )
          {
            delimiter = cur;
            cur = source.charAt(pos++);
            return attr_value(delimiter);
          }
          return attr_value(' ');
        }
      },
      attr_value = function(delimiter)
      {
        var buffer = '';
        while(cur)
        {
          if( cur == delimiter )
          {
            cur = source.charAt(pos++);
            return buffer;
          }
          if( cur == '>' && delimiter == ' ' )
          {
            return buffer;
          }
          buffer += cur;
          cur = source.charAt(pos++);
        }
      };

      this.lex = function(_source)
      {
        source = _source;
        pos = 0;
        cur = source.charAt(pos++);
        return text();
      }
    },
    innerXML = function(ele, str)
    {
      var
      parser = new DOMParser(),
      source = "<temp-root xmlns=\"" + namespace + "\">" + str + "</temp-root>",
      doc = parser.parseFromString(source, "application/xml");

      if( doc.documentElement.nodeName.toLowerCase() == "temp-root" )
      {
        range = doc.createRange();
        range.selectNodeContents(doc.documentElement);
        ele.appendChild(range.extractContents());
      }
      else
      {
        ele.textContent = "<parseerror/>";
      }
    },
    clean_up_temp = function()
    {
      var temp = null;
      while (temp = document.getElementsByTagName('df-temp-element')[0])
      {
        temp.parentNode.removeChild(temp);
      }
    };

    range_target.selectNode(target);
    this.__defineSetter__
    (
      "outerHTML",
      function(str)
      {
        new_str = str;
        timeout || ( timeout = setTimeout(update, 100) );
      }
    );
    this.cancel_edit = function()
    {
      timeout && ( timeout = clearTimeout(timeout) );
      if(enter_node)
      {
        range_target.deleteContents();
        range_target.insertNode(enter_node);
      }
      this.exit_edit();
    };
    this.exit_edit = function()
    {
      if( timeout )
      {
        timeout = clearTimeout(timeout);
        update();
      };
      clean_up_temp();
      lexer = range_target = timeout = new_str = enter_node = update = null;
    };
  };

  this["return new Host_updater(target)"] =
    "return new (" + Host_updater.toString() + ")(target)";

  this.register_host_updater = function(status, message, rt_id)
  {
    var
    status = message[STATUS],
    obj_id = message[OBJECT_VALUE][OBJECT_ID];

    if(  status == 'completed' && obj_id )
    {
      this.context_enter.host_target = this.context_cur.host_target = obj_id;
    }
    else
    {
      opera.postError(ui_strings.DRAGONFLY_INFO_MESSAGE +
        "failed register_host_updater in DOMMarkupEditor");
    }
  }

  // complete the edit call
  this.handle_get_outer_html = function(dom, rt_id, obj_id, ele, event)
  {
    var
    outerHTML = this.domnodeserializer.serialize(dom),
    parent = ele.parentNode,
    parent_parent = parent.parentElement,
    margin = parseInt(parent.style.marginLeft),
    next = null;
    this.context_enter.outerHTML = this.context_cur.outerHTML = outerHTML;
    if( !this.base_style['font-size'] )
    {
      this.get_base_style(ele);
    }
    // this should never be needed
    if( this.textarea_container.parentElement )
    {
      opera.postError(ui_strings.DRAGONFLY_INFO_MESSAGE +
        "this.textarea_container.parentElement is not null in submit");
    }
    this.textarea.value = outerHTML;
    var scroll_position = new Element.ScrollPosition(parent);
    parent.innerHTML = "";
    parent.appendChild(this.textarea_container);
    while( ( next = parent.nextElementSibling ) && parseInt(next.style.marginLeft) > margin )
    {
      parent_parent.removeChild(next);
    };
    if( next && parseInt(next.style.marginLeft) == margin && /<\//.test(next.textContent) )
    {
      parent_parent.removeChild(next);
    }
    this.set_textarea_dimensions();
    // only for click events
    if( event )
    {
      this.textarea.focus();
    }
    scroll_position.reset(null, this.textarea);
    this.textarea.selectionEnd = this.textarea.selectionStart = 0;
    // it seems it needs to be set twice to get set correctly
    this.set_textarea_dimensions();
  }
}

DOMMarkupEditor.prototype = BaseEditor;
<commit_msg>Make sure monospace font changes takes effect in markup editor.<commit_after>/**
  * @constructor
  * @extends BaseEditor
  */

var DOMMarkupEditor = function()
{
  this.base_init(this);
  this.type = "dom-markup-editor";
  this.domnodeserializer = new cls.DOMSerializer();
  // specific context
  this.context_enter =
  {
    rt_id: '',
    obj_id: '',
    parent_obj_id: '',
    outerHTML: '',
    host_target: ''
  }

  this.context_cur =
  {
    rt_id: '',
    obj_id: '',
    parent_obj_id: '',
    outerHTML: '',
    host_target: ''
  }

  const
  STATUS = 0,
  OBJECT_VALUE = 3,
  OBJECT_ID = 0,
  NODE_LIST = 0;

  this.edit = function(event, ref_ele)
  {
    var
    ele = ref_ele || event.target,
    rt_id = parseInt(ele.get_attr('parent-node-chain', 'rt-id')),
    obj_id = parseInt(ele.parentElement.getAttribute('ref-id')),
    model_id = ele.get_attr('parent-node-chain', 'data-model-id'),
    script = '',
    tag = '',
    prop = '',
    container = ele,
    model = null,
    dom = null,
    cb = null;

    while( container
            && !/container/i.test(container.nodeName)
            && ( container = container.parentElement ) );
    if(container && model_id)
    {
      model = window.dominspections[model_id];
      this.context_enter =
      {
        rt_id: rt_id,
        obj_id: obj_id,
        parent_obj_id: model.getParentElement(obj_id),
        outerHTML: '',
        host_target: '',
        model: model
      };
      this.context_cur = {};
      for( prop in this.context_enter )
      {
        this.context_cur[prop] = this.context_enter[prop];
      }
      script = this["return new Host_updater(target)"];
      tag = tagManager.set_callback(this, this.register_host_updater, [rt_id]);
      services['ecmascript-debugger'].requestEval(tag, [rt_id, 0, 0, script, [['target', obj_id]]]);
      dom = new cls.InspectableDOMNode(rt_id, obj_id);
      cb = this.handle_get_outer_html.bind(this, dom, rt_id, obj_id, ele, event);
      dom.expand(cb, obj_id, "subtree");
    }
  }

  this.oninput = function(event)
  {
    var
    script = "",
    state = this.context_cur;

    window.hostspotlighter.clearSpotlight();

    if( this.textarea_container.parentElement && state.host_target )
    {
      this.set_textarea_dimensions();
      state.outerHTML = this.textarea.value;
      var script = "host_target.outerHTML = '" + encode(state.outerHTML) + "';";
      services['ecmascript-debugger'].requestEval
      (
        0, [state.rt_id, 0, 0, script, [['host_target', state.host_target]]]
      );
    }
  }

  this.submit = function()
  {
    // return a valid navigation target or null
    var
    state = this.context_cur,
    nav_target = this.textarea_container.parentElement;

    if(nav_target)
    {
      var tag = tagManager.set_callback(this, this.on_exit_edit, [state, nav_target]);
      var script = "host_target.exit_edit()";
      services['ecmascript-debugger'].requestEval
      (
        tag, [state.rt_id, 0, 0, script, [['host_target', state.host_target]]]
      );
    }
    return nav_target;
  }

  this.cancel = function()
  {
    // return a valid navigation target or null.
    // this is aproblem. the view will be updated anyway.
    var
    script = "",
    state = this.context_enter,
    nav_target = this.textarea_container.parentElement;

    if( nav_target )
    {
      if(state.outerHTML != this.context_cur.outerHTML)
      {
        var tag = tagManager.set_callback(this, this.on_exit_edit, [state, nav_target]);
        var script = "host_target.cancel_edit()";
        services['ecmascript-debugger'].requestEval
        (
          tag, [state.rt_id, 0, 0, script, [['host_target', state.host_target]]]
        );
      }
      else
      {
        nav_target.textContent = "";
        this.context_cur = this.context_enter = null;
        views.dom.update();
      }
    }
    return nav_target;
  }

  this.nav_next = function()
  {
    var
    val = this.textarea.value,
    start = this.textarea.selectionStart,
    end = this.textarea.selectionEnd,
    indent = '  ';

    this.textarea.value = val.slice(0, start) + indent +  val.slice(end);
    this.textarea.selectionStart = start + indent.length;
    this.textarea.selectionEnd = start + indent.length;
  }

  this.set_textarea_dimensions = function()
  {
    this.textarea.style.height = this.textarea.scrollHeight + 'px';
  }

  this.__is_active = function()
  {
    return this.context_enter && this.context_enter.host_target && true || false;
  }

  // could be the default method?
  this.onclick = function(event)
  {
    if (!this.textarea_container.contains(event.target))
    {
      this.submit(true);
      return false;
    }
    return true;
  }

  // helpers

  this.on_exit_edit = function(status, message, state, nav_target)
  {
    if( message[STATUS] == 'completed' )
    {
      var scroll_position = new Element.ScrollPosition(nav_target);
      var cb = function()
      {
        window.views.dom.update();
        scroll_position.reset(document.getElementById('target-element'));
        nav_target.textContent = "";
      }
      this.context_enter.model.collapse(state.parent_obj_id);
      this.context_enter.model.expand(cb, state.parent_obj_id, 'children');
      this.context_cur = this.context_enter = null;
    }
    else
    {
      opera.postError(ui_strings.DRAGONFLY_INFO_MESSAGE +
        'exit markup edit failed in DOMMarkupEditor');
    }
  };

  var encode = function(str)
  {
    return str.replace(/\r\n/g, "\\n").replace(/'/g, "\\'");
  };

  // class on the host side to update the given DOM range
  var Host_updater = function(target)
  {
    var
    range_target = document.createRange(),
    timeout = 0,
    new_str = '',
    enter_node = null,
    namespace = target.namespaceURI,
    scripts = null,
    script = null,
    script_type_attr = '',
    i = 0,
    update = function(str)
    {
      if( enter_node )
      {
        range_target.deleteContents();
      }
      else
      {
        enter_node = range_target.extractContents();
      };
      var range_source = document.createRange();
      var temp = null;
      if( document.documentElement )
      {
        temp = namespace
                ? document.createElementNS(namespace, 'df-temp-element')
                : document.createElement('df-temp-element');
        ( document.body || document.documentElement ).appendChild(temp);
        if(typeof temp.innerHTML == 'string')
        {
          temp.innerHTML = new_str;
        }
        else
        {
          innerXML(temp, new_str)
        }
      }
      else
      {
        var new_source = lexer.lex(new_str), tag = new_source.length && new_source[0];

        if(tag)
        {
          var attrs = tag[2], i = 0, attr = null;
          for( ; ( attr = attrs[i] ) && attr[0] != 'xmlns'; i++);
          if( attr )
          {
            attrs.splice(i, 1);
            temp = document.createElementNS(attr[1], tag[1]);
          }
          else
          {
            temp = document.createElement(tag[1]);
          }
          for( i = 0 ; attr = attrs[i]; i++)
          {
            temp.setAttribute(attr[0], attr[1]);
          }
          document.appendChild(temp);
          if(typeof temp.innerHTML == 'string')
          {
            temp.innerHTML = new_source[1];
          }
          else
          {
            innerXML(temp, new_source[1].replace(new RegExp('</' + tag[1] + '>$'), ''))
          }
          range_target.selectNode(document.documentElement);
          temp = null;
        }
      };
      for( scripts = (temp || document).getElementsByTagName('script'), i = 0; script = scripts[i]; i++)
      {
        script_attr = script.getAttribute('type');
        script.setAttribute('type', ( script_attr ? script_attr + '/' : '' ) + 'edited');
      }
      var first =  temp && temp.firstChild;
      var last = temp && temp.lastChild;
      if(first)
      {
        range_source.selectNodeContents(temp);
        var fragment = range_source.extractContents();
        range_target.insertNode(fragment);
        range_target.setStartBefore(first);
        range_target.setEndAfter(last);

      };
      clean_up_temp();
      timeout = 0;
    },
    lexer = new function()
    {
      /* adjusted to get only the first tag */
      const TEXT = 'TEXT', TAG = 'TAG';
      var
      source = '',
      cur = '',
      pos = 0,
      text = function()
      {
        var ret = [], buffer = '', _tag = null;
        while( cur )
        {
          if( cur == '<' )
          {
            cur = source.charAt(pos++);
            if(buffer)
            {
              ret[ret.length] = [TEXT, buffer];
              buffer = '';
            }
            _tag = tag();
            if( _tag && !/[!?]/.test(_tag[1].charAt(0)) )
            {
              return [_tag, source.slice(pos-1)];
            }
            continue;
          }
          buffer += cur;
          cur = source.charAt(pos++);
        }
        return ret;
      },
      tag = function()
      {
        var buffer = '', attrs = [], has_attrs = false;
        while( cur )
        {
          if( cur == '>' )
          {
            cur = source.charAt(pos++);
            return [TAG, buffer, attrs];
          }
          if( cur == ' ' )
          {
            cur = source.charAt(pos++);
            has_attrs = true;
            continue;
          }
          if( has_attrs )
          {
            attrs = attr_key();
            continue;
          }
          buffer += cur;
          cur = source.charAt(pos++);
        }
      },
      attr_key = function()
      {
        var attrs = [], attr_key = '', buffer = '';
        while( cur )
        {
          if( cur == ' ' )
          {
            if(buffer)
            {
              attr_key = buffer;
              buffer = '';
            }
            cur = source.charAt(pos++);
            continue;
          }
          if( cur == '=' )
          {
            if(buffer)
            {
              attr_key = buffer;
              buffer = '';
            }
            cur = source.charAt(pos++);
            attrs[attrs.length] = [attr_key, set_attr_delimiter()];
            attr_key = '';
            continue;
          }
          if( cur == '>' )
          {
            if(buffer)
            {
              attr_key = buffer;
              buffer = '';
            }
            if(attr_key)
            {
              attrs[attrs.length] = [attr_key];
            }
            return attrs;
          }
          buffer += cur;
          cur = source.charAt(pos++);
        }
      },
      set_attr_delimiter = function()
      {
        var delimiter = '';
        while( cur )
        {
          if( cur == ' ' )
          {
            cur = source.charAt(pos++);
            continue;
          }
          if( cur == '\'' || cur == '"' )
          {
            delimiter = cur;
            cur = source.charAt(pos++);
            return attr_value(delimiter);
          }
          return attr_value(' ');
        }
      },
      attr_value = function(delimiter)
      {
        var buffer = '';
        while(cur)
        {
          if( cur == delimiter )
          {
            cur = source.charAt(pos++);
            return buffer;
          }
          if( cur == '>' && delimiter == ' ' )
          {
            return buffer;
          }
          buffer += cur;
          cur = source.charAt(pos++);
        }
      };

      this.lex = function(_source)
      {
        source = _source;
        pos = 0;
        cur = source.charAt(pos++);
        return text();
      }
    },
    innerXML = function(ele, str)
    {
      var
      parser = new DOMParser(),
      source = "<temp-root xmlns=\"" + namespace + "\">" + str + "</temp-root>",
      doc = parser.parseFromString(source, "application/xml");

      if( doc.documentElement.nodeName.toLowerCase() == "temp-root" )
      {
        range = doc.createRange();
        range.selectNodeContents(doc.documentElement);
        ele.appendChild(range.extractContents());
      }
      else
      {
        ele.textContent = "<parseerror/>";
      }
    },
    clean_up_temp = function()
    {
      var temp = null;
      while (temp = document.getElementsByTagName('df-temp-element')[0])
      {
        temp.parentNode.removeChild(temp);
      }
    };

    range_target.selectNode(target);
    this.__defineSetter__
    (
      "outerHTML",
      function(str)
      {
        new_str = str;
        timeout || ( timeout = setTimeout(update, 100) );
      }
    );
    this.cancel_edit = function()
    {
      timeout && ( timeout = clearTimeout(timeout) );
      if(enter_node)
      {
        range_target.deleteContents();
        range_target.insertNode(enter_node);
      }
      this.exit_edit();
    };
    this.exit_edit = function()
    {
      if( timeout )
      {
        timeout = clearTimeout(timeout);
        update();
      };
      clean_up_temp();
      lexer = range_target = timeout = new_str = enter_node = update = null;
    };
  };

  this["return new Host_updater(target)"] =
    "return new (" + Host_updater.toString() + ")(target)";

  this.register_host_updater = function(status, message, rt_id)
  {
    var
    status = message[STATUS],
    obj_id = message[OBJECT_VALUE][OBJECT_ID];

    if(  status == 'completed' && obj_id )
    {
      this.context_enter.host_target = this.context_cur.host_target = obj_id;
    }
    else
    {
      opera.postError(ui_strings.DRAGONFLY_INFO_MESSAGE +
        "failed register_host_updater in DOMMarkupEditor");
    }
  }

  // complete the edit call
  this.handle_get_outer_html = function(dom, rt_id, obj_id, ele, event)
  {
    var
    outerHTML = this.domnodeserializer.serialize(dom),
    parent = ele.parentNode,
    parent_parent = parent.parentElement,
    margin = parseInt(parent.style.marginLeft),
    next = null;
    this.context_enter.outerHTML = this.context_cur.outerHTML = outerHTML;
    if( !this.base_style['font-size'] )
    {
      this.get_base_style(ele);
    }
    // this should never be needed
    if( this.textarea_container.parentElement )
    {
      opera.postError(ui_strings.DRAGONFLY_INFO_MESSAGE +
        "this.textarea_container.parentElement is not null in submit");
    }
    this.textarea.value = outerHTML;
    var scroll_position = new Element.ScrollPosition(parent);
    parent.innerHTML = "";
    parent.appendChild(this.textarea_container);
    while( ( next = parent.nextElementSibling ) && parseInt(next.style.marginLeft) > margin )
    {
      parent_parent.removeChild(next);
    };
    if( next && parseInt(next.style.marginLeft) == margin && /<\//.test(next.textContent) )
    {
      parent_parent.removeChild(next);
    }
    this.set_textarea_dimensions();
    // only for click events
    if( event )
    {
      this.textarea.focus();
    }
    scroll_position.reset(null, this.textarea);
    this.textarea.selectionEnd = this.textarea.selectionStart = 0;
    // it seems it needs to be set twice to get set correctly
    this.set_textarea_dimensions();
  }

  this._onmonospacefontchange = function(msg)
  {
    this.base_style['font-size'] = 0;
  }

  messages.addListener('monospace-font-changed',
                       this._onmonospacefontchange.bind(this));
}

DOMMarkupEditor.prototype = BaseEditor;
<|endoftext|>


<commit_before>/*
 * Feature: OEmbed
 *
 *   As an embedly user
 *   I want to call the the embedly api
 *   Because I want and oembed for a specific url
 */
require.paths.unshift(require('path').join(__dirname, '../lib'))

var vows = require('vows')
  , embedly = require('embedly')
  , require_either = embedly.utils.require_either
  , assert = require('assert')
  , Hash = require_either('hashish', 'traverse/hash')
  , util = require_either('util', 'utils')

/*
 * HELPERS
 */
function canonize_value(val) {
  return (typeof(val) == 'undefined' || val == null) ?
           ''
         : val
}

/*
 * assert that a response obj arrays properties (names) match
 * an array of expected values.  name can be a period seperated
 * hierarchy of properties.  If the response object is known to
 * be an array of length == 1, or we want to match the same expected
 * value to each obj, then expected can be a string value.
 */
function assertObjValue(name, expected) {
  return function(objs) {
    var expect = expected
      , isString = typeof(expected) == 'string'

    if (typeof(expected) == 'string') {
      assert.ok(objs.length >= 1)
    } else {
      assert.ok(expected.length <= objs.length)
    }

    objs.forEach(function(obj,i) {
      // if expected is a string, then the same expected
      // value should be used against all objects
      if (!isString) { expect = expected[i] }
      // this traverses the object hierarchy with name
      var value = name.split('.').reduce(function(o, field) {
        return o[field]
      }, obj)
      assert.equal(canonize_value(value), expect)
    })
  }
}

/*
 *  Scenario Outline: Get the provider_url
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL
 *      Then the provider_url should be <provider_url>
 */
var oembed_provider_url_vows = {}
/*
 * Examples:
 ** url                                             | provider_url            */
;[" http://www.scribd.com/doc/13994900/Easter       | http://www.scribd.com/  "
, " http://www.scribd.com/doc/28452730/Easter-Cards | http://www.scribd.com/  "
, " http://www.youtube.com/watch?v=Zk7dDekYej0      | http://www.youtube.com/ "
, " http://tweetphoto.com/14784358                  | http://plixi.com        "
].forEach(function(line) {
  var parts = line.split('|')
    , url = parts[0].trim()
    , expected = parts[1].trim()

  oembed_provider_url_vows['when oembed is called for provider with url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected provider_url': assertObjValue('provider_url', expected)
  }
})

/*
 *  Scenario Outline: Get the types
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL
 *      Then the type should be <type>
 */
var oembed_type_vows = {}
/*
 * Examples:
 ** url                                             | type  */
;[" http://www.scribd.com/doc/13994900/Easter       | rich  "
, " http://www.scribd.com/doc/28452730/Easter-Cards | rich  "
, " http://www.youtube.com/watch?v=Zk7dDekYej0      | video "
, " http://tweetphoto.com/14784358                  | photo "
].forEach(function(line) {
  var parts = line.split('|')
    , url = parts[0].trim()
    , expected = parts[1].trim()

  oembed_type_vows['when oembed is called for type with url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected type': assertObjValue('type', expected)
  }
})

/*
 *  Scenario Outline: Get the provider_url with force flag
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL and force flag
 *      Then the provider_url should be <provider_url>
 */
var oembed_provider_with_force_vows = {}
/*
 * Examples:
 ** url                                        | provider_url            */
;[" http://www.youtube.com/watch?v=Zk7dDekYej0 | http://www.youtube.com/ "
].forEach(function(line) {
  var parts = line.split('|')
    , url = parts[0].trim()
    , expected = parts[1].trim()

  oembed_provider_with_force_vows['when oembed is called with force for provider with url '+url] = {
    topic: function (api) {
      return api.oembed({url: url, force: 'true'}).on('complete', this.callback).start()
    }
    , 'reponds with expected provider_url': assertObjValue('provider_url', expected)
  }
})

/*
 *  Scenario Outline: Get multiple provider_urls
 *      Given an embedly endpoint
 *      When oembed is called with the <urls> URLs
 *      Then provider_url should be <provider_urls>
 */
var oembed_multiple_provider_vows = {}
/*
 * Examples:
 ** urls                                                                                | provider_urls                                 */
;[" http://www.scribd.com/doc/13994900/Easter,http://www.scribd.com/doc/28452730/Easter-Cards | http://www.scribd.com/,http://www.scribd.com/ "
, " http://www.youtube.com/watch?v=Zk7dDekYej0,http://plixi.com/p/16044847                    | http://www.youtube.com/,http://plixi.com      "
].forEach(function(line) {
  var parts = line.split('|')
    , urls = parts[0].trim()
    , expected = parts[1].trim()

  oembed_multiple_provider_vows['when oembed is called for provider with multi-urls '+urls] = {
    topic: function (api) {
      return api.oembed({urls: urls.split(','), force: 'true'}).on('complete', this.callback).start()
    }
    , 'reponds with expected provider_url': assertObjValue('provider_url', expected.split(','))
  }
})

/*
 *  Scenario Outline: Get the provider_url with pro
 *      Given an embedly endpoint with key
 *      When oembed is called with the <url> URL
 *      Then the provider_url should be <provider_url>
 */
var oembed_pro_provider_vows = {}
/*
 * Examples:
 ** url                                                                              | provider_url               */
;[" http://blog.embed.ly/bob                                                         | http://posterous.com       "
, " http://blog.doki-pen.org/cassandra-rules                                         | http://posterous.com       "
, " http://www.guardian.co.uk/media/2011/jan/21/andy-coulson-phone-hacking-statement | http://www.guardian.co.uk/ "
].forEach(function(line) {
  var parts = line.split('|')
    , url = parts[0].trim()
    , expected = parts[1].trim()

  oembed_pro_provider_vows['when pro oembed is called for provider with url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected provider_url': assertObjValue('provider_url', expected)
  }
})

/*
 *  Scenario Outline: Attempt to get 404 URL
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL
 *      Then type should be error
 *      And error_code should be 404
 *      And type should be error
 */
var oembed_404_vows = {}
/*
 * Examples:
 ** url                                                       */
;[" http://www.youtube.com/watch/is/a/bad/url                 "
, " http://www.scribd.com/doc/zfldsf/asdfkljlas/klajsdlfkasdf "
, " http://tweetphoto.com/alsdfldsf/asdfkljlas/klajsdlfkasdf  "
].forEach(function(line) {
  var url = line.trim()

  oembed_404_vows['when oembed is called with a 404 url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected error_code': assertObjValue('error_code', '404')
    , 'reponds with expected type': assertObjValue('type', 'error')
  }
})

/*
 *  Scenario Outline: Attempt multi get 404 URLs
 *      Given an embedly endpoint
 *      When oembed is called with the <urls> URLs
 *      Then error_code should be <errcode>
 *      And type should be <types>
 */
var oembed_404_multi_vows = {}
/*      Examples:
 ** urls                                                                             | errcode | types       */
;[" http://www.youtube.com/watch/a/bassd/url,http://www.youtube.com/watch/ldf/asdlfj | 404,404 | error,error "
, " http://www.scribd.com/doc/lsbsdlfldsf/kl,http://www.scribd.com/doc/zasdf/asdfl   | 404,404 | error,error "
, " http://www.youtube.com/watch/zzzzasdf/kl,http://tweetphoto.com/14784358          | 404,    | error,photo "
, " http://tweetphoto.com/14784358,http://www.scribd.com/doc/asdfasdfasdf            | ,404    | photo,error "
].forEach(function(line) {
  var parts = line.split('|')
    , urls = parts[0].trim().split(',')
    , error_codes = parts[1].trim().split(',')
    , types = parts[2].trim().split(',')

  oembed_404_multi_vows['when oembed is called with a 404 urls '+urls.join(',')] = {
    topic: function (api) {
      return api.oembed({urls: urls}).on('complete', this.callback).start()
    }
    , 'reponds with expected error_codes': assertObjValue('error_code', error_codes)
    , 'reponds with expected types': assertObjValue('type', types)
  }
})

/*
 *  Scenario Outline: Attempt at non-api service without key
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL
 *      Then error_code should be 401
 *      And error_message should be This service requires an Embedly Pro account
 *      And type should be error
 */
var oembed_non_provider_vows = {}
/*
 * Examples:
 ** urls                                                                            */
;[" http://hn.embed.ly/                                                             "
, " http://bit.ly/enZRxO                                                            "
, " http://techcrunch.com/2011/02/03/linkedins-next-data-dive-professional-skills/  "
, " http://teachertube.com/rssPhoto.php                                             "
, " http://goo.gl/y1i9p                                                             "
].forEach(function(line) {
  var url = line.trim()

  oembed_non_provider_vows['when oembed is called with a unsupported url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected error_codes': assertObjValue('error_code', '401')
    , 'reponds with expected error_messages': assertObjValue('error_message', 'This service requires an Embedly Pro account')
    , 'reponds with expected types': assertObjValue('type', 'error')
  }
})

/*
 * Build vows
 */
vows.describe('OEmbed').addBatch({
    'An API instance': Hash({
      topic: new(embedly.Api)
    }).
    merge(oembed_provider_url_vows).
    merge(oembed_type_vows).
    merge(oembed_provider_with_force_vows).
    merge(oembed_multiple_provider_vows).
    merge(oembed_404_vows).
    merge(oembed_404_multi_vows).
    merge(oembed_non_provider_vows).
    end
  , 'A Pro API Instance': Hash({
      topic: new(embedly.Api)({key: process.env.EMBEDLY_KEY})
    }).
    merge(oembed_pro_provider_vows).
    end
}).export(module, {error: false})

<commit_msg>update tests<commit_after>/*
 * Feature: OEmbed
 *
 *   As an embedly user
 *   I want to call the the embedly api
 *   Because I want and oembed for a specific url
 */
require.paths.unshift(require('path').join(__dirname, '../lib'))

var vows = require('vows')
  , embedly = require('embedly')
  , require_either = embedly.utils.require_either
  , assert = require('assert')
  , Hash = require_either('hashish', 'traverse/hash')
  , util = require_either('util', 'utils')

/*
 * HELPERS
 */
function canonize_value(val) {
  return (typeof(val) == 'undefined' || val == null) ?
           ''
         : val
}

/*
 * assert that a response obj arrays properties (names) match
 * an array of expected values.  name can be a period seperated
 * hierarchy of properties.  If the response object is known to
 * be an array of length == 1, or we want to match the same expected
 * value to each obj, then expected can be a string value.
 */
function assertObjValue(name, expected) {
  return function(objs) {
    var expect = expected
      , isString = typeof(expected) == 'string'

    if (typeof(expected) == 'string') {
      assert.ok(objs.length >= 1)
    } else {
      assert.ok(expected.length <= objs.length)
    }

    objs.forEach(function(obj,i) {
      // if expected is a string, then the same expected
      // value should be used against all objects
      if (!isString) { expect = expected[i] }
      // this traverses the object hierarchy with name
      var value = name.split('.').reduce(function(o, field) {
        return o[field]
      }, obj)
      assert.equal(canonize_value(value), expect)
    })
  }
}

/*
 *  Scenario Outline: Get the provider_url
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL
 *      Then the provider_url should be <provider_url>
 */
var oembed_provider_url_vows = {}
/*
 * Examples:
 ** url                                             | provider_url            */
;[" http://www.scribd.com/doc/13994900/Easter       | http://www.scribd.com/  "
, " http://www.scribd.com/doc/28452730/Easter-Cards | http://www.scribd.com/  "
, " http://www.youtube.com/watch?v=Zk7dDekYej0      | http://www.youtube.com/ "
, " http://tweetphoto.com/14784358                  | http://plixi.com        "
].forEach(function(line) {
  var parts = line.split('|')
    , url = parts[0].trim()
    , expected = parts[1].trim()

  oembed_provider_url_vows['when oembed is called for provider with url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected provider_url': assertObjValue('provider_url', expected)
  }
})

/*
 *  Scenario Outline: Get the types
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL
 *      Then the type should be <type>
 */
var oembed_type_vows = {}
/*
 * Examples:
 ** url                                             | type  */
;[" http://www.scribd.com/doc/13994900/Easter       | rich  "
, " http://www.scribd.com/doc/28452730/Easter-Cards | rich  "
, " http://www.youtube.com/watch?v=Zk7dDekYej0      | video "
, " http://tweetphoto.com/14784358                  | photo "
].forEach(function(line) {
  var parts = line.split('|')
    , url = parts[0].trim()
    , expected = parts[1].trim()

  oembed_type_vows['when oembed is called for type with url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected type': assertObjValue('type', expected)
  }
})

/*
 *  Scenario Outline: Get the provider_url with force flag
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL and force flag
 *      Then the provider_url should be <provider_url>
 */
var oembed_provider_with_force_vows = {}
/*
 * Examples:
 ** url                                        | provider_url            */
;[" http://www.youtube.com/watch?v=Zk7dDekYej0 | http://www.youtube.com/ "
].forEach(function(line) {
  var parts = line.split('|')
    , url = parts[0].trim()
    , expected = parts[1].trim()

  oembed_provider_with_force_vows['when oembed is called with force for provider with url '+url] = {
    topic: function (api) {
      return api.oembed({url: url, force: 'true'}).on('complete', this.callback).start()
    }
    , 'reponds with expected provider_url': assertObjValue('provider_url', expected)
  }
})

/*
 *  Scenario Outline: Get multiple provider_urls
 *      Given an embedly endpoint
 *      When oembed is called with the <urls> URLs
 *      Then provider_url should be <provider_urls>
 */
var oembed_multiple_provider_vows = {}
/*
 * Examples:
 ** urls                                                                                | provider_urls                                 */
;[" http://www.scribd.com/doc/13994900/Easter,http://www.scribd.com/doc/28452730/Easter-Cards                                                 | http://www.scribd.com/,http://www.scribd.com/ "
, " http://www.youtube.com/watch?v=Zk7dDekYej0,http://confreaks.net/videos/160-rubyconf2009-bert-and-ernie-scaling-your-ruby-site-with-erlang | http://www.youtube.com/,http://confreaks.net  "
].forEach(function(line) {
  var parts = line.split('|')
    , urls = parts[0].trim()
    , expected = parts[1].trim()

  oembed_multiple_provider_vows['when oembed is called for provider with multi-urls '+urls] = {
    topic: function (api) {
      return api.oembed({urls: urls.split(','), force: 'true'}).on('complete', this.callback).start()
    }
    , 'reponds with expected provider_url': assertObjValue('provider_url', expected.split(','))
  }
})

/*
 *  Scenario Outline: Get the provider_url with pro
 *      Given an embedly endpoint with key
 *      When oembed is called with the <url> URL
 *      Then the provider_url should be <provider_url>
 */
var oembed_pro_provider_vows = {}
/*
 * Examples:
 ** url                                                                              | provider_url               */
;[" http://blog.embed.ly/bob                                                         | http://posterous.com       "
, " http://blog.embed.ly/boston-hack-day                                             | http://posterous.com       "
, " http://www.guardian.co.uk/media/2011/jan/21/andy-coulson-phone-hacking-statement | http://www.guardian.co.uk/ "
].forEach(function(line) {
  var parts = line.split('|')
    , url = parts[0].trim()
    , expected = parts[1].trim()

  oembed_pro_provider_vows['when pro oembed is called for provider with url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected provider_url': assertObjValue('provider_url', expected)
  }
})

/*
 *  Scenario Outline: Attempt to get 404 URL
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL
 *      Then type should be error
 *      And error_code should be 404
 *      And type should be error
 */
var oembed_404_vows = {}
/*
 * Examples:
 ** url                                                       */
;[" http://www.youtube.com/watch/is/a/bad/url                 "
, " http://www.scribd.com/doc/zfldsf/asdfkljlas/klajsdlfkasdf "
, " http://tweetphoto.com/alsdfldsf/asdfkljlas/klajsdlfkasdf  "
].forEach(function(line) {
  var url = line.trim()

  oembed_404_vows['when oembed is called with a 404 url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected error_code': assertObjValue('error_code', '404')
    , 'reponds with expected type': assertObjValue('type', 'error')
  }
})

/*
 *  Scenario Outline: Attempt multi get 404 URLs
 *      Given an embedly endpoint
 *      When oembed is called with the <urls> URLs
 *      Then error_code should be <errcode>
 *      And type should be <types>
 */
var oembed_404_multi_vows = {}
/*      Examples:
 ** urls                                                                             | errcode | types       */
;[" http://www.youtube.com/watch/a/bassd/url,http://www.youtube.com/watch/ldf/asdlfj | 404,404 | error,error "
, " http://www.scribd.com/doc/lsbsdlfldsf/kl,http://www.scribd.com/doc/zasdf/asdfl   | 404,404 | error,error "
, " http://www.youtube.com/watch/zzzzasdf/kl,http://tweetphoto.com/14784358          | 404,    | error,photo "
, " http://tweetphoto.com/14784358,http://www.scribd.com/doc/asdfasdfasdf            | ,404    | photo,error "
].forEach(function(line) {
  var parts = line.split('|')
    , urls = parts[0].trim().split(',')
    , error_codes = parts[1].trim().split(',')
    , types = parts[2].trim().split(',')

  oembed_404_multi_vows['when oembed is called with a 404 urls '+urls.join(',')] = {
    topic: function (api) {
      return api.oembed({urls: urls}).on('complete', this.callback).start()
    }
    , 'reponds with expected error_codes': assertObjValue('error_code', error_codes)
    , 'reponds with expected types': assertObjValue('type', types)
  }
})

/*
 *  Scenario Outline: Attempt at non-api service without key
 *      Given an embedly endpoint
 *      When oembed is called with the <url> URL
 *      Then error_code should be 401
 *      And error_message should be This service requires an Embedly Pro account
 *      And type should be error
 */
var oembed_non_provider_vows = {}
/*
 * Examples:
 ** urls                                                                            */
;[" http://hn.embed.ly/                                                             "
, " http://bit.ly/enZRxO                                                            "
, " http://techcrunch.com/2011/02/03/linkedins-next-data-dive-professional-skills/  "
, " http://teachertube.com/rssPhoto.php                                             "
, " http://goo.gl/y1i9p                                                             "
].forEach(function(line) {
  var url = line.trim()

  oembed_non_provider_vows['when oembed is called with a unsupported url '+url] = {
    topic: function (api) {
      return api.oembed({url: url}).on('complete', this.callback).start()
    }
    , 'reponds with expected error_codes': assertObjValue('error_code', '401')
    , 'reponds with expected error_messages': assertObjValue('error_message', 'This service requires an Embedly Pro account')
    , 'reponds with expected types': assertObjValue('type', 'error')
  }
})

/*
 * Build vows
 */
vows.describe('OEmbed').addBatch({
    'An API instance': Hash({
      topic: new(embedly.Api)
    }).
    merge(oembed_provider_url_vows).
    merge(oembed_type_vows).
    merge(oembed_provider_with_force_vows).
    merge(oembed_multiple_provider_vows).
    merge(oembed_404_vows).
    merge(oembed_404_multi_vows).
    merge(oembed_non_provider_vows).
    end
  , 'A Pro API Instance': Hash({
      topic: new(embedly.Api)({key: process.env.EMBEDLY_KEY})
    }).
    merge(oembed_pro_provider_vows).
    end
}).export(module, {error: false})

<|endoftext|>


<commit_before>var fs = require('fs'),
    http = require('http'),
    exec = require('child_process').exec,
	_ = require('underscore'),
    Geo = require('./geo');

function dpc(t,fn) { if(typeof(t) == 'function') setTimeout(t,0); else setTimeout(fn,t); }

function Scanner(options) {

	var self = this;
    self.options = options;

    var config = eval('('+fs.readFileSync("scanner.cfg",'utf8')+')');
    var upload = fs.existsSync('upload.cfg') ? eval('('+fs.readFileSync("upload.cfg",'utf8')+')') : null;

    self.addr_pending = { }     // list of addresses waiting scan
    self.addr_digested = { }    // list of scanned addresses
    self.addr_working = { }     // list of working addresses

    self.geo = new Geo({ timeout : config.http_socket_timeout });

  	// -----------------------------------------
   	// local http server interface
    if(config.http_port)
    {
        var express = require('express');
        var app = express();
        app.configure(function(){
            app.use(express.bodyParser());
        });
        app.get('/', function(req, res) {
            var str = self.render();
            res.write(str);
            res.end();
        });

        http.createServer(app).listen(config.http_port, function() {
            console.log("HTTP server listening on port: ",config.http_port);
        });
    }

    var logo = fs.readFileSync("resources/"+config.currency.toLowerCase()+".png","base64");
    if(logo)
        logo = "data:image/png;base64,"+logo;

    self.render = function() {
        var str = ""
            +""
            +"";
        if(logo)
            str += "

";
        str += "PEER TO PEER "+(config.currency.toUpperCase())+" MINING NETWORK - PUBLIC NODE LIST
GENERATED ON: "+(new Date())+"
"
        if(self.poolstats)
            str += "Pool speed: "+(self.poolstats.pool_hash_rate/1000000).toFixed(2)+" "+config.speed_abbrev+"";
        str += "Currently observing "+(self.nodes_total || "N/A")+" nodes.
"+_.size(self.addr_working)+" nodes are public with following IPs:
";
        str += "
";
        str += "

IPs

Fee

Uptime

Location
";
        str += "

";

        var list = _.sortBy(_.toArray(self.addr_working), function(o) { return o.stats ? -o.stats.uptime : 0; })

        var row = 0;
        _.each(list, function(info) {
            var ip = info.ip;

            var uptime = info.stats ? (info.stats.uptime / 60 / 60 / 24).toFixed(1) : "N/A";
            var fee = (info.fee || 0).toFixed(2);

            str += "

"+ip+":9327

"+fee+"%

"+uptime+" days
";
            str += "
";
            if(info.geo) {
                str += ""+info.geo.country+" "+"";
            }
            str += "
";
            str += "
";
            str += "
";
        })
        str += "

";
        str += ""
        return str;
    }

    // setup flushing of rendered HTML page to a file (useful for uploading to other sites)
    if(config.flush_to_file_every_N_msec && config.flush_filename) {
        function flush_rendering() {
            var str = self.render();
            fs.writeFile(config.flush_filename, str, { encoding : 'utf8'});
            dpc(config.flush_to_file_every_N_msec, flush_rendering);
        }

        dpc(5000, flush_rendering);
    }

    // defer init
    dpc(function(){
        self.restore_working();
        self.update();
    })

    var p2pool_init = true;

    // main function that reloads 'addr' file from p2pool
    self.update = function() {
        var filename = config.addr_file;
        if(!fs.existsSync(filename)) {
            console.error("Unable to fetch p2pool address list from:",config.addr_file);
            filename = config.init_file;    // if we can't read p2pool's addr file, we just cycle on the local default init...
        }

        fs.readFile(filename, { encoding : 'utf8' }, function(err, data) {
            if(err) {
                console.error(err);
            }
            else {
                try {
                    var addr_list = JSON.parse(data);
                    self.inject(addr_list);

                    // main init
                    if(p2pool_init) {
                        p2pool_init = false;

                        // if we can read p2pool addr file, also add our pre-collected IPs
                        // if(filename != config.init_file) {
                            var init_addr = JSON.parse(fs.readFileSync(config.init_file, 'utf8'));
                            self.inject(init_addr);
                        //}

                        for(var i = 0; i < (config.probe_N_IPs_simultaneously || 1); i++)
                            self.digest();
                        dpc(60*1000, function() { self.store_working(); })
                    }
                }
                catch(ex) {
                    console.error("Unable to parse p2pool address list");
                    console.error(ex);
                }
            }

            dpc(1000 * 60, self.update);
        })
    }

    // store public pools in a file that reloads at startup
    self.store_working = function() {
        var data = JSON.stringify(self.addr_working);
        fs.writeFile(config.store_file, data, { encoding : 'utf8' }, function(err) {
            dpc(60*1000, self.store_working);
        })
    }

    // reload public list at startup
    self.restore_working = function() {
        try {
            self.addr_working = JSON.parse(fs.readFileSync(config.store_file, 'utf8'));
        } catch(ex) { console.log(ex); }
    }

    // inject new IPs from p2pool addr file
    self.inject = function(addr_list) {
        _.each(addr_list, function(info) {
            var ip = info[0][0];
            var port = info[0][1];

            if(!self.addr_digested[ip] && !self.addr_pending[ip]) {
                self.addr_pending[ip] = { ip : ip, port : port }
            }

            self.nodes_total = _.size(self.addr_digested) + _.size(self.addr_pending);
        });
    }

    // as we scan pools, we fetch global info from them to update the page
    self.update_global_stats = function(poolstats) {
        self.poolstats = poolstats;
    }

    // execute scan of a single IP
    self.digest = function() {
        if(!_.size(self.addr_pending))
            return self.list_complete();

        var info = _.find(self.addr_pending, function() { return true; });
        delete self.addr_pending[info.ip];
        self.addr_digested[info.ip] = info;
        console.log("P2POOL DIGESTING:" + info.ip + ":" + info.port);

        digest_ip(info, function(err, fee){
            if(!err) {
                info.fee = fee;
                self.addr_working[info.ip] = info;
                console.log("FOUND WORKING POOL: " + info.ip + ":" + info.port);

                digest_local_stats(info, function(err, stats) {
                    if(!err)
                        info.stats = stats;
                    digest_global_stats(info, function(err, stats) {
                        if(!err)
                            self.update_global_stats(stats);

                        if(!info.geo)
                            self.geo.get(info.ip, function(err, geo) {
                                if(!err)
                                    info.geo = geo;

                                continue_digest();
                            });
                        else
                            continue_digest();
                    });
                });
            }
            else {
                delete self.addr_working[info.ip];
                continue_digest();
            }

            function continue_digest() {
                self.working_size = _.size(self.addr_working);
                dpc(self.digest);
            }
        });
    }

    // schedule restar of the scan once all IPs are done
    self.list_complete = function() {
        self.addr_pending = self.addr_digested;
        self.addr_digested = { }
        dpc(config.rescan_list_delay, self.digest);
    }

    // functions to fetch data from target node IP

    function digest_ip(info, callback) {

        var options = {
          host: info.ip,
          port: 9327,
          path: '/fee',
          method: 'GET'
        };

        self.request(options, callback);
    }

    function digest_local_stats(info, callback) {

        var options = {
          host: info.ip,
          port: 9327,
          path: '/local_stats',
          method: 'GET'
        };

        self.request(options, callback);
    }

    function digest_global_stats(info, callback) {

        var options = {
          host: info.ip,
          port: 9327,
          path: '/global_stats',
          method: 'GET'
        };

        self.request(options, callback);
    }

    // make http request to the target node ip
    self.request = function(options, callback, is_plain)
    {
        http_handler = http;
        var req = http_handler.request(options, function(res) {
            res.setEncoding('utf8');
            var result = '';
            res.on('data', function (data) {
                result += data;
            });

            res.on('end', function () {
                if(options.plain)
                    callback(null, result);
                else {
                    try {
                        var o = JSON.parse(result);
                        callback(null, o);
                    } catch(ex) {
                        console.error(ex);
                        callback(ex);
                    }
                }
            });
        });

        req.on('socket', function (socket) {
            socket.setTimeout(config.http_socket_timeout);
            socket.on('timeout', function() {
                req.abort();
            });
        });

        req.on('error', function(e) {
            callback(e);
        });

        req.end();
    }

    if(upload && process.platform != 'win32') {

        function do_upload() {

            if(upload.ftp) {
                var ftp = upload.ftp;
                if(!ftp.address || !ftp.username || !ftp.password)
                    return console.error("upload.cfg ftp configuration must contain target address, username and password");
                var cmd = "curl -T "+config.flush_filename+" "+ftp.address+" --user "+ftp.address+":"+ftp.password;
                exec(cmd, function(error){ if(error) console.error(error); });
            }

            if(upload.scp) {
                var scp = upload.scp;
                if(!scp.address)
                    return console.error("upload.cfg scp configuration must contain target address");
                var cmd = "scp -q ./"+config.flush_filename+" "+scp.address;
                exec(cmd, function(error){ if(error) console.error(error); });
            }

            dpc(config.upload_interval, do_upload);
        }

        dpc(5000, do_upload);
    }
    else
        console.log("upload.cfg not found, rendering available only on the local interface");
}

GLOBAL.scanner = new Scanner();

//
<commit_msg>Don't include self in list<commit_after>var fs = require('fs'),
    http = require('http'),
    exec = require('child_process').exec,
	_ = require('underscore'),
    Geo = require('./geo');

function dpc(t,fn) { if(typeof(t) == 'function') setTimeout(t,0); else setTimeout(fn,t); }

function Scanner(options) {

	var self = this;
    self.options = options;

    var config = eval('('+fs.readFileSync("scanner.cfg",'utf8')+')');
    var upload = fs.existsSync('upload.cfg') ? eval('('+fs.readFileSync("upload.cfg",'utf8')+')') : null;

    self.addr_pending = { }     // list of addresses waiting scan
    self.addr_digested = { }    // list of scanned addresses
    self.addr_working = { }     // list of working addresses

    self.geo = new Geo({ timeout : config.http_socket_timeout });

  	// -----------------------------------------
   	// local http server interface
    if(config.http_port)
    {
        var express = require('express');
        var app = express();
        app.configure(function(){
            app.use(express.bodyParser());
        });
        app.get('/', function(req, res) {
            var str = self.render();
            res.write(str);
            res.end();
        });

        http.createServer(app).listen(config.http_port, function() {
            console.log("HTTP server listening on port: ",config.http_port);
        });
    }

    var logo = fs.readFileSync("resources/"+config.currency.toLowerCase()+".png","base64");
    if(logo)
        logo = "data:image/png;base64,"+logo;

    self.render = function() {
        var str = ""
            +""
            +"";
        if(logo)
            str += "

";
        str += "PEER TO PEER "+(config.currency.toUpperCase())+" MINING NETWORK - PUBLIC NODE LIST
GENERATED ON: "+(new Date())+"
"
        if(self.poolstats)
            str += "Pool speed: "+(self.poolstats.pool_hash_rate/1000000).toFixed(2)+" "+config.speed_abbrev+"";
        str += "Currently observing "+(self.nodes_total || "N/A")+" nodes.
"+_.size(self.addr_working)+" nodes are public with following IPs:
";
        str += "
";
        str += "

IPs

Fee

Uptime

Location
";
        str += "

";

        var list = _.sortBy(_.toArray(self.addr_working), function(o) { return o.stats ? -o.stats.uptime : 0; })

        var row = 0;
        _.each(list, function(info) {
            var ip = info.ip;

            var uptime = info.stats ? (info.stats.uptime / 60 / 60 / 24).toFixed(1) : "N/A";
            var fee = (info.fee || 0).toFixed(2);

            str += "

"+ip+":9327

"+fee+"%

"+uptime+" days
";
            str += "
";
            if(info.geo) {
                str += ""+info.geo.country+" "+"";
            }
            str += "
";
            str += "
";
            str += "
";
        })
        str += "

";
        str += ""
        return str;
    }

    // setup flushing of rendered HTML page to a file (useful for uploading to other sites)
    if(config.flush_to_file_every_N_msec && config.flush_filename) {
        function flush_rendering() {
            var str = self.render();
            fs.writeFile(config.flush_filename, str, { encoding : 'utf8'});
            dpc(config.flush_to_file_every_N_msec, flush_rendering);
        }

        dpc(5000, flush_rendering);
    }

    // defer init
    dpc(function(){
        self.restore_working();
        self.update();
    })

    var p2pool_init = true;

    // main function that reloads 'addr' file from p2pool
    self.update = function() {
        var filename = config.addr_file;
        if(!fs.existsSync(filename)) {
            console.error("Unable to fetch p2pool address list from:",config.addr_file);
            filename = config.init_file;    // if we can't read p2pool's addr file, we just cycle on the local default init...
        }

        fs.readFile(filename, { encoding : 'utf8' }, function(err, data) {
            if(err) {
                console.error(err);
            }
            else {
                try {
                    var addr_list = JSON.parse(data);
                    self.inject(addr_list);

                    // main init
                    if(p2pool_init) {
                        p2pool_init = false;

                        // if we can read p2pool addr file, also add our pre-collected IPs
                        // if(filename != config.init_file) {
                            var init_addr = JSON.parse(fs.readFileSync(config.init_file, 'utf8'));
                            self.inject(init_addr);
                        //}

                        for(var i = 0; i < (config.probe_N_IPs_simultaneously || 1); i++)
                            self.digest();
                        dpc(60*1000, function() { self.store_working(); })
                    }
                }
                catch(ex) {
                    console.error("Unable to parse p2pool address list");
                    console.error(ex);
                }
            }

            dpc(1000 * 60, self.update);
        })
    }

    // store public pools in a file that reloads at startup
    self.store_working = function() {
        var data = JSON.stringify(self.addr_working);
        fs.writeFile(config.store_file, data, { encoding : 'utf8' }, function(err) {
            dpc(60*1000, self.store_working);
        })
    }

    // reload public list at startup
    self.restore_working = function() {
        try {
            self.addr_working = JSON.parse(fs.readFileSync(config.store_file, 'utf8'));
        } catch(ex) { console.log(ex); }
    }

    // inject new IPs from p2pool addr file
    self.inject = function(addr_list) {
        _.each(addr_list, function(info) {
            var ip = info[0][0];
            var port = info[0][1];

            if(!self.addr_digested[ip] && !self.addr_pending[ip]) {
                self.addr_pending[ip] = { ip : ip, port : port }
            }

            self.nodes_total = _.size(self.addr_digested) + _.size(self.addr_pending);
        });
    }

    // as we scan pools, we fetch global info from them to update the page
    self.update_global_stats = function(poolstats) {
        self.poolstats = poolstats;
    }

    // execute scan of a single IP
    self.digest = function() {
        if(!_.size(self.addr_pending))
            return self.list_complete();

        var info = _.find(self.addr_pending, function() { return true; });
        delete self.addr_pending[info.ip];

	if(info.ip == "0.0.0.0" || info.ip == "127.0.0.1") {
	    return;
	}

        self.addr_digested[info.ip] = info;
        console.log("P2POOL DIGESTING:" + info.ip + ":" + info.port);

        digest_ip(info, function(err, fee){
            if(!err) {
                info.fee = fee;
                self.addr_working[info.ip] = info;
                console.log("FOUND WORKING POOL: " + info.ip + ":" + info.port);

                digest_local_stats(info, function(err, stats) {
                    if(!err)
                        info.stats = stats;
                    digest_global_stats(info, function(err, stats) {
                        if(!err)
                            self.update_global_stats(stats);

                        if(!info.geo)
                            self.geo.get(info.ip, function(err, geo) {
                                if(!err)
                                    info.geo = geo;

                                continue_digest();
                            });
                        else
                            continue_digest();
                    });
                });
            }
            else {
                delete self.addr_working[info.ip];
                continue_digest();
            }

            function continue_digest() {
                self.working_size = _.size(self.addr_working);
                dpc(self.digest);
            }
        });
    }

    // schedule restar of the scan once all IPs are done
    self.list_complete = function() {
        self.addr_pending = self.addr_digested;
        self.addr_digested = { }
        dpc(config.rescan_list_delay, self.digest);
    }

    // functions to fetch data from target node IP

    function digest_ip(info, callback) {

        var options = {
          host: info.ip,
          port: 9327,
          path: '/fee',
          method: 'GET'
        };

        self.request(options, callback);
    }

    function digest_local_stats(info, callback) {

        var options = {
          host: info.ip,
          port: 9327,
          path: '/local_stats',
          method: 'GET'
        };

        self.request(options, callback);
    }

    function digest_global_stats(info, callback) {

        var options = {
          host: info.ip,
          port: 9327,
          path: '/global_stats',
          method: 'GET'
        };

        self.request(options, callback);
    }

    // make http request to the target node ip
    self.request = function(options, callback, is_plain)
    {
        http_handler = http;
        var req = http_handler.request(options, function(res) {
            res.setEncoding('utf8');
            var result = '';
            res.on('data', function (data) {
                result += data;
            });

            res.on('end', function () {
                if(options.plain)
                    callback(null, result);
                else {
                    try {
                        var o = JSON.parse(result);
                        callback(null, o);
                    } catch(ex) {
                        console.error(ex);
                        callback(ex);
                    }
                }
            });
        });

        req.on('socket', function (socket) {
            socket.setTimeout(config.http_socket_timeout);
            socket.on('timeout', function() {
                req.abort();
            });
        });

        req.on('error', function(e) {
            callback(e);
        });

        req.end();
    }

    if(upload && process.platform != 'win32') {

        function do_upload() {

            if(upload.ftp) {
                var ftp = upload.ftp;
                if(!ftp.address || !ftp.username || !ftp.password)
                    return console.error("upload.cfg ftp configuration must contain target address, username and password");
                var cmd = "curl -T "+config.flush_filename+" "+ftp.address+" --user "+ftp.address+":"+ftp.password;
                exec(cmd, function(error){ if(error) console.error(error); });
            }

            if(upload.scp) {
                var scp = upload.scp;
                if(!scp.address)
                    return console.error("upload.cfg scp configuration must contain target address");
                var cmd = "scp -q ./"+config.flush_filename+" "+scp.address;
                exec(cmd, function(error){ if(error) console.error(error); });
            }

            dpc(config.upload_interval, do_upload);
        }

        dpc(5000, do_upload);
    }
    else
        console.log("upload.cfg not found, rendering available only on the local interface");
}

GLOBAL.scanner = new Scanner();

//
<|endoftext|>


<commit_before>/**
 * Created by alnedorezov on 5/26/17.
 */
function initPolygon() {
    function handleClicksOnSvgWithPolygonTool() {
        svgImgOnClick(event);
    }

    function handleKeyUp(event) {
        if (event.keyCode === 8 || event.keyCode === 46) {
            svgImgDeleteSelectedPolygon();
        }
    }

    function handleKeydown(event) {
        if ((event.ctrlKey || event.metaKey) && event.keyCode === 90) {
            if (event.shiftKey) {
                redoLastPoint();
            } else {
                undoLastPoint();
            }
        }
    }

    function handleContextMenu(event) {
        // console.log(event);
        event.returnValue = false;
        svgImgCancelPolygon();
    }

    /*global Tool*/
    /*eslint no-undef: "error"*/
    Tool.polygon = function () {
        /*global fromPrototype*/
        /*eslint no-undef: "error"*/
        return fromPrototype(Tool, {
            onClick(isButtonPressed) {
                if (typeof isButtonPressed === "boolean" || isButtonPressed instanceof Boolean) {
                    if (isButtonPressed) {
                        console.log("polygon enabled");
                        svgImg.addEventListener("click", handleClicksOnSvgWithPolygonTool, false);
                        svgImg.addEventListener("contextmenu", handleContextMenu, true);
                        window.addEventListener("keydown", handleKeydown, true);
                        window.addEventListener("keyup", handleKeyUp, true);

                    } else {
                        console.log("polygon disabled");
                        svgImg.removeEventListener("click", handleClicksOnSvgWithPolygonTool, false);
                        svgImg.removeEventListener("contextmenu", handleContextMenu, true);
                        window.removeEventListener("keydown", handleKeydown, true);
                        window.removeEventListener("keyup", handleKeyUp, true);
                    }
                }
            },
            isProlonged: true,
            buttonId: "btn_polygon"
        });
    };
}
<commit_msg>Fixed an issue in handleClicksOnSvgWithPolygonTool on FireFox browser in polygon.js<commit_after>/**
 * Created by alnedorezov on 5/26/17.
 */
function initPolygon() {
    function handleClicksOnSvgWithPolygonTool(event) {
        svgImgOnClick(event);
    }

    function handleKeyUp(event) {
        if (event.keyCode === 8 || event.keyCode === 46) {
            svgImgDeleteSelectedPolygon();
        }
    }

    function handleKeydown(event) {
        if ((event.ctrlKey || event.metaKey) && event.keyCode === 90) {
            if (event.shiftKey) {
                redoLastPoint();
            } else {
                undoLastPoint();
            }
        }
    }

    function handleContextMenu(event) {
        // console.log(event);
        event.returnValue = false;
        svgImgCancelPolygon();
    }

    /*global Tool*/
    /*eslint no-undef: "error"*/
    Tool.polygon = function () {
        /*global fromPrototype*/
        /*eslint no-undef: "error"*/
        return fromPrototype(Tool, {
            onClick(isButtonPressed) {
                if (typeof isButtonPressed === "boolean" || isButtonPressed instanceof Boolean) {
                    if (isButtonPressed) {
                        console.log("polygon enabled");
                        svgImg.addEventListener("click", handleClicksOnSvgWithPolygonTool, false);
                        svgImg.addEventListener("contextmenu", handleContextMenu, true);
                        window.addEventListener("keydown", handleKeydown, true);
                        window.addEventListener("keyup", handleKeyUp, true);

                    } else {
                        console.log("polygon disabled");
                        svgImg.removeEventListener("click", handleClicksOnSvgWithPolygonTool, false);
                        svgImg.removeEventListener("contextmenu", handleContextMenu, true);
                        window.removeEventListener("keydown", handleKeydown, true);
                        window.removeEventListener("keyup", handleKeyUp, true);
                    }
                }
            },
            isProlonged: true,
            buttonId: "btn_polygon"
        });
    };
}
<|endoftext|>


<commit_before>angular.module('MyApp')
  .factory('Account', function($http, $auth) {
    return {
      getProfile: function() {
        return $http.get('/api/me');
      },
      updateProfile: function(profileData) {
        return $http.put('/api/me', profileData);
      }
    };
  });<commit_msg>removed unused $auth argument<commit_after>angular.module('MyApp')
  .factory('Account', function($http) {
    return {
      getProfile: function() {
        return $http.get('/api/me');
      },
      updateProfile: function(profileData) {
        return $http.put('/api/me', profileData);
      }
    };
  });<|endoftext|>