Skip to content

Commit c95ea8d

Browse files
QuestlogQuestlog
Questlog
authored and
Questlog
committed
Initial commit
0 parents  commit c95ea8d

File tree

3 files changed

+383
-0
lines changed

3 files changed

+383
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Vanilla-MTG-Card-Tooltips

class.mtg.plugin.php

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<?php if (!defined('APPLICATION')) exit();
2+
3+
$PluginInfo['MTG'] = array(
4+
'Name' => 'MTG',
5+
'Description' => "Adds MTG BBcode.",
6+
'Version' => '0.1',
7+
'MobileFriendly' => TRUE,
8+
'Author' => "Questlog",
9+
'AuthorEmail' => '[email protected]',
10+
'AuthorUrl' => 'http://github.com/questlog'
11+
);
12+
13+
use Nbbc\BBCode as BBCode;
14+
15+
class SpoilersPlugin extends Gdn_Plugin {
16+
17+
public function bbcode_afterBBCodeSetup_handler($Sender) {
18+
$nbbc = $Sender->EventArguments['BBCode'];
19+
20+
$nbbc->addRule('mtg', [
21+
'mode' => BBCode::BBCODE_MODE_CALLBACK,
22+
'method' => [$this, 'doCard'],
23+
'allow_in' => ['listitem', 'block', 'columns', 'inline'],
24+
'content' => BBCode::BBCODE_REQUIRED
25+
]);
26+
}
27+
28+
public function doCard(BBCode $bbcode, $action, $name, $default, $params, $content) {
29+
30+
return "<a href=\"https://deckbox.org/mtg/". htmlspecialchars($content) ."\">" . htmlspecialchars($content) . "</a>";
31+
}
32+
33+
34+
public function DiscussionController_Render_Before(&$Sender) {
35+
$this->PrepareController($Sender);
36+
}
37+
38+
public function PostController_Render_Before(&$Sender) {
39+
$this->PrepareController($Sender);
40+
}
41+
42+
public function MessagesController_Render_Before(&$Sender) {
43+
$this->PrepareController($Sender);
44+
}
45+
46+
protected function PrepareController(&$Sender) {
47+
$Sender->AddJsFile('tooltip.js', 'plugins/MTG');
48+
}
49+
50+
}

js/tooltip.js

+332
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
// Initialize namespaces.
2+
if (typeof Deckbox == "undefined") Deckbox = {};
3+
Deckbox.ui = Deckbox.ui || {};
4+
5+
6+
/**
7+
* Main tooltip type. Will be initialized for both the image tooltips and for the wow tooltips, or simple text tooltips.
8+
*/
9+
Deckbox.ui.Tooltip = function(className, type) {
10+
this.el = document.createElement('div');
11+
this.el.className = className + ' ' + type;
12+
this.type = type;
13+
this.el.style.display = 'none';
14+
document.body.appendChild(this.el);
15+
this.tooltips = {};
16+
};
17+
18+
Deckbox.ui.Tooltip.prototype = {
19+
_padContent: function(content) {
20+
return "<table><tr><td>" + content + "</td><th style='background-position: right top;'></th></tr><tr>" +
21+
"<th style='background-position: left bottom;'/><th style='background-position: right bottom;'/></tr></table>";
22+
},
23+
24+
showWow: function(posX, posY, content, url, el) {
25+
/* IE does NOT escape quotes apparently. */
26+
url = url.replace(/"/g, "%22");
27+
/* Problematic with routes on server. */
28+
url = url.replace(/\?/g, "");
29+
30+
if (this.tooltips[url] && this.tooltips[url].content) {
31+
content = this._padContent(this.tooltips[url].content);
32+
} else {
33+
content = this._padContent('Loading...');
34+
this.tooltips[url] = this.tooltips[url] || {el: el};
35+
Deckbox._.loadJS(url);
36+
/* Remeber these for when (if) the register call wants to show the tooltip. */
37+
this.posX = posX; this.posY = posY;
38+
}
39+
40+
this.el.style.width = '';
41+
this.el.innerHTML = content;
42+
this.el.style.display = '';
43+
this.el.style.width = (20 + Math.min(330, this.el.childNodes[0].offsetWidth)) + 'px';
44+
this.move(posX, posY);
45+
},
46+
47+
showText: function(posX, posY, text) {
48+
this.el.innerHTML = text;
49+
this.el.style.display = '';
50+
this.move(posX, posY);
51+
},
52+
53+
showImage: function(posX, posY, image) {
54+
if (image.complete) {
55+
this.el.innerHTML = '';
56+
this.el.appendChild(image);
57+
} else {
58+
this.el.innerHTML = 'Loading...';
59+
image.onload = function() {
60+
var self = Deckbox._.tooltip('image');
61+
self.el.innerHTML = '';
62+
image.onload = null;
63+
self.el.appendChild(image);
64+
self.move(posX, posY);
65+
}
66+
}
67+
this.el.style.display = '';
68+
this.move(posX, posY);
69+
},
70+
71+
hide: function() {
72+
this.el.style.display = 'none';
73+
},
74+
75+
move: function(posX, posY) {
76+
// The tooltip should be offset to the right so that it's not exactly next to the mouse.
77+
posX += 15;
78+
posY -= this.el.offsetHeight / 3;
79+
80+
// Remeber these for when (if) the register call wants to show the tooltip.
81+
this.posX = posX;
82+
this.posY = posY;
83+
if (this.el.style.display == 'none') return;
84+
85+
var pos = Deckbox._.fitToScreen(posX, posY, this.el);
86+
87+
this.el.style.top = pos[1] + "px";
88+
this.el.style.left = pos[0] + "px";
89+
},
90+
91+
register: function(url, content) {
92+
this.tooltips[url].content = content;
93+
if (this.tooltips[url].el._shown) {
94+
this.el.style.width = '';
95+
this.el.innerHTML = this._padContent(content);
96+
this.el.style.width = (20 + Math.min(330, this.el.childNodes[0].offsetWidth)) + 'px';
97+
this.move(this.posX, this.posY);
98+
}
99+
}
100+
};
101+
Deckbox.ui.Tooltip.hide = function() {
102+
Deckbox._.tooltip('image').hide();
103+
Deckbox._.tooltip('wow').hide();
104+
Deckbox._.tooltip('text').hide();
105+
};
106+
107+
108+
Deckbox._ = {
109+
onDocumentLoad: function(callback) {
110+
if (window.addEventListener) {
111+
window.addEventListener("load", callback, false);
112+
} else {
113+
window.attachEvent && window.attachEvent("onload", callback);
114+
}
115+
},
116+
117+
preloadImg: function(link) {
118+
var img = document.createElement('img');
119+
img.style.display = "none"
120+
img.style.width = "1px"
121+
img.style.height = "1px"
122+
img.src = link.href + '/tooltip';
123+
return img;
124+
},
125+
126+
pointerX: function(event) {
127+
var docElement = document.documentElement,
128+
body = document.body || { scrollLeft: 0 };
129+
130+
return event.pageX ||
131+
(event.clientX +
132+
(docElement.scrollLeft || body.scrollLeft) -
133+
(docElement.clientLeft || 0));
134+
},
135+
136+
pointerY: function(event) {
137+
var docElement = document.documentElement,
138+
body = document.body || { scrollTop: 0 };
139+
140+
return event.pageY ||
141+
(event.clientY +
142+
(docElement.scrollTop || body.scrollTop) -
143+
(docElement.clientTop || 0));
144+
},
145+
146+
scrollOffsets: function() {
147+
return [
148+
window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft,
149+
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
150+
];
151+
},
152+
153+
viewportSize: function() {
154+
var ua = navigator.userAgent, rootElement;
155+
if (ua.indexOf('AppleWebKit/') > -1 && !document.evaluate) {
156+
rootElement = document;
157+
} else if (Object.prototype.toString.call(window.opera) == '[object Opera]' && window.parseFloat(window.opera.version()) < 9.5) {
158+
rootElement = document.body;
159+
} else {
160+
rootElement = document.documentElement;
161+
}
162+
163+
/* IE8 in quirks mode returns 0 for these sizes. */
164+
var size = [rootElement['clientWidth'], rootElement['clientHeight']];
165+
if (size[1] == 0) {
166+
return [document.body['clientWidth'], document.body['clientHeight']];
167+
} else {
168+
return size;
169+
}
170+
},
171+
172+
fitToScreen: function(posX, posY, el) {
173+
var scroll = Deckbox._.scrollOffsets(), viewport = Deckbox._.viewportSize();
174+
175+
/* decide if wee need to switch sides for the tooltip */
176+
/* too big for X */
177+
if ((el.offsetWidth + posX) >= (viewport[0] - 15) ) {
178+
posX = posX - el.offsetWidth - 20;
179+
}
180+
181+
/* If it's too high, we move it down. */
182+
if (posY - scroll[1] < 0) {
183+
posY += scroll[1] - posY + 5;
184+
}
185+
/* If it's too low, we move it up. */
186+
if (posY + el.offsetHeight - scroll[1] > viewport[1]) {
187+
posY -= posY + el.offsetHeight + 5 - scroll[1] - viewport[1];
188+
}
189+
190+
return [posX, posY];
191+
},
192+
193+
addEvent: function(obj, type, fn) {
194+
if (obj.addEventListener) {
195+
if (type == 'mousewheel') obj.addEventListener('DOMMouseScroll', fn, false);
196+
obj.addEventListener( type, fn, false );
197+
} else if (obj.attachEvent) {
198+
obj["e"+type+fn] = fn;
199+
obj[type+fn] = function() { obj["e"+type+fn]( window.event ); };
200+
obj.attachEvent( "on"+type, obj[type+fn] );
201+
}
202+
},
203+
204+
removeEvent: function(obj, type, fn) {
205+
if (obj.removeEventListener) {
206+
if(type == 'mousewheel') obj.removeEventListener('DOMMouseScroll', fn, false);
207+
obj.removeEventListener( type, fn, false );
208+
} else if (obj.detachEvent) {
209+
obj.detachEvent( "on"+type, obj[type+fn] );
210+
obj[type+fn] = null;
211+
obj["e"+type+fn] = null;
212+
}
213+
},
214+
215+
loadJS: function(url) {
216+
var s = document.createElement('s' + 'cript');
217+
s.setAttribute("type", "text/javascript");
218+
s.setAttribute("src", url);
219+
document.getElementsByTagName("head")[0].appendChild(s);
220+
},
221+
222+
loadCSS: function(url) {
223+
var s = document.createElement("link");
224+
s.type = "text/css";
225+
s.rel = "stylesheet";
226+
s.href = url;
227+
document.getElementsByTagName("head")[0].appendChild(s);
228+
},
229+
230+
needsTooltip: function(el) {
231+
if (el.getAttribute('data-tt')) return true;
232+
233+
var href;
234+
if (!el || !(el.tagName == 'A') || !(href = el.getAttribute('href'))) return false;
235+
if (el.className.match('no_tooltip')) return false;
236+
return href.match(/^https?:\/\/[^\/]*\/(mtg|wow|whi)\/.+/);
237+
},
238+
239+
tooltip: function(which) {
240+
if (which == 'image') return this._iT = this._iT || new Deckbox.ui.Tooltip('deckbox_i_tooltip', 'image');
241+
if (which == 'wow') return this._wT = this._wT || new Deckbox.ui.Tooltip('deckbox_tooltip', 'wow');
242+
if (which == 'text') return this._tT = this._tT || new Deckbox.ui.Tooltip('deckbox_t_tooltip', 'text');
243+
},
244+
245+
target: function(event) {
246+
var target = event.target || event.srcElement || document;
247+
/* check if target is a textnode (safari) */
248+
if (target.nodeType == 3) target = target.parentNode;
249+
return target;
250+
}
251+
};
252+
253+
/**
254+
* Bind the listeners.
255+
*/
256+
(function() {
257+
function onmouseover(event) {
258+
var el = Deckbox._.target(event);
259+
if (Deckbox._.needsTooltip(el)) {
260+
var no = el.getAttribute('data-nott'), url, img,
261+
posX = Deckbox._.pointerX(event), posY = Deckbox._.pointerY(event);
262+
if (!no) {
263+
el._shown = true;
264+
if (url = el.getAttribute('data-tt')) {
265+
showImage(el, url, posX, posY);
266+
} else if (el.href.match('/(mtg|whi)/')) {
267+
showImage(el, el.href + '/tooltip', posX, posY);
268+
} else {
269+
Deckbox._.tooltip('wow').showWow(posX, posY, null, el.href + '/tooltip', el);
270+
}
271+
}
272+
}
273+
}
274+
275+
function showImage(el, url, posX, posY) {
276+
var img = document.createElement('img');
277+
url = url.replace(/\?/g, ""); /* Problematic with routes on server. */
278+
img.src = url;
279+
img.height = 310;
280+
281+
setTimeout(function() {
282+
if (el._shown) Deckbox._.tooltip('image').showImage(posX, posY, img);
283+
}, 200);
284+
}
285+
286+
function onmousemove(event) {
287+
var el = Deckbox._.target(event), posX = Deckbox._.pointerX(event), posY = Deckbox._.pointerY(event);
288+
if (Deckbox._.needsTooltip(el)) {
289+
Deckbox._.tooltip('image').move(posX, posY);
290+
Deckbox._.tooltip('wow').move(posX, posY, el.href);
291+
}
292+
}
293+
294+
function onmouseout(event) {
295+
var el = Deckbox._.target(event);
296+
if (Deckbox._.needsTooltip(el)) {
297+
el._shown = false;
298+
Deckbox._.tooltip('image').hide();
299+
Deckbox._.tooltip('wow').hide();
300+
}
301+
}
302+
303+
function click(event) {
304+
Deckbox._.tooltip('image').hide();
305+
Deckbox._.tooltip('wow').hide();
306+
}
307+
308+
Deckbox._.addEvent(document, 'mouseover', onmouseover);
309+
Deckbox._.addEvent(document, 'mousemove', onmousemove);
310+
Deckbox._.addEvent(document, 'mouseout', onmouseout);
311+
Deckbox._.addEvent(document, 'click', click);
312+
313+
var protocol = (document.location.protocol == 'https:') ? 'https:' : 'http:';
314+
Deckbox._.loadCSS(protocol + '//deckbox.org/assets/external/deckbox_tooltip.css');
315+
/* IE needs more shit */
316+
317+
if (!!window.attachEvent && !(Object.prototype.toString.call(window.opera) == '[object Opera]')) {
318+
Deckbox._.loadCSS(protocol + '//deckbox.org/assets/external/deckbox_tooltip_ie.css');
319+
}
320+
321+
/* Preload the tooltip images. */
322+
Deckbox._.onDocumentLoad(function() {
323+
return;
324+
var allLinks = document.getElementsByTagName('a');
325+
for (var i = 0; i < allLinks.length; i ++) {
326+
var link = allLinks[i];
327+
if (Deckbox._.needsTooltip(link)) {
328+
document.body.appendChild(Deckbox._.preloadImg(link));
329+
}
330+
}
331+
});
332+
})();

0 commit comments

Comments
 (0)