<commit_before>
chrome.browserAction.onClicked.addListener(
  function(tab) {
    chrome.tabs.create({url:
      chrome.extension.getURL('newtab.html')});
  }
);

function removeDummyTab(tabs) {
  for(var i=0; i < tabs.length; i++) {
    if(/dummy.html/.test(tabs[i].url)) {
      chrome.tabs.remove(tabs[i].id);
    }
  }
}

var ignoreWindowRemove = -1;
var ignoreWindowCreate = false;
var ignoreTabAttach = [];
var ignoreTabDetach = [];
var nextNewWindowTitle = null;

function getWindows(results) {
  var warr = [];
  for(var i=0; i < results.rows.length; i++) {
    var w = results.rows.item(i);
    warr.push({
      wid : w.wid,
      title : w.title
    });
  }
  return warr;
}

function makeTabDb(t) {
  return {
    tid : t.tid,
    wid : t.wid,
    title : t.title,
    url : t.url,
    faviconurl : t.faviconurl,
    index : t.index,
    parent : t.parent,
    depth : t.depth,
    collapsed : t.collapsed,
    hidden : t.hidden,
    isparent : t.isparent
  };
}

function getTabs(results) {
  var tarr = [];
  for(var i=0; i < results.rows.length; i++) {
    var t = results.rows.item(i);
    tarr.push(makeTabDb(t));
  }
  tarr.sort(function(a,b) {return (a.depth-b.depth)});
  var tree = [];
  for(var i=0; i<tarr.length; i++) {
    if(tarr[i].depth == 0) {
      tree.push(tarr[i]);
    } else {
      for(var j=0; j < tree.length; j++) {
        if(tree[j].tid == tarr[i].parent) {
          tree.splice(j+1, 0, tarr[i]); // insert into tree at j+1
          break;
        }
      }
    }
  }
  return tree;
}

var currentTab = null;
/*
 * Tab Event listeners
 */
chrome.tabs.onUpdated.addListener(
  // Update tab entry in our data model
  function(tid, changeInfo, tab) {
    if(is_newtab(tab) || is_devtools(tab) ||
      is_tabsense(tab) || is_dummy(tab)) return;

    if(tab.status == 'loading') {
      tab.favIconUrl = rotateFavIcon;
    } else {
      tab.favIconUrl = sanitizeFavIcon(tab.favIconUrl);
    }

    db.tab.update('url = ?, status =  ?, title = ?, faviconurl = ? ',
          'WHERE tid = ?',
          [tab.url, tab.status, tab.title, tab.favIconUrl, tid]);

    db.tab.get('WHERE tid = '+tid,
      function(tx, r) {
        if(r.rows.length != 1) {
          console.error('onUpdated '+r.rows.length+' for '+tid);
          return;
        }
        var tab = r.rows.item(0);
        if(uiport) {
          uiport.postMessage({
            name : 'updatetab',
            tab : makeTabDb(tab)
          });
        }
      }
    );
  }
);

chrome.tabs.onSelectionChanged.addListener(
  function(tid, selectInfo) {
    // Update the current selected tab.
    // This will be the parent tab of newly created tabs
    db.tab.get('WHERE tid = '+tid, function(tx, r) {
        if(r.rows.length != 1) {
          return;
        }
        var t = r.rows.item(0);
        if(is_tabsense(t) || is_devtools(t)) {
          return;
        }
        currentTab = makeTabDb(t);
      });
  }
);

function is_tabsense(tab) {
  return /chrome-extension:\/\/.*\/newtab.html/.test(tab.url);
}
function is_dummy(tab) {
  return /chrome-extension:\/\/.*\/dummy.html/.test(tab.url);
}

function is_devtools(tab) {
  return /chrome:\/\/devtools\/devtools.html/.test(tab.url);
}
function is_attic(tab) {
  return /chrome-extension:\/\/.*\/attic.html/.test(tab.url);
}
function is_newtab(tab) {
  return (tab.url == 'chrome://newtab/');
}

chrome.tabs.onCreated.addListener(
  function(tab) {
    if(is_tabsense(tab) || is_devtools(tab) ||
        is_dummy(tab) || is_attic(tab)) return;

    tab.favIconUrl = sanitizeFavIcon(tab.favIconUrl);
    if(is_newtab(tab) ||
      !currentTab ||
      currentTab.wid != tab.windowId)
    {
      var t = new db.tab(
        tab.id, tab.title, tab.url, tab.favIconUrl,
        tab.index, tab.windowId, 0, 0);
      currentTab = t;
    } else {
      var t = new db.tab(
        tab.id, tab.title, tab.url, tab.favIconUrl,
        tab.index, tab.windowId,
        currentTab.tid, currentTab.depth+1);
      db.tab.update('isparent=1','WHERE tid='+currentTab.tid);
      if(currentTab.collapsed) {
        expand_tab(currentTab.tid);
      }
    }
    db.put(t);

    if(uiport) {
      uiport.postMessage({
        name : 'addtab',
        tab : makeTabDb(t)
      });
    }
  }
);
chrome.tabs.onRemoved.addListener(
  function(tid) {

    db.tab.get('WHERE tid = '+tid,
      function(tx, r) {
        if(r.rows.length == 0) {
          return;
        }
        var tab = r.rows.item(0);

        bubble_up_children(tab);
        unparent_children(tab);

        if(is_tabsense(tab) || is_devtools(tab) || is_dummy(tab)) {
          return;
        }

        db.tab.del('WHERE saved = 0 AND tid = '+tid);

        // check if the parent has any other children and update its
        // isparent flag accordingly
        db.tab.get('WHERE parent = '+tab.parent, function(tx, r) {
          if(r.rows.length > 0) {
            // there are other children of this parent
            // do nothing
          } else {
            // this was the last child of its parent, mark its
            // isparent to 0
            db.tab.update('isparent=0','WHERE tid='+tab.parent);
          }
        })

        if(uiport) {
          uiport.postMessage({
            name : 'removetab',
            tab : makeTabDb(tab)
          });
        }

      }
    );
  }
);

function unparent_children(parnt) {
  db.tab.update('parent = ? ',
    'WHERE parent = ?', [parnt.parent, parnt.tid]);
}

function bubble_up_children(parnt) {
  db.tab.get('WHERE parent = '+parnt.tid,
    function(tx, r) {
      for(var i=0; i < r.rows.length; i++)  {
        bubble_up_children(r.rows.item(i));
      }
    }
  );
  db.tab.update('depth = depth-1 ', 'WHERE parent = ?', [parnt.tid]);
}

/*
function triggerUIRefresh() {
  db.tab.get('WHERE url LIKE \'chrome-extension://%/newtab.html\'',
    function(tx, r) {
      for(var i=0; i < r.rows.length; i++) {
        var tab = r.rows.item(i);
        chrome.tabs.sendRequest(tab.tid, { msg: "RELOAD" });
      }
    }
  );
}
*/

chrome.tabs.onAttached.addListener(
  function(tid, attachInfo) {
    if(ignoreTabAttach.indexOf(tid) >= 0) {
      ignoreTabAttach.splice(ignoreTabAttach.indexOf(tid), 1);
      return;
    }
    // Update our data model
    db.tab.update('wid = ?', 'WHERE tid = ?',
      [attachInfo.newWindowId, tid]);

    // Send UI update message
    db.tab.get('WHERE tid = '+tid, function(tx, results){
      if(results.rows.length != 1) {
        console.error('onAttached: '+results.rows.length+' for '+tid);
        return;
      }
      var tab = results.rows.item(0);
      if(uiport) {
        uiport.postMessage({
          name : 'addtab',
          tab : makeTabDb(tab)
        });
      }
    });
  }
);
chrome.tabs.onDetached.addListener(
  function(tid, detachInfo) {
    if(ignoreTabDetach.indexOf(tid) >= 0) {
      ignoreTabDetach.splice(ignoreTabDetach.indexOf(tid), 1);
      return;
    }
    // Update our data model
    db.tab.update('wid = ?, parent = ?, depth = ? ', 'WHERE tid = ?',
            [-1, 0, 0, tid]);

    // Send UI update message
    db.tab.get('WHERE tid = '+tid, function(tx, results){
      if(results.rows.length != 1) {
        console.error('onDetached : '+results.rows.length+' for '+tid);
        return;
      }
      var tab = results.rows.item(0);
      if(uiport) {
        uiport.postMessage({
          name : 'removetab',
          tab : makeTabDb(tab)
        });
      }
    });
  }
);
chrome.tabs.onMoved.addListener(
  function(tid, moveInfo) {
  }
);

/*
 * Window event listeners
 */
chrome.windows.onRemoved.addListener(
  function(wid) {
    if(wid == ignoreWindowRemove) {
      ignoreWindowRemove = -1;
      return;
    }

    if(wid == atticId) {
      atticId = 0;
    }

    db.window.del('WHERE wid = '+wid);
    db.tab.del('WHERE wid = '+wid);

    if(uiport) {
      uiport.postMessage({
        name : 'removewindow',
        win : { wid:wid }
      });
    }
  }
);
chrome.windows.onCreated.addListener(
  function(win) {
    if(win.type == 'app') {
      return;
    }

    if(ignoreWindowCreate) {
      ignoreWindowCreate = false;
      return;
    }

    var w = { wid : win.id };
    if(nextNewWindowTitle) {
      w.title = nextNewWindowTitle;
      nextNewWindowTitle = null;
    }

    db.put(new db.window(win.id, w.title));

    if(uiport) {
      uiport.postMessage({
        name : 'addwindow',
        win : w
      });
    }
  }
);
chrome.windows.onFocusChanged.addListener(
  function(wid) {
    chrome.tabs.getSelected(null,
      function(tab) {
        db.tab.get('WHERE tid = '+tab.id, function(tx, r) {
          if(r.rows.length != 1) {
            return;
          }
          var t = r.rows.item(0);
          if(is_newtab(t) || is_tabsense(t) ||
            is_devtools(t) || is_attic(t))
          {
            return;
          }
          currentTab = makeTabDb(t);
        });
      }
    );
  }
);
<commit_msg>Make sure all db operations on tab are on unsaved tabs<commit_after>
chrome.browserAction.onClicked.addListener(
  function(tab) {
    chrome.tabs.create({url:
      chrome.extension.getURL('newtab.html')});
  }
);

function removeDummyTab(tabs) {
  for(var i=0; i < tabs.length; i++) {
    if(/dummy.html/.test(tabs[i].url)) {
      chrome.tabs.remove(tabs[i].id);
    }
  }
}

var ignoreWindowRemove = -1;
var ignoreWindowCreate = false;
var ignoreTabAttach = [];
var ignoreTabDetach = [];
var nextNewWindowTitle = null;

function getWindows(results) {
  var warr = [];
  for(var i=0; i < results.rows.length; i++) {
    var w = results.rows.item(i);
    warr.push({
      wid : w.wid,
      title : w.title
    });
  }
  return warr;
}

function makeTabDb(t) {
  return {
    tid : t.tid,
    wid : t.wid,
    title : t.title,
    url : t.url,
    faviconurl : t.faviconurl,
    index : t.index,
    parent : t.parent,
    depth : t.depth,
    collapsed : t.collapsed,
    hidden : t.hidden,
    isparent : t.isparent
  };
}

function getTabs(results) {
  var tarr = [];
  for(var i=0; i < results.rows.length; i++) {
    var t = results.rows.item(i);
    tarr.push(makeTabDb(t));
  }
  tarr.sort(function(a,b) {return (a.depth-b.depth)});
  var tree = [];
  for(var i=0; i<tarr.length; i++) {
    if(tarr[i].depth == 0) {
      tree.push(tarr[i]);
    } else {
      for(var j=0; j < tree.length; j++) {
        if(tree[j].tid == tarr[i].parent) {
          tree.splice(j+1, 0, tarr[i]); // insert into tree at j+1
          break;
        }
      }
    }
  }
  return tree;
}

var currentTab = null;
/*
 * Tab Event listeners
 */
chrome.tabs.onUpdated.addListener(
  // Update tab entry in our data model
  function(tid, changeInfo, tab) {
    if(is_newtab(tab) || is_devtools(tab) ||
      is_tabsense(tab) || is_dummy(tab)) return;

    if(tab.status == 'loading') {
      tab.favIconUrl = rotateFavIcon;
    } else {
      tab.favIconUrl = sanitizeFavIcon(tab.favIconUrl);
    }

    db.tab.update('url = ?, status =  ?, title = ?, faviconurl = ? ',
          'WHERE saved=0 and tid = ?',
          [tab.url, tab.status, tab.title, tab.favIconUrl, tid]);

    db.tab.get('WHERE saved=0 and tid = '+tid,
      function(tx, r) {
        if(r.rows.length != 1) {
          console.error('onUpdated '+r.rows.length+' for '+tid);
          return;
        }
        var tab = r.rows.item(0);
        if(uiport) {
          uiport.postMessage({
            name : 'updatetab',
            tab : makeTabDb(tab)
          });
        }
      }
    );
  }
);

chrome.tabs.onSelectionChanged.addListener(
  function(tid, selectInfo) {
    // Update the current selected tab.
    // This will be the parent tab of newly created tabs
    db.tab.get('WHERE saved=0 and tid = '+tid, function(tx, r) {
        if(r.rows.length != 1) {
          return;
        }
        var t = r.rows.item(0);
        if(is_tabsense(t) || is_devtools(t)) {
          return;
        }
        currentTab = makeTabDb(t);
      });
  }
);

function is_tabsense(tab) {
  return /chrome-extension:\/\/.*\/newtab.html/.test(tab.url);
}
function is_dummy(tab) {
  return /chrome-extension:\/\/.*\/dummy.html/.test(tab.url);
}

function is_devtools(tab) {
  return /chrome:\/\/devtools\/devtools.html/.test(tab.url);
}
function is_attic(tab) {
  return /chrome-extension:\/\/.*\/attic.html/.test(tab.url);
}
function is_newtab(tab) {
  return (tab.url == 'chrome://newtab/');
}

chrome.tabs.onCreated.addListener(
  function(tab) {
    if(is_tabsense(tab) || is_devtools(tab) ||
        is_dummy(tab) || is_attic(tab)) return;

    tab.favIconUrl = sanitizeFavIcon(tab.favIconUrl);
    if(is_newtab(tab) ||
      !currentTab ||
      currentTab.wid != tab.windowId)
    {
      var t = new db.tab(
        tab.id, tab.title, tab.url, tab.favIconUrl,
        tab.index, tab.windowId, 0, 0);
      currentTab = t;
    } else {
      var t = new db.tab(
        tab.id, tab.title, tab.url, tab.favIconUrl,
        tab.index, tab.windowId,
        currentTab.tid, currentTab.depth+1);
      db.tab.update('isparent=1','WHERE saved=0 and tid='+currentTab.tid);
      if(currentTab.collapsed) {
        expand_tab(currentTab.tid);
      }
    }
    db.put(t);

    if(uiport) {
      uiport.postMessage({
        name : 'addtab',
        tab : makeTabDb(t)
      });
    }
  }
);
chrome.tabs.onRemoved.addListener(
  function(tid) {

    db.tab.get('WHERE saved=0 and tid = '+tid,
      function(tx, r) {
        if(r.rows.length == 0) {
          return;
        }
        var tab = r.rows.item(0);

        bubble_up_children(tab);
        unparent_children(tab);

        if(is_tabsense(tab) || is_devtools(tab) || is_dummy(tab)) {
          return;
        }

        db.tab.del('WHERE saved=0 AND tid = '+tid);

        // check if the parent has any other children and update its
        // isparent flag accordingly
        db.tab.get('WHERE saved=0 and parent='+tab.parent, function(tx, r) {
          if(r.rows.length > 0) {
            // there are other children of this parent
            // do nothing
          } else {
            // this was the last child of its parent, mark its
            // isparent to 0
            db.tab.update('isparent=0','WHERE saved=0 and tid='+tab.parent);
          }
        })

        if(uiport) {
          uiport.postMessage({
            name : 'removetab',
            tab : makeTabDb(tab)
          });
        }

      }
    );
  }
);

function unparent_children(parnt) {
  db.tab.update('parent = ? ',
    'WHERE saved=0 and parent = ?', [parnt.parent, parnt.tid]);
}

function bubble_up_children(parnt) {
  db.tab.get('WHERE saved=0 and parent = '+parnt.tid,
    function(tx, r) {
      for(var i=0; i < r.rows.length; i++)  {
        bubble_up_children(r.rows.item(i));
      }
    }
  );
  db.tab.update('depth=depth-1 ', 'WHERE saved=0 and parent=?', [parnt.tid]);
}

/*
function triggerUIRefresh() {
  db.tab.get('WHERE url LIKE \'chrome-extension://%/newtab.html\'',
    function(tx, r) {
      for(var i=0; i < r.rows.length; i++) {
        var tab = r.rows.item(i);
        chrome.tabs.sendRequest(tab.tid, { msg: "RELOAD" });
      }
    }
  );
}
*/

chrome.tabs.onAttached.addListener(
  function(tid, attachInfo) {
    if(ignoreTabAttach.indexOf(tid) >= 0) {
      ignoreTabAttach.splice(ignoreTabAttach.indexOf(tid), 1);
      return;
    }
    // Update our data model
    db.tab.update('wid = ?', 'WHERE saved=0 and tid = ?',
      [attachInfo.newWindowId, tid]);

    // Send UI update message
    db.tab.get('WHERE saved=0 and tid = '+tid, function(tx, results){
      if(results.rows.length != 1) {
        console.error('onAttached: '+results.rows.length+' for '+tid);
        return;
      }
      var tab = results.rows.item(0);
      if(uiport) {
        uiport.postMessage({
          name : 'addtab',
          tab : makeTabDb(tab)
        });
      }
    });
  }
);
chrome.tabs.onDetached.addListener(
  function(tid, detachInfo) {
    if(ignoreTabDetach.indexOf(tid) >= 0) {
      ignoreTabDetach.splice(ignoreTabDetach.indexOf(tid), 1);
      return;
    }
    // Update our data model
    db.tab.update('wid=?, parent=?, depth=? ', 'WHERE saved=0 and tid=?',
            [-1, 0, 0, tid]);

    // Send UI update message
    db.tab.get('WHERE saved=0 and tid='+tid, function(tx, results){
      if(results.rows.length != 1) {
        console.error('onDetached : '+results.rows.length+' for '+tid);
        return;
      }
      var tab = results.rows.item(0);
      if(uiport) {
        uiport.postMessage({
          name : 'removetab',
          tab : makeTabDb(tab)
        });
      }
    });
  }
);
chrome.tabs.onMoved.addListener(
  function(tid, moveInfo) {
  }
);

/*
 * Window event listeners
 */
chrome.windows.onRemoved.addListener(
  function(wid) {
    if(wid == ignoreWindowRemove) {
      ignoreWindowRemove = -1;
      return;
    }

    if(wid == atticId) {
      atticId = 0;
    }

    db.window.del('WHERE saved=0 and wid='+wid);
    db.tab.del('WHERE saved=0 and wid='+wid);

    if(uiport) {
      uiport.postMessage({
        name : 'removewindow',
        win : { wid:wid }
      });
    }
  }
);
chrome.windows.onCreated.addListener(
  function(win) {
    if(win.type == 'app') {
      return;
    }

    if(ignoreWindowCreate) {
      ignoreWindowCreate = false;
      return;
    }

    var w = { wid : win.id };
    if(nextNewWindowTitle) {
      w.title = nextNewWindowTitle;
      nextNewWindowTitle = null;
    }

    db.put(new db.window(win.id, w.title));

    if(uiport) {
      uiport.postMessage({
        name : 'addwindow',
        win : w
      });
    }
  }
);
chrome.windows.onFocusChanged.addListener(
  function(wid) {
    chrome.tabs.getSelected(null,
      function(tab) {
        db.tab.get('WHERE saved=0 and tid='+tab.id, function(tx, r) {
          if(r.rows.length != 1) {
            return;
          }
          var t = r.rows.item(0);
          if(is_newtab(t) || is_tabsense(t) ||
            is_devtools(t) || is_attic(t))
          {
            return;
          }
          currentTab = makeTabDb(t);
        });
      }
    );
  }
);
<|endoftext|>


<commit_before>/*
 * Licensed under the terms of the GNU Lesser General Public License:
 * 		http://www.opensource.org/licenses/lgpl-license.php
 *
 * File Name: fckplugin.js
 * 	This is the Clean HTML plugin definition file.
 */

// Define the command.
var CleanHTML_loaded = false;
var FCKCleanHTMLCommand = function()
{
	this.Name = 'CleanHTML' ;
}

function CleanWord( html )
{
  bIgnoreFont = true;
  bRemoveStyles = true;

  html = html.replace(/<o:p>\s*<\/o:p>/g, '') ;
  html = html.replace(/<o:p>.*?<\/o:p>/g, ' ') ;

  // Remove mso-xxx styles.
  html = html.replace( /\s*mso-[^:]+:[^;"]+;?/gi, '' ) ;

  // Remove margin styles.
  html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*;/gi, '' ) ;
  html = html.replace( /\s*MARGIN: 0cm 0cm 0pt\s*"/gi, "\"" ) ;

  html = html.replace( /\s*TEXT-INDENT: 0cm\s*;/gi, '' ) ;
  html = html.replace( /\s*TEXT-INDENT: 0cm\s*"/gi, "\"" ) ;

  html = html.replace( /\s*TEXT-ALIGN: [^\s;]+;?"/gi, "\"" ) ;

  html = html.replace( /\s*PAGE-BREAK-BEFORE: [^\s;]+;?"/gi, "\"" ) ;

  html = html.replace( /\s*FONT-VARIANT: [^\s;]+;?"/gi, "\"" ) ;

  html = html.replace( /\s*tab-stops:[^;"]*;?/gi, '' ) ;
  html = html.replace( /\s*tab-stops:[^"]*/gi, '' ) ;

  // Remove FONT face attributes.
  if ( bIgnoreFont )
  {
    html = html.replace( /\s*face="[^"]*"/gi, '' ) ;
    html = html.replace( /\s*face=[^ >]*/gi, '' ) ;

    html = html.replace( /\s*FONT-FAMILY:[^;"]*;?/gi, '' ) ;
  }

  // Remove Class attributes
  html = html.replace(/<(\w[^>]*) class=([^ |>]*)([^>]*)/gi, "<$1$3") ;

  // Remove styles.
  if ( bRemoveStyles )
    html = html.replace( /<(\w[^>]*) style="([^\"]*)"([^>]*)/gi, "<$1$3" ) ;

  // Remove empty styles.
  html =  html.replace( /\s*style="\s*"/gi, '' ) ;

  html = html.replace( /]*>\s* \s*<\/SPAN>/gi, ' ' ) ;
  html = html.replace( /]*><\/SPAN>/gi, '' ) ;

  // Remove Lang attributes
  html = html.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3") ;

  html = html.replace( /(.*?)<\/SPAN>/gi, '$1' ) ;

  html = html.replace( /(.*?)<\/FONT>/gi, '$1' ) ;

  // Remove XML elements and declarations
  html = html.replace(/<\\?\?xml[^>]*>/gi, '' ) ;

  // Remove Tags with XML namespace declarations: <o:p><\/o:p>
  html = html.replace(/<\/?\w+:[^>]*>/gi, '' ) ;

  // Remove comments [SF BUG-1481861].
  html = html.replace(/<\!--.*?-->/mg, '' ) ;

  html = html.replace( /<(U|I|STRIKE)> <\/\1>/g, ' ' ) ;

  html = html.replace( /<H\d>\s*<\/H\d>/gi, '' ) ;

  // Remove "display:none" tags.
  html = html.replace( /<(\w+)[^>]*\sstyle="[^"]*DISPLAY\s?:\s?none(.*?)<\/\1>/ig, '' ) ;

  // Remove language tags
  html = html.replace( /<(\w[^>]*) language=([^ |>]*)([^>]*)/gi, "<$1$3") ;

  // Remove onmouseover and onmouseout events (from MS Word comments effect)
  html = html.replace( /<(\w[^>]*) onmouseover="([^\"]*)"([^>]*)/gi, "<$1$3") ;
  html = html.replace( /<(\w[^>]*) onmouseout="([^\"]*)"([^>]*)/gi, "<$1$3") ;

  if ( FCKConfig.CleanWordKeepsStructure )
  {
    // The original <Hn> tag send from Word is something like this: <Hn style="margin-top:0px;margin-bottom:0px">
    html = html.replace( /<H(\d)([^>]*)>/gi, '<h$1>' ) ;

    // Word likes to insert extra  tags, when using MSIE. (Wierd).
    html = html.replace( /<(H\d)>]*>(.*?)<\/FONT><\/\1>/gi, '<$1>$2<\/$1>' );
    html = html.replace( /<(H\d)>(.*?)<\/EM><\/\1>/gi, '<$1>$2<\/$1>' );
  }
  else
  {
    html = html.replace( /
]*)>/gi, '
' ) ;
    html = html.replace( /
]*)>/gi, '
' ) ;
    html = html.replace( /
]*)>/gi, '
' ) ;
    html = html.replace( /
]*)>/gi, '
' ) ;
    html = html.replace( /
]*)>/gi, '
' ) ;
    html = html.replace( /
]*)>/gi, '
' ) ;

    html = html.replace( /<\/H\d>/gi, '<\/font><\/b><\/div>' ) ;

    // Transform
 to

    var re = new RegExp( '(
]*>.*?)(<\/P>)', 'gi' ) ; // Different because of a IE 5.0 error
    html = html.replace( re, '<div$2<\/div>' ) ;

    // Remove empty tags (three times, just to be sure).
    // This also removes any empty anchor
    html = html.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' ) ;
    html = html.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' ) ;
    html = html.replace( /<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '' ) ;
  }
  // Remove comments
  html = html.replace( /<\!--.*-->/mg, '' ) ;

  return html ;
}

FCKCleanHTMLCommand.prototype.Execute = function()
{
	var DOMDocument = FCK.EditorDocument ;
	// The are two diffent browser specific ways to get the text.
	// I also use a trick to count linebreaks (
/
) as one-stroke.
	if ( FCKBrowserInfo.IsIE ) {
		var HTMLText = DOMDocument.body.innerHTML ;
    DOMDocument.body.innerHTML = CleanWord (HTMLText);
	} else {
		var r = DOMDocument.createRange() ;
		r.selectNodeContents( DOMDocument.body ) ;
		var HTMLText = r.startContainer.innerHTML ;
    r.startContainer.innerHTML =  CleanWord (HTMLText);
	}
}

FCKCleanHTMLCommand.prototype.GetState = function()
{
	return FCK_TRISTATE_OFF ;
}

// Register the related command.
FCKCommands.RegisterCommand( 'CleanHTML', new FCKCleanHTMLCommand( ) ) ;

// Create the "CleanHTML" toolbar button.
var oCleanHTMLItem = new FCKToolbarButton( 'CleanHTML', 'CleanHTML') ;
oCleanHTMLItem.IconPath = FCKPlugins.Items['CleanHTML'].Path + 'html.png';
FCKToolbarItems.RegisterItem( 'CleanHTML', oCleanHTMLItem ) ;

// Define the event handler.
function CleanHTMLEventHandler()
{
FCKCommands.GetCommand( 'CleanHTML' ).Execute() ;
}
<commit_msg>[ENH] Code cleanup as of quality.tikiwiki.org<commit_after>/*
 * Licensed under the terms of the GNU Lesser General Public License:
 * http://www.opensource.org/licenses/lgpl-license.php
 *
 * File Name: fckplugin.js
 * This is the Clean HTML plugin definition file.
 */

// Define the command.
var CleanHTML_loaded = false;
var FCKCleanHTMLCommand = function () {
	this.Name = 'CleanHTML';
};

function CleanWord(html)
{
	bIgnoreFont = true;
	bRemoveStyles = true;

	html = html.replace(/<o:p>\s*<\/o:p>/g, '');
	html = html.replace(/<o:p>.*?<\/o:p>/g, ' ');

	// Remove mso-xxx styles.
	html = html.replace(/\s*mso-[^:]+:[^;"]+;?/gi, '');

	// Remove margin styles.
	html = html.replace(/\s*MARGIN: 0cm 0cm 0pt\s*;/gi, '');
	html = html.replace(/\s*MARGIN: 0cm 0cm 0pt\s*"/gi, "\"");

	html = html.replace(/\s*TEXT-INDENT: 0cm\s*;/gi, '');
	html = html.replace(/\s*TEXT-INDENT: 0cm\s*"/gi, "\"");

	html = html.replace(/\s*TEXT-ALIGN: [^\s;]+;?"/gi, "\"");

	html = html.replace(/\s*PAGE-BREAK-BEFORE: [^\s;]+;?"/gi, "\"");

	html = html.replace(/\s*FONT-VARIANT: [^\s;]+;?"/gi, "\"");

	html = html.replace(/\s*tab-stops:[^;"]*;?/gi, '');
	html = html.replace(/\s*tab-stops:[^"]*/gi, '');

	// Remove FONT face attributes.
	if ( bIgnoreFont )
	{
		html = html.replace(/\s*face="[^"]*"/gi, '');
		html = html.replace(/\s*face=[^ >]*/gi, '');

		html = html.replace(/\s*FONT-FAMILY:[^;"]*;?/gi, '');
	}

	// Remove Class attributes
	html = html.replace(/<(\w[^>]*) class=([^ |>]*)([^>]*)/gi, "<$1$3");

	// Remove styles.
	if ( bRemoveStyles ) {
		html = html.replace(/<(\w[^>]*) style="([^\"]*)"([^>]*)/gi, "<$1$3");
	}

	// Remove empty styles.
	html =  html.replace(/\s*style="\s*"/gi, '');

	html = html.replace(/]*>\s* \s*<\/SPAN>/gi, ' ');
	html = html.replace(/]*><\/SPAN>/gi, '');

	// Remove Lang attributes
	html = html.replace(/<(\w[^>]*) lang=([^ |>]*)([^>]*)/gi, "<$1$3");

	html = html.replace(/(.*?)<\/SPAN>/gi, '$1');

	html = html.replace(/(.*?)<\/FONT>/gi, '$1');

	// Remove XML elements and declarations
	html = html.replace(/<\\?\?xml[^>]*>/gi, '');

	// Remove Tags with XML namespace declarations: <o:p><\/o:p>
	html = html.replace(/<\/?\w+:[^>]*>/gi, '');

	// Remove comments [SF BUG-1481861].
	html = html.replace(/<\!--.*?-->/mg, '');

	html = html.replace(/<(U|I|STRIKE)> <\/\1>/g, ' ');

	html = html.replace(/<H\d>\s*<\/H\d>/gi, '');

	// Remove "display:none" tags.
	html = html.replace(/<(\w+)[^>]*\sstyle="[^"]*DISPLAY\s?:\s?none(.*?)<\/\1>/ig, '');

	// Remove language tags
	html = html.replace(/<(\w[^>]*) language=([^ |>]*)([^>]*)/gi, "<$1$3");

	// Remove onmouseover and onmouseout events (from MS Word comments effect)
	html = html.replace(/<(\w[^>]*) onmouseover="([^\"]*)"([^>]*)/gi, "<$1$3");
	html = html.replace(/<(\w[^>]*) onmouseout="([^\"]*)"([^>]*)/gi, "<$1$3");

	if ( FCKConfig.CleanWordKeepsStructure )
	{
		// The original <Hn> tag send from Word is something like this: <Hn style="margin-top:0px;margin-bottom:0px">
		html = html.replace(/<H(\d)([^>]*)>/gi, '<h$1>');

		// Word likes to insert extra  tags, when using MSIE. (Wierd).
		html = html.replace(/<(H\d)>]*>(.*?)<\/FONT><\/\1>/gi, '<$1>$2<\/$1>');
		html = html.replace(/<(H\d)>(.*?)<\/EM><\/\1>/gi, '<$1>$2<\/$1>');
	}
	else
	{
		html = html.replace(/
]*)>/gi, '
');
		html = html.replace(/
]*)>/gi, '
');
		html = html.replace(/
]*)>/gi, '
');
		html = html.replace(/
]*)>/gi, '
');
		html = html.replace(/
]*)>/gi, '
');
		html = html.replace(/
]*)>/gi, '
');

		html = html.replace(/<\/H\d>/gi, '<\/font><\/b><\/div>');

		// Transform
 to

		var re = new RegExp('(
]*>.*?)(<\/P>)', 'gi') ; // Different because of a IE 5.0 error
		html = html.replace(re, '<div$2<\/div>');

		// Remove empty tags (three times, just to be sure).
		// This also removes any empty anchor
		html = html.replace(/<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '');
		html = html.replace(/<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '');
		html = html.replace(/<([^\s>]+)(\s[^>]*)?>\s*<\/\1>/g, '');
	}
	// Remove comments
	html = html.replace(/<\!--.*-->/mg, '');

	return html;
}

FCKCleanHTMLCommand.prototype.Execute = function () {
	var DOMDocument = FCK.EditorDocument;
	// The are two diffent browser specific ways to get the text.
	// I also use a trick to count linebreaks (
/
) as one-stroke.
	if ( FCKBrowserInfo.IsIE ) {
		var HTMLText = DOMDocument.body.innerHTML;
    DOMDocument.body.innerHTML = CleanWord (HTMLText);
	} else {
		var r = DOMDocument.createRange();
		r.selectNodeContents(DOMDocument.body);
		var HTMLText = r.startContainer.innerHTML;
    r.startContainer.innerHTML =  CleanWord(HTMLText);
	}
};

FCKCleanHTMLCommand.prototype.GetState = function () {
	return FCK_TRISTATE_OFF;
};

// Register the related command.
FCKCommands.RegisterCommand('CleanHTML', new FCKCleanHTMLCommand( ));

// Create the "CleanHTML" toolbar button.
var oCleanHTMLItem = new FCKToolbarButton('CleanHTML', 'CleanHTML');
oCleanHTMLItem.IconPath = FCKPlugins.Items['CleanHTML'].Path + 'html.png';
FCKToolbarItems.RegisterItem('CleanHTML', oCleanHTMLItem);

// Define the event handler.
function CleanHTMLEventHandler()
{
FCKCommands.GetCommand('CleanHTML').Execute();
}
<|endoftext|>


<commit_before>var fs		= require("fs");
var crypto  = require("crypto");
var zlib    = require("zlib");
var Storage	= require("./Storage");
var _		= require("../utils");
var log 	= console.log;

module.exports = _.class(Storage, {

	constructor: function() {
		Storage.constructor.apply(this, arguments);
	},

    initStorage: function(server) {
        Storage.initStorage.apply(this, arguments);
        var filename = this.dir + '/storage.dat';
        this.storagePos = fs.existsSync(filename) && fs.statSync(filename).size || 0;
        this.storage = fs.createWriteStream(filename, { flags: 'a+' });
        this.storage.on('error', function(err) {
            throw err;
        });
    },

	//-------- public methods ----------------
	getRingSize: function() {
		return 1 << this.ring * 3; // 8^ring
	},

	getAllocatedSize: function() {
		return (4 << this.ring) *_.GiB; // 2^ring * 4GiB
	},

	getMaxPostSize: function() {
		return (1 << this.ring * 2) * 2 * _.MiB; // 4^ring 2MiB
	},

	//-------- DB -----------
	createDataTables: function() {
		this.db.exec(
			// ------ data ---------
			"CREATE TABLE IF NOT EXISTS data (			" +
			"	hash        BLOB    NOT NULL,			" + // uid. hash of file. (binary32)
			"	pos         INT     NOT NULL,           " + // data position in storage
			"	len         INT     NOT NULL,           " + // data length

			"	aid         TEXT    NOT NULL,			" + // author-id  (char20)
			"	sign        BLOB    NOT NULL,           " + // sign data by pubkey   sign(author, hash)

			"	seq         INT     NOT NULL,           " + // last sequence in data source
			"	ds          TEXT    NOT NULL,			" + // data source.
			"	dshash      BLOB    NOT NULL,           " + // ds-hash := sha256(<pre-dshash>|<hash>)
			"	dssign      BLOB    NOT NULL,           " + // sign(dsPubkey, "<hash:binary><seg:str><seq:int>")
			"	PRIMARY KEY(hash, ds)                   " +
			");" +

			"CREATE UNIQUE INDEX IF NOT EXISTS idx_data_seq  ON data (ds, seq);" +

			// ------ triggers ---------
			"CREATE TRIGGER IF NOT EXISTS data_insert AFTER INSERT ON data " +
			" BEGIN " +
			"  UPDATE sources SET size = size + length(new.len), seq = new.seq, ts = current_timestamp WHERE ds = new.ds; " +
			"  UPDATE authors SET size = size + length(new.len) WHERE aid = new.aid; " +
			" END; "
		);

		this.prepareSQL({
			insertData:
				"INSERT INTO data(hash, aid, sign, pos, len, ds, seq, dshash, dssign)" +
				"VALUES($hash, $aid, $sign, $pos, $len, $ds, $seq, $dshash, $dssign)"
			,
			selectData:
				"SELECT pos, len, sign," +
                    "authors.cert as author" +
				" FROM data" +
                " JOIN authors ON authors.aid=data.aid" +
				" WHERE hash=$hash" +
				" LIMIT 1"
			,
			selectSourceData:
				"SELECT " +
					"seq," +
					"authors.cert as author," +
					"sign," +
					"hash," +
					"dshash," +
					"dssign" +
				" FROM data" +
                " JOIN authors ON authors.aid=data.aid" +
				" WHERE ds=$ds AND seq>$seq" +
				" ORDER BY seq" +
				" LIMIT $limit"
		});
	},

	//------------- data ----------------
	prepareData: function(post, src, nid) {
		var hash = _.b64ToHex(post.hash);
		var sign = _.b64ToHex(post.sign);
		var cert = this.parseCertificate(post.author);
		if(!cert) return { err: 'Incorrect author-certificate' };
		if(!this.checkUid(hash)) return { err: 'Hash is not appropriate' };
		if(this.ring == 0 && !cert.isRegistrar()) return { err: 'Method allowed only for registrar' };
		if(!nid || this.ring == 0 || Math.random() < 0.10) { // data is uploaded by client or by registrar
			if(!cert.isRegistrar() && !cert.isSignedByRootRegistrar()) return { err: 'Certificate is not signed by registrar' };
			if(!cert.verify(this.seg + hash, sign)) return { err: 'Invalid sign' };
		}
		this.saveCertificate(cert);
		var aid = cert.getID();
		if(nid) { // data is uploaded from node
			return {
				_nid:     nid,
				$aid: 	  aid,
				$hash:    new Buffer(hash,   'hex'),
				$sign:    new Buffer(sign,   'hex'),
				$len:     post.len|0
			}
		} else { // data is uploaded by client
			var file = post.file || {};
			if(hash !== file.hash) return { err: 'Invalid hash' };
			return {
				_tmpfile: file.tmpfile,
				$aid: 	  aid,
				$hash:    new Buffer(hash,   'hex'),
				$sign:    new Buffer(sign,   'hex'),
				$len:     file.size|0
			}
		}
	},

	_appendToFileStorage: function(hash, filepath, size, fn) {
		if(this.storageLocked) return fn('Storage is locked');
		this.storageLocked = true;
		var pos = this.storagePos;
		var rs = fs.createReadStream(filepath);
        rs = rs.pipe(zlib.createGzip());
		rs.on('data', function(chunk){
			this.storage.write(chunk, 'binary');
			this.storagePos += chunk.length;
		}.bind(this));
		rs.on('end', function(){
			this.storageLocked = false;
			_.unlink(filepath);
			fn(null, pos);
		}.bind(this));
	},

	_insertData: function(data, pos, fn) {
		delete data._tmpfile;
		delete data._nid;
		data.$pos = pos;
		Storage.insertData.call(this, data, fn);
	},

	insertData: function(data, fn) {
		this._sql.selectData.get({ $hash: data.$hash }, function(err, row) { // check existed hash
			if(err) return fn(err);
			if(row && row.len) {
				if(data._tmpfile) return fn(); // upload by user
				return this._insertData(data, row.pos, fn);
			}
			var saveData = function(tmpfile) {
				this._appendToFileStorage(data.$hash, tmpfile, data.$len, function(err, pos) {
					if(err) return fn(err);
					this._insertData(data, pos, fn);
				}.bind(this));
			}.bind(this);
			if(data._tmpfile) {
				saveData(data._tmpfile);
			} else { // http request to node
				var hash = data.$hash.toString('hex');
				this.server.requestToNode({
					nid: data._nid,
					path: '/-/'+this.seg+'/data/'+hash,
					type: 'file',
					onFinish: function(err, file) {
						if(err) return fn(err);
						if(hash !== file.hash) return fn('Invalid hash');
						data.$len = file.size;
						saveData(file.tmpfile);
					}
				});
			}
		}.bind(this));
	},

	//--------------- HTTP -----------------
	/**
	 * process GET-request:
	 * 		/-/<seg>/data/<uid:hex>.<ext:str>
	 */
	httpCommandData: function(conn) {
		if(conn.method !== 'GET') return conn.response400('Method Not Allowed');
		var q = _.str(conn.urlParts[4]).split('.');
		var uid = q[0], ext = q[1]||'';
		if(!this.checkUid(uid)) return conn.response400('Isn`t appropriate uid');
		this._sql.selectData.get({
			$hash: new Buffer(uid, 'hex')
		}, function(err, row) {
			if(err) return conn.response500('Can`t read DB');
			if(!row) return conn.response(404, 'UID not found');
			conn.setResponseHeader('Content-Length', row.len);
            conn.setExpire();
			conn.setResponseHeader('x-base-author', row.author.toString("base64") + '; sign=' + row.sign.toString("base64"), true);
			conn.setContentType(ext);
            var rs = fs.createReadStream(this.dir + '/storage.dat', { start:row.pos, end:row.pos + row.len-1 });
            rs = rs.pipe(zlib.createGunzip());
            conn.response(200, rs);
		}.bind(this));
	}

});
<commit_msg>remove content length header<commit_after>var fs		= require("fs");
var crypto  = require("crypto");
var zlib    = require("zlib");
var Storage	= require("./Storage");
var _		= require("../utils");
var log 	= console.log;

module.exports = _.class(Storage, {

	constructor: function() {
		Storage.constructor.apply(this, arguments);
	},

    initStorage: function(server) {
        Storage.initStorage.apply(this, arguments);
        var filename = this.dir + '/storage.dat';
        this.storagePos = fs.existsSync(filename) && fs.statSync(filename).size || 0;
        this.storage = fs.createWriteStream(filename, { flags: 'a+' });
        this.storage.on('error', function(err) {
            throw err;
        });
    },

	//-------- public methods ----------------
	getRingSize: function() {
		return 1 << this.ring * 3; // 8^ring
	},

	getAllocatedSize: function() {
		return (4 << this.ring) *_.GiB; // 2^ring * 4GiB
	},

	getMaxPostSize: function() {
		return (1 << this.ring * 2) * 2 * _.MiB; // 4^ring 2MiB
	},

	//-------- DB -----------
	createDataTables: function() {
		this.db.exec(
			// ------ data ---------
			"CREATE TABLE IF NOT EXISTS data (			" +
			"	hash        BLOB    NOT NULL,			" + // uid. hash of file. (binary32)
			"	pos         INT     NOT NULL,           " + // data position in storage
			"	len         INT     NOT NULL,           " + // data length

			"	aid         TEXT    NOT NULL,			" + // author-id  (char20)
			"	sign        BLOB    NOT NULL,           " + // sign data by pubkey   sign(author, hash)

			"	seq         INT     NOT NULL,           " + // last sequence in data source
			"	ds          TEXT    NOT NULL,			" + // data source.
			"	dshash      BLOB    NOT NULL,           " + // ds-hash := sha256(<pre-dshash>|<hash>)
			"	dssign      BLOB    NOT NULL,           " + // sign(dsPubkey, "<hash:binary><seg:str><seq:int>")
			"	PRIMARY KEY(hash, ds)                   " +
			");" +

			"CREATE UNIQUE INDEX IF NOT EXISTS idx_data_seq  ON data (ds, seq);" +

			// ------ triggers ---------
			"CREATE TRIGGER IF NOT EXISTS data_insert AFTER INSERT ON data " +
			" BEGIN " +
			"  UPDATE sources SET size = size + length(new.len), seq = new.seq, ts = current_timestamp WHERE ds = new.ds; " +
			"  UPDATE authors SET size = size + length(new.len) WHERE aid = new.aid; " +
			" END; "
		);

		this.prepareSQL({
			insertData:
				"INSERT INTO data(hash, aid, sign, pos, len, ds, seq, dshash, dssign)" +
				"VALUES($hash, $aid, $sign, $pos, $len, $ds, $seq, $dshash, $dssign)"
			,
			selectData:
				"SELECT pos, len, sign," +
                    "authors.cert as author" +
				" FROM data" +
                " JOIN authors ON authors.aid=data.aid" +
				" WHERE hash=$hash" +
				" LIMIT 1"
			,
			selectSourceData:
				"SELECT " +
					"seq," +
					"authors.cert as author," +
					"sign," +
					"hash," +
					"dshash," +
					"dssign" +
				" FROM data" +
                " JOIN authors ON authors.aid=data.aid" +
				" WHERE ds=$ds AND seq>$seq" +
				" ORDER BY seq" +
				" LIMIT $limit"
		});
	},

	//------------- data ----------------
	prepareData: function(post, src, nid) {
		var hash = _.b64ToHex(post.hash);
		var sign = _.b64ToHex(post.sign);
		var cert = this.parseCertificate(post.author);
		if(!cert) return { err: 'Incorrect author-certificate' };
		if(!this.checkUid(hash)) return { err: 'Hash is not appropriate' };
		if(this.ring == 0 && !cert.isRegistrar()) return { err: 'Method allowed only for registrar' };
		if(!nid || this.ring == 0 || Math.random() < 0.10) { // data is uploaded by client or by registrar
			if(!cert.isRegistrar() && !cert.isSignedByRootRegistrar()) return { err: 'Certificate is not signed by registrar' };
			if(!cert.verify(this.seg + hash, sign)) return { err: 'Invalid sign' };
		}
		this.saveCertificate(cert);
		var aid = cert.getID();
		if(nid) { // data is uploaded from node
			return {
				_nid:     nid,
				$aid: 	  aid,
				$hash:    new Buffer(hash,   'hex'),
				$sign:    new Buffer(sign,   'hex'),
				$len:     post.len|0
			}
		} else { // data is uploaded by client
			var file = post.file || {};
			if(hash !== file.hash) return { err: 'Invalid hash' };
			return {
				_tmpfile: file.tmpfile,
				$aid: 	  aid,
				$hash:    new Buffer(hash,   'hex'),
				$sign:    new Buffer(sign,   'hex'),
				$len:     file.size|0
			}
		}
	},

	_appendToFileStorage: function(hash, filepath, size, fn) {
		if(this.storageLocked) return fn('Storage is locked');
		this.storageLocked = true;
		var pos = this.storagePos;
		var rs = fs.createReadStream(filepath);
        rs = rs.pipe(zlib.createGzip());
		rs.on('data', function(chunk){
			this.storage.write(chunk, 'binary');
			this.storagePos += chunk.length;
		}.bind(this));
		rs.on('end', function(){
			this.storageLocked = false;
			_.unlink(filepath);
			fn(null, pos);
		}.bind(this));
	},

	_insertData: function(data, pos, fn) {
		delete data._tmpfile;
		delete data._nid;
		data.$pos = pos;
		Storage.insertData.call(this, data, fn);
	},

	insertData: function(data, fn) {
		this._sql.selectData.get({ $hash: data.$hash }, function(err, row) { // check existed hash
			if(err) return fn(err);
			if(row && row.len) {
				if(data._tmpfile) return fn(); // upload by user
				return this._insertData(data, row.pos, fn);
			}
			var saveData = function(tmpfile) {
				this._appendToFileStorage(data.$hash, tmpfile, data.$len, function(err, pos) {
					if(err) return fn(err);
					this._insertData(data, pos, fn);
				}.bind(this));
			}.bind(this);
			if(data._tmpfile) {
				saveData(data._tmpfile);
			} else { // http request to node
				var hash = data.$hash.toString('hex');
				this.server.requestToNode({
					nid: data._nid,
					path: '/-/'+this.seg+'/data/'+hash,
					type: 'file',
					onFinish: function(err, file) {
						if(err) return fn(err);
						if(hash !== file.hash) return fn('Invalid hash');
						data.$len = file.size;
						saveData(file.tmpfile);
					}
				});
			}
		}.bind(this));
	},

	//--------------- HTTP -----------------
	/**
	 * process GET-request:
	 * 		/-/<seg>/data/<uid:hex>.<ext:str>
	 */
	httpCommandData: function(conn) {
		if(conn.method !== 'GET') return conn.response400('Method Not Allowed');
		var q = _.str(conn.urlParts[4]).split('.');
		var uid = q[0], ext = q[1]||'';
		if(!this.checkUid(uid)) return conn.response400('Isn`t appropriate uid');
		this._sql.selectData.get({
			$hash: new Buffer(uid, 'hex')
		}, function(err, row) {
			if(err) return conn.response500('Can`t read DB');
			if(!row) return conn.response(404, 'UID not found');
			//conn.setResponseHeader('Content-Length', row.len);
            conn.setExpire();
			conn.setResponseHeader('x-base-author', row.author.toString("base64") + '; sign=' + row.sign.toString("base64"), true);
			conn.setContentType(ext);
            var rs = fs.createReadStream(this.dir + '/storage.dat', { start:row.pos, end:row.pos + row.len-1 });
            rs = rs.pipe(zlib.createGunzip());
            conn.response(200, rs);
		}.bind(this));
	}

});
<|endoftext|>


<commit_before>(function () {
    'use strict';

    /**
     * @param {User} user
     * @param Base
     * @param {$rootScope.Scope} $scope
     * @param {TransactionsCsvGen} transactionsCsvGen
     * @param {Waves} waves
     * @param {IPollCreate} createPoll
     * @return {TransactionsCtrl}
     */
    const controller = function (user, Base, $scope, transactionsCsvGen, waves, createPoll) {

        const analytics = require('@waves/event-sender');

        class TransactionsCtrl extends Base {

            constructor() {
                super($scope);
                /**
                 * @type {ITransaction[]}
                 */
                this.transactions = [];
                /**
                 * @type {boolean}
                 */
                this.pending = true;
                /**
                 * @type {string}
                 */
                this.filter = null;
                /**
                 * @type {ITransaction[]}
                 * @private
                 */
                this.txList = [];
                /**
                 * @type {number}
                 */
                this.limit = 100;

                this.observe(['filter'], this._sendAnalytics);

                this.syncSettings({ filter: 'wallet.transactions.filter' });

                const poll = createPoll(this, this._getTxList, this._setTxList, 4000, { isBalance: true });

                this.observe(['txList', 'filter'], this._applyTransactionList);
                this.observe('limit', () => poll.restart());
            }

            exportTransactions(maxTransactions = 10000) {
                // analytics.push('TransactionsPage', `TransactionsPage.CSV.${WavesApp.type}`, 'download');
                analytics.send({ name: 'Transactions Export Click', target: 'ui' });
                const MAX_LIMIT = 1000;

                const getSeriesTransactions = async ({ allTransactions = [], after = '' }) => {

                    let transactions;
                    try {
                        transactions = await waves.node.transactions.list(MAX_LIMIT, after);
                    } catch (e) {
                        transactions = [];
                    }

                    allTransactions = allTransactions.concat(transactions.filter(el => !user.scam[el.assetId]));

                    if (transactions.length < MAX_LIMIT || allTransactions.length > maxTransactions) {
                        return allTransactions;
                    } else {
                        return getSeriesTransactions({
                            allTransactions,
                            after: transactions[transactions.length - 1].id
                        });
                    }
                };

                const promiseGetTransactions = getSeriesTransactions();
                promiseGetTransactions.then((allTransactions) => {
                    transactionsCsvGen.generate(allTransactions);
                });
                return promiseGetTransactions;
            }

            _getTxList() {
                return waves.node.transactions.list(this.limit);
            }

            /**
             * @private
             */
            _applyTransactionList() {
                const filter = this.filter;
                const list = this.txList;
                const availableTypes = filter.split(',')
                    .map((type) => type.trim())
                    .reduce((result, type) => {
                        result[type] = true;
                        return result;
                    }, Object.create(null));

                if (filter === 'all') {
                    this.transactions = list.slice();
                } else {
                    this.transactions = list.filter(({ typeName }) => availableTypes[typeName]);
                }
            }

            /**
             * @private
             */
            _sendAnalytics() {
                let actionName;
                switch (this.filter) {
                    case 'all':
                        actionName = 'Transactions All Show';
                        break;
                    case 'send,mass-send':
                        actionName = 'Transactions Sent Show';
                        break;
                    case 'receive,mass-receive,sponsorship-fee':
                        actionName = 'Transactions Received Show';
                        break;
                    case 'exchange-buy,exchange-sell':
                        actionName = 'Transactions Exchanged Show';
                        break;
                    case 'lease-in,lease-out,cancel-leasing':
                        actionName = 'Transactions Leased Show';
                        break;
                    case 'issue,reissue,burn,sponsorship-stop,sponsorship-start':
                        actionName = 'Transactions Issued Show';
                        break;
                    default:
                        break;
                }
                analytics.send({ name: actionName, target: 'ui' });
            }

            /**
             * @param {ITransaction[]} transactions
             * @private
             */
            _setTxList(transactions) {
                this.pending = false;
                this.txList = transactions;
                $scope.$digest();
            }

        }

        return new TransactionsCtrl();
    };

    controller.$inject = ['user', 'Base', '$scope', 'transactionsCsvGen', 'waves', 'createPoll'];

    angular.module('app.wallet.transactions').controller('TransactionsCtrl', controller);
})();
<commit_msg>CLIENT-1432: fix defalt value<commit_after>(function () {
    'use strict';

    /**
     * @param {User} user
     * @param Base
     * @param {$rootScope.Scope} $scope
     * @param {TransactionsCsvGen} transactionsCsvGen
     * @param {Waves} waves
     * @param {IPollCreate} createPoll
     * @return {TransactionsCtrl}
     */
    const controller = function (user, Base, $scope, transactionsCsvGen, waves, createPoll) {

        const analytics = require('@waves/event-sender');

        class TransactionsCtrl extends Base {

            constructor() {
                super($scope);
                /**
                 * @type {ITransaction[]}
                 */
                this.transactions = [];
                /**
                 * @type {boolean}
                 */
                this.pending = true;
                /**
                 * @type {string}
                 */
                this.filter = null;
                /**
                 * @type {ITransaction[]}
                 * @private
                 */
                this.txList = [];
                /**
                 * @type {number}
                 */
                this.limit = 100;

                this.observe(['filter'], this._sendAnalytics);

                this.syncSettings({ filter: 'wallet.transactions.filter' });

                const poll = createPoll(this, this._getTxList, this._setTxList, 4000, { isBalance: true });

                this.observe(['txList', 'filter'], this._applyTransactionList);
                this.observe('limit', () => poll.restart());
            }

            exportTransactions(maxTransactions = 10000) {
                // analytics.push('TransactionsPage', `TransactionsPage.CSV.${WavesApp.type}`, 'download');
                analytics.send({ name: 'Transactions Export Click', target: 'ui' });
                const MAX_LIMIT = 1000;

                const getSeriesTransactions = async ({ allTransactions = [], after = '' } = {}) => {

                    let transactions;
                    try {
                        transactions = await waves.node.transactions.list(MAX_LIMIT, after);
                    } catch (e) {
                        transactions = [];
                    }

                    allTransactions = allTransactions.concat(transactions.filter(el => !user.scam[el.assetId]));

                    if (transactions.length < MAX_LIMIT || allTransactions.length > maxTransactions) {
                        return allTransactions;
                    } else {
                        return getSeriesTransactions({
                            allTransactions,
                            after: transactions[transactions.length - 1].id
                        });
                    }
                };

                const promiseGetTransactions = getSeriesTransactions();
                promiseGetTransactions.then((allTransactions) => {
                    transactionsCsvGen.generate(allTransactions);
                });
                return promiseGetTransactions;
            }

            _getTxList() {
                return waves.node.transactions.list(this.limit);
            }

            /**
             * @private
             */
            _applyTransactionList() {
                const filter = this.filter;
                const list = this.txList;
                const availableTypes = filter.split(',')
                    .map((type) => type.trim())
                    .reduce((result, type) => {
                        result[type] = true;
                        return result;
                    }, Object.create(null));

                if (filter === 'all') {
                    this.transactions = list.slice();
                } else {
                    this.transactions = list.filter(({ typeName }) => availableTypes[typeName]);
                }
            }

            /**
             * @private
             */
            _sendAnalytics() {
                let actionName;
                switch (this.filter) {
                    case 'all':
                        actionName = 'Transactions All Show';
                        break;
                    case 'send,mass-send':
                        actionName = 'Transactions Sent Show';
                        break;
                    case 'receive,mass-receive,sponsorship-fee':
                        actionName = 'Transactions Received Show';
                        break;
                    case 'exchange-buy,exchange-sell':
                        actionName = 'Transactions Exchanged Show';
                        break;
                    case 'lease-in,lease-out,cancel-leasing':
                        actionName = 'Transactions Leased Show';
                        break;
                    case 'issue,reissue,burn,sponsorship-stop,sponsorship-start':
                        actionName = 'Transactions Issued Show';
                        break;
                    default:
                        break;
                }
                analytics.send({ name: actionName, target: 'ui' });
            }

            /**
             * @param {ITransaction[]} transactions
             * @private
             */
            _setTxList(transactions) {
                this.pending = false;
                this.txList = transactions;
                $scope.$digest();
            }

        }

        return new TransactionsCtrl();
    };

    controller.$inject = ['user', 'Base', '$scope', 'transactionsCsvGen', 'waves', 'createPoll'];

    angular.module('app.wallet.transactions').controller('TransactionsCtrl', controller);
})();
<|endoftext|>


<commit_before>import { Shaders } from '../shaders/Shaders';
import { ColourCache } from '../core/ColourCache';

import { ShaderMaterial } from '../Three';

function HypsometricMaterial ( survey, viewer ) {

	const terrain = survey.terrain;
	const limits = terrain.boundingBox;

	const zMin = limits.min.z;
	const zMax = limits.max.z;

	ShaderMaterial.call( this, {
		vertexShader: Shaders.surfaceVertexShader,
		fragmentShader: Shaders.surfaceFragmentShader,
		type: 'CV.HypsometricMaterial',
		uniforms: {
			uLight:     { value: viewer.surfaceLightDirection },
			datumShift: { value: terrain.activeDatumShift },
			minZ:       { value: zMin },
			scaleZ:     { value: 1 / ( zMax - zMin ) },
			cmap:       { value: ColourCache.getTexture( 'hypsometric' ) },
			opacity:    { value: 0.5 }
		}
	} );

	this.transparent = true;

	Object.defineProperty( this, 'opacity', {
		writeable: true,
		get: function () { return this.uniforms.opacity.value; },
		set: function ( value ) { this.uniforms.opacity.value = value; }
	} );

	const self = this;

	viewer.addEventListener( 'lightingChange', _lightingChanged );

	return this;

	function _lightingChanged ( /* event */ ) {

		self.uniforms.uLight.value = viewer.surfaceLightDirection;

	}
}

HypsometricMaterial.prototype = Object.create( ShaderMaterial.prototype );

HypsometricMaterial.prototype.setDatumShift = function ( shift ) {

	this.uniforms.datumShift.value = shift;

};

export { HypsometricMaterial };

// EOF<commit_msg>fix hypsometric shading to height range<commit_after>import { Shaders } from '../shaders/Shaders';
import { ColourCache } from '../core/ColourCache';

import { ShaderMaterial } from '../Three';

function HypsometricMaterial ( survey, viewer ) {

	const terrain = survey.terrain;
	const datumShift = terrain === null ? 0 : terrain.activeDatumShift;

	const zMin = 0;
	const zMax = 500;

	ShaderMaterial.call( this, {
		vertexShader: Shaders.surfaceVertexShader,
		fragmentShader: Shaders.surfaceFragmentShader,
		type: 'CV.HypsometricMaterial',
		uniforms: {
			uLight:     { value: viewer.surfaceLightDirection },
			datumShift: { value: datumShift },
			minZ:       { value: zMin },
			scaleZ:     { value: 1 / ( zMax - zMin ) },
			cmap:       { value: ColourCache.getTexture( 'hypsometric' ) },
			opacity:    { value: 0.5 }
		}
	} );

	this.transparent = true;

	Object.defineProperty( this, 'opacity', {
		writeable: true,
		get: function () { return this.uniforms.opacity.value; },
		set: function ( value ) { this.uniforms.opacity.value = value; }
	} );

	const self = this;

	viewer.addEventListener( 'lightingChange', _lightingChanged );

	return this;

	function _lightingChanged ( /* event */ ) {

		self.uniforms.uLight.value = viewer.surfaceLightDirection;

	}
}

HypsometricMaterial.prototype = Object.create( ShaderMaterial.prototype );

HypsometricMaterial.prototype.setDatumShift = function ( shift ) {

	this.uniforms.datumShift.value = shift;

};

export { HypsometricMaterial };

// EOF<|endoftext|>


<commit_before>require('should')
const level = require('level-test')()
const levelGeospatial = require('../lib/level-geospatial')
const { streamPromise } = require('./utils')
const getNewDb = () => {
  const db = level()
  const geo = levelGeospatial(db)
  return { db, geo }
}
const lat = 52.081959
const lon = 1.415904

describe('level-geospatial', () => {
  describe('put', () => {
    it('should put with a callback', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        streamPromise(db.createReadStream())
        .then(results => {
          results.should.deepEqual([
            {
              key: `geos~1202020102220223130002~${lat}~${lon}~somekey`,
              value: 'somevalue'
            },
            {
              key: 'keys~somekey',
              value: `1202020102220223130002~${lat}~${lon}~somekey`
            }
          ])
          done()
        })
        .catch(done)
      })
    })

    it('should put with a promise', async () => {
      const { geo, db } = getNewDb()
      const putPromise = geo.put({ lat, lon }, 'somekey', 'somevalue')
      putPromise.then.should.be.a.Function()
      putPromise.catch.should.be.a.Function()
      await putPromise
      const results = await streamPromise(db.createReadStream())
      results.should.deepEqual([
        {
          key: `geos~1202020102220223130002~${lat}~${lon}~somekey`,
          value: 'somevalue'
        },
        {
          key: 'keys~somekey',
          value: `1202020102220223130002~${lat}~${lon}~somekey`
        }
      ])
    })
  })

  describe('get', () => {
    it('should get with a callback', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        geo.get({ lat, lon }, 'somekey', (err, res) => {
          if (err) return done(err)
          res.should.deepEqual({
            quadKey: '1202020102220223130002',
            position: { lat, lon },
            id: 'somekey',
            value: 'somevalue'
          })
          done()
        })
      })
    })

    it('should get with a promise', async () => {
      const { geo, db } = getNewDb()
      await geo.put({ lat, lon }, 'somekey', 'somevalue')
      const getPromise = geo.get({ lat, lon }, 'somekey')
      getPromise.then.should.be.a.Function()
      getPromise.catch.should.be.a.Function()
      const res = await getPromise
      res.should.deepEqual({
        quadKey: '1202020102220223130002',
        position: { lat, lon },
        id: 'somekey',
        value: 'somevalue'
      })
    })
  })

  describe('del', () => {
    it('should del with a callback', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        geo.del('somekey', (err, res) => {
          if (err) return done(err)
          geo.getByKey('somekey', (err, res) => {
            err.name.should.equal('NotFoundError')
            done()
          })
        })
      })
    })

    it('should del with a promise', async () => {
      const { geo, db } = getNewDb()
      await geo.put({ lat, lon }, 'somekey', 'somevalue')
      const delPromise = geo.del('somekey')
      delPromise.then.should.be.a.Function()
      delPromise.catch.should.be.a.Function()
      await delPromise
      try {
        const res = await geo.getByKey('somekey')
        throw new Error('should not get here')
      } catch (err) {
        err.name.should.equal('NotFoundError')
      }
    })
  })

  describe('getByKey', () => {
    it('should getByKey with a callback', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        geo.getByKey('somekey', (err, res) => {
          if (err) return done(err)
          res.should.deepEqual({
            quadKey: '1202020102220223130002',
            position: { lat, lon },
            id: 'somekey',
            value: 'somevalue'
          })
          done()
        })
      })
    })

    it('should getByKey with a promise', async () => {
      const { geo, db } = getNewDb()
      await geo.put({ lat, lon }, 'somekey', 'somevalue')
      const getByKeyPromise = geo.getByKey('somekey')
      getByKeyPromise.then.should.be.a.Function()
      getByKeyPromise.catch.should.be.a.Function()
      const res = await getByKeyPromise
      res.should.deepEqual({
        quadKey: '1202020102220223130002',
        position: { lat, lon },
        id: 'somekey',
        value: 'somevalue'
      })
    })
  })

  describe('search', () => {
    it('should return search results as a stream', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        const searchData = { lat: Math.floor(lat), lon: Math.floor(lon) }
        const radius = 100000
        streamPromise(geo.search(searchData, radius))
        .then(res => {
          res.should.be.an.Array()
          res[0].should.deepEqual({
            quadKey: '1202020102220223130002',
            distance: 29903.681508047284,
            position: { lat, lon },
            id: 'somekey',
            value: 'somevalue'
          })
          done()
        })
        .catch(done)
      })
    })
  })
})
<commit_msg>test: add test for failing requests<commit_after>require('should')
const level = require('level-test')()
const levelGeospatial = require('../lib/level-geospatial')
const { streamPromise } = require('./utils')
const getNewDb = () => {
  const db = level()
  const geo = levelGeospatial(db)
  return { db, geo }
}
const lat = 52.081959
const lon = 1.415904

describe('level-geospatial', () => {
  describe('put', () => {
    it('should put with a callback', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        streamPromise(db.createReadStream())
        .then(results => {
          results.should.deepEqual([
            {
              key: `geos~1202020102220223130002~${lat}~${lon}~somekey`,
              value: 'somevalue'
            },
            {
              key: 'keys~somekey',
              value: `1202020102220223130002~${lat}~${lon}~somekey`
            }
          ])
          done()
        })
        .catch(done)
      })
    })

    it('should put with a promise', async () => {
      const { geo, db } = getNewDb()
      const putPromise = geo.put({ lat, lon }, 'somekey', 'somevalue')
      putPromise.then.should.be.a.Function()
      putPromise.catch.should.be.a.Function()
      await putPromise
      const results = await streamPromise(db.createReadStream())
      results.should.deepEqual([
        {
          key: `geos~1202020102220223130002~${lat}~${lon}~somekey`,
          value: 'somevalue'
        },
        {
          key: 'keys~somekey',
          value: `1202020102220223130002~${lat}~${lon}~somekey`
        }
      ])
    })
  })

  describe('get', () => {
    it('should get with a callback', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        geo.get({ lat, lon }, 'somekey', (err, res) => {
          if (err) return done(err)
          res.should.deepEqual({
            quadKey: '1202020102220223130002',
            position: { lat, lon },
            id: 'somekey',
            value: 'somevalue'
          })
          done()
        })
      })
    })

    it('should get with a promise', async () => {
      const { geo, db } = getNewDb()
      await geo.put({ lat, lon }, 'somekey', 'somevalue')
      const getPromise = geo.get({ lat, lon }, 'somekey')
      getPromise.then.should.be.a.Function()
      getPromise.catch.should.be.a.Function()
      const res = await getPromise
      res.should.deepEqual({
        quadKey: '1202020102220223130002',
        position: { lat, lon },
        id: 'somekey',
        value: 'somevalue'
      })
    })

    it('should get with a promise', done => {
      const { geo, db } = getNewDb()
      geo.get({ lat, lon }, 'somekey')
      .catch(err => {
        err.name.should.equal('NotFoundError')
        done()
      })
      .catch(done)
    })
  })

  describe('del', () => {
    it('should del with a callback', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        geo.del('somekey', (err, res) => {
          if (err) return done(err)
          geo.getByKey('somekey', (err, res) => {
            err.name.should.equal('NotFoundError')
            done()
          })
        })
      })
    })

    it('should del with a promise', async () => {
      const { geo, db } = getNewDb()
      await geo.put({ lat, lon }, 'somekey', 'somevalue')
      const delPromise = geo.del('somekey')
      delPromise.then.should.be.a.Function()
      delPromise.catch.should.be.a.Function()
      await delPromise
      try {
        const res = await geo.getByKey('somekey')
        throw new Error('should not get here')
      } catch (err) {
        err.name.should.equal('NotFoundError')
      }
    })

    it('should fail with a promise', done => {
      const { geo, db } = getNewDb()
      geo.del('somekey')
      .catch(err => {
        err.name.should.equal('NotFoundError')
        done()
      })
      .catch(done)
    })
  })

  describe('getByKey', () => {
    it('should getByKey with a callback', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        geo.getByKey('somekey', (err, res) => {
          if (err) return done(err)
          res.should.deepEqual({
            quadKey: '1202020102220223130002',
            position: { lat, lon },
            id: 'somekey',
            value: 'somevalue'
          })
          done()
        })
      })
    })

    it('should getByKey with a promise', async () => {
      const { geo, db } = getNewDb()
      await geo.put({ lat, lon }, 'somekey', 'somevalue')
      const getByKeyPromise = geo.getByKey('somekey')
      getByKeyPromise.then.should.be.a.Function()
      getByKeyPromise.catch.should.be.a.Function()
      const res = await getByKeyPromise
      res.should.deepEqual({
        quadKey: '1202020102220223130002',
        position: { lat, lon },
        id: 'somekey',
        value: 'somevalue'
      })
    })

    it('should fail with a promise', done => {
      const { geo, db } = getNewDb()
      geo.getByKey('somekey')
      .catch(err => {
        err.name.should.equal('NotFoundError')
        done()
      })
      .catch(done)
    })
  })

  describe('search', () => {
    it('should return search results as a stream', done => {
      const { geo, db } = getNewDb()
      geo.put({ lat, lon }, 'somekey', 'somevalue', err => {
        if (err) return done(err)
        const searchData = { lat: Math.floor(lat), lon: Math.floor(lon) }
        const radius = 100000
        streamPromise(geo.search(searchData, radius))
        .then(res => {
          res.should.be.an.Array()
          res[0].should.deepEqual({
            quadKey: '1202020102220223130002',
            distance: 29903.681508047284,
            position: { lat, lon },
            id: 'somekey',
            value: 'somevalue'
          })
          done()
        })
        .catch(done)
      })
    })
  })
})
<|endoftext|>


<commit_before>const express = require('express');
const router = express.Router();
const models = require('../../db/models');
const async = require('async');
const Subreddit = models.Subreddit;
const chalk = require('chalk');
const Heap = require('heap');
const ProgressBar = require('console-progress');
const routeUtils = require('./utils.js');

/*
returns all unique tags of provided subreddits that are within the provided distance
  The req body will have:
    a list of subreddits
    maximum tag distance
  The res body will have:
    a map of tags and their distances
*/
router.post('/getTagsForSubreddits', function(req, res, next) {
    if (!!req.body.subreddits && !!req.body.maxDistance) {
        Subreddit.find({
            url: {
                $in: req.body.subreddits
            }
        }, function(err, subreddits) {
            var tags = {};
            var index;
            for (index in subreddits) {
                var currTags = subreddits[index].tags;
                var i;
                for (i in currTags) {
                    var tag = currTags[i];
                    if (!tags[tag.name]) {
                        if (tag.distance <= req.body.maxDistance) {
                            tags[tag.name] = tag.distance;
                        }
                    } else {
                        if (tags[tag.name].distance > tag.distance) {
                            tags[tag.name] = tag.distance;
                        }
                    }
                }
            }
            res.status(200);
            res.json(tags);
        }).catch(next);
    } else {
        res.status(422).send('Unprocessable Entity')
    }
});

/*
returns the top (n) recommended subreddits
  The req body will have:
    number of recommendations to return (n)
    a list of tags
    a list of subscribed subreddits
    a list of blacklisted subreddits (unwanted)
  The res body will have:
    a list of the top (n) recommended, in order of relevancy
*/
router.post('/recommended', function(req, res, next) {
    console.log("Searching For " + JSON.stringify(req.body));

    var heap = new Heap(function(subreddit1, subreddit2) {
        // Inverse the score so that elements in the heap with the "highest score" are the worst match.

        var subreddit1Tags = !subreddit1.tags ? [] : routeUtils.getMatchingTags(subreddit1.tags, req.body.tags);
        var subreddit2Tags = !subreddit2.tags ? [] : routeUtils.getMatchingTags(subreddit2.tags, req.body.tags);

        var integralScore = -1 * routeUtils.calculateIntegralScore(subreddit1Tags, subreddit2Tags);
        if (integralScore !== 0) {
            //console.log("Result: " + tagDifference);
            return integralScore;
        }

        // Popularity Difference
        return subreddit1.total_subscribers - subreddit2.total_subscribers;
    });

    if (!!req.body.tags && !!req.body.subscribed && !!req.body.blacklisted && !!req.body.maxRecommendations) {
        var blacklist = req.body.subscribed.concat(req.body.blacklisted);

        Subreddit.find({
            url: {
                $nin: blacklist
            }
        }, function(err, parsedSubreddits) {
            var progressBarScale = 100;
            var bar = ProgressBar.getNew('[:bar] :eta Seconds Remaining', {
                complete: '=',
                incomplete: ' ',
                width: 40,
                total: parsedSubreddits.length / progressBarScale
            });

            for (index in parsedSubreddits) {
                // heap will never exceed maxRecommendations size for maximum efficiency of algorithm
                if (heap.size() >= req.body.maxRecommendations) {
                    heap.pushpop(parsedSubreddits[index]);
                } else {
                    heap.push(parsedSubreddits[index]);
                }
                if (index % progressBarScale === 0) {
                    bar.tick();
                }
            }
            var output = [];
            for (index = 0; index < req.body.maxRecommendations; index++) {
                if (heap.empty()) {
                    break;
                }
                var subreddit = heap.pop();
                output.push({
                    subreddit: subreddit.url,
                    rank: req.body.maxRecommendations - index
                });
            }
            // Reverse the output so that the best recommendations come first.
            output.reverse();

            res.status(200);
            res.json(output);
        }).catch(next);
    } else {
        res.status(422).send('Unprocessable Entity')
    }
});

module.exports = router;
<commit_msg>fixed same problem as tags route in earlier commit<commit_after>const express = require('express');
const router = express.Router();
const models = require('../../db/models');
const async = require('async');
const Subreddit = models.Subreddit;
const chalk = require('chalk');
const Heap = require('heap');
const ProgressBar = require('console-progress');
const routeUtils = require('./utils.js');

router.use(function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
    next();
});

/*
returns all unique tags of provided subreddits that are within the provided distance
  The req body will have:
    a list of subreddits
    maximum tag distance
  The res body will have:
    a map of tags and their distances
*/
router.post('/getTagsForSubreddits', function(req, res, next) {
    if (!!req.body.subreddits && !!req.body.maxDistance) {
        Subreddit.find({
            url: {
                $in: req.body.subreddits
            }
        }, function(err, subreddits) {
            var tags = {};
            var index;
            for (index in subreddits) {
                var currTags = subreddits[index].tags;
                var i;
                for (i in currTags) {
                    var tag = currTags[i];
                    if (!tags[tag.name]) {
                        if (tag.distance <= req.body.maxDistance) {
                            tags[tag.name] = tag.distance;
                        }
                    } else {
                        if (tags[tag.name].distance > tag.distance) {
                            tags[tag.name] = tag.distance;
                        }
                    }
                }
            }
            res.status(200);
            res.json(tags);
        }).catch(next);
    } else {
        res.status(422).send('Unprocessable Entity')
    }
});

/*
returns the top (n) recommended subreddits
  The req body will have:
    number of recommendations to return (n)
    a list of tags
    a list of subscribed subreddits
    a list of blacklisted subreddits (unwanted)
  The res body will have:
    a list of the top (n) recommended, in order of relevancy
*/
router.post('/recommended', function(req, res, next) {
    console.log("Searching For " + JSON.stringify(req.body));

    var heap = new Heap(function(subreddit1, subreddit2) {
        // Inverse the score so that elements in the heap with the "highest score" are the worst match.

        var subreddit1Tags = !subreddit1.tags ? [] : routeUtils.getMatchingTags(subreddit1.tags, req.body.tags);
        var subreddit2Tags = !subreddit2.tags ? [] : routeUtils.getMatchingTags(subreddit2.tags, req.body.tags);

        var integralScore = -1 * routeUtils.calculateIntegralScore(subreddit1Tags, subreddit2Tags);
        if (integralScore !== 0) {
            //console.log("Result: " + tagDifference);
            return integralScore;
        }

        // Popularity Difference
        return subreddit1.total_subscribers - subreddit2.total_subscribers;
    });

    if (!!req.body.tags && !!req.body.subscribed && !!req.body.blacklisted && !!req.body.maxRecommendations) {
        var blacklist = req.body.subscribed.concat(req.body.blacklisted);

        Subreddit.find({
            url: {
                $nin: blacklist
            }
        }, function(err, parsedSubreddits) {
            var progressBarScale = 100;
            var bar = ProgressBar.getNew('[:bar] :eta Seconds Remaining', {
                complete: '=',
                incomplete: ' ',
                width: 40,
                total: parsedSubreddits.length / progressBarScale
            });

            for (index in parsedSubreddits) {
                // heap will never exceed maxRecommendations size for maximum efficiency of algorithm
                if (heap.size() >= req.body.maxRecommendations) {
                    heap.pushpop(parsedSubreddits[index]);
                } else {
                    heap.push(parsedSubreddits[index]);
                }
                if (index % progressBarScale === 0) {
                    bar.tick();
                }
            }
            var output = [];
            for (index = 0; index < req.body.maxRecommendations; index++) {
                if (heap.empty()) {
                    break;
                }
                var subreddit = heap.pop();
                output.push({
                    subreddit: subreddit.url,
                    rank: req.body.maxRecommendations - index
                });
            }
            // Reverse the output so that the best recommendations come first.
            output.reverse();

            res.status(200);
            res.json(output);
        }).catch(next);
    } else {
        res.status(422).send('Unprocessable Entity')
    }
});

module.exports = router;
<|endoftext|>


<commit_before>'use strict';

/**
* Platform on which the current process is running.
*
* @module @stdlib/utils/pluck
*
* @example
* var pluck = require( '@stdlib/utils/pluck' );
*
* var arr = [
* 	{'a':1,'b':2},
* 	{'a':0.5,'b':3}
* ];
*
* var out = pluck( arr, 'a' );
* // returns [ 1, 0.5 ]
*
* arr = [
* 	{'a':1,'b':2},
* 	{'a':0.5,'b':3}
* ];
*
* out = pluck( arr, 'a', {'copy':false} );
* // returns [ 1, 0.5 ]
*
* var bool = ( arr[ 0 ] === out[ 0 ] );
* // returns true
*/

// MODULES //

var pluck = require( './pluck.js' );

// EXPORTS //

module.exports = pluck;
<commit_msg>Fix module description<commit_after>'use strict';

/**
* Extract a property value from each element of an object array.
*
* @module @stdlib/utils/pluck
*
* @example
* var pluck = require( '@stdlib/utils/pluck' );
*
* var arr = [
* 	{'a':1,'b':2},
* 	{'a':0.5,'b':3}
* ];
*
* var out = pluck( arr, 'a' );
* // returns [ 1, 0.5 ]
*
* arr = [
* 	{'a':1,'b':2},
* 	{'a':0.5,'b':3}
* ];
*
* out = pluck( arr, 'a', {'copy':false} );
* // returns [ 1, 0.5 ]
*
* var bool = ( arr[ 0 ] === out[ 0 ] );
* // returns true
*/

// MODULES //

var pluck = require( './pluck.js' );

// EXPORTS //

module.exports = pluck;
<|endoftext|>


<commit_before>import ChromeExtensionMessageService from "../../../reuse/messageServiceDefinition";

/**
 *
 * @param {ChromeExtensionMessageService} messageService
 * @returns
 */
function ValueStreamingInformationDirective(messageService) {
	return {
		restrict: 'E',
		controller: ['$scope', ValueStreamingInformationController],
		controllerAs: 'valueStreamingInformation',
		bindToController: true,
		templateUrl: 'ui/ng/partials/valueStreamingInformantion.html'
	};

	function ValueStreamingInformationController($scope) {
		var valueStreamingInformationController = this;

		valueStreamingInformationController.isV4vConfigured = false;
		valueStreamingInformationController.unsettledValue = 0;
		valueStreamingInformationController.balance = 0;
		valueStreamingInformationController.invoice = "";

		valueStreamingInformationController.generateInvoice = generateInvoice;
		valueStreamingInformationController.clearInvoice = clearInvoice;

		messageService.for('valueHandlerService').sendMessage('getValueSummary', null, (valueSummary) => {
			updateControllerFromValueSummary(valueSummary);

			if(valueSummary.isV4vConfigured) {
				updateBalance();
			}
		});

		messageService.for('valueHandlerService').onMessage('valueChanged', (valueSummary) => updateControllerFromValueSummary(valueSummary));
		messageService.for('valueHandlerService').onMessage('valuePaid', () => updateBalance());

		return valueStreamingInformationController;

		function updateControllerFromValueSummary(valueSummary) {
			valueStreamingInformationController.isV4vConfigured = valueSummary.isActive;
			valueStreamingInformationController.unsettledValue = Math.round(valueSummary.unsettledValue/1000);
			valueStreamingInformationController.settledValue = Math.round(valueSummary.settledValue/1000);
		}

		function updateBalance() {
			messageService.for('lightningService').sendMessage('getBalance', null, (response) => {
				if(response.result) {
					$scope.$apply(() => {
						valueStreamingInformationController.balance = response.result.balanceInSats;
					});
				}
			});
		}

		function generateInvoice() {
			const invoiceAmountUserInput = prompt("How many sats?", "10000");

			if(invoiceAmountUserInput === null) {
				return;
			}

			const invoiceAmount = parseInt(invoiceAmountUserInput);

			if(isNaN(invoiceAmount) || invoiceAmount <= 0) {
				alert("Invalid amount, enter a positive number of sats greater than zero")
				return;
			}

			messageService.for('lightningService').sendMessage('generateInvoice', convertFrom_SatsTo_mSats(invoiceAmount), (response) => {
				const ERROR_MESSAGE = 'Error generating invoice';

				if(response.invoice) {
					$scope.$apply(() => {
						valueStreamingInformationController.invoice = response.invoice;
					});
				}
				else {
					alert(typeof response.error === 'string' ? `${ERROR_MESSAGE}: ${response.error}`: ERROR_MESSAGE);
				}
			});
		}

		function clearInvoice() {
			valueStreamingInformationController.invoice = null;
		}

		function convertFrom_SatsTo_mSats(amountInSats) {
			return amountInSats * 1000;
		}
	}
}

export default ValueStreamingInformationDirective;<commit_msg>Fix #293 - Delay in v4v balance retrieval<commit_after>import ChromeExtensionMessageService from "../../../reuse/messageServiceDefinition";

/**
 *
 * @param {ChromeExtensionMessageService} messageService
 * @returns
 */
function ValueStreamingInformationDirective(messageService) {
	return {
		restrict: 'E',
		controller: ['$scope', ValueStreamingInformationController],
		controllerAs: 'valueStreamingInformation',
		bindToController: true,
		templateUrl: 'ui/ng/partials/valueStreamingInformantion.html'
	};

	function ValueStreamingInformationController($scope) {
		var valueStreamingInformationController = this;

		valueStreamingInformationController.isV4vConfigured = false;
		valueStreamingInformationController.unsettledValue = 0;
		valueStreamingInformationController.balance = 0;
		valueStreamingInformationController.invoice = "";

		valueStreamingInformationController.generateInvoice = generateInvoice;
		valueStreamingInformationController.clearInvoice = clearInvoice;

		messageService.for('valueHandlerService').sendMessage('getValueSummary', null, (valueSummary) => {
			updateControllerFromValueSummary(valueSummary);

			if(valueStreamingInformationController.isV4vConfigured) {
				updateBalance();
			}
		});

		messageService.for('valueHandlerService').onMessage('valueChanged', (valueSummary) => updateControllerFromValueSummary(valueSummary));
		messageService.for('valueHandlerService').onMessage('valuePaid', () => updateBalance());

		return valueStreamingInformationController;

		function updateControllerFromValueSummary(valueSummary) {
			valueStreamingInformationController.isV4vConfigured = valueSummary.isActive;
			valueStreamingInformationController.unsettledValue = Math.round(valueSummary.unsettledValue/1000);
			valueStreamingInformationController.settledValue = Math.round(valueSummary.settledValue/1000);
		}

		function updateBalance() {
			messageService.for('lightningService').sendMessage('getBalance', null, (response) => {
				if(response.result) {
					$scope.$apply(() => {
						valueStreamingInformationController.balance = response.result.balanceInSats;
					});
				}
			});
		}

		function generateInvoice() {
			const invoiceAmountUserInput = prompt("How many sats?", "10000");

			if(invoiceAmountUserInput === null) {
				return;
			}

			const invoiceAmount = parseInt(invoiceAmountUserInput);

			if(isNaN(invoiceAmount) || invoiceAmount <= 0) {
				alert("Invalid amount, enter a positive number of sats greater than zero")
				return;
			}

			messageService.for('lightningService').sendMessage('generateInvoice', convertFrom_SatsTo_mSats(invoiceAmount), (response) => {
				const ERROR_MESSAGE = 'Error generating invoice';

				if(response.invoice) {
					$scope.$apply(() => {
						valueStreamingInformationController.invoice = response.invoice;
					});
				}
				else {
					alert(typeof response.error === 'string' ? `${ERROR_MESSAGE}: ${response.error}`: ERROR_MESSAGE);
				}
			});
		}

		function clearInvoice() {
			valueStreamingInformationController.invoice = null;
		}

		function convertFrom_SatsTo_mSats(amountInSats) {
			return amountInSats * 1000;
		}
	}
}

export default ValueStreamingInformationDirective;<|endoftext|>


<commit_before>#!/usr/bin/env node
"use strict";

/**
 * This file will search a directory for images and auto-populate a config file
 * for use with the densities.js script.  While this does the bulk of the work,
 * it would still be a good idea to tweak the config file and confirm that
 * everything is in working order.
 */

var path = require("path");
var exec = require('child_process').exec;
var fs = require('fs');
var mime = require('mime');
var sourceDir = __dirname;
var outFile = "temp_config.js";

var USAGE = [
	"find_images.js",
	"   Usage: ./find_images.js SEARCH_DIRECTORY OUTFILE",
	""
].join("\n");

if (process.argv.length < 4) {
	console.log(USAGE);
	return;
} else {
	sourceDir = process.argv[2];
	outFile = process.argv[3];
}

var images = searchForImages();

function searchForImages() {
	console.log("Searching for images in:", path.resolve(sourceDir) + "...");

	if (!fs.existsSync(sourceDir)) {
		console.log(sourceDir, "does not exist!\nStopping.");
		return;
	}

	var stats = fs.statSync(sourceDir);
	if (!stats.isDirectory()) {
		console.log(sourceDir, "is not a directory!\nStopping.");
		return;
	}

	var files = fs.readdirSync(sourceDir);
	var filteredFiles = [];
	if (!files.length) {
		console.log("No files found.");
		return;
	}
	files.forEach(function(file, index){
		console.log("Processing file #" + index + " ("+file+")...");

		// Ignore all dot-files
		if (file[0] === ".") {
			return;
		}

		// Detect MIME type
		var mimeType = mime.lookup(file);

		switch (mimeType) {
			case 'image/jpeg':
			case 'image/svg+xml':
			case 'image/png':
				filteredFiles.push(file);
				break;
			default:
				console.log("NOT AN IMAGE. Ignoring...")
				break;
		}
	});
	console.log("Image files found:", filteredFiles);
}

function writeConfigFile() {

}<commit_msg>Finished script that auto-populates config file based on reading images in a specified directory.<commit_after>#!/usr/bin/env node
"use strict";

/**
 * This file will search a directory for images and auto-populate a config file
 * for use with the densities.js script.  While this does the bulk of the work,
 * it would still be a good idea to tweak the config file and confirm that
 * everything is in working order.
 */

var path = require("path");
var exec = require('child_process').exec;
var fs = require('fs');
var mime = require('mime');
var sourceDir = __dirname;
var outFile = "temp_config.js";

var USAGE = [
	"find_images.js",
	"   Usage: ./find_images.js SEARCH_DIRECTORY OUTFILE",
	""
].join("\n");

if (process.argv.length < 4) {
	console.log(USAGE);
	return;
} else {
	sourceDir = process.argv[2];
	outFile = process.argv[3];
}

var images = searchForImages();

if (images.length > 0) {
	writeConfigFile(images);
}
console.log("FINISHED!");

/**
 * Does the actual search.  This does NOT recurse into directories.  Since Android's drawables filesystem
 * is flat, it’s best to keep your source directory flat as well, to prevent any overwriting of existing
 * files with duplicate filenames.
 */
function searchForImages() {
	console.log("Searching for images in:", path.resolve(sourceDir) + "...");

	if (!fs.existsSync(sourceDir)) {
		console.log(sourceDir, "does not exist!\nStopping.");
		return;
	}

	var stats = fs.statSync(sourceDir);
	if (!stats.isDirectory()) {
		console.log(sourceDir, "is not a directory!\nStopping.");
		return;
	}

	var files = fs.readdirSync(sourceDir);
	var filteredFiles = [];
	if (!files.length) {
		console.log("No files found.");
		return;
	}
	files.forEach(function(file, index){
		console.log("Processing file #" + index + " ("+file+")...");

		// Ignore all dot-files
		if (file[0] === ".") {
			return;
		}

		// Detect MIME type
		var mimeType = mime.lookup(file);
		switch (mimeType) {
			case 'image/jpeg':
			case 'image/svg+xml':
			case 'image/png':
				filteredFiles.push({"file": file });
				break;
			default:
				console.log("NOT AN IMAGE. Ignoring...");
				break;
		}
	});
	return filteredFiles;
}

/**
 * Writes the actual config file.  I’d love to bolster this once I figure out where
 * I want to go with the configuration stuff... but for the time being it gets the
 * job done.
 */
function writeConfigFile(files) {
	var config = [
		'// AUTOGENERATED BY find_images.js',
		'exports.sourceDir = "'+sourceDir+'";',
		'exports.outputDir = "./res";',
		'',
		'exports.files = ' + JSON.stringify(files, null, 2) + ';'
	];

	// If someone's trying to be cute and write to a directory instead of a filename,
	// we'll save it as "_temp_config.js"
	if (fs.existsSync(outFile)) {
		var stats = fs.statSync(sourceDir);
		if (stats.isDirectory()) {
			outFile += path.combine(outFile, "_temp_config.js");
		}
	}

	// If someone's trying to be cute and output to a directory that doesn't exist,
	// we'll show them who's boss.
	var outputDir = path.dirname(outFile);
	if (!fs.existsSync(outputDir)) {
		var mkdirp = require('mkdirp');
		mkdirp.sync(outputDir);
	}

	fs.writeFile(outFile, config.join("\n"));
	console.log("Config file written at: ", outFile);
}<|endoftext|>