Tagger: 20070930 0153

git-svn-id: file:///home/shish/svn/shimmie2/trunk@505 7f39781d-f577-437e-ae19-be835c7a54ca
This commit is contained in:
Artanis 2007-09-30 08:53:47 +00:00
parent 6f69f52614
commit ced91f14dd
5 changed files with 417 additions and 362 deletions

View File

@ -30,9 +30,9 @@ class tagger extends Extension {
$base_href = $config->get_string('base_href');
$tags_min = $config->get_int('ext-tagger_tags-min',2);
$hidden = $config->get_bool(
'ext-tagger_respect-hidden',
true) ? "AND substring(tag,1,1) != '.' " : null;
$hidden = $config->get_string(
'ext-tagger_show-hidden','N')=='N' ?
" AND substring(tag,1,1) != '.' " : null;
$tags = $database->Execute("
SELECT tag

View File

@ -3,9 +3,18 @@
// Do not remove this notice.
// All other code copyright by their authors, see comments for details.
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Tagger Management *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
var remove_tagme = null;
function taggerInit() {
taggerResetPos();
tagger_tagIndicators()
DragHandler.attach(byId("tagger_titlebar"));
remove_tagme = byId('tagme');
}
function taggerResetPos() {
// In case the drag point goes off screen.
tagger = byId("tagger_window");
tagger.style.top="";
@ -22,86 +31,120 @@ function taggerResetPos() {
tagger.style.bottom="";
}
function tagExists(tag) {
var tags = byId("tags");
tags_list = tags.value;
tags_array = tags_list.split(" ");
function tagger_tagIndicators() {
tags = byId("tags");
arObjTags = getElementsByTagNames('a',byId('tagger_body'));
tags_list = "";
for (x in tags_array) {
if(tags_array[x] == tag) {
return true;
for (i in arObjTags) {
objTag = arObjTags[i];
if(tagExists(objTag)) {
objTag.style.fontWeight="bold";
}
}
return false;
}
function toggleTag(tag,rTagme) {
if (!tagExists(tag)) {
addTag(tag);
if(rTagme && tag != "tagme") {
remTag("tagme");
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Tagging *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function toggleTag(objTag) {
if(!tagExists(objTag)) {
addTag(objTag);
if (remove_tagme && objTag.attributes.tag.value != 'tagme') {
remTag(remove_tagme);
}
} else {
remTag(tag);
remTag(objTag);
}
obj = byId("tagger_custTag");
if(obj.value) {
obj.select();
t = byId("tagger_new-tag");
if(t.value) { t.select(); }
}
function addTag (objTag) {
var tags = byId('tags');
delim = tags.value==" "?"":" ";
tags.value += delim + objTag.attributes.tag.value;
if(objTag.value != 'Add') {
objTag.style.fontWeight = "bold";
}
}
function addTagById(id) {
tag = byId(id);
toggleTag(tag.value);
}
function addTag (tag) {
function remTag (objTag) {
var tags = byId("tags");
var tag_link = byId("tagger_tag_"+tag);
var delim = " ";
if(tags.value == "") {
delim="";
}
tags.value = tags.value + delim + tag;
if(tag_link) {
tag_link.style.fontWeight = "bold";
}
}
function remTag (tag) {
var tags = byId("tags");
var tag_link = byId("tagger_tag_"+tag);
_tags = tags.value.split(" ");
aTags = tags.value.split(" ");
tags.value="";
for (i in _tags) {
_tag = _tags[i];
if(_tag != tag) {
addTag(_tag);
for(i in aTags) {
aTag = aTags[i];
if(aTag != objTag.attributes.tag.value) {
if(tags.value=="") {
tags.value += aTag;
} else {
tags.value += " "+aTag;
}
}
if(tag_link) {
tag_link.style.fontWeight = "";
}
if(objTag.value != 'Add') {
objTag.style.fontWeight = "";
}
}
function setTagIndicators() {
taggerResetPos();
function tagExists(objTag) {
return byId("tags").value.match(reescape(objTag.attributes.tag.value));
}
tags = byId("tags");
tags = tags.value.split(" ");
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Filtering *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
for (x in tags) {
tag = tags[x];
obj = byId("tagger_tag_"+tag);
if(obj) {
obj.style.fontWeight="bold";
var tagger_filter_focus = false;
function tagger_filter(override) {
if(tagger_filter_focus || override) {
var filter = byId('tagger_new-tag');
var arObjTags = getElementsByTagNames('a',byId('tagger_body'));
var prepend = filter.value.length<2? " ":"_";
var search = prepend + reescape(filter.value);
for(i in arObjTags) {
objTag = arObjTags[i];
tag = prepend + objTag.attributes.tag.value;
if(tag.match(search) && taggerFilterMode(objTag)) {
objTag.style.display='';
} else {
objTag.style.display='none';
}
}
}
}
function taggerToggleMode() {
var obj = byId('tagger_mode');
if(obj.attributes.mode.value=='all') {
obj.attributes.mode.value='applied';
obj.innerHTML = 'View All Tags';
} else {
obj.attributes.mode.value='all';
obj.innerHTML = 'View Applied Tags';
}
tagger_filter(true);
}
function taggerFilterMode(objTag) {
var obj = byId('tagger_mode');
if(obj.attributes.mode.value == 'all') {
return true;
} else {
return objTag.style.fontWeight=='bold';
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* Forms *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function pushSet(form_id) {
var set = getSetButton(form_id);
@ -121,35 +164,11 @@ function getSetButton(form_id) {
return false;
}
var _f_custTag = false;
function tagger_filter(id) {
if (_f_custTag) {
var filter = byId(id);
var e;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
* quirksmode.org *
\* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
search = filter.value;
// set up single letter filters for first-letter matching only.
if (search.length == 1)
search = " "+search;
tag_links = getElementsByTagNames('div',byId('tagger_body'));
for (x in tag_links) {
tag_id = tag_links[x].id;
// remove tagger_tag from id, prepend space for first-letter matching.
tag = " "+tag_id.replace(/tagger_tag_/,"");
e = byId(tag_id);
if (!tag.match(search)) {
e.style.display = 'none';
} else {
e.style.display = '';
}
}
}
}
// Quirksmode.org //
// http://www.quirksmode.org/dom/getElementsByTagNames.html //
// http://www.quirksmode.org/dom/getElementsByTagNames.html
function getElementsByTagNames(list,obj) {
if (!obj) var obj = document;
var tagNames = list.split(',');
@ -174,7 +193,8 @@ function getElementsByTagNames(list,obj) {
}
return resultArray;
}
// http://www.quirksmode.org/js/findpos.html //
// http://www.quirksmode.org/js/findpos.html
function findPos(obj) {
var curleft = curtop = 0;
if (obj.offsetParent) {
@ -187,163 +207,24 @@ function findPos(obj) {
}
return [curleft,curtop];
}
// End //
// Drag Code //
//*****************************************************************************
// Do not remove this notice.
//
// Copyright 2001 by Mike Hall.
// See http://www.brainjar.com for terms of use.
//*****************************************************************************
// Determine browser and version.
function Browser() {
var ua, s, i;
this.isIE = false;
this.isNS = false;
this.version = null;
ua = navigator.userAgent;
s = "MSIE";
if ((i = ua.indexOf(s)) >= 0) {
this.isIE = true;
this.version = parseFloat(ua.substr(i + s.length));
return;
}
s = "Netscape6/";
if ((i = ua.indexOf(s)) >= 0) {
this.isNS = true;
this.version = parseFloat(ua.substr(i + s.length));
return;
}
// Treat any other "Gecko" browser as NS 6.1.
s = "Gecko";
if ((i = ua.indexOf(s)) >= 0) {
this.isNS = true;
this.version = 6.1;
return;
// ripped from a forum.
// todo: cite source
function reescape(str){
var resp="()?:=[]*+{}^$|/,.!\\"
var found=false
var ret=""
for(var i=0;i<str.length;i++) {
found=false
for(var j=0;j<resp.length;j++) {
if(str.charAt(i)==resp.charAt(j)) {
found=true;break
}
}
var browser = new Browser();
// Global object to hold drag information.
var dragObj = new Object();
dragObj.zIndex = 0;
function dragStart(event, id) {
var el;
var x, y;
// If an element id was given, find it. Otherwise use the element being
// clicked on.
if (id)
dragObj.elNode = document.getElementById(id);
else {
if (browser.isIE)
dragObj.elNode = window.event.srcElement;
if (browser.isNS)
dragObj.elNode = event.target;
// If this is a text node, use its parent element.
if (dragObj.elNode.nodeType == 3)
dragObj.elNode = dragObj.elNode.parentNode;
if(found) {
ret+="\\"
}
// Get cursor position with respect to the page.
if (browser.isIE) {
x = window.event.clientX + document.documentElement.scrollLeft
+ document.body.scrollLeft;
y = window.event.clientY + document.documentElement.scrollTop
+ document.body.scrollTop;
ret+=str.charAt(i)
}
if (browser.isNS) {
x = event.clientX + window.scrollX;
y = event.clientY + window.scrollY;
return ret
}
// Save starting positions of cursor and element.
dragObj.cursorStartX = x;
dragObj.cursorStartY = y;
dragObj.elStartLeft = parseInt(dragObj.elNode.style.left, 10);
dragObj.elStartTop = parseInt(dragObj.elNode.style.top, 10);
if (isNaN(dragObj.elStartLeft)) dragObj.elStartLeft = 0;
if (isNaN(dragObj.elStartTop)) dragObj.elStartTop = 0;
// Update element's z-index.
dragObj.elNode.style.zIndex = ++dragObj.zIndex;
// Capture mousemove and mouseup events on the page.
if (browser.isIE) {
document.attachEvent("onmousemove", dragGo);
document.attachEvent("onmouseup", dragStop);
window.event.cancelBubble = true;
window.event.returnValue = false;
}
if (browser.isNS) {
document.addEventListener("mousemove", dragGo, true);
document.addEventListener("mouseup", dragStop, true);
event.preventDefault();
}
}
function dragGo(event) {
var x, y;
// Get cursor position with respect to the page.
if (browser.isIE) {
x = window.event.clientX + document.documentElement.scrollLeft
+ document.body.scrollLeft;
y = window.event.clientY + document.documentElement.scrollTop
+ document.body.scrollTop;
}
if (browser.isNS) {
x = event.clientX + window.scrollX;
y = event.clientY + window.scrollY;
}
// Move drag element by the same amount the cursor has moved.
dragObj.elNode.style.left = (dragObj.elStartLeft + x - dragObj.cursorStartX) + "px";
dragObj.elNode.style.top = (dragObj.elStartTop + y - dragObj.cursorStartY) + "px";
if (browser.isIE) {
window.event.cancelBubble = true;
window.event.returnValue = false;
}
if (browser.isNS)
event.preventDefault();
}
function dragStop(event) {
// Stop capturing mousemove and mouseup events.
if (browser.isIE) {
document.detachEvent("onmousemove", dragGo);
document.detachEvent("onmouseup", dragStop);
}
if (browser.isNS) {
document.removeEventListener("mousemove", dragGo, true);
document.removeEventListener("mouseup", dragStop, true);
}
}
// End //

View File

@ -4,40 +4,48 @@
* Do not remove this notice. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#tagger_window {
position:fixed;
#Tagger {
text-align:left;
}
#tagger_titlebar {
font-weight:bold;
text-align:center;
position:relative;
top:-0em;
padding:.25em;
border:2px solid;
-moz-border-radius:1em;
background-image:none;
background-color:#DDDDDD;
margin-bottom:.5em;
cursor:move;
#tagger_window {
position:fixed;
text-align:left;
min-width:250px;
}
#tagger_body, #tagger_filter {
overflow:scroll;
padding:1em;
#tagger_titlebar {
background-color:#DDDDDD;
background-image:none;
border:2px solid;
cursor:move;
font-weight:bold;
/*margin-bottom:.5em;*/
-moz-border-radius:2em 2em 0 0;
padding:.25em;
position:relative;
text-align:center;
top:-0em;
}
#tagger_body {
background-color:#EEEEEE;
border:2px solid;
border-top:none;
background-color:white;
overflow:scroll;
padding:1em;
max-height:200px;
}
#tagger_filter {
overflow:hidden;
-moz-border-radius:1em 1em 0 0;
background-color:#EEEEEE;
border:2px solid;
border-bottom:none;
border-top:none;
margin-bottom:-1px;
text-align:center;
/*-moz-border-radius:1em 1em 0 0;*/
padding:1em;
padding-bottom:1px;
text-align:center;
}
#tagger_filter input {
width:auto;
@ -45,9 +53,20 @@
#tagger_body a {
font-size:1.25em;
display:block;
cursor:pointer;
}
#Tagger li {
margin-bottom:1em;
width:20em;
#Use #Tagger {
-moz-column-gap:20px;
-moz-column-count:3;
}
#Use #Tagger li {
margin-bottom:1em;
max-width:30em;
}
#Tagger .tagger_js {
cursor:pointer;
}

View File

@ -4,108 +4,178 @@
// Do not remove this notice.
class taggerTheme extends Themelet {
public function build ($page, $tags) {
public function build($event,$tags) {
// When overriding this function, take care that all tag attributes are
// maintained UNCHANGED. There are no attributes in the HTML code below
// that go unused by the javascript. If you fail to do this,
// the extension will BREAK!
//
// Now that that's sunk in, chaniging title attributes is fine.
global $config;
$tagme = $config->get_string(
"ext-tagger_clear-tagme","N") == "Y" ?"true":"false";
$base_href = $config->get_string("base_href");
$tag_html = "";
foreach ($tags as $tag) {
$tag_name = $tag['tag'];
$tag_trunc = $this->trimTag($tag_name,20,"_");
$tag_html .= "
<div id='tagger_tag_".$tag_name."'>
<a style='cursor:pointer;'
onclick='toggleTag(&quot;".$tag_name."&quot;,".$tagme.");'
title='Add &quot;".$tag_name."&quot; to the tag list'>".$tag_trunc."
</a>
</div>";
}
$url_more = make_link("about/tagger");
$html = <<<EOD
<img style='display:none;' src='$base_href/ext/tagger/onload.gif'
onload='setTagIndicators();'/>
<span style="font-size:.7em;">Collapse this block to hide Tagger.</span>
<br/>
<a onclick="taggerResetPos();" style="cursor:pointer;">Default Location</a>
<hr/>
<a href='$url_more'>About Tagger</a>
<div id="tagger_window" style="bottom:25px;right:25px;">
<div id="tagger_titlebar" title="Drag to move"
onmousedown="dragStart(event,&quot;tagger_window&quot;);">
Tagger
</div>
<div id="tagger_filter">
<input type="text" id="tagger_custTag" value="" onfocus="_f_custTag = true;"
onblur="_f_custTag = false;"
onkeyup="tagger_filter(&quot;tagger_custTag&quot;)" size='12'>
</input>
<input type="button" value="Add"
onclick="addTagById(&quot;tagger_custTag&quot;)">
</input> <input type="button" onclick="pushSet(&quot;imgdata&quot;);"
value="Set">
</input>
<hr/>
</div>
<div id="tagger_body" style="height:200px;">$tag_html</div>
</div>
EOD;
$page->add_block( new Block("Tagger - Advanced Tagging",
"".$html,
"left",
50));
}
public function trimTag($s,$len=80,$break=" ") {
if(strlen($s) > $len) {
$s = substr($s, 0,$len-1);
$s = substr($s,0, strrpos($s,$break))."...";
}
return $s;
}
public function show_about ($event) {
global $page;
$html = <<<EOD
<ul id="Tagger">
// set up data
$base_href = $config->get_string('base_href');
$tagme = $config->get_string(
'ext-tagger_clear-tagme','N')=="Y"?
"<input type='hidden' id='tagme' tag='tagme'></input>":
null;
$url_about = make_link("about/tagger");
// build floater tags
$h_tags = "";
foreach($tags as $tag) {
$h_tags .= $this->tag_to_html($tag);
}
$html = "
<img src='$base_href/ext/tagger/onload.gif' style='display:none;'
onload='taggerInit();' />
<span style='font-size:0.7em;'>
Collapse this block to hide Tagger
</span>
<br/>
<a onclick='taggerResetPos();' class='tagger_js'>Default Location</a>
<hr/>
<a href='$url_about'>About Tagger</a>".
// Tagger Floater
"<div id='tagger_window'>
<div id='tagger_titlebar' title='Drag to move'>Tagger</div>
<div id='tagger_filter'>
<input type='text' id='tagger_new-tag' value='' size='12'
onfocus='tagger_filter_focus = true;'
onblur='tagger_filter_focus = false;'
onkeyup='tagger_filter();' focus='' title='Type to search' >
</input>
<input type='button' value='Add' tag='' title='Add typed tag'
onclick='
this.attributes.tag.value=
byId(\"tagger_new-tag\").value;
toggleTag(this);'>
</input>
<input type='button' value='Set' onclick='pressSet();'
title='Save tags'></input>
$tagme
<hr/>
<a id='tagger_mode' class='tagger_js' mode='all'
onclick='taggerToggleMode()'>View Applied Tags</a> |
<a onclick='tagger_tagIndicators(); tagger_filter(true);'
class='tagger_js' >Refresh Filter</a>
</div>
<div id='tagger_body'>$h_tags</div>
</div>";
$page->add_block( new Block(
"Tagger",
$html,
"left"));
$page->add_header(
"<script
src='$base_href/ext/tagger/webtoolkit.drag.js'
type='text/javascript'></script>");
}
final public function show_about ($event) {
// The about page for Tagger. No override. Feel free to CSS it, though.
global $page;
global $config;
$base_href = $config->get_string('base_href');
$script1 = "$base_href/ext/tagger/script.js";
$script2 = "$base_href/ext/tagger/webtoolkit.drag.js";
$html = str_replace("\"","&quot;",str_replace("\'","&#39;","
<ul id='Tagger'>
<li>
If Tagger is in you way, click and drag it&#39;s title bar to move it to a
more convienient location.
If Tagger is in your way, click and drag it\'s title bar to move
it to a more convienient location.
<li>
Click the links to add the tag to the image&#39;s tag list, when done, press
the Set button to save the tags.
Click the links to add the tag to the image\'s tag list, when
done, press the Set button to save the tags.
</li>
<li>
<p>Tagger gets all the tags in use with 2 or more uses, so the list can get
quite large. If you are having trouble finding the tag you are looking for,
you can enter it into the box at the top and as you type, Tagger will remove
tags that do not match to aid your search. Usually, you&#39;ll only need one
or two letters to trim the list down to the tag you are looking for.</p>
<p>If the tag is not in the list, finish typing out the tag and click "Add"
to add the tag to the image&#39;s tag list.</p>
<p>Tags must have two uses to appear in Tagger&#39;s list, so you'll have to
enter the tag for at least one other image for it to show up.</p>
<p>Tagger gets all the tags with 2 or more uses, so the list can
get quite large. If you are having trouble finding the tag you
are looking for, you can enter it into the box at the top and as
you type, Tagger will remove tags that do not match to aid your
search. Usually, you\'ll only need one or two letters to trim
the list down to the tag you are looking for.
</p>
<p>One letter filters will look only at the first letter of the
tag. Two or more letters will search the beginning of every
word in every tag.
</p>
<p>If the tag is not in the list, finish typing out the tag and
click \"Add\" to add the tag to the image\'s tag list.
</p>
<p>Tags must have two uses to appear in Tagger\'s list, so
you'll have to enter the tag for at least one other image for it
to show up.
</p>
</li>
<li>
<p>Tagger requires javascript for its functionality. Sorry, but there&#39;s
no other way to accomplish the tag list modifications.</p>
<p>If you have javascript completely disabled, you will not be able to use
Tagger.</p>
<p>Due to the manner in which Tagger is constructed, it will hide along with
it&#39;s block on the side bar and block behaviour will remember that
setting in shimmie&#39;s cookies.</p>
<li><h4>Requirements</h4>
<p>Tagger requires javascript for its functionality. Sorry, but
there\'s no other way to accomplish the tag list
modifications.
</p>
<p>If you have javascript completely disabled, you will not be
able to use Tagger.
</p>
<p>Depending on your method of disabling javascript, you may be
able to whitelist scripts. The script files used by Tagger are
<a href='$script1'>script.js</a> and
<a href='$script2'>webtoolkit.drag.js</a>.
</p>
</li>
</ul>
EOD;
$page->set_title("About / Extension / Tagger - Advanced Tagging");
$page->set_heading("About / Extension / Tagger - Advanced Tagging");
</ul>"));
$page->set_title("Shimmie - About / Tagger - Advanced Tagging");
$page->set_heading("About / Tagger - Advanced Tagging");
$page->add_block( new Block("Author",
"Artanis (Erik Youngren &lt;artanis.00@gmail.com&gt;)","main",0));
$page->add_block( new Block("Use", $html,"main",1));
}
final function tag_to_html ($tag) {
// Important for script.js, no override. You can CSS this, though.
// If you must, remove the 'final' keyword, but MAKE SURE the entire <A>
// tag is COPIED TO THE NEW FUNCTION EXACTLY! If you fail to do this,
// the extension will BREAK!
$tag_name = $tag['tag'];
$tag_id = $this->tag_id($tag_name);
$stag = $this->trimTag($tag_name,20,"_");
$html = "
<a id='$tag_id' title='Apply &quot;$tag_name&quot;' tag='$tag_name'
onclick='toggleTag(this)'>$stag</a>";
return $html;
}
// Important for script.js, no override.
final function tag_id ($tag) {
$tag_id = "";
$m=null;
for($i=0; $i < strlen($tag); $i++) {
$l = substr($tag,$i,1);
$m=null;
preg_match("[\pP]",$l,$m);
$tag_id .= !isset($m[0]) ? $l:"_";
}
return trim(str_replace("__","_",$tag_id)," _");
}
function trimTag($s,$len=80,$br=" ") {
if(strlen($s) > $len) {
$s = substr($s, 0,$len-1);
$s = substr($s,0, strrpos($s,$br))."...";
}
return $s;
}
}
?>

View File

@ -0,0 +1,85 @@
/**
*
* Crossbrowser Drag Handler
* http://www.webtoolkit.info/
*
* Modified by Erik Youngren to move parent node
**/
var DragHandler = {
// private property.
_oElem : null,
// public method. Attach drag handler to an element.
attach : function(oElem) {
oElem.onmousedown = DragHandler._dragBegin;
// callbacks
oElem.dragBegin = new Function();
oElem.drag = new Function();
oElem.dragEnd = new Function();
return oElem;
},
// private method. Begin drag process.
_dragBegin : function(e) {
var oElem = DragHandler._oElem = this;
if (isNaN(parseInt(oElem.parentNode.style.left))) { oElem.parentNode.style.left = '0px'; }
if (isNaN(parseInt(oElem.parentNode.style.top))) { oElem.parentNode.style.top = '0px'; }
var x = parseInt(oElem.parentNode.style.left);
var y = parseInt(oElem.parentNode.style.top);
e = e ? e : window.event;
oElem.mouseX = e.clientX;
oElem.mouseY = e.clientY;
oElem.dragBegin(oElem, x, y);
document.onmousemove = DragHandler._drag;
document.onmouseup = DragHandler._dragEnd;
return false;
},
// private method. Drag (move) element.
_drag : function(e) {
var oElem = DragHandler._oElem;
var x = parseInt(oElem.parentNode.style.left);
var y = parseInt(oElem.parentNode.style.top);
e = e ? e : window.event;
oElem.parentNode.style.left = x + (e.clientX - oElem.mouseX) + 'px';
oElem.parentNode.style.top = y + (e.clientY - oElem.mouseY) + 'px';
oElem.mouseX = e.clientX;
oElem.mouseY = e.clientY;
oElem.drag(oElem, x, y);
return false;
},
// private method. Stop drag process.
_dragEnd : function() {
var oElem = DragHandler._oElem;
var x = parseInt(oElem.parentNode.style.left);
var y = parseInt(oElem.parentNode.style.top);
oElem.dragEnd(oElem, x, y);
document.onmousemove = null;
document.onmouseup = null;
DragHandler._oElem = null;
}
}