var ImageEditor = Class.create(JSControl, { initialize: function($super, element){ $super(element); this.hs_ = new HandleStorage(this.element); this.imgbox_ = this.hs_.GetHandle("image-box"); if (!this.imgbox_) return; Element.clear(this.imgbox_); this.zoom_ = 1; var callbacks = {"zoom-out": this.zoomout, "zoom-in": this.zoomin, "zoom-original": this.zoomorig}; for (var key in callbacks){ var btn = this.hs_.GetHandle(key); if (btn) Event.observe(btn, "click", callbacks[key].bind(this)); } ["crop", "resize", "rotate", "bri-con"].each(function(key){ var btn = this.hs_.GetHandle(key); if (btn) Event.observe(btn, "click", this.set_mode.bind(this, key, (btn.getAttribute("controls") || "").split(" "))); }, this); var awnd = AControl.GetParentWindow(this.ID()); if (awnd){ awnd.RegisterResizeableChild(this.ID()); awnd.Subscribe("CHANGE", this.CHANGE, this); awnd.Subscribe("CLICK", this.CLICK, this); } Event.observe(this.imgbox_, "controls:update", this.update_controls.bindAsEventListener(this)); }, getImgDimsZoomed: function(){ if (!this.imgdims_) return null; var res = {}; for (var key in this.imgdims_) res[key] = (this.imgdims_[key] * this.zoom_) + "px"; return res; }, setImgZoom: function(){ if (this.zoom_ == 1) Element.setStyle(this.img_, {"width": "", "height": ""}); else if (!this.imgdims_) return; else if (this.imgdims_.width > 0 && this.imgdims_.height > 0) Element.setStyle(this.img_, this.getImgDimsZoomed()); switch (this.mode_){ case "crop": this.refresh_cropper_dims(); break; } }, imgload: function(){ this.imgdims_ = {"width": this.img_.width, "height": this.img_.height}; if (this.mode_ == "resize") this.set_control_values({"Width": int(this.imgdims_.width), "Height": int(this.imgdims_.height)}); this.setImgZoom(); }, imgerror: function(){ alert("Error loading image \"" + this.img_.src + "\""); }, SetAttribute: function($super, nam, val){ switch (nam){ case "src": if (this.img_ && this.img_.src === val) return; var img = new Image(); if (this.img_) this.imgbox_.replaceChild(img, this.img_); else this.imgbox_.appendChild(img); this.img_ = img; delete this.imgdims_; img.onload = this.imgload.bind(this); img.onerror = this.imgerror.bind(this); img.src = val; break; default: $super(nam, val); } }, SetTabOrder: function(tabbase){ this.imgbox_.tabIndex = tabbase++; ["zoom-out", "zoom-in", "zoom-original", "crop", "resize", "rotate", "bri-con"].each(function(hook){ var elem = this.hs_.GetHandle(hook); if (elem) elem.tabIndex = tabbase++; }, this); ["X", "Y", "Width", "Height", "Angle", "Brightness", "Contrast", "Apply", "Clear"].each(function(id){ var jscontrol = this.getChildControl(id); if (jscontrol && jscontrol.SetTabOrder) tabbase = jscontrol.SetTabOrder(tabbase); }, this); return tabbase; }, client_w: function(elem){ return elem.clientWidth || elem.offsetWidth; }, client_h: function(elem){ return elem.clientHeight || elem.offsetHeight; }, border_x: function(elem){ var result = 0; var nams = ["borderLeftWidth", "borderRightWidth"]; for (var iii = 0; iii < nams.length; ++iii) result += parseInt(Element.getStyle(elem, nams[iii]), 10) || 0; return result; }, border_y: function(elem){ var result = 0; var nams = ["borderTopWidth", "borderBottomWidth"]; for (var iii = 0; iii < nams.length; ++iii) result += parseInt(Element.getStyle(elem, nams[iii]), 10) || 0; return result; }, outer_w: function(elem){ return elem.offsetWidth + this.border_x(elem); }, outer_h: function(elem){ return elem.offsetHeight + this.border_y(elem); }, OnResize: function(){ var gridframe = Element.up(this.element, "div[type='gridFrame']"); if (!gridframe) return; var ancestor = this.element.parentNode; while (ancestor && ancestor.parentNode != gridframe) ancestor = ancestor.parentNode; if (!ancestor) return; var height_delta = this.client_h(ancestor) - this.client_h(this.imgbox_); var dims = {"width": this.client_w(gridframe), "height": this.client_h(gridframe)}; dims.height -= height_delta; var panel = this.hs_.GetHandle("panel"); Element.setStyle(this.imgbox_, {// "width": (dims.width - this.outer_w(panel) - this.border_x(this.imgbox_)) + "px", "height": (dims.height - this.border_y(this.imgbox_)) + "px"}); }, zoomfactor: 1.1, zoomout: function(){ this.zoom_ = Math.max(.01, this.zoom_ / this.zoomfactor); this.setImgZoom(); }, zoomin: function(){ this.zoom_ = Math.min(100, this.zoom_ * this.zoomfactor); this.setImgZoom(); }, zoomorig: function(){ this.zoom_ = 1; this.setImgZoom(); }, set_mode: function(mode, controls){ if (this.mode_ == mode) return; switch (this.mode_){ case "crop": this.hide_cropper(); break; case "resize": case "rotate": case "bri-con": if (this.changed_) this.apply_changes(); break; } if (typeof this.mode_callbacks_ != "undefined") this.mode_callbacks_.each(function(args){ Event.stopObserving.apply(null, args); }); delete this.mode_callbacks_; for (var row = this.hs_.GetHandle("controls").firstChild; row; row = row.nextSibling){ if (row.nodeType != Node.ELEMENT_NODE) continue; Element.setStyle(row, {"display": controls.indexOf(row.getAttribute("hook")) != -1 ? "" : "none"}); } if (mode == "crop"){ this.clear_controls(controls); this.disable_controls(controls); }else this.enable_controls(controls); switch (this.mode_ = mode){ case "crop": this.mode_callbacks_ = [[this.imgbox_, "mousedown", this.start_cropper.bindAsEventListener(this)]]; break; case "resize": if (this.imgdims_) this.set_control_values({"Width": int(this.imgdims_.width), "Height": int(this.imgdims_.height)}); break; case "rotate": this.set_control_values({"Angle": double(.0)}); break; case "bri-con": this.set_control_values({"Brightness": double(.0), "Contrast": double(.0)}); break; } if (typeof this.mode_callbacks_ != "undefined") this.mode_callbacks_.each(function(args){ Event.observe.apply(null, args); }); }, get_relpos: function(event){ var offs = Element.viewportOffset(this.imgbox_); var scrl = document.viewport.getScrollOffsets(); return {"x": Math.min(this.imgbox_.scrollWidth, Math.max(0, Event.pointerX(event) - scrl.left - offs.left)), // "y": Math.min(this.imgbox_.scrollHeight, Math.max(0, Event.pointerY(event) - scrl.top - offs.top))}; }, start_cropper: function(event){ if (!this.cropper_){ this.cropper_ = document.createElement("div"); Element.writeAttribute(this.cropper_, "class", "Cropper Initializing"); ["top", "bottom", "left", "right"].each(function(classname){ var ant = document.createElement("div"); Element.writeAttribute(ant, "class", "ants " + classname); this.cropper_.appendChild(ant); }, this); ["tl", "tr", "bl", "br"].each(function(classname){ var corner = document.createElement("div"); Element.writeAttribute(corner, "class", "corner resizer " + classname); corner.type_ = classname; this.cropper_.appendChild(corner); Event.observe(corner, "mousedown", this.start_cropper_resize.bindAsEventListener(this)); }, this); }else Element.addClassName(this.cropper_, "Initializing"); var pos = this.get_relpos(event); Element.setStyle(this.cropper_, {// "left": pos.x + "px", "top": pos.y + "px", "width": "0", "height": "0"}); this.cropper_.pos_ = pos; this.imgbox_.appendChild(this.cropper_); this.cropper_.callbacks_ = [ [this.imgbox_, "mousemove", this.update_cropper.bindAsEventListener(this)], [document, "mouseup", this.stop_cropper.bindAsEventListener(this)]]; this.cropper_.callbacks_.each(function(args){ Event.observe.apply(null, args); }); event.preventDefault(); }, get_dims: function(pos1, pos2){ return { "left": Math.min(pos1.x, pos2.x), "top": Math.min(pos1.y, pos2.y), "width": Math.abs(pos1.x - pos2.x), "height": Math.abs(pos1.y - pos2.y) }; }, get_dims_css: function(pos1, pos2){ var dims = this.get_dims(pos1, pos2); var style = {}; for (var key in dims) style[key] = dims[key] + "px"; return style; }, update_cropper: function(event){ var pos = this.get_relpos(event); pos.x = Math.min(pos.x, this.imgbox_.scrollWidth); pos.y = Math.min(pos.y, this.imgbox_.scrollHeight); if (this.cropper_ && this.cropper_.parentNode && this.cropper_.pos_){ Element.setStyle(this.cropper_, this.get_dims_css(pos, this.cropper_.pos_)); } event.preventDefault(); }, stop_cropper: function(event){ if (!this.cropper_) return; var pos = this.get_relpos(event); if (Math.min(this.cropper_.pos_.x, pos.x) > this.img_.width ||// Math.min(this.cropper_.pos_.y, pos.y) > this.img_.height) this.hide_cropper(); else{ if (this.img_){ this.cropper_.pos_.x = Math.min(this.cropper_.pos_.x, this.img_.width); this.cropper_.pos_.y = Math.min(this.cropper_.pos_.y, this.img_.height); pos.x = Math.min(pos.x, this.img_.width); pos.y = Math.min(pos.y, this.img_.height); } if (pos.x === this.cropper_.pos_.x && pos.y === this.cropper_.pos_.y) this.hide_cropper(); else if (this.cropper_.parentNode) this.set_cropper_dims(this.get_dims(pos, this.cropper_.pos_)); } this.cropper_.pos_ = null; if (typeof this.cropper_.callbacks_ !== "undefined"){ this.cropper_.callbacks_.each(function(args){ Event.stopObserving.apply(null, args); }); this.cropper_.callbacks_ = null; } Element.removeClassName(this.cropper_, "Initializing"); this.fire_cropper_dims(); }, set_cropper_dims: function(dims){ if (!this.cropper_) return; var style = {}; this.cropper_.dims_ = {}; for (var key in dims){ this.cropper_.dims_[key] = Math.round(dims[key] / this.zoom_); style[key] = dims[key] + "px"; } Element.setStyle(this.cropper_, style); }, refresh_cropper_dims: function(){ if (this.cropper_ && this.cropper_.parentNode && this.cropper_.dims_){ var style = {}; for (var key in this.cropper_.dims_) style[key] = (this.cropper_.dims_[key] * this.zoom_) + "px"; Element.setStyle(this.cropper_, style); } }, start_cropper_resize: function(event){ var corner = Event.element(event); if (!corner.type_) return; var dims = this.cropper_.dims_; var corner_pos = {}; var other_pos = {}; switch (corner.type_){ case "tl": corner_pos.x = dims.left; corner_pos.y = dims.top; other_pos.x = dims.left + dims.width; other_pos.y = dims.top + dims.height; break; case "tr": corner_pos.x = dims.left + dims.width; corner_pos.y = dims.top; other_pos.x = dims.left; other_pos.y = dims.top + dims.height; break; case "bl": corner_pos.x = dims.left; corner_pos.y = dims.top + dims.height; other_pos.x = dims.left + dims.width; other_pos.y = dims.top; break; case "br": corner_pos.x = dims.left + dims.width; corner_pos.y = dims.top + dims.height; other_pos.x = dims.left; other_pos.y = dims.top; break; default: return; } var pos = this.get_relpos(event); corner.diff_ = {"x": pos.x - corner_pos.x * this.zoom_, "y": pos.y - corner_pos.y * this.zoom_}; other_pos.x *= this.zoom_; other_pos.y *= this.zoom_; corner.other_pos_ = other_pos; this.cropper_.corner_ = corner; if (!corner.callbacks_) corner.callbacks_ = [ [this.imgbox_, "mousemove", this.update_cropper_resize.bindAsEventListener(this)], [document, "mouseup", this.stop_cropper_resize.bindAsEventListener(this)] ]; corner.callbacks_.each(function(args){ Event.observe.apply(null, args); }); Event.stop(event); }, update_cropper_resize: function(event){ var corner = this.cropper_.corner_; if (!corner) return; var pos = this.get_relpos(event); if (corner.diff_ && corner.other_pos_){ for (var key in pos) pos[key] = Math.max(pos[key] - corner.diff_[key], 0); Element.setStyle(this.cropper_, this.get_dims_css(pos, corner.other_pos_)); } event.preventDefault(); }, stop_cropper_resize: function(event){ var corner = this.cropper_.corner_; if (!corner) return; var pos = this.get_relpos(event); for (var key in pos) pos[key] = Math.max(pos[key] - corner.diff_[key], 0); corner.diff_ = null; if (this.img_){ corner.other_pos_.x = Math.min(corner.other_pos_.x, this.img_.offsetWidth); corner.other_pos_.y = Math.min(corner.other_pos_.y, this.img_.offsetHeight); pos.x = Math.min(pos.x, this.img_.offsetWidth); pos.y = Math.min(pos.y, this.img_.offsetHeight); } this.set_cropper_dims(this.get_dims(pos, corner.other_pos_)); corner.other_pos_ = null; if (corner.callbacks_) corner.callbacks_.each(function(args){ Event.stopObserving.apply(null, args); }); this.cropper_.corner_ = null; this.fire_cropper_dims(); }, fire_cropper_dims: function(){ if (!this.cropper_ || !this.cropper_.parentNode || !this.cropper_.dims_) return; var dims = this.cropper_.dims_; Element.fire(this.cropper_, "controls:update", // {"X": int(dims.left), "Y": int(dims.top), "Width": int(dims.width), "Height": int(dims.height)}); }, getChildControl: function(localid){ var elem = document.getElementById(this.ID() + "." + localid); return elem ? elem.JSControl : null; }, update_controls: function(event){ this.set_control_values(event.memo); this.enable_controls(Object.keys(event.memo)); }, set_control_values: function(args){ for (var key in args){ var jscontrol = this.getChildControl(key); if (jscontrol) jscontrol.SetValue(args[key]); } }, hide_cropper: function(){ if (this.cropper_ && this.cropper_.parentNode) this.cropper_.parentNode.removeChild(this.cropper_); if (this.mode_ == "crop"){ var btn = this.hs_.GetHandle("crop"); if (!btn) return; var ids = (btn.getAttribute("controls") || "").split(" "); this.clear_controls(ids); this.disable_controls(ids); } }, CHANGE: function(aevent){ var emitter = aevent.Emitter(); if (!this.isChildControl(emitter)) return; var localid = document.getElementById(emitter.ID()).getAttribute("localid"); var val = emitter.Value(); while (val instanceof Optional) val = val.Value(); switch (this.mode_){ case "crop": if (!(val instanceof Int)) return; val = val.Value(); if (!this.cropper_ || !this.cropper_.parentNode || !this.cropper_.dims_) return; switch (localid){ case "X": this.cropper_.dims_.left = val; break; case "Y": this.cropper_.dims_.top = val; break; case "Width": this.cropper_.dims_.width = val; break; case "Height": this.cropper_.dims_.height = val; break; default: return; } this.refresh_cropper_dims(); break; case "resize": if (["Width", "Height"].indexOf(localid) == -1) return; new AEvent("RESIZE-PREVIEW", {"args": this.collect_values("Width", "Height")}, this); this.changed_ = true; break; case "rotate": if (localid != "Angle") return; new AEvent("ROTATE-PREVIEW", {"args": this.collect_values("Angle")}, this); this.changed_ = true; break; case "bri-con": if (["Brightness", "Contrast"].indexOf(localid) == -1) return; new AEvent("BRICON-PREVIEW", {"args": this.collect_values("Brightness", "Contrast")}, this); this.changed_ = true; break; } }, collect_values: function(){ var result = {}; var ids = arguments; for (var iii = 0; iii < ids.length; ++iii){ var jscontrol = this.getChildControl(ids[iii]); if (jscontrol) result[ids[iii]] = jscontrol.Value(); } return result; }, apply_changes: function(){ switch (this.mode_){ case "crop": new AEvent("CROP", {"args": this.collect_values("X", "Y", "Width", "Height")}, this); this.hide_cropper(); break; case "resize": new AEvent("RESIZE", {"args": this.collect_values("Width", "Height")}, this); this.changed_ = false; break; case "rotate": new AEvent("ROTATE", {"args": this.collect_values("Angle")}, this); this.set_control_values({"Angle": double(.0)}); this.changed_ = false; break; case "bri-con": new AEvent("BRICON", {"args": this.collect_values("Brightness", "Contrast")}, this); this.set_control_values({"Brightness": double(.0), "Contrast": double(.0)}); this.changed_ = false; break; } }, CLICK: function(aevent){ var emitter = aevent.Emitter(); if (!this.isChildControl(emitter)) return; var localid = document.getElementById(emitter.ID()).getAttribute("localid"); if (localid == "Apply") this.apply_changes(); else if (localid == "Clear") switch (this.mode_){ case "crop": this.hide_cropper(); break; case "resize": if (this.changed_) this.refresh(); break; case "rotate": if (this.changed_) this.refresh(); this.set_control_values({"Angle": double(.0)}); break; case "bri-con": if (this.changed_) this.refresh(); this.set_control_values({"Brightness": double(.0), "Contrast": double(.0)}); break; } }, refresh: function(){ new AEvent("REFRESH", {}, this); this.changed_ = false; }, enable_controls: function(ids){ for (var iii = 0; iii < ids.length; ++iii){ var jscontrol = this.getChildControl(ids[iii]); if (jscontrol) jscontrol.SetAttribute("visibility", "yes"); } }, disable_controls: function(ids){ for (var iii = 0; iii < ids.length; ++iii){ var jscontrol = this.getChildControl(ids[iii]); if (jscontrol) jscontrol.SetAttribute("visibility", "ro"); } }, clear_controls: function(ids){ for (var iii = 0; iii < ids.length; ++iii){ var jscontrol = this.getChildControl(ids[iii]); if (jscontrol) jscontrol.SetValue(optional()); } } });