Sync with WebODF e50bc588ff3e380178ffabecfe2fbca92bef6aed.

* Keyboard support in iOS and other touchscreens
* Pinch-zoom support on touch devices
* SVG selections that look like those in LibreOffice
* Major speed improvements with all editing operations and cursor movements
* IME support on desktop browsers
* Word-by-word navigation with Ctrl+Left/Right
* Fixes for IE11
* Ability to add/edit/remove Hyperlinks.
pull/1/head
Aditya Bhatt 10 years ago
parent 8b71362d0e
commit 3cd491a5ef

@ -25,13 +25,18 @@ body.claro, #mainContainer {
#toolbar {
overflow: hidden;
top: 0;
left: 0;
right: 0;
position: absolute;
z-index: 5;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.25);
}
#container {
text-align: center;
background-color: #ddd;
overflow: auto;
position: absolute;
top: 30px;
bottom: 0;
@ -40,7 +45,6 @@ body.claro, #mainContainer {
}
#canvas {
box-shadow: 0px 0px 20px #aaa;
margin-top: 30px;
margin-left: 10px;
margin-right: 10px;
@ -50,8 +54,16 @@ body.claro, #mainContainer {
-webkit-transform-origin: top center;
-moz-transform-origin: top center;
-o-transform-origin: top center;
overflow: hidden;
overflow: visible;
}
/* Add shadow to the sizer and not the canvas,
* so that it will follow the smooth zooming
* of the slider and not have to be updated
* every time a gesture ends
*/
#canvas > div {
box-shadow: 0px 0px 20px #aaa;
border: 1px solid #ccc;
}
@ -241,7 +253,7 @@ div.memberListLabel[fullname]:before {
text-align: center;
}
cursor div {
cursor .handle {
margin-top: 5px;
padding-top: 3px;
margin-left: auto;
@ -269,11 +281,11 @@ cursor img {
margin: auto;
}
cursor div.active {
cursor .handle.active {
opacity: 0.8;
}
cursor div:after {
cursor .handle:after {
content: ' ';
position: absolute;
width: 0px;

@ -317,6 +317,7 @@ define("webodf/editor/Editor", [
runtime.assert(editorSession, "editorSession should exist here.");
tools.setEditorSession(editorSession);
editorSession.sessionController.insertLocalCursor();
editorSession.sessionController.startEditing();
};
@ -330,6 +331,7 @@ define("webodf/editor/Editor", [
tools.setEditorSession(undefined);
editorSession.sessionController.endEditing();
editorSession.sessionController.removeLocalCursor();
};
/**
@ -364,6 +366,40 @@ define("webodf/editor/Editor", [
eventNotifier.unsubscribe(eventid, listener);
};
/**
* Applies a CSS transformation to the toolbar
* to ensure that if there is a body-scroll,
* the toolbar remains visible at the top of
* the screen.
* The bodyscroll quirk has been observed on
* iOS, generally when the keyboard appears.
* But this workaround should function on
* other platforms that exhibit this behaviour
* as well.
* @return {undefined}
*/
function translateToolbar() {
var bar = document.getElementById('toolbar'),
y = document.body.scrollTop;
bar.style.WebkitTransformOrigin = "center top";
bar.style.WebkitTransform = 'translateY(' + y + 'px)';
}
/**
* FIXME: At the moment both the toolbar and the canvas
* container are absolutely positioned. Changing them to
* relative positioning to ensure that they do not overlap
* causes scrollbars *within* the container to disappear.
* Not sure why this happens, and a proper CSS fix has not
* been found yet, so for now we need to reposition
* the container using Js.
* @return {undefined}
*/
function repositionContainer() {
document.getElementById('container').style.top = document.getElementById('toolbar').getBoundingClientRect().height + 'px';
}
/**
* @param {!function(!Object=)} callback, passing an error object in case of error
* @return {undefined}
@ -371,6 +407,11 @@ define("webodf/editor/Editor", [
this.destroy = function (callback) {
var destroyMemberListView = memberListView ? memberListView.destroy : function(cb) { cb(); };
window.removeEventListener('scroll', translateToolbar);
window.removeEventListener('focusout', translateToolbar);
window.removeEventListener('touchmove', translateToolbar);
window.removeEventListener('resize', repositionContainer);
// TODO: decide if some forced close should be done here instead of enforcing proper API usage
runtime.assert(!session, "session should not exist here.");
@ -407,10 +448,12 @@ define("webodf/editor/Editor", [
var editorPane, memberListPane,
inviteButton,
canvasElement = document.getElementById("canvas"),
container = document.getElementById('container'),
memberListElement = document.getElementById('memberList'),
collabEditing = Boolean(server),
directParagraphStylingEnabled = (! collabEditing) || args.unstableFeaturesEnabled,
imageInsertingEnabled = (! collabEditing) || args.unstableFeaturesEnabled,
hyperlinkEditingEnabled = (! collabEditing) || args.unstableFeaturesEnabled,
// annotations not yet properly supported for OT
annotationsEnabled = (! collabEditing) || args.unstableFeaturesEnabled,
// undo manager is not yet integrated with collaboration
@ -471,18 +514,19 @@ define("webodf/editor/Editor", [
}
tools = new Tools({
onToolDone: setFocusToOdfCanvas,
loadOdtFile: loadOdtFile,
saveOdtFile: saveOdtFile,
close: close,
directParagraphStylingEnabled: directParagraphStylingEnabled,
imageInsertingEnabled: imageInsertingEnabled,
annotationsEnabled: annotationsEnabled,
undoRedoEnabled: undoRedoEnabled
});
onToolDone: setFocusToOdfCanvas,
loadOdtFile: loadOdtFile,
saveOdtFile: saveOdtFile,
close: close,
directParagraphStylingEnabled: directParagraphStylingEnabled,
imageInsertingEnabled: imageInsertingEnabled,
hyperlinkEditingEnabled: hyperlinkEditingEnabled,
annotationsEnabled: annotationsEnabled,
undoRedoEnabled: undoRedoEnabled
});
odfCanvas = new odf.OdfCanvas(canvasElement);
odfCanvas.enableAnnotations(annotationsEnabled);
odfCanvas.enableAnnotations(annotationsEnabled, true);
odfCanvas.addListener("statereadychange", function () {
var viewOptions = {
@ -496,7 +540,8 @@ define("webodf/editor/Editor", [
editorSession = new EditorSession(session, pendingMemberId, {
viewOptions: viewOptions,
directParagraphStylingEnabled: directParagraphStylingEnabled,
imageInsertingEnabled: imageInsertingEnabled
imageInsertingEnabled: imageInsertingEnabled,
hyperlinkEditingEnabled: hyperlinkEditingEnabled
});
if (undoRedoEnabled) {
editorSession.sessionController.setUndoManager(new gui.TrivialUndoManager());
@ -512,6 +557,13 @@ define("webodf/editor/Editor", [
pendingEditorReadyCallback = null;
pendingMemberId = null;
});
repositionContainer();
window.addEventListener('scroll', translateToolbar);
window.addEventListener('focusout', translateToolbar);
window.addEventListener('touchmove', translateToolbar);
window.addEventListener('resize', repositionContainer);
}
init();

@ -43,10 +43,6 @@ define("webodf/editor/EditorSession", [
], function (fontsCSS) { // fontsCSS is retrieved as a string, using dojo's text retrieval AMD plugin
"use strict";
runtime.libraryPaths = function () {
return [ "../../webodf/lib" ];
};
runtime.loadClass("core.DomUtils");
runtime.loadClass("odf.OdfUtils");
runtime.loadClass("ops.OdtDocument");
@ -54,11 +50,13 @@ define("webodf/editor/EditorSession", [
runtime.loadClass("ops.Session");
runtime.loadClass("odf.Namespaces");
runtime.loadClass("odf.OdfCanvas");
runtime.loadClass("odf.OdfUtils");
runtime.loadClass("gui.CaretManager");
runtime.loadClass("gui.Caret");
runtime.loadClass("gui.SessionController");
runtime.loadClass("gui.SessionView");
runtime.loadClass("gui.TrivialUndoManager");
runtime.loadClass("gui.SvgSelectionView");
runtime.loadClass("gui.SelectionViewManager");
runtime.loadClass("core.EventNotifier");
runtime.loadClass("gui.ShadowCursor");
@ -208,7 +206,7 @@ define("webodf/editor/EditorSession", [
function trackCurrentParagraph(info) {
var cursor = odtDocument.getCursor(localMemberId),
range = cursor && cursor.getSelectedRange(),
paragraphRange = odtDocument.getDOM().createRange();
paragraphRange = odtDocument.getDOMDocument().createRange();
paragraphRange.selectNode(info.paragraphElement);
if ((range && domUtils.rangesIntersect(range, paragraphRange)) || info.paragraphElement === currentParagraphNode) {
self.emit(EditorSession.signalParagraphChanged, info);
@ -312,7 +310,7 @@ define("webodf/editor/EditorSession", [
/**
* Round the step up to the next step
* @param {!number} step
* @returns {!boolean}
* @return {!boolean}
*/
function roundUp(step) {
return step === ops.StepsTranslator.NEXT_STEP;
@ -387,14 +385,14 @@ define("webodf/editor/EditorSession", [
return formatting.isStyleUsed(styleElement);
};
function getDefaultParagraphStyleAttributes () {
function getDefaultParagraphStyleAttributes() {
var styleNode = formatting.getDefaultStyleElement('paragraph');
if (styleNode) {
return formatting.getInheritedStyleAttributes(styleNode);
}
return null;
};
}
/**
* Returns the attributes of a given paragraph style name
@ -522,22 +520,30 @@ define("webodf/editor/EditorSession", [
return array;
};
this.getSelectedHyperlinks = function () {
var cursor = odtDocument.getCursor(localMemberId);
// no own cursor yet/currently added?
if (!cursor) {
return [];
}
return odfUtils.getHyperlinkElements(cursor.getSelectedRange());
};
this.getSelectedRange = function () {
var cursor = odtDocument.getCursor(localMemberId);
return cursor && cursor.getSelectedRange();
};
function undoStackModified(e) {
self.emit(EditorSession.signalUndoStackChanged, e);
}
this.hasUndoManager = function () {
return Boolean(self.sessionController.getUndoManager());
};
this.undo = function () {
var undoManager = self.sessionController.getUndoManager();
undoManager.moveBackward(1);
self.sessionController.undo();
};
this.redo = function () {
var undoManager = self.sessionController.getUndoManager();
undoManager.moveForward(1);
self.sessionController.redo();
};
/**
@ -548,8 +554,8 @@ define("webodf/editor/EditorSession", [
* @param {!number} height
*/
this.insertImage = function (mimetype, content, width, height) {
self.sessionController.getTextManipulator().removeCurrentSelection();
self.sessionController.getImageManager().insertImage(mimetype, content, width, height);
self.sessionController.getTextController().removeCurrentSelection();
self.sessionController.getImageController().insertImage(mimetype, content, width, height);
};
/**
@ -569,12 +575,12 @@ define("webodf/editor/EditorSession", [
head.removeChild(fontStyles);
odtDocument.unsubscribe(ops.OdtDocument.signalMemberAdded, onMemberAdded);
odtDocument.unsubscribe(ops.OdtDocument.signalMemberUpdated, onMemberUpdated);
odtDocument.unsubscribe(ops.OdtDocument.signalMemberRemoved, onMemberRemoved);
odtDocument.unsubscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded);
odtDocument.unsubscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved);
odtDocument.unsubscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved);
odtDocument.unsubscribe(ops.Document.signalMemberAdded, onMemberAdded);
odtDocument.unsubscribe(ops.Document.signalMemberUpdated, onMemberUpdated);
odtDocument.unsubscribe(ops.Document.signalMemberRemoved, onMemberRemoved);
odtDocument.unsubscribe(ops.Document.signalCursorAdded, onCursorAdded);
odtDocument.unsubscribe(ops.Document.signalCursorRemoved, onCursorRemoved);
odtDocument.unsubscribe(ops.Document.signalCursorMoved, onCursorMoved);
odtDocument.unsubscribe(ops.OdtDocument.signalCommonStyleCreated, onStyleCreated);
odtDocument.unsubscribe(ops.OdtDocument.signalCommonStyleDeleted, onStyleDeleted);
odtDocument.unsubscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified);
@ -623,17 +629,17 @@ define("webodf/editor/EditorSession", [
directParagraphStylingEnabled: config.directParagraphStylingEnabled
});
caretManager = new gui.CaretManager(self.sessionController);
selectionViewManager = new gui.SelectionViewManager();
selectionViewManager = new gui.SelectionViewManager(gui.SvgSelectionView);
self.sessionView = new gui.SessionView(config.viewOptions, localMemberId, session, caretManager, selectionViewManager);
self.availableFonts = getAvailableFonts();
selectionViewManager.registerCursor(shadowCursor, true);
// Custom signals, that make sense in the Editor context. We do not want to expose webodf's ops signals to random bits of the editor UI.
odtDocument.subscribe(ops.OdtDocument.signalMemberAdded, onMemberAdded);
odtDocument.subscribe(ops.OdtDocument.signalMemberUpdated, onMemberUpdated);
odtDocument.subscribe(ops.OdtDocument.signalMemberRemoved, onMemberRemoved);
odtDocument.subscribe(ops.OdtDocument.signalCursorAdded, onCursorAdded);
odtDocument.subscribe(ops.OdtDocument.signalCursorRemoved, onCursorRemoved);
odtDocument.subscribe(ops.OdtDocument.signalCursorMoved, onCursorMoved);
odtDocument.subscribe(ops.Document.signalMemberAdded, onMemberAdded);
odtDocument.subscribe(ops.Document.signalMemberUpdated, onMemberUpdated);
odtDocument.subscribe(ops.Document.signalMemberRemoved, onMemberRemoved);
odtDocument.subscribe(ops.Document.signalCursorAdded, onCursorAdded);
odtDocument.subscribe(ops.Document.signalCursorRemoved, onCursorRemoved);
odtDocument.subscribe(ops.Document.signalCursorMoved, onCursorMoved);
odtDocument.subscribe(ops.OdtDocument.signalCommonStyleCreated, onStyleCreated);
odtDocument.subscribe(ops.OdtDocument.signalCommonStyleDeleted, onStyleDeleted);
odtDocument.subscribe(ops.OdtDocument.signalParagraphStyleModified, onParagraphStyleModified);

@ -49,11 +49,12 @@ define("webodf/editor/Tools", [
"webodf/editor/widgets/undoRedoMenu",
"webodf/editor/widgets/toolbarWidgets/currentStyle",
"webodf/editor/widgets/annotation",
"webodf/editor/widgets/paragraphStylesDialog",
"webodf/editor/widgets/editHyperlinks",
"webodf/editor/widgets/imageInserter",
"webodf/editor/widgets/paragraphStylesDialog",
"webodf/editor/widgets/zoomSlider",
"webodf/editor/EditorSession"],
function (ready, MenuItem, DropDownMenu, Button, DropDownButton, Toolbar, ParagraphAlignment, SimpleStyles, UndoRedoMenu, CurrentStyle, AnnotationControl, ParagraphStylesDialog, ImageInserter, ZoomSlider, EditorSession) {
function (ready, MenuItem, DropDownMenu, Button, DropDownButton, Toolbar, ParagraphAlignment, SimpleStyles, UndoRedoMenu, CurrentStyle, AnnotationControl, EditHyperlinks, ImageInserter, ParagraphStylesDialog, ZoomSlider, EditorSession) {
"use strict";
return function Tools(args) {
@ -72,6 +73,7 @@ define("webodf/editor/Tools", [
paragraphAlignment,
imageInserter,
annotationControl,
editHyperlinks,
sessionSubscribers = [];
function handleCursorMoved(cursor) {
@ -128,6 +130,7 @@ define("webodf/editor/Tools", [
widget.startup();
});
sessionSubscribers.push(undoRedoMenu);
undoRedoMenu.onToolDone = onToolDone;
}
// Add annotation
@ -226,6 +229,15 @@ define("webodf/editor/Tools", [
sessionSubscribers.push(paragraphStylesDialog);
paragraphStylesDialog.onToolDone = onToolDone;
if (args.hyperlinkEditingEnabled) {
editHyperlinks = new EditHyperlinks(function (widget) {
widget.placeAt(toolbar);
widget.startup();
});
sessionSubscribers.push(editHyperlinks);
editHyperlinks.onToolDone = onToolDone;
}
formatMenuButton = new DropDownButton({
dropDown: formatDropDownMenu,
label: tr('Format'),

@ -94,7 +94,9 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
EVENT_BEFORESAVETOFILE,
EVENT_SAVEDTOFILE,
EVENT_HASLOCALUNSYNCEDOPERATIONSCHANGED,
EVENT_HASSESSIONHOSTCONNECTIONCHANGED
EVENT_HASSESSIONHOSTCONNECTIONCHANGED,
ops.OperationRouter.signalProcessingBatchStart,
ops.OperationRouter.signalProcessingBatchEnd
]),
/**@type{!boolean} tells if any local ops have been modifying ops */
hasPushedModificationOps = false,
@ -150,6 +152,8 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
// take start time
startTime = (new Date()).getTime();
eventNotifier.emit(ops.OperationRouter.signalProcessingBatchStart, {});
// apply as much as possible in the given time
while (unplayedServerOpspecQueue.length > 0) {
// time over?
@ -164,11 +168,13 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
runtime.log(" op in: "+runtime.toJson(opspec));
if (op !== null) {
if (!playbackFunction(op)) {
eventNotifier.emit(ops.OperationRouter.signalProcessingBatchEnd, {});
hasError = true;
errorCallback("opExecutionFailure");
return;
}
} else {
eventNotifier.emit(ops.OperationRouter.signalProcessingBatchEnd, {});
hasError = true;
runtime.log("ignoring invalid incoming opspec: " + opspec);
errorCallback("unknownOpReceived");
@ -176,6 +182,8 @@ define("webodf/editor/server/pullbox/OperationRouter", [], function () {
}
}
eventNotifier.emit(ops.OperationRouter.signalProcessingBatchEnd, {});
// still unplayed opspecs?
if (unplayedServerOpspecQueue.length > 0) {
// let other events be handled. then continue
@ -493,6 +501,8 @@ runtime.log("OperationRouter: instant opsSync requested");
return;
}
eventNotifier.emit(ops.OperationRouter.signalProcessingBatchStart, {});
for (i = 0; i < operations.length; i += 1) {
op = operations[i];
opspec = op.spec();
@ -518,6 +528,8 @@ runtime.log("OperationRouter: instant opsSync requested");
triggerPushingOps();
updateHasLocalUnsyncedOpsState();
eventNotifier.emit(ops.OperationRouter.signalProcessingBatchEnd, {});
};
/**

@ -38,10 +38,11 @@
/*global runtime,core,define,require,dijit */
runtime.loadClass("core.CSSUnits");
define("webodf/editor/widgets/dialogWidgets/alignmentPane", [], function () {
"use strict";
runtime.loadClass("core.CSSUnits");
var AlignmentPane = function (callback) {
var self = this,
editorSession,

@ -0,0 +1,30 @@
<html>
<head></head>
<body>
<div data-dojo-type="dijit/form/Form" id="editHyperlinkPaneForm" data-dojo-id="editHyperlinkPaneForm">
<div>
<label text-i18n="Display Text" for="linkDisplayText"></label><br/>
<input data-dojo-type="dijit/form/TextBox" id="linkDisplayText"
value=""
data-dojo-props="trim:true"
name="linkDisplayText" />
<input data-dojo-type="dijit/form/TextBox" id="isReadOnlyText"
type ="hidden"
value=""
data-dojo-props="trim:true"
name="isReadOnlyText" />
</div>
<div>
<label text-i18n="URL" for="linkUrl"></label><br/>
<input data-dojo-type="dijit/form/TextBox" id="linkUrl"
value="http://"
data-dojo-props="trim:true"
name="linkUrl" />
</div>
<div>
<button data-dojo-type="dijit/form/Button" id="saveHyperlinkChangeButton" type="submit">Ok</button>
<button data-dojo-type="dijit/form/Button" id="cancelHyperlinkChangeButton" data-dojo-id="cancelHyperlinkChangeButton">Cancel</button>
</div>
</div>
</body>
</html>

@ -0,0 +1,110 @@
/**
* @license
* Copyright (C) 2013 KO GmbH <copyright@kogmbh.com>
*
* @licstart
* The JavaScript code in this page is free software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* (GNU AGPL) as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. The code is distributed
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU AGPL for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU AGPL version 3 section 7, you
* may distribute non-source (e.g., minimized or compacted) forms of
* that code without the copy of the GNU GPL normally required by
* section 4, provided you include this license notice and a URL
* through which recipients can access the Corresponding Source.
*
* As a special exception to the AGPL, any HTML file which merely makes function
* calls to this code, and for that purpose includes it by reference shall be
* deemed a separate work for copyright law purposes. In addition, the copyright
* holders of this code give you permission to combine this code with free
* software libraries that are released under the GNU LGPL. You may copy and
* distribute such a system following the terms of the GNU AGPL for this code
* and the LGPL for the libraries. If you modify this code, you may extend this
* exception to your version of the code, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This license applies to this entire compilation.
* @licend
* @source: http://www.webodf.org/
* @source: https://github.com/kogmbh/WebODF/
*/
/*global runtime,core,define,require,document,dijit */
define("webodf/editor/widgets/dialogWidgets/editHyperlinkPane", [
"dojo",
"dijit/layout/ContentPane"],
function (dojo, ContentPane) {
"use strict";
runtime.loadClass("core.CSSUnits");
var EditHyperlinkPane = function () {
var self = this,
editorBase = dojo.config && dojo.config.paths && dojo.config.paths['webodf/editor'],
contentPane,
form,
displayTextField,
initialValue;
runtime.assert(editorBase, "webodf/editor path not defined in dojoConfig");
function onSave() {
if (self.onSave) {
self.onSave();
}
return false;
}
function onCancel() {
form.set('value', initialValue);
if (self.onCancel) {
self.onCancel();
}
}
contentPane = new ContentPane({
title: runtime.tr("editLink"),
href: editorBase+"/widgets/dialogWidgets/editHyperlinkPane.html",
preload: true,
onLoad : function () {
form = dijit.byId('editHyperlinkPaneForm');
form.onSubmit = onSave;
dijit.byId('cancelHyperlinkChangeButton').onClick = onCancel;
displayTextField = dijit.byId('linkDisplayText');
runtime.translateContent(form.domNode);
if (initialValue) {
form.set('value', initialValue);
displayTextField.set('disabled', initialValue.isReadOnlyText);
initialValue = undefined;
}
}
});
this.widget = function () {
return contentPane;
};
this.value = function () {
return form && form.get('value');
};
this.set = function (value) {
initialValue = value;
if (form) {
form.set('value', value);
displayTextField.set('disabled', value.isReadOnlyText);
}
};
};
return EditHyperlinkPane;
});

@ -0,0 +1,181 @@
/**
* @license
* Copyright (C) 2013 KO GmbH <copyright@kogmbh.com>
*
* @licstart
* The JavaScript code in this page is free software: you can redistribute it
* and/or modify it under the terms of the GNU Affero General Public License
* (GNU AGPL) as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. The code is distributed
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU AGPL for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
* As additional permission under GNU AGPL version 3 section 7, you
* may distribute non-source (e.g., minimized or compacted) forms of
* that code without the copy of the GNU GPL normally required by
* section 4, provided you include this license notice and a URL
* through which recipients can access the Corresponding Source.
*
* As a special exception to the AGPL, any HTML file which merely makes function
* calls to this code, and for that purpose includes it by reference shall be
* deemed a separate work for copyright law purposes. In addition, the copyright
* holders of this code give you permission to combine this code with free
* software libraries that are released under the GNU LGPL. You may copy and
* distribute such a system following the terms of the GNU AGPL for this code
* and the LGPL for the libraries. If you modify this code, you may extend this
* exception to your version of the code, but you are not obligated to do so.
* If you do not wish to do so, delete this exception statement from your
* version.
*
* This license applies to this entire compilation.
* @licend
* @source: http://www.webodf.org/
* @source: https://github.com/kogmbh/WebODF/
*/
/*global define,require,document,odf */
define("webodf/editor/widgets/editHyperlinks", [
"webodf/editor/EditorSession",
"webodf/editor/widgets/dialogWidgets/editHyperlinkPane",
"dijit/form/Button",
"dijit/form/DropDownButton",
"dijit/TooltipDialog"],
function (EditorSession, EditHyperlinkPane, Button, DropDownButton, TooltipDialog) {
"use strict";
runtime.loadClass("odf.OdfUtils");
var EditHyperlinks = function (callback) {
var self = this,
widget = {},
editorSession,
hyperlinkController,
linkEditorContent,
editHyperlinkButton,
removeHyperlinkButton,
odfUtils = new odf.OdfUtils(),
dialog;
linkEditorContent = new EditHyperlinkPane();
dialog = new TooltipDialog({
title: runtime.tr("Edit link"),
content: linkEditorContent.widget()
});
editHyperlinkButton = new DropDownButton({
label: runtime.tr('Edit link'),
showLabel: false,
iconClass: 'dijitEditorIcon dijitEditorIconCreateLink',
dropDown: dialog
});
removeHyperlinkButton = new Button({
label: runtime.tr('Remove link'),
showLabel: false,
disabled: true,
iconClass: 'dijitEditorIcon dijitEditorIconUnlink',
onClick: function () {
hyperlinkController.removeHyperlinks();
self.onToolDone();
}
});
linkEditorContent.onSave = function () {
var hyperlinkData = linkEditorContent.value();
editHyperlinkButton.closeDropDown(false);
if (hyperlinkData.isReadOnlyText == "true") {
hyperlinkController.removeHyperlinks();
hyperlinkController.addHyperlink(hyperlinkData.linkUrl);
} else {
hyperlinkController.addHyperlink(hyperlinkData.linkUrl, hyperlinkData.linkDisplayText);
}
self.onToolDone();
};
linkEditorContent.onCancel = function () {
editHyperlinkButton.closeDropDown(false);
self.onToolDone();
};
widget.children = [editHyperlinkButton, removeHyperlinkButton];
widget.startup = function () {
widget.children.forEach(function (element) {
element.startup();
});
};
widget.placeAt = function (container) {
widget.children.forEach(function (element) {
element.placeAt(container);
});
return widget;
};
function checkHyperlinkButtons() {
var selection = editorSession.getSelectedRange(),
textContent,
linksInSelection = editorSession.getSelectedHyperlinks(),
linkTarget = linksInSelection[0] ? odfUtils.getHyperlinkTarget(linksInSelection[0]) : "http://";
if (selection && selection.collapsed && linksInSelection.length === 1) {
// Selection is collapsed within a single hyperlink. Assume user is modifying the hyperlink
textContent = selection.cloneRange();
textContent.selectNodeContents(linksInSelection[0]);
linkEditorContent.set({
linkDisplayText: textContent.toString(),
linkUrl: linkTarget,
isReadOnlyText: true
});
textContent.detach();
} else if (selection && !selection.collapsed) {
// User has selected part of a hyperlink or a block of text. Assume user is attempting to modify the
// existing hyperlink, or wants to convert the selection into a hyperlink
linkEditorContent.set({
linkDisplayText: selection.toString(),
linkUrl: linkTarget,
isReadOnlyText: true
});
} else {
// Selection is collapsed and is not in an existing hyperlink
linkEditorContent.set({
linkDisplayText: "",
linkUrl: linkTarget,
isReadOnlyText: false
});
}
// The 3rd parameter is false to avoid firing onChange when setting the value programmatically.
removeHyperlinkButton.set('disabled', linksInSelection.length === 0, false);
}
this.setEditorSession = function (session) {
if (editorSession) {
editorSession.unsubscribe(EditorSession.signalCursorMoved, checkHyperlinkButtons);
editorSession.unsubscribe(EditorSession.signalParagraphChanged, checkHyperlinkButtons);
editorSession.unsubscribe(EditorSession.signalParagraphStyleModified, checkHyperlinkButtons);
}
editorSession = session;
hyperlinkController = session && session.sessionController.getHyperlinkController();
widget.children.forEach(function (element) {
element.setAttribute('disabled', !hyperlinkController);
});
if (editorSession) {
editorSession.subscribe(EditorSession.signalCursorMoved, checkHyperlinkButtons);
editorSession.subscribe(EditorSession.signalParagraphChanged, checkHyperlinkButtons);
editorSession.subscribe(EditorSession.signalParagraphStyleModified, checkHyperlinkButtons);
checkHyperlinkButtons();
}
};
this.onToolDone = function () {};
callback(widget);
};
return EditHyperlinks;
});

@ -50,7 +50,7 @@ define("webodf/editor/widgets/paragraphAlignment", [
var self = this,
editorSession,
widget = {},
directParagraphStyler,
directFormattingController,
justifyLeft,
justifyCenter,
justifyRight,
@ -65,7 +65,7 @@ define("webodf/editor/widgets/paragraphAlignment", [
checked: false,
iconClass: "dijitEditorIcon dijitEditorIconJustifyLeft",
onChange: function () {
directParagraphStyler.alignParagraphLeft();
directFormattingController.alignParagraphLeft();
self.onToolDone();
}
});
@ -77,7 +77,7 @@ define("webodf/editor/widgets/paragraphAlignment", [
checked: false,
iconClass: "dijitEditorIcon dijitEditorIconJustifyCenter",
onChange: function () {
directParagraphStyler.alignParagraphCenter();
directFormattingController.alignParagraphCenter();
self.onToolDone();
}
});
@ -89,7 +89,7 @@ define("webodf/editor/widgets/paragraphAlignment", [
checked: false,
iconClass: "dijitEditorIcon dijitEditorIconJustifyRight",
onChange: function () {
directParagraphStyler.alignParagraphRight();
directFormattingController.alignParagraphRight();
self.onToolDone();
}
});
@ -101,7 +101,7 @@ define("webodf/editor/widgets/paragraphAlignment", [
checked: false,
iconClass: "dijitEditorIcon dijitEditorIconJustifyFull",
onChange: function () {
directParagraphStyler.alignParagraphJustified();
directFormattingController.alignParagraphJustified();
self.onToolDone();
}
});
@ -112,7 +112,7 @@ define("webodf/editor/widgets/paragraphAlignment", [
showLabel: false,
iconClass: "dijitEditorIcon dijitEditorIconOutdent",
onClick: function () {
directParagraphStyler.outdent();
directFormattingController.outdent();
self.onToolDone();
}
});
@ -123,7 +123,7 @@ define("webodf/editor/widgets/paragraphAlignment", [
showLabel: false,
iconClass: "dijitEditorIcon dijitEditorIconIndent",
onClick: function () {
directParagraphStyler.indent();
directFormattingController.indent();
self.onToolDone();
}
});
@ -174,21 +174,21 @@ define("webodf/editor/widgets/paragraphAlignment", [
}
this.setEditorSession = function (session) {
if (directParagraphStyler) {
directParagraphStyler.unsubscribe(gui.DirectParagraphStyler.paragraphStylingChanged, updateStyleButtons);
if (directFormattingController) {
directFormattingController.unsubscribe(gui.DirectFormattingController.paragraphStylingChanged, updateStyleButtons);
}
directParagraphStyler = session && session.sessionController.getDirectParagraphStyler();
if (directParagraphStyler) {
directParagraphStyler.subscribe(gui.DirectParagraphStyler.paragraphStylingChanged, updateStyleButtons);
directFormattingController = session && session.sessionController.getDirectFormattingController();
if (directFormattingController) {
directFormattingController.subscribe(gui.DirectFormattingController.paragraphStylingChanged, updateStyleButtons);
}
widget.children.forEach(function (element) {
element.setAttribute('disabled', !directParagraphStyler);
element.setAttribute('disabled', !directFormattingController);
});
updateStyleButtons({
isAlignedLeft: directParagraphStyler ? directParagraphStyler.isAlignedLeft() : false,
isAlignedCenter: directParagraphStyler ? directParagraphStyler.isAlignedCenter() : false,
isAlignedRight: directParagraphStyler ? directParagraphStyler.isAlignedRight() : false,
isAlignedJustified: directParagraphStyler ? directParagraphStyler.isAlignedJustified() : false
isAlignedLeft: directFormattingController ? directFormattingController.isAlignedLeft() : false,
isAlignedCenter: directFormattingController ? directFormattingController.isAlignedCenter() : false,
isAlignedRight: directFormattingController ? directFormattingController.isAlignedRight() : false,
isAlignedJustified: directFormattingController ? directFormattingController.isAlignedJustified() : false
});
if (editorSession) {

@ -51,7 +51,7 @@ define("webodf/editor/widgets/simpleStyles", [
var self = this,
editorSession,
widget = {},
directTextStyler,
directFormattingController,
boldButton,
italicButton,
underlineButton,
@ -67,7 +67,7 @@ define("webodf/editor/widgets/simpleStyles", [
checked: false,
iconClass: "dijitEditorIcon dijitEditorIconBold",
onChange: function (checked) {
directTextStyler.setBold(checked);
directFormattingController.setBold(checked);
self.onToolDone();
}
});
@ -79,7 +79,7 @@ define("webodf/editor/widgets/simpleStyles", [
checked: false,
iconClass: "dijitEditorIcon dijitEditorIconItalic",
onChange: function (checked) {
directTextStyler.setItalic(checked);
directFormattingController.setItalic(checked);
self.onToolDone();
}
});
@ -91,7 +91,7 @@ define("webodf/editor/widgets/simpleStyles", [
checked: false,
iconClass: "dijitEditorIcon dijitEditorIconUnderline",
onChange: function (checked) {
directTextStyler.setHasUnderline(checked);
directFormattingController.setHasUnderline(checked);
self.onToolDone();
}
});
@ -103,7 +103,7 @@ define("webodf/editor/widgets/simpleStyles", [
checked: false,
iconClass: "dijitEditorIcon dijitEditorIconStrikethrough",
onChange: function (checked) {
directTextStyler.setHasStrikethrough(checked);
directFormattingController.setHasStrikethrough(checked);
self.onToolDone();
}
});
@ -117,7 +117,7 @@ define("webodf/editor/widgets/simpleStyles", [
constraints: {min:6, max:96},
intermediateChanges: true,
onChange: function (value) {
directTextStyler.setFontSize(value);
directFormattingController.setFontSize(value);
},
onClick: function () {
self.onToolDone();
@ -134,7 +134,7 @@ define("webodf/editor/widgets/simpleStyles", [
fontPickerWidget = fontPicker.widget();
fontPickerWidget.setAttribute('disabled', true);
fontPickerWidget.onChange = function(value) {
directTextStyler.setFontName(value);
directFormattingController.setFontName(value);
self.onToolDone();
};
@ -183,24 +183,24 @@ define("webodf/editor/widgets/simpleStyles", [
}
this.setEditorSession = function(session) {
if (directTextStyler) {
directTextStyler.unsubscribe(gui.DirectTextStyler.textStylingChanged, updateStyleButtons);
if (directFormattingController) {
directFormattingController.unsubscribe(gui.DirectFormattingController.textStylingChanged, updateStyleButtons);
}
directTextStyler = session && session.sessionController.getDirectTextStyler();
directFormattingController = session && session.sessionController.getDirectFormattingController();
fontPicker.setEditorSession(session);
if (directTextStyler) {
directTextStyler.subscribe(gui.DirectTextStyler.textStylingChanged, updateStyleButtons);
if (directFormattingController) {
directFormattingController.subscribe(gui.DirectFormattingController.textStylingChanged, updateStyleButtons);
}
widget.children.forEach(function (element) {
element.setAttribute('disabled', !directTextStyler);
element.setAttribute('disabled', !directFormattingController);
});
updateStyleButtons({
isBold: directTextStyler ? directTextStyler.isBold() : false,
isItalic: directTextStyler ? directTextStyler.isItalic() : false,
hasUnderline: directTextStyler ? directTextStyler.hasUnderline() : false,
hasStrikeThrough: directTextStyler ? directTextStyler.hasStrikeThrough() : false,
fontSize: directTextStyler ? directTextStyler.fontSize() : undefined,
fontName: directTextStyler ? directTextStyler.fontName() : undefined
isBold: directFormattingController ? directFormattingController.isBold() : false,
isItalic: directFormattingController ? directFormattingController.isItalic() : false,
hasUnderline: directFormattingController ? directFormattingController.hasUnderline() : false,
hasStrikeThrough: directFormattingController ? directFormattingController.hasStrikeThrough() : false,
fontSize: directFormattingController ? directFormattingController.fontSize() : undefined,
fontName: directFormattingController ? directFormattingController.fontName() : undefined
});
if (editorSession) {

@ -39,61 +39,57 @@
/*global define,require*/
define("webodf/editor/widgets/undoRedoMenu",
["webodf/editor/EditorSession"],
["webodf/editor/EditorSession", "dijit/form/Button"],
function (EditorSession) {
function (EditorSession, Button) {
"use strict";
return function UndoRedoMenu(callback) {
var editorSession,
var self = this,
editorSession,
undoButton,
redoButton;
redoButton,
widget = {};
function makeWidget(callback) {
require(["dijit/form/Button"], function (Button) {
var widget = {};
undoButton = new Button({
label: runtime.tr('Undo'),
showLabel: false,
disabled: true, // TODO: get current session state
iconClass: "dijitEditorIcon dijitEditorIconUndo",
onClick: function () {
if (editorSession) {
editorSession.undo();
}
}
});
redoButton = new Button({
label: runtime.tr('Redo'),
showLabel: false,
disabled: true, // TODO: get current session state
iconClass: "dijitEditorIcon dijitEditorIconRedo",
onClick: function () {
if (editorSession) {
editorSession.redo();
}
}
});
undoButton = new Button({
label: runtime.tr('Undo'),
showLabel: false,
disabled: true, // TODO: get current session state
iconClass: "dijitEditorIcon dijitEditorIconUndo",
onClick: function () {
if (editorSession) {
editorSession.undo();
self.onToolDone();
}
}
});
widget.children = [undoButton, redoButton];
widget.startup = function () {
widget.children.forEach(function (element) {
element.startup();
});
};
redoButton = new Button({
label: runtime.tr('Redo'),
showLabel: false,
disabled: true, // TODO: get current session state
iconClass: "dijitEditorIcon dijitEditorIconRedo",
onClick: function () {
if (editorSession) {
editorSession.redo();
self.onToolDone();
}
}
});
widget.placeAt = function (container) {
widget.children.forEach(function (element) {
element.placeAt(container);
});
return widget;
};
widget.children = [undoButton, redoButton];
widget.startup = function () {
widget.children.forEach(function (element) {
element.startup();
});
};
return callback(widget);
widget.placeAt = function (container) {
widget.children.forEach(function (element) {
element.placeAt(container);
});
}
return widget;
};
function checkUndoButtons(e) {
if (undoButton) {
@ -115,9 +111,9 @@ define("webodf/editor/widgets/undoRedoMenu",
}
};
this.onToolDone = function () {};
// init
makeWidget(function (widget) {
return callback(widget);
});
callback(widget);
};
});

@ -38,24 +38,29 @@
/*global define,require*/
define("webodf/editor/widgets/zoomSlider", [], function () {
define("webodf/editor/widgets/zoomSlider", [
"webodf/editor/EditorSession"],
function (EditorSession) {
"use strict";
return function ZoomSlider(callback) {
var self = this,
editorSession,
slider;
slider,
extremeZoomFactor = 4;
// The slider zooms from -1 to +1, which corresponds
// to zoom levels of 1/extremeZoomFactor to extremeZoomFactor.
function makeWidget(callback) {
require(["dijit/form/HorizontalSlider", "dijit/form/NumberTextBox", "dojo"], function (HorizontalSlider, NumberTextBox, dojo) {
var widget = {};
slider = new HorizontalSlider({
name: 'zoomSlider',
value: 100,
minimum: 30,
maximum: 150,
discreteValues: 100,
value: 0,
minimum: -1,
maximum: 1,
discreteValues: 0.01,
intermediateChanges: true,
style: {
width: '150px',
@ -66,7 +71,7 @@ define("webodf/editor/widgets/zoomSlider", [], function () {
slider.onChange = function (value) {
if (editorSession) {
editorSession.getOdfCanvas().setZoomLevel(value / 100.0);
editorSession.getOdfCanvas().getZoomHelper().setZoomLevel(Math.pow(extremeZoomFactor, value));
}
self.onToolDone();
};
@ -75,9 +80,23 @@ define("webodf/editor/widgets/zoomSlider", [], function () {
});
}
function updateSlider(zoomLevel) {
if (slider) {
slider.set('value', Math.log(zoomLevel) / Math.log(extremeZoomFactor), false);
}
}
this.setEditorSession = function(session) {
var zoomHelper;
if (editorSession) {
editorSession.getOdfCanvas().getZoomHelper().unsubscribe(gui.ZoomHelper.signalZoomChanged, updateSlider);
}
editorSession = session;
// if (slider) { slider.setValue(editorSession.getOdfCanvas().getZoomLevel() ); TODO!
if (editorSession) {
zoomHelper = editorSession.getOdfCanvas().getZoomHelper();
zoomHelper.subscribe(gui.ZoomHelper.signalZoomChanged, updateSlider);
updateSlider(zoomHelper.getZoomLevel());
}
};
this.onToolDone = function () {};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long
Loading…
Cancel
Save