MediaWiki:Gadget-ImageAnnotator.js: Difference between revisions
Content deleted Content added
mNo edit summary |
Remove v3.0-alpha experiment. Let's keep the history in the central MediaWiki:Gadget-ImageAnnotator and iterate further from v2.x onwards. |
||
Line 40:
var ImageAnnotator_config = null;
var ImageAnnotation = function () {
this.initialize.apply(this, arguments); }; ImageAnnotation.compare = function (a, b) {
var result = b.area() - a.area();
if (result !== 0) return result;
return a.model.id - b.model.id;
};
ImageAnnotation.prototype = {
view: null,
▲ view : null, // Rectangle to be displayed on image: a div with pos and size
// Tooltip to display the annotation // Content of the tooltip // Reference to the viewer this note belongs to viewer: null,
initialize
var is_new = false;
var view_w = 0, view_h = 0, view_x = 0, view_y = 0;
Line 70 ⟶ 75:
var html = IA.getRawItem('content_' + id, viewer.scope);
if (x === null || y === null || w === null || h === null || html === null)
throw new Error
if (x < 0 || x >= viewer.full_img.width || y < 0 || y >= viewer.full_img.height)
throw new Error
if ( x + w > viewer.full_img.width + 10
|| y + h > viewer.full_img.height + 10)
{
throw new Error
}
// Notes written by early versions may be slightly too large, whence the + 10 above. Fix this.
Line 88 ⟶ 93:
LAPI.make(
'div', null
, { position
,display
,lineHeight
,fontSize
,top
,left
,width
,height
}
);
// We'll add the view to the DOM once we've loaded all notes
this.model =
{ id
,dimension: {x: x, y: y, w: w, h: h}
,wiki
,html
};
} else {
Line 109 ⟶ 114:
this.view = node;
this.model =
{ id
,dimension: null
,wiki
,html
};
view_w = this.view.offsetWidth - 2; // Subtract cumulated border widths
Line 124 ⟶ 129:
if (view_h < 6) {view_y = Math.floor(view_y + view_h / 2 - 3); view_h = 6; }
Object.merge(
{
top: '' + view_y + 'px', height: '' + view_h + 'px' },
this.view.style );
this.view.style.zIndex
try {
this.view.style.border = '1px solid ' + this.viewer.outer_border;
Line 137 ⟶ 146:
LAPI.make(
'div', null
, { lineHeight
,fontSize
,width
,height
}
)
Line 163 ⟶ 172:
},
setTooltip
if (this.tooltip || !this.view) return; // Already set, or corrupt
// Note: on IE, don't have tooltips appear automatically. IE doesn't do it right for transparent
Line 172 ⟶ 180:
( this.view.firstChild
, this.display.bind(this)
, { activate
,deactivate
,close_button
,mode
,mouse_offset : {x: -5, y: -5, dx: (IA.is_rtl ? -1 : 1), dy: 1}
,open_delay
,hide_delay
,onclose
if (this.view) {
try {
Line 192 ⟶ 200:
if (evt) this.viewer.hide(evt);
}).bind(this)
,onopen
if (this.view) {
try {
Line 207 ⟶ 215:
},
display
if (!this.content) {
this.content = LAPI.make('div');
Line 251 ⟶ 258:
},
edit
if (IA.canEdit()) IA.editor.editNote(this);
if (evt) return LAPI.Evt.kill(evt);
Line 258 ⟶ 264:
},
remove_event
if (IA.canEdit()) this.remove();
return LAPI.Evt.kill(evt);
},
remove
if (!this.content) { // New note: just destroy it.
this.destroy();
Line 295 ⟶ 299:
LAPI.Ajax.editPage(
mw.config.get('wgPageName')
, function (doc, editForm, failureFunc, revision_id) {
{▼
try {
if (revision_id && revision_id != mw.config.get('wgCurRevisionId'))
throw new Error
var textbox = editForm.wpTextbox1;
if (!textbox) throw new Error
var pagetext = textbox.value.replace(/\r\n/g, '\n');
// Normalize different end-of-line handling. Opera and IE may use \r\n, whereas other
Line 329 ⟶ 332:
var summary = editForm.wpSummary;
if (!summary)
throw new Error
IA.setSummary(
summary
Line 348 ⟶ 351:
var revision_id = LAPI.WP.revisionFromHtml(request.responseText);
if (!revision_id) {
failureFunc (request, new Error
return;
}
Line 373 ⟶ 376:
},
destroy
if (this.view) LAPI.DOM.removeNode(this.view);
if (this.dummy) LAPI.DOM.removeNode(this.dummy);
Line 386 ⟶ 388:
},
area
if (!this.model || !this.model.dimension) return 0;
return (this.model.dimension.w * this.model.dimension.h);
},
cannotEdit
if (this.content && this.content.button_section) {
LAPI.DOM.removeNode(this.content.button_section);
Line 407:
ImageAnnotationEditor.prototype =
{
initialize
var editor_width = 50;
// Respect potential user-defined width setting
Line 420 ⟶ 419:
new LAPI.Edit(
'' , editor_width, 6
, { box
,preview
,save
,revert
,cancel
,nullsave : ImageAnnotator_config.mayDelete()
? ImageAnnotator.UI.get('wpImageAnnotatorDelete', true).capitalizeFirst()
: null
,post
}
, {
onsave
,onpreview : this.onpreview.bind(this)
,oncancel
,ongettext
if (text == null) return '';
text = text.trim()
Line 474 ⟶ 473:
IA.get_cover()
, this.get_editor.bind(this)
, { activate
,deactivate
,close_button : null // We have a cancel button anyway
,mode
,anchor
,mouse_offset : {x:10, y: 10, dx:1, dy:1} // Misuse this: fixed offset from view
,max_pixels
,z_index
,open_delay
,hide_delay
,onclose
}
, IA.tooltip_styles
Line 499 ⟶ 498:
},
get_editor
return this.box;
},
editNote
var same_note = (note == this.note);
this.note = note;
Line 521 ⟶ 519:
LAPI.Ajax.apiGet(
'query'
, { prop
,titles
,rvlimit
,rvstartid : mw.config.get('wgCurRevisionId')
,rvprop
}
, function (request, json_result) {
Line 553 ⟶ 551:
},
open_editor
this.editor.hidePreview();
if (!same_note || this.editor.textarea.readOnly)
Line 592 ⟶ 589:
},
hide_editor
if (!this.visible) return;
this.visible = false;
Line 613 ⟶ 609:
},
save
var data = editor.getText();
if (!data || !data.length) {
Line 700 ⟶ 695:
LAPI.Ajax.editPage(
mw.config.get('wgPageName')
, function (doc, editForm, failureFunc, revision_id) {
{▼
try {
if (revision_id && revision_id != mw.config.get('wgCurRevisionId'))
Line 746 ⟶ 740:
var summary = editForm.wpSummary;
if (!summary)
throw new Error
// If [[MediaWiki:Copyrightwarning]] is invalid XHTML, we may not have wpSummary!
if (self.note.content != null) {
Line 783 ⟶ 777:
if (doc.isFake && (typeof doc.dispose === 'function')) doc.dispose();
failureFunc
(request, new Error
return;
}
Line 790 ⟶ 784:
if (doc.isFake && (typeof doc.dispose === 'function')) doc.dispose();
failureFunc
(request, new Error
return;
}
Line 828 ⟶ 822:
);
}
, function (request, ex) {
self.editor.busy(false);
self.saving = false;
Line 864 ⟶ 857:
},
onpreview
if (this.tooltip) this.tooltip.size_change();
},
cancel
if (!this.note) return;
if (!this.note.content) {
Line 880 ⟶ 871:
},
close_tooltip
this.hide_editor(evt);
this.cancel();
Line 890 ⟶ 880:
var ImageNotesViewer = function () {this.initialize.apply(this, arguments); };
ImageNotesViewer.prototype = {
▲ initialize : function (descriptor, may_edit)
Object.merge(descriptor, this);
this.annotations = [];
Line 903 ⟶ 891:
this.tip = null;
this.icon = null;
this.factors = {
if (!this.isThumbnail && !this.isOther) {
Line 921 ⟶ 909:
},
setup
this.setup_done = true;
var name = this.realName;
Line 948 ⟶ 935:
LAPI.make(
'div', null
, { cssFloat
,styleFloat: (IA.is_rtl ? 'right' : 'left') // For IE...
,width
,position
}
);
Line 1,148 ⟶ 1,135:
},
cannotEdit
if (!this.may_edit) return;
this.may_edit = false;
Line 1,155 ⟶ 1,141:
},
setShowHideEvents
if (this.icon) return;
if (set) {
Line 1,170 ⟶ 1,155:
},
removeMoveListener
if (this.icon) return;
this.move_listening = false;
Line 1,181 ⟶ 1,165:
},
adjustRectangleSize
if (this.icon) return;
// Make sure the note boxes don't overlap the image boundary; we might get an event
Line 1,213 ⟶ 1,196:
},
toggle
var i;
if (!this.annotations || this.annotations.length === 0 || this.icon) return;
Line 1,237 ⟶ 1,219:
},
show
if (this.visible || this.icon) return;
this.toggle(IA.is_adding || IA.is_editing);
Line 1,249 ⟶ 1,230:
},
hide
if (this.icon) return true;
if (!this.visible) {
Line 1,328 ⟶ 1,308:
},
check_hide
if (this.icon) return true;
if (this.visible)
Line 1,336 ⟶ 1,315:
},
register
this.annotations[this.annotations.length] = new_note;
if (new_note.model.id > 0) {
Line 1,346 ⟶ 1,324:
},
deregister
Array.remove(this.annotations, note);
if (note.model.id == this.max_id) this.max_id--;
Line 1,353 ⟶ 1,330:
},
setDefaultMsg
if (this.annotations && this.annotations.length && this.msg) {
LAPI.DOM.removeChildren(this.msg);
Line 1,386 ⟶ 1,362:
};
var IA = {
// This object is responsible for setting up annotations when a page is loaded. It loads all
// annotations in the page source, and adds an "Annotate this image" button plus the support
// for drawing rectangles onto the image if there is only one image and editing is allowed.
haveAjax
button_div
add_button
cover
border
definer
mouse_in
mouse_out
annotation_class : 'image_annotation',
Line 1,412 ⟶ 1,387:
note_delim :
[
{ start
,end
,content_start : '<div id="image_annotation_content_$1">\n'
,content_end
}
,{ start
,end
,content_start : '}}\n'
,content_end
}
],
tooltip_styles : // The style for all our tooltips
{ border
, backgroundColor : '#ffffe0'
, padding
, fontSize
// Scale up to default text size
},
editor
wiki_read
is_rtl
move_listening : false,
is_tracking
is_adding
is_editing
zoom_threshold : 8.0,
zoom_factor
install_attempts
max_install_attempts : 10, // Maximum 5 seconds
imgs_with_notes : [],
thumbs
other_images
// Fallback
indication_icon : '//upload.wikimedia.org/wikipedia/commons/8/8a/Gtk-dialog-info-14px.png',
install
if (typeof ImageAnnotator_disable !== 'undefined' && !!ImageAnnotator_disable) return;
if (!config || ImageAnnotator_config != null) return;
Line 1,656 ⟶ 1,630:
},
wait_for_required_libraries
if (typeof Tooltip == 'undefined' || typeof LAPI == 'undefined') {
if (IA.install_attempts++ < IA.max_install_attempts) {
Line 1,670 ⟶ 1,643:
},
setup: function () {
var self = IA;
self.imgs = [];
Line 1,815 ⟶ 1,787:
}
}
return { scope
,file_div
,img
,realName
,isThumbnail: is_thumb
,isOther
,thumb
,iconOnly
,noCaption
};
}
Line 1,971 ⟶ 1,943:
function (length, titles) {
var idx = ImageAnnotator.info_callbacks.length;
ImageAnnotator.info_callbacks[idx] = {
},
};▼
ImageAnnotator.info_callbacks[idx].script =
IA.getScript(
Line 1,994 ⟶ 1,966:
// up in that case.
if ( ImageAnnotator.info_callbacks && ImageAnnotator.info_callbacks[idx]
&& ImageAnnotator.info_callbacks[idx].done && ImageAnnotator.info_callbacks[idx].script
) {
LAPI.DOM.removeNode(ImageAnnotator.info_callbacks[idx].script);
ImageAnnotator.info_callbacks[idx].script = null;
Line 2,008 ⟶ 1,980:
'query'
, { titles : titles
,prop
,inprop : 'protection'
,iiprop : 'size'
Line 2,023 ⟶ 1,995:
},
setup_ui
// Complete the UI object we've gotten from config.
Line 2,052 ⟶ 2,023:
};
ImageAnnotator.UI.setup = function () {
if (ImageAnnotator.UI.repo) return;
var self = ImageAnnotator.UI;
Line 2,079 ⟶ 2,049:
};
ImageAnnotator.UI.get = function (id, basic, no_plea) {
var self = ImageAnnotator.UI;
if (!self.repo) self.setup();
Line 2,110 ⟶ 2,079:
};
ImageAnnotator.UI.get_plea = function () {
var self = ImageAnnotator.UI;
var translate = self.get('wpTranslate', false, true) || 'translate';
Line 2,129 ⟶ 2,097:
};
ImageAnnotator.UI.init = function (html_text_or_json) {
var text;
if (typeof html_text_or_json === 'string')
Line 2,213 ⟶ 2,180:
},
setup_step_two
var self = IA;
Line 2,236 ⟶ 2,202:
},
complete_setup
// We can be sure to have the UI here because this is called only when the ready event of the
// UI object is fired.
Line 2,398 ⟶ 2,363:
}
function start_tracking (evt) {
if (!self.is_tracking) {
self.is_tracking = true;
Line 2,409 ⟶ 2,373:
self.base_y = mouse_pos.y - origin.y;
Object.merge(
{ left
,top
,width
,height : '0px'
,display: ''
Line 2,442 ⟶ 2,406:
LAPI.make(
'div', null
,{ border
,display
,position
,top
,left
,width
,height
,padding
,lineHeight : '0px' // IE needs this, even though there are no lines within
,fontSize
,zIndex
}
);
Line 2,642 ⟶ 2,606:
for (var i = 0; pages && i < pages.length; i++) {
var notes = getElementsByClassName (pages[i], 'div', self.annotation_class);
if (!notes || notes.length === 0)
var page = self.getItem('inline_name', pages[i]);
if (!page) continue;
page = page.replace(/ /g, '_');
var viewers = cache[page] || cache['File:' + page.substring(page.indexOf(':') + 1)];
if (!viewers || viewers.length === 0)
continue;
// Update rules.
var rules = getElementsByClassName (pages[i], 'div', 'wpImageAnnotatorInlinedRules');
var local_rules = {
if (rules && rules.length) {
rules = rules[0];
Line 2,721 ⟶ 2,689:
var idx = ImageAnnotator.script_callbacks.length;
ImageAnnotator.script_callbacks[idx] =
{ callback
if (json && json.parse && json.parse.text && json.parse.text['*']) {
setup_thumb_viewers (json.parse.text['*']);
Line 2,731 ⟶ 2,699:
}
}
,done
};
ImageAnnotator.script_callbacks[idx].script =
Line 2,776 ⟶ 2,744:
},
show_zoom
var self = IA;
if ( ( self.viewers[0].factors.dx < self.zoom_threshold
Line 2,793 ⟶ 2,760:
'div'
, {id : 'image_annotator_zoom'}
, { overflow
,width
,height
,position
,display
,top: '0px'
,left: '0px' ,
,backgroundColor : 'white'
,zIndex
}
);
Line 2,833 ⟶ 2,800:
LAPI.make(
'div', null
, { width
,height
,borderLeft : '1px solid red'
,position
,top
,left
}
)
Line 2,845 ⟶ 2,812:
LAPI.make(
'div', null
, { width
,height
,borderTop : '1px solid red'
,position
,top
,left
}
)
Line 2,874 ⟶ 2,841:
},
update_zoom
if (!evt) return; // We need an event to calculate positions!
var self = IA;
Line 2,911 ⟶ 2,877:
},
hide_zoom
if (!IA.zoom) return;
if (evt) {
Line 2,921 ⟶ 2,886:
},
createHelpLink
var msg = ImageAnnotator.UI.get('wpImageAnnotatorHelp', false, true);
if (!msg || !msg.lastChild) return null;
Line 2,979 ⟶ 2,943:
},
get_cover
var self = IA;
var shim;
if (!self.cover) {
var pos = { position : 'absolute'
,left
,top
,width
,height
};
self.cover = LAPI.make('div', null, pos);
Line 3,010 ⟶ 2,973:
shim = LAPI.make('div', null, pos);
Object.merge(
{ top
,backgroundImage: 'url(' + self.viewers[0].img.src + ')'
,zIndex
}
, shim.style
Line 3,038 ⟶ 3,001:
},
show_cover
var self = IA;
if (self.cover && !self.cover_visible) {
Line 3,052 ⟶ 3,014:
},
hide_cover
var self = IA;
if (self.cover && self.cover_visible) {
Line 3,066 ⟶ 3,027:
},
getRawItem
var node = null;
if (!scope || scope == document) {
Line 3,078 ⟶ 3,038:
},
getItem
var node = IA.getRawItem(what, scope);
if (!node) return null;
Line 3,085 ⟶ 3,044:
},
getIntItem
var x = IA.getItem(what, scope);
if (x !== null) x = parseInt (x, 10);
Line 3,092 ⟶ 3,050:
},
findNote
function find (text, id, delim) {
var start = delim.start.replace('$1', id);
Line 3,111 ⟶ 3,068:
},
setWikitext
var self = IA;
if (self.wiki_read) return;
Line 3,136 ⟶ 3,092:
},
setSummary
if (initial_text.contains('$1')) {
var max = (summary.maxlength || 200) - initial_text.length;
Line 3,149 ⟶ 3,104:
},
getScript
// Don't use LAPI here, it may not yet be available
if (bypass_caches) {
Line 3,168 ⟶ 3,122:
},
canEdit
var self = IA;
if (self.may_edit) {
|