+ jQuery is completely out of the content/js files we're using in revision-a!

+ The function you give to TabMirror.customize now takes a Mirror rather than a jQuery object
+ iQ is coming along. I tried to put asserts in for all the standard functionality that's missing (and that we might not need, but that someone used to jQuery might expect). I've also been adding Natural Docs comments, to give a sense of the scope+ Got rid of TabCanvas.animate(), which we haven't used in a while.
+ No longer including switch.js, which we also haven't used in a while
This commit is contained in:
Ian Gilman 2010-05-26 17:04:56 -07:00
parent 67e2c9f640
commit a41b27f803
6 changed files with 440 additions and 190 deletions

View File

@ -277,7 +277,10 @@ window.TabItems = {
var self = this;
function mod($div){
function mod(mirror) {
var $div = $(mirror.el);
var tab = mirror.tab;
if(window.Groups) {
$div.data('isDragging', false);
$div.draggable(window.Groups.dragOptions);
@ -295,65 +298,32 @@ window.TabItems = {
if(!same)
return;
if(e.target.className == "close") {
$(this).find("canvas").data("link").tab.close(); }
if(e.target.className == "close")
tab.close();
else {
if(!$(this).data('isDragging')) {
if(!$(this).data('isDragging'))
self.zoomTo(this);
} else {
$(this).find("canvas").data("link").tab.raw.pos = $(this).position();
}
else
tab.raw.pos = $(this).position(); // TODO: is this necessary?
}
});
$("<div class='close'></div>").appendTo($div);
$("<div class='expander'></div>").appendTo($div);
var reconnected = false;
$div.each(function() {
var tab = Tabs.tab(this);
if(tab == Utils.homeTab) {
$(this).hide();
reconnected = true;
} else {
var item = new TabItem(this, tab);
$(this).data('tabItem', item);
item.addOnClose(self, function() {
Items.unsquish(null, item);
});
if(TabItems.reconnect(item))
reconnected = true;
else
Groups.newTab(item);
}
});
/* Utils.log("reconnected: "+reconnected); */
/*
Utils.log(reconnected, $div.length, !!Groups);
if(!reconnected && $div.length == 1 && Groups){
Utils.log('new tab');
Groups.newTab($div.data('tabItem'));
}
*/
if(tab == Utils.homeTab)
$div.hide();
else {
var item = new TabItem(mirror.el, tab);
$div.data('tabItem', item);
// TODO: Figure out this really weird bug?
// Why is that:
// $div.find("canvas").data("link").tab.url
// returns chrome://tabcandy/content/candies/original/index.html for
// every $div (which isn't right), but that
// $div.bind("test", function(){
// var url = $(this).find("canvas").data("link").tab.url;
// });
// $div.trigger("test")
// returns the right result (i.e., the per-tab URL)?
// I'm so confused...
// Although I can use the trigger trick, I was thinking about
// adding code in here which sorted the tabs into groups.
// -- Aza
item.addOnClose(self, function() {
Items.unsquish(null, item);
});
if(!TabItems.reconnect(item))
Groups.newTab(item);
}
}
window.TabMirror.customize(mod);
@ -404,7 +374,7 @@ window.TabItems = {
function onZoomDone(){
UI.tabBar.show(false);
TabMirror.resumePainting();
$(tabEl).find("canvas").data("link").tab.focus();
tab.focus();
$(tabEl).css({
top: orig.pos.top,
left: orig.pos.left,

View File

@ -300,7 +300,7 @@ window.Page = {
// Zoom out!
var mirror = currentTab.mirror;
var $tab = $(mirror.el);
var item = $tab.data().tabItem;
var item = TabItems.getItemByTab(mirror.el);
self.setActiveTab(item);
var rotation = $tab.css("-moz-transform");

View File

@ -88,14 +88,26 @@ var iQ = function(selector, context) {
slice = Array.prototype.slice,
indexOf = Array.prototype.indexOf;
var rclass = /[\n\t]/g,
rspace = /\s+/,
rreturn = /\r/g,
rspecialurl = /href|src|style/,
rtype = /(button|input)/i,
rfocusable = /(button|input|object|select|textarea)/i,
rclickable = /^(a|area)$/i,
rradiocheck = /radio|checkbox/;
// ##########
// Class: iQ.fn
// An individual element or group of elements.
iQ.fn = iQ.prototype = {
// ----------
// Function: init
// You don't call this directly; this is what's called by iQ().
// It works pretty much like jQuery(), with a few exceptions,
// most notably that you can't use strings with complex html,
// just simple tags like '<div>'.
init: function( selector, context ) {
return null; // TODO: this routine not ready yet
var match, elem, ret, doc;
// Handle $(""), $(null), or $(undefined)
@ -127,7 +139,7 @@ iQ.fn = iQ.prototype = {
// Verify a match, and that no context was specified for #id
if ( match && (match[1] || !context) ) {
// HANDLE: $(html) -> $(array)
// HANDLE $(html) -> $(array)
if ( match[1] ) {
doc = (context ? context.ownerDocument || context : document);
@ -137,6 +149,7 @@ iQ.fn = iQ.prototype = {
if ( ret ) {
if ( iQ.isPlainObject( context ) ) {
Utils.assert('does not support HTML creation with context', false);
selector = [ document.createElement( ret[1] ) ];
/* iQ.fn.attr.call( selector, context, true ); */
@ -145,6 +158,7 @@ iQ.fn = iQ.prototype = {
}
} else {
Utils.assert('does not support complex HTML creation', false);
/* ret = doc.createDocumentFragment([match */
ret = buildFragment( [ match[1] ], [ doc ] );
selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
@ -152,18 +166,11 @@ iQ.fn = iQ.prototype = {
return iQ.merge( this, selector );
// HANDLE: $("#id")
// HANDLE $("#id")
} else {
elem = document.getElementById( match[2] );
if ( elem ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootiQ.find( selector );
}
// Otherwise, we inject the element directly into the iQ object
this.length = 1;
this[0] = elem;
}
@ -173,24 +180,24 @@ iQ.fn = iQ.prototype = {
return this;
}
// HANDLE: $("TAG")
// HANDLE $("TAG")
} else if ( !context && /^\w+$/.test( selector ) ) {
this.selector = selector;
this.context = document;
selector = document.getElementsByTagName( selector );
return iQ.merge( this, selector );
// HANDLE: $(expr, $(...))
// HANDLE $(expr, $(...))
} else if ( !context || context.iq ) {
return (context || rootiQ).find( selector );
// HANDLE: $(expr, context)
// HANDLE $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return iQ( context ).find( selector );
}
// HANDLE: $(function)
// HANDLE $(function)
// Shortcut for document ready
} else if ( iQ.isFunction( selector ) ) {
Utils.log('iQ does not support ready functions');
@ -212,7 +219,279 @@ iQ.fn = iQ.prototype = {
iq: "1.4.2",
// The default length of a iQ object is 0
length: 0
length: 0,
// ----------
// Function: toArray
toArray: function() {
return slice.call( this, 0 );
},
// ----------
// Function: get
// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get: function( num ) {
return num == null ?
// Return a 'clean' array
this.toArray() :
// Return just the object
( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
},
// ----------
// Function: pushStack
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new iQ matched element set
var ret = iQ();
if ( iQ.isArray( elems ) ) {
push.apply( ret, elems );
} else {
iQ.merge( ret, elems );
}
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
if ( name === "find" ) {
ret.selector = this.selector + (this.selector ? " " : "") + selector;
} else if ( name ) {
ret.selector = this.selector + "." + name + "(" + selector + ")";
}
// Return the newly-formed element set
return ret;
},
// ----------
// Function: each
// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
each: function( callback, args ) {
return iQ.each( this, callback, args );
},
// ----------
// Function: slice
slice: function() {
return this.pushStack( slice.apply( this, arguments ),
"slice", slice.call(arguments).join(",") );
},
// ----------
// Function: addClass
addClass: function( value ) {
if ( iQ.isFunction(value) ) {
Utils.assert('does not support function argument', false);
return null;
}
if ( value && typeof value === "string" ) {
var classNames = (value || "").split( rspace );
for ( var i = 0, l = this.length; i < l; i++ ) {
var elem = this[i];
if ( elem.nodeType === 1 ) {
if ( !elem.className ) {
elem.className = value;
} else {
var className = " " + elem.className + " ", setClass = elem.className;
for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
setClass += " " + classNames[c];
}
}
elem.className = iQ.trim( setClass );
}
}
}
}
return this;
},
// ----------
// Function: removeClass
removeClass: function( value ) {
if ( iQ.isFunction(value) ) {
Utils.assert('does not support function argument', false);
return null;
}
if ( (value && typeof value === "string") || value === undefined ) {
var classNames = (value || "").split(rspace);
for ( var i = 0, l = this.length; i < l; i++ ) {
var elem = this[i];
if ( elem.nodeType === 1 && elem.className ) {
if ( value ) {
var className = (" " + elem.className + " ").replace(rclass, " ");
for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
className = className.replace(" " + classNames[c] + " ", " ");
}
elem.className = iQ.trim( className );
} else {
elem.className = "";
}
}
}
}
return this;
},
// ----------
// Function: find
find: function( selector ) {
var ret = [], length = 0;
for ( var i = 0, l = this.length; i < l; i++ ) {
length = ret.length;
try {
iQ.merge(ret, this[i].querySelectorAll( selector ) );
} catch(e) {
Utils.log('iQ.find error (bad selector)', e);
}
if ( i > 0 ) {
// Make sure that the results are unique
for ( var n = length; n < ret.length; n++ ) {
for ( var r = 0; r < length; r++ ) {
if ( ret[r] === ret[n] ) {
ret.splice(n--, 1);
break;
}
}
}
}
}
return iQ(ret);
},
// ----------
// Function: remove
remove: function(unused) {
Utils.assert('does not accept a selector', unused === undefined);
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
if ( elem.parentNode ) {
elem.parentNode.removeChild( elem );
}
}
return this;
},
// ----------
// Function: empty
empty: function() {
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
while ( elem.firstChild ) {
elem.removeChild( elem.firstChild );
}
}
return this;
},
// ----------
// Function: width
width: function(unused) {
Utils.assert('does not yet support setting', unused === undefined);
Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
return this[0].clientWidth;
},
// ----------
// Function: height
height: function(unused) {
Utils.assert('does not yet support setting', unused === undefined);
Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
return this[0].clientHeight;
},
// ----------
// Function: bounds
bounds: function(unused) {
Utils.assert('does not yet support setting', unused === undefined);
Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
var el = this[0];
return new Rect(
parseInt(el.style.left) || el.offsetLeft,
parseInt(el.style.top) || el.offsetTop,
el.clientWidth,
el.clientHeight
);
},
// ----------
// Function: data
data: function(key, value) {
Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
var data = this[0].iQData;
if(value === undefined)
return (data ? data[key] : null);
if(!data)
data = this[0].iQData = {};
data[key] = value;
return this;
},
// ----------
// Function: html
// TODO: security
html: function(value) {
Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
if(value === undefined)
return this[0].innerHTML;
this[0].innerHTML = value;
return this;
},
// ----------
// Function: text
text: function(value) {
Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
if(value === undefined) {
return this[0].textContent;
}
return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode(value));
},
// ----------
// Function: appendTo
appendTo: function(selector) {
Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
iQ(selector).append(this);
return this;
},
// ----------
// Function: append
append: function(selector) {
Utils.assert('does not yet support multi-objects (or null objects)', this.length == 1);
var object = iQ(selector);
Utils.assert('does not yet support multi-objects (or null objects)', object.length == 1);
this[0].appendChild(object[0]);
return this;
}
};
// ----------
@ -331,43 +610,6 @@ iQ.extend({
return true;
},
// ----------
// Function: merge
merge: function( first, second ) {
var i = first.length, j = 0;
if ( typeof second.length === "number" ) {
for ( var l = second.length; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
} else {
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
}
first.length = i;
return first;
},
// ----------
// Function: grep
grep: function( elems, callback, inv ) {
var ret = [];
// Go through the array, only saving the items
// that pass the validator function
for ( var i = 0, length = elems.length; i < length; i++ ) {
if ( !inv !== !callback( elems[ i ], i ) ) {
ret.push( elems[ i ] );
}
}
return ret;
},
// ----------
// Function: each
// args is for internal usage only
@ -406,7 +648,70 @@ iQ.extend({
}
return object;
}
},
// ----------
// Function: trim
trim: function( text ) {
return (text || "").replace( rtrim, "" );
},
// ----------
// Function: makeArray
// results is for internal usage only
makeArray: function( array, results ) {
var ret = results || [];
if ( array != null ) {
// The window, strings (and functions) also have 'length'
// The extra typeof function check is to prevent crashes
// in Safari 2 (See: #3039)
if ( array.length == null || typeof array === "string" || iQ.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
push.call( ret, array );
} else {
iQ.merge( ret, array );
}
}
return ret;
},
// ----------
// Function: merge
merge: function( first, second ) {
var i = first.length, j = 0;
if ( typeof second.length === "number" ) {
for ( var l = second.length; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
} else {
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
}
first.length = i;
return first;
},
// ----------
// Function: grep
grep: function( elems, callback, inv ) {
var ret = [];
// Go through the array, only saving the items
// that pass the validator function
for ( var i = 0, length = elems.length; i < length; i++ ) {
if ( !inv !== !callback( elems[ i ], i ) ) {
ret.push( elems[ i ] );
}
}
return ret;
}
});
// ----------

View File

@ -11,19 +11,24 @@ function _isIframe(doc){
return win.parent != win;
}
// ----------
// ##########
// Class: TabCanvas
// Takes care of the actual canvas for the tab thumbnail
var TabCanvas = function(tab, canvas){ this.init(tab, canvas) }
TabCanvas.prototype = {
// ----------
// Function: init
init: function(tab, canvas){
this.tab = tab;
this.canvas = canvas;
this.window = window;
$(canvas).data("link", this);
var $canvas = iQ(canvas).data("link", this);
var w = $(canvas).width();
var h = $(canvas).height();
$(canvas).attr({width:w, height:h});
var w = $canvas.width();
var h = $canvas.height();
canvas.width = w;
canvas.height = h;
var self = this;
this.paintIt = function(evt) {
@ -33,10 +38,14 @@ TabCanvas.prototype = {
};
},
// ----------
// Function: attach
attach: function() {
this.tab.contentWindow.addEventListener("MozAfterPaint", this.paintIt, false);
},
// ----------
// Function: detach
detach: function() {
try {
this.tab.contentWindow.removeEventListener("MozAfterPaint", this.paintIt, false);
@ -45,18 +54,13 @@ TabCanvas.prototype = {
}
},
// ----------
// Function: paint
paint: function(evt){
var $ = this.window.$;
if( $ == null ) {
Utils.log('null $ in paint');
return;
}
var $canvas = $(this.canvas);
var ctx = this.canvas.getContext("2d");
var w = $canvas.attr('width');
var h = $canvas.attr('height');
var w = this.canvas.width;
var h = this.canvas.height;
if(!w || !h)
return;
@ -81,30 +85,11 @@ TabCanvas.prototype = {
ctx.restore();
},
// ----------
// Function: animate
// Deprecated
animate: function(options, duration){
Utils.log('on animate', this.tab.contentWindow.location.href);
// TODO: This doesn't seem to scale the rest of the interface elements at the same
// width, leaving unfortunately long trails.
var self = this;
if( duration == null ) duration = 0;
var $canvas = $(this.canvas);
var w = $canvas.width();
var h = $canvas.height();
var newW = (w/h)*options.height;
var newH = options.height;
$canvas.width(w);
$canvas.height(h);
$canvas.animate({width:newW, height:newH}, duration, function(){
$canvas.attr("width", newW);
$canvas.attr("height", newH);
self.paint(null);
} );
this.paint(null);
Utils.assert('this routine no longer exists', false);
}
}
@ -116,23 +101,21 @@ function Mirror(tab, manager) {
this.tab = tab;
this.manager = manager;
var html = "<div class='tab'>" +
"<div class='favicon'><img/></div>" +
"<div class='thumb'><div class='thumbShadow'></div><canvas/></div>" +
"<span class='tab-title'>&nbsp;</span>" +
"</div>";
var div = $(html)
var $div = iQ('<div>')
.data("tab", this.tab)
.appendTo("body");
.addClass('tab')
.html("<div class='favicon'><img/></div>" +
"<div class='thumb'><div class='thumbShadow'></div><canvas/></div>" +
"<span class='tab-title'>&nbsp;</span>"
)
.appendTo('body');
this.needsPaint = 0;
this.canvasSizeForced = false;
this.el = div.get(0);
this.favEl = $('.favicon>img', div).get(0);
this.nameEl = $('.tab-title', div).get(0);
this.canvasEl = $('.thumb canvas', div).get(0);
this.el = $div.get(0);
this.favEl = iQ('.favicon>img', $div).get(0);
this.nameEl = iQ('.tab-title', $div).get(0);
this.canvasEl = iQ('.thumb canvas', $div).get(0);
var doc = this.tab.contentDocument;
if( !_isIframe(doc) ) {
@ -142,10 +125,10 @@ function Mirror(tab, manager) {
}
this.tab.mirror = this;
this.manager._customize(div);
this.manager._customize(this);
}
Mirror.prototype = $.extend(new Subscribable(), {
Mirror.prototype = iQ.extend(new Subscribable(), {
// ----------
// Function: triggerPaint
// Forces the mirror in question to update its thumbnail.
@ -160,9 +143,8 @@ Mirror.prototype = $.extend(new Subscribable(), {
// to stay that resolution until unforceCanvasSize is called.
forceCanvasSize: function(w, h) {
this.canvasSizeForced = true;
var $canvas = $(this.canvasEl);
$canvas.attr('width', w);
$canvas.attr('height', h);
this.canvasEl.width = w;
this.canvasEl.height = h;
this.tabCanvas.paint();
},
@ -246,12 +228,11 @@ TabMirror.prototype = {
if(mirror) {
var iconUrl = tab.raw.linkedBrowser.mIconURL;
var label = tab.raw.label;
$fav = $(mirror.favEl);
$name = $(mirror.nameEl);
$canvas = $(mirror.canvasEl);
$name = iQ(mirror.nameEl);
$canvas = iQ(mirror.canvasEl);
if(iconUrl != $fav.attr("src")) {
$fav.attr("src", iconUrl);
if(iconUrl != mirror.favEl.src) {
mirror.favEl.src = iconUrl;
mirror.triggerPaint();
}
@ -270,9 +251,9 @@ TabMirror.prototype = {
if(!mirror.canvasSizeForced) {
var w = $canvas.width();
var h = $canvas.height();
if(w != $canvas.attr('width') || h != $canvas.attr('height')) {
$canvas.attr('width', w);
$canvas.attr('height', h);
if(w != mirror.canvasEl.width || h != mirror.canvasEl.height) {
mirror.canvasEl.width = w;
mirror.canvasEl.height = h;
mirror.triggerPaint();
}
}
@ -333,7 +314,7 @@ TabMirror.prototype = {
if(tabCanvas)
tabCanvas.detach();
$(mirror.el).remove();
iQ(mirror.el).remove();
tab.mirror = null;
}
@ -350,14 +331,13 @@ window.TabMirror = {
// Parameters:
// func - a callback function that will be called every time a new
// tab or tabs are created. func should take in one parameter, a
// jQuery object representing the div enclosing the tab in question.
// This jQuery object may be singular or multiple, depending on
// the number of tabs being created.
// <Mirror> representing the tab in question.
customize: function(func) {
// Apply the custom handlers to all existing elements
// TODO: Make this modular: so that it only exists in one place.
// No breaking DRY!
func($("div.tab"));
iQ('div.tab').each(function() {
var tab = Tabs.tab(this);
func(tab.mirror);
});
// Apply it to all future elements.
TabMirror.prototype._customize = func;

View File

@ -277,9 +277,12 @@ window.TabsManager = iQ.extend(new Subscribable(), {
// ----------
tab: function tab(value) {
// assuming value is a DOM element for the time being
var result = $(value).data('tab');
if(!result)
result = $(value).find("canvas").data("link").tab;
var result = iQ(value).data('tab');
if(!result) {
result = iQ(value).find("canvas").data("link").tab;
if(result)
Utils.log('turns out the secondary strategy in Tabs.tab() is needed');
}
return result;
},

View File

@ -12,8 +12,6 @@
<body>
<a href="http://feedback.mozillalabs.com/forums/56804-tabcandy" target="new"><div id="feedback">give feedback</div></a>
<div id="bg" />
<script type="text/javascript;version=1.8">
</script>
<script type="text/javascript;version=1.8" src="../../js/optional/stacktrace.js"></script>
<script type="text/javascript;version=1.8" src="../../js/core/jquery.js"></script>
@ -21,23 +19,17 @@
<script type="text/javascript;version=1.8" src="../../js/optional/jquery-tabcandy-easing.js"></script>
<script type="text/javascript;version=1.8" src="../../js/optional/jquery-ui.js"></script>
<script type="text/javascript;version=1.8" src="../../js/core/utils.js"></script>
<script type="text/javascript;version=1.8" src="js/storage.js"></script>
<script type="text/javascript;version=1.8" src="../../js/core/toolbar-button.js"></script>
<script type="text/javascript;version=1.8">
Utils.log('it begins! --------------------');
/* Utils.testLogging(); */
</script>
<script type="text/javascript;version=1.8" src="js/storage.js"></script>
<script type="text/javascript;version=1.8" src="../../js/core/toolbar-button.js"></script>
<script type="text/javascript;version=1.8" src="../../js/core/tabs.js"></script>
<script type="text/javascript;version=1.8" src="../../js/core/mirror.js"></script>
<script type="text/javascript;version=1.8" src="js/items.js"></script>
<script type="text/javascript;version=1.8" src="js/tabitems.js"></script>
<script type="text/javascript;version=1.8" src="js/groups.js"></script>
<!-- BEGIN Switch Control -->
<script type="text/javascript;version=1.8" src="../../js/optional/switch.js"></script>
<!-- We create the actual switch in ui.js -->
<!-- END Switch Control -->
<script type="text/javascript;version=1.8" src="js/ui.js"></script>
</body>