tabbro/background.js

614 lines
20 KiB
JavaScript
Raw Normal View History

2015-02-08 20:42:59 -08:00
_tabbro_ = function() {
2015-02-13 18:05:12 -08:00
// Options
this.options = {
"autoStickyTabs":true
2015-02-13 18:05:12 -08:00
}
2015-02-08 20:42:59 -08:00
// Database version
this.__VERSION = 1;
// All tabbro data
this.data = null;
// Tab tree
this.tree = null;
// Chrome window ID -> our data object
this.windows_by_id = [];
// Chrome tab ID -> our data object
this.tabs_by_id = [];
2015-02-08 20:42:59 -08:00
2015-02-13 15:05:25 -08:00
// Detached tabs
this.detached_tabs = [];
2015-02-13 18:15:03 -08:00
// Storage engines
2015-02-08 20:42:59 -08:00
this._storage = chrome.storage.local;
2015-02-13 18:05:12 -08:00
this._cloudstorage = chrome.storage.local;
2015-02-08 20:42:59 -08:00
2015-02-08 22:33:17 -08:00
// the next created window should be stored in the tree window at this index. null for disabled
this.nextCreatedWindowIndex = null
// Action hook - called when we've done something that potentionally could require SAVING and/or any of the guis to be updated
2015-02-08 20:42:59 -08:00
this.hook_repaint == null
2015-02-13 18:05:12 -08:00
2015-02-08 20:42:59 -08:00
this.notify = function() {
tabbro.updateCount()
if(this.hook_repaint!=null) {
setTimeout(this.hook_repaint, 1)
}
2015-02-09 20:27:51 -08:00
this.save()
2015-02-08 20:42:59 -08:00
}
// TREE HELPERS
this.t_getWindow = function(winid) {
for(var i in this.tree) {
2015-02-08 20:42:59 -08:00
if(this.tree[i].id==winid) {
return this.tree[i]
}
}
//var window = this.windows_by_id[winid]
//if(this.tree.indexOf(window)===-1) return null
return window
2015-02-08 20:42:59 -08:00
}
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
this.t_getWindowFromTab = function(tabid) {
2015-02-08 21:02:57 -08:00
// Return the window the specified tab id belongs to
2015-02-08 20:42:59 -08:00
for(var w in this.tree) {
for(var t in this.tree[w].tabs) {
if(this.tree[w].tabs[t].id==tabid) {
return this.tree[w]
}
}
}
}
this.t_getTab = function(tabid) {
//if(this.tabs_by_id[tabid]) {
// return this.tabs_by_id[tabid]
//}
2015-02-08 20:42:59 -08:00
for(var w in this.tree) {
for(var t in this.tree[w].tabs) {
if(this.tree[w].tabs[t].id==tabid) {
return this.tree[w].tabs[t]
}
}
}
}
this.t_addTabtoWindow = function(winid, tabinfo, index) {
2015-02-08 20:42:59 -08:00
// Add a tab record to a window specified by winid
var win = this.t_getWindow(winid)
2015-02-13 18:17:38 -08:00
if(win) {
win.tabs.splice(index, 0, tabinfo)
2015-02-13 18:17:38 -08:00
if(this.options.autoStickyTabs && win.sticky) {
tabinfo.sticky = true;
2015-02-13 18:17:38 -08:00
}
2015-02-13 18:15:03 -08:00
}
this.tabs_by_id[tabinfo.id] = tabinfo
2015-02-13 19:52:16 -08:00
//console.log(this.tabs_by_id[tabinfo.id])
2015-02-08 20:42:59 -08:00
}
this.t_removeTab = function(tabid) {
// Removed tab record
2015-02-09 22:03:45 -08:00
var thewindow = this.t_getWindowFromTab(tabid)
// If the window is missing, it was probably ignored because we ignore non-"normal" windows
//if(typeof thewindow == "undefined") return
2015-02-08 20:42:59 -08:00
for(var i in thewindow.tabs) {
if(thewindow.tabs[i].id == tabid) {
thewindow.tabs.splice(i, 1)
return
}
}
}
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
this.t_removeWindow = function(winid) {
// Remove window
for(var i in this.tree) {
if(this.tree[i].id == winid) {
this.tree.splice(i, 1)
return
}
}
}
// UI UTIL FUNCTIONS
this.ui_delete_tab = function(winindex, tabindex) {
2015-02-08 21:02:57 -08:00
// Delete tab at tabindex from window at windowindex
2015-02-08 20:42:59 -08:00
var window = this.tree[winindex]
var tab = window.tabs[tabindex]
// Close tab if it's active
if(tab.id!=null) {
chrome.tabs.remove(tab.id)
2015-02-08 23:30:51 -08:00
} else {
// Check if the window will have 0 tabs - delete the window it if it does, tab is taken with it
if(this.tree[winindex].tabs.length == 1) {
this.tree.splice(winindex,1)
} else {
// Delete from it's window
this.tree[winindex].tabs.splice(tabindex, 1)
}
2015-02-08 20:42:59 -08:00
}
2015-02-09 20:27:51 -08:00
this.notify()
2015-02-08 20:42:59 -08:00
}
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
this.ui_delete_window = function(winindex) {
2015-02-08 21:02:57 -08:00
// Delete window at windowindex
2015-02-08 20:42:59 -08:00
var window = this.tree[winindex]
// If the window isn't loaded, delete it
if(window.id==null) {
2015-02-08 23:34:10 -08:00
this.tree.splice(winindex, 1)
2015-02-08 20:42:59 -08:00
} else {
// If the window is loaded, close it and the events fired will take care of cleanup
chrome.windows.remove(window.id)
}
2015-02-09 20:27:51 -08:00
this.notify()
2015-02-08 20:42:59 -08:00
}
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
this.ui_stick_tab = function(winindex, tabindex) {
2015-02-08 21:02:57 -08:00
// Toggle sticky state for tab at tabindex from window at windowindex
2015-02-08 20:42:59 -08:00
this.tree[winindex].tabs[tabindex].sticky = !this.tree[winindex].tabs[tabindex].sticky
// Stick the window if we just stuck a tab
if(this.tree[winindex].tabs[tabindex].sticky) {
this.tree[winindex].sticky = true
}
2015-02-09 20:27:51 -08:00
this.notify()
2015-02-08 20:42:59 -08:00
}
this.ui_stick_window = function(winindex) {
2015-02-08 21:02:57 -08:00
// Toggle sticky state for window at windowindex
2015-02-08 20:42:59 -08:00
this.tree[winindex].sticky = !this.tree[winindex].sticky
// Apply same setting to every tab in the window
for(var i in this.tree[winindex].tabs) {
this.tree[winindex].tabs[i].sticky = this.tree[winindex].sticky
}
2015-02-09 20:27:51 -08:00
this.notify()
2015-02-08 22:33:17 -08:00
}
this.ui_open_window = function(winindex) {
2015-02-09 20:27:51 -08:00
var bro = this
2015-02-08 22:33:17 -08:00
// Open saved window at index winindex
// Get the window
var win = this.tree[winindex]
var opening_sticky = win.sticky
if(win.id!=null) {
opening_sticky = false
}
if(opening_sticky) {
this.nextCreatedWindowIndex = winindex
}
2015-02-08 22:33:17 -08:00
var moreTabsToOpen = win.tabs.slice(1)
2015-02-13 19:52:16 -08:00
//console.log("More="+moreTabsToOpen.length)
// If the initial tab needed to be sticky, do so, and move it to 0 - handled in tab oncreate listener
if(win.tabs[0].pinned) {
this.pinNextCreatedTab = true;
}
2015-02-13 17:04:42 -08:00
2015-02-08 22:33:17 -08:00
// Open new chrome window with only the first tab from this group
chrome.windows.create({
focused:true,
2015-02-13 17:04:42 -08:00
url:win.tabs[0].url,
2015-02-08 22:33:17 -08:00
}, function(ev) {
2015-02-08 22:33:17 -08:00
var newwindowid = ev.id
if(opening_sticky) {
win.id = newwindowid
}
2015-02-13 16:26:39 -08:00
2015-02-13 17:04:42 -08:00
// Open the rest of the tabs in this group
2015-02-08 22:33:17 -08:00
if(moreTabsToOpen.length>0) {
// Delete existing tabs after first from record
if(opening_sticky) {
win.tabs.splice(1, 9999)
}
2015-02-08 22:33:17 -08:00
// Recreate all tabs in new window
for(var i in moreTabsToOpen) {
chrome.tabs.create({
windowId:newwindowid,
2015-02-13 17:04:42 -08:00
url:moreTabsToOpen[i].url,
pinned:moreTabsToOpen[i].pinned
2015-02-08 22:33:17 -08:00
}, function(ev) {
// Mark tab as sticky again
if(opening_sticky) {
bro.tabs_by_id[ev.id].sticky = true
2015-02-08 22:33:17 -08:00
}
})
}
}
// Mark the 1st tab sticky if needed
if(opening_sticky) {
bro.t_getWindow(newwindowid).tabs[0].sticky=true
}
2015-02-08 22:33:17 -08:00
})
2015-02-08 20:42:59 -08:00
}
this.ui_open_tab = function(winindex, tabindex) {
// Open a single tab
var tab = this.tree[winindex].tabs[tabindex]
chrome.windows.create({
focused:true,
url:tab.url
2015-02-13 17:04:42 -08:00
}, function(ev) {
//debugger
console.log("ui_open_tab")
console.log(ev)
2015-02-13 17:04:42 -08:00
})
}
this.ui_rename_window = function(winindex, newname) {
this.tree[winindex].name = newname
2015-02-13 18:05:12 -08:00
this.notify()
}
2015-02-08 20:42:59 -08:00
this.getCount = function() {
2015-02-08 21:02:57 -08:00
// Return count of loaded tabs
2015-02-08 20:42:59 -08:00
var count = 0;
2015-02-08 20:58:09 -08:00
for(var w in this.tree) {
for(var t in this.tree[w].tabs) {
if(this.tree[w].tabs[t].id!=null) {
count++
}
2015-02-08 20:42:59 -08:00
}
2015-02-08 20:58:09 -08:00
}
return count;
2015-02-08 20:42:59 -08:00
}
this.updateCount = function() {
// Update open tab count badge
2015-02-08 20:58:09 -08:00
chrome.browserAction.setBadgeText({"text":this.getCount()+""})
2015-02-08 20:42:59 -08:00
}
// Entry point - load previous session data or create a database
this.load = function() {
2015-02-09 20:27:51 -08:00
var bro = this
2015-02-08 20:42:59 -08:00
// Load data from sync
this._storage.get("tabbro", function(_data){
// If there's no data we get {}
if(_data.tabbro==undefined) {
//console.log("setup: initializing tabbro")
2015-02-08 20:42:59 -08:00
bro.tree = []
bro.data = {
version: bro.__VERSION,
tree: bro.tree
}
} else {
bro.data = _data.tabbro
bro.tree = bro.data.tree
}
bro.setup()
})
2015-02-13 18:05:12 -08:00
this._storage.get("tabbro_options", function(_data){
if(_data.tabbro_options==undefined) {
// Use defaults
} else {
for(var i in _data.tabbro_options) {
bro.options[i] = _data.tabbro_options[i]
}
}
})
2015-02-08 20:42:59 -08:00
}
this.setup = function() {
// Set the notification color
chrome.browserAction.setBadgeBackgroundColor({"color":"#990000"})
// Set up the data tree
this.initializeTree()
// Add evnt listeners
this.addListeners()
//console.log("Tabbro v" + this.__VERSION + " ready!")
}
this.initializeTree = function() {
2015-02-08 20:42:59 -08:00
// If any non-sticky windows are in our data from a previous session, remove them
this.pruneData();
// Add all open windows/tabs to the database tree
this.loadInitialTree()
}
this.loadInitialTree = function() {
// Add all open windows/tabs to the database tree
2015-02-09 20:27:51 -08:00
var bro = this
2015-02-08 20:42:59 -08:00
// Get all windows
chrome.windows.getAll(function(_windows){
for(var w in _windows) {
//if(_windows[w].type!="normal") continue;
var newWindowInfo = {
2015-02-08 20:42:59 -08:00
id: _windows[w].id,
tabs:[],
sticky: false,
name: "Window"
}
bro.tree.push(newWindowInfo)
bro.windows_by_id[_windows[w].id] = newWindowInfo
2015-02-08 20:42:59 -08:00
// Get all tabs in this window
chrome.tabs.getAllInWindow(_windows[w].id, function(_tabs){
//console.log(_tabs)
2015-02-08 20:42:59 -08:00
for(var i in _tabs) {
var w = bro.t_getWindow(_tabs[i].windowId)
var newTabInfo = {
2015-02-08 20:42:59 -08:00
id: _tabs[i].id,
title: _tabs[i].title,
url: _tabs[i].url,
sticky: false,
2015-02-09 22:03:45 -08:00
name: "Tab",
2015-02-13 16:37:53 -08:00
icon: (_tabs[i].favIconUrl?_tabs[i].favIconUrl:null),
pinned: _tabs[i].pinned
}
w.tabs.push(newTabInfo)
bro.tabs_by_id[_tabs[i].id] = newTabInfo
2015-02-08 20:42:59 -08:00
}
bro.save()
2015-02-08 20:58:09 -08:00
// Update open tab count badge
bro.updateCount()
2015-02-08 20:42:59 -08:00
})
}
})
}
this.pruneData = function() {
// If any non-sticky windows are in our data from a previous session, remove them
var pruneWindows = []
for(var w in this.tree) {
// Null all window IDs
this.tree[w].id = null
// Should window be pruned?
if(!this.tree[w].sticky) {
pruneWindows.push(w)
continue
}
// Null all tab IDs
var pruneTabs = []
for(var t in this.tree[w].tabs) {
this.tree[w].tabs[t].id = null
// Should tab be pruned?
if( !this.tree[w].tabs[t].sticky ) pruneTabs.push(t)
}
// Prune tabs
pruneTabs = pruneTabs.reverse()
//console.log("pruneData: pruneTabs:" )
//console.log(pruneTabs)
2015-02-08 21:02:57 -08:00
for(var p in pruneTabs) { // why the fuck is p a string?
2015-02-08 20:42:59 -08:00
this.tree[w].tabs.splice(pruneTabs[p], 1)
}
}
2015-02-08 22:33:17 -08:00
// Prune windows
2015-02-08 20:42:59 -08:00
pruneWindows = pruneWindows.reverse()
//console.log("pruneData: pruneWindows:" )
//console.log(pruneWindows)
2015-02-08 21:02:57 -08:00
for(var p in pruneWindows) { // why the fuck is p a string?
2015-02-08 22:33:17 -08:00
var removed = this.tree.splice(pruneWindows[p], 1)
//console.log("Pruned: ")
//console.log(removed)
2015-02-08 20:42:59 -08:00
}
//console.log("after pruneData: tree length: " + this.tree.length)
2015-02-08 20:42:59 -08:00
}
this.pruneWindowsTabsForClose = function(win) {
2015-02-08 21:02:57 -08:00
// Remove non-sticky tabs from a window
2015-02-08 20:42:59 -08:00
var pruneTabs = []
for(var t in win.tabs) {
win.tabs[t].id = null
// Should tab be pruned?
if( !win.tabs[t].sticky ) pruneTabs.push(t)
}
// Prune tabs
pruneTabs = pruneTabs.reverse()
for(var p in pruneTabs) { // why the fuck is p a string?
win.tabs.splice(pruneTabs[p], 1)
}
}
this.save = function() {
2015-02-08 23:34:10 -08:00
// Save data to chrome
2015-02-19 21:20:19 -08:00
this._storage.set({"tabbro":this.data})
2015-02-13 18:05:12 -08:00
// Save options to cloud
this._cloudstorage.set({"tabbro_options":this.options})
2015-02-08 20:42:59 -08:00
}
this.addListeners = function() {
2015-02-09 20:27:51 -08:00
var bro = this
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
// Add window listeners
chrome.windows.onCreated.addListener(function(e) {
//if(e.type!="normal") return
console.log("windows.onCreated: "+e.id)
//console.log(e)
2015-02-08 22:33:17 -08:00
if(bro.nextCreatedWindowIndex==null) {
var newWindowInfo = {
id: e.id,
tabs:[],
sticky: false,
name: "New Window"
}
bro.tree.push(newWindowInfo)
bro.windows_by_id[e.id]=newWindowInfo
2015-02-08 22:33:17 -08:00
} else {
// We were just ordered to restore a saved window
// bypass adding it to the tree and update the window in our tree
var win = bro.tree[bro.nextCreatedWindowIndex]
win.id = e.id
bro.windows_by_id[win.id]=win
2015-02-08 22:33:17 -08:00
//win.sticky = true
bro.nextCreatedWindowIndex = null;
}
2015-02-08 20:42:59 -08:00
})
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
chrome.windows.onRemoved.addListener(function(windowid) {
console.log("windows.onRemoved")
2015-02-08 20:42:59 -08:00
//console.log(windowid)
var thewindow = bro.t_getWindow(windowid)
if(thewindow.sticky) {
// If the window is sticky, we only mark it as closed
thewindow.id = null
// and remove non-sticky tabs in it
bro.pruneWindowsTabsForClose(thewindow)
} else {
// Not sticky = delete window and contained tabs
bro.t_removeWindow(windowid)
}
bro.windows_by_id[windowid]=undefined
2015-02-08 20:42:59 -08:00
bro.notify()
})
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
chrome.windows.onFocusChanged.addListener(function(x) {
//console.log("windows.onFocusChanged")
//console.log(x)
2015-02-08 20:42:59 -08:00
})
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
// Add tab listeners
chrome.tabs.onCreated.addListener(function(e) {
console.log("tabs.onCreated")
//console.log(e)
var pinned = e.pinned
if(bro.pinNextCreatedTab) {
bro.pinNextCreatedTab=false
chrome.tabs.update(e.id, {pinned:true},function() {
//console.log("Success!!!!")
})
chrome.tabs.move(e.id, {index:0},function() {
//console.log("Success!!!!1")
})
pinned=true
}
2015-02-08 20:42:59 -08:00
bro.t_addTabtoWindow(e.windowId, {
id: e.id,
title: e.title,
url: e.url,
sticky: false,
2015-02-13 16:37:53 -08:00
name: "",
pinned: pinned
2015-02-08 20:42:59 -08:00
}, e.index)
bro.notify()
})
2015-02-08 21:02:57 -08:00
chrome.tabs.onUpdated.addListener(function(tabid) {
console.log("tabs.onUpdated "+tabid)
2015-02-08 20:42:59 -08:00
// TODO loading indicator when a tab is loading
2015-02-13 15:32:12 -08:00
// TOOO determine if other attributes need to be tracked
// Update tab title
tab = bro.t_getTab(tabid)
2015-02-08 22:35:11 -08:00
if(tab) chrome.tabs.get(tabid, function(_tab) {
//if(_tab == null) debugger
//if(tab == null) debugger
tab.title = _tab.title
2015-02-13 15:32:12 -08:00
tab.url = _tab.url
2015-02-13 16:37:53 -08:00
tab.pinned = _tab.pinned
2015-02-09 22:03:45 -08:00
if(_tab.favIconUrl) {
tab.icon = _tab.favIconUrl;
}
})
2015-02-08 20:42:59 -08:00
})
2015-02-08 21:02:57 -08:00
2015-02-13 15:24:01 -08:00
chrome.tabs.onMoved.addListener(function(tabid) {
console.log("tabs.onMoved "+tabid)
2015-02-13 15:24:01 -08:00
// Fetch tab
chrome.tabs.get(tabid, function(_tab) {
// Fetch window
var thewindow = bro.t_getWindow(_tab.windowId)
// Find it in the array
var thetab = bro.t_getTab(tabid);
var tabindex = thewindow.tabs.indexOf(thetab);
// Splice it out
thewindow.tabs.splice(tabindex, 1);
// Splice it into the new spot
thewindow.tabs.splice(_tab.index, 0, thetab);
})
2015-02-08 20:42:59 -08:00
})
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
chrome.tabs.onActivated.addListener(function(x) {
console.log("tabs.onActivated")
console.log(x)
2015-02-08 20:42:59 -08:00
// TODO indicate that this tab is the active one
})
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
chrome.tabs.onHighlighted.addListener(function(x) {
console.log("tabs.onHighlighted")
console.log(x)
2015-02-08 20:42:59 -08:00
// This seems the same as tabs.onActivated?
})
2015-02-08 21:02:57 -08:00
2015-02-13 15:05:25 -08:00
chrome.tabs.onDetached.addListener(function(tabid) {
console.log("tabs.onDetached "+tabid)
2015-02-13 15:05:25 -08:00
// Remove tab from it's window
var tab = bro.t_getTab(tabid)
bro.t_removeTab(tabid)
// Add tab to bro.detached_tabs
bro.detached_tabs[tabid] = tab
2015-02-08 20:42:59 -08:00
})
2015-02-08 21:02:57 -08:00
2015-02-13 15:05:25 -08:00
chrome.tabs.onAttached.addListener(function(tabid) {
console.log("tabs.onAttached "+tabid)
2015-02-13 15:05:25 -08:00
// Remove from bro.detached_tabs
var tab = bro.detached_tabs.splice(tabid, 1)[0]
// Add tab to window
chrome.tabs.get(tabid, function(_tab) {
var thewindow = bro.t_getWindow(_tab.windowId)
thewindow.tabs.splice(_tab.index, 0, tab)
})
2015-02-08 20:42:59 -08:00
})
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
chrome.tabs.onRemoved.addListener(function(tabid) {
console.log("tabs.onRemoved "+tabid)
//console.log(tabid)
2015-02-08 20:42:59 -08:00
var thewindow = bro.t_getWindowFromTab(tabid)
var thetab = bro.t_getTab(tabid)
if(thetab && thetab.sticky) {
2015-02-08 20:42:59 -08:00
// If the tab is sticky, we only mark it as closed
thetab.id = null
} else {
// Not sticky = delete window and contained tabs
bro.t_removeTab(tabid)
}
bro.notify()
})
2015-02-08 21:02:57 -08:00
2015-02-08 20:42:59 -08:00
chrome.tabs.onReplaced.addListener(function(x) {
console.log("tabs.onReplaced")
2015-02-08 20:42:59 -08:00
//console.log(x)
// TODO handle when a tab is inexplicable replaced with another tab
})
}
2015-02-13 18:05:12 -08:00
this.update_setting = function(settingName, newValue) {
if(typeof(this.options[settingName]) != "undefined") {
this.options[settingName] = newValue
}
this.save()
}
2015-02-08 20:42:59 -08:00
this.load()
}
window.tabbro = new _tabbro_()