From bc276dc809a2f356f33ed6a578d7b429d588397e Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Tue, 4 Apr 2017 13:05:02 +0300 Subject: [PATCH 01/27] Update translations. --- apps/documenteditor/main/locale/de.json | 5 +- apps/documenteditor/main/locale/en.json | 13 ++- apps/documenteditor/main/locale/fr.json | 11 ++- apps/documenteditor/main/locale/ru.json | 5 +- apps/documenteditor/mobile/locale/de.json | 6 +- apps/documenteditor/mobile/locale/en.json | 10 +-- apps/documenteditor/mobile/locale/fr.json | 8 +- apps/documenteditor/mobile/locale/ru.json | 12 +-- apps/presentationeditor/main/locale/de.json | 5 +- apps/presentationeditor/main/locale/en.json | 13 ++- apps/presentationeditor/main/locale/fr.json | 7 +- apps/presentationeditor/main/locale/ru.json | 5 +- apps/presentationeditor/mobile/locale/de.json | 4 +- apps/presentationeditor/mobile/locale/fr.json | 6 +- apps/presentationeditor/mobile/locale/ru.json | 8 +- apps/spreadsheeteditor/main/locale/de.json | 55 +++++++++---- apps/spreadsheeteditor/main/locale/en.json | 55 +++++++------ apps/spreadsheeteditor/main/locale/fr.json | 67 ++++++++++------ apps/spreadsheeteditor/main/locale/ru.json | 45 ++++++++--- apps/spreadsheeteditor/mobile/locale/de.json | 66 +++++++-------- apps/spreadsheeteditor/mobile/locale/en.json | 43 +++++----- apps/spreadsheeteditor/mobile/locale/fr.json | 80 ++++++++++--------- apps/spreadsheeteditor/mobile/locale/ru.json | 42 +++++----- 23 files changed, 330 insertions(+), 241 deletions(-) diff --git a/apps/documenteditor/main/locale/de.json b/apps/documenteditor/main/locale/de.json index 21609fe1a..17f1b0f35 100644 --- a/apps/documenteditor/main/locale/de.json +++ b/apps/documenteditor/main/locale/de.json @@ -141,7 +141,6 @@ "Common.Views.ExternalMergeEditor.textTitle": "Seriendruckempfänger\t", "Common.Views.Header.openNewTabText": "In neuer Registerkarte öffnen", "Common.Views.Header.textBack": "Zu Dokumenten übergehen", - "Common.Views.Header.txtHeaderDeveloper": "ENTWICKLERMODUS", "Common.Views.Header.txtRename": "Umbenennen", "Common.Views.History.textCloseHistory": "Historie schließen", "Common.Views.History.textHide": "Reduzieren", @@ -262,6 +261,7 @@ "DE.Controllers.Main.splitMaxRowsErrorText": "Die Zeilenanzahl muss weniger als %1 sein.", "DE.Controllers.Main.textAnonymous": "Anonym", "DE.Controllers.Main.textBuyNow": "Webseite besuchen", + "DE.Controllers.Main.textChangesSaved": "Alle Änderungen werden gespeichert", "DE.Controllers.Main.textCloseTip": "Klicken Sie, um den Tipp zu schließen", "DE.Controllers.Main.textContactUs": "Verkaufsteam kontaktieren", "DE.Controllers.Main.textLoadingDocument": "Dokument wird geladen...\t", @@ -923,6 +923,7 @@ "DE.Views.FileMenuPanels.Settings.strCoAuthModeDescStrict": "Sie müssen die Änderungen annehmen, bevor Sie diese sehen können", "DE.Views.FileMenuPanels.Settings.strFast": "Schnell", "DE.Views.FileMenuPanels.Settings.strFontRender": "Hinting der Schriftarten", + "DE.Views.FileMenuPanels.Settings.strForcesave": "Immer auf dem Server speichern (ansonsten auf dem Server beim Schließen des Dokuments speichern)", "DE.Views.FileMenuPanels.Settings.strInputMode": "Hieroglyphen einschalten", "DE.Views.FileMenuPanels.Settings.strLiveComment": "Live-Kommentare einschalten", "DE.Views.FileMenuPanels.Settings.strShowChanges": "Änderungen bei der Echtzeit-Zusammenarbeit zeigen", @@ -938,6 +939,7 @@ "DE.Views.FileMenuPanels.Settings.textAutoRecover": "Autowiederherstellen", "DE.Views.FileMenuPanels.Settings.textAutoSave": "Automatisch speichern", "DE.Views.FileMenuPanels.Settings.textDisabled": "Deaktiviert", + "DE.Views.FileMenuPanels.Settings.textForceSave": "Auf dem Server speichern", "DE.Views.FileMenuPanels.Settings.textMinute": "Jede Minute", "DE.Views.FileMenuPanels.Settings.txtAll": "Alle anzeigen", "DE.Views.FileMenuPanels.Settings.txtCm": "Zentimeter", @@ -1070,6 +1072,7 @@ "DE.Views.LeftMenu.tipSearch": "Suchen", "DE.Views.LeftMenu.tipSupport": "Feedback und Support", "DE.Views.LeftMenu.tipTitles": "Titel", + "DE.Views.LeftMenu.txtDeveloper": "ENTWICKLERMODUS", "DE.Views.MailMergeEmailDlg.cancelButtonText": "Abbrechen", "DE.Views.MailMergeEmailDlg.filePlaceholder": "PDF", "DE.Views.MailMergeEmailDlg.okButtonText": "Senden", diff --git a/apps/documenteditor/main/locale/en.json b/apps/documenteditor/main/locale/en.json index ba1a65e3a..1301956cf 100644 --- a/apps/documenteditor/main/locale/en.json +++ b/apps/documenteditor/main/locale/en.json @@ -141,7 +141,6 @@ "Common.Views.ExternalMergeEditor.textTitle": "Mail Merge Recipients", "Common.Views.Header.openNewTabText": "Open in New Tab", "Common.Views.Header.textBack": "Go to Documents", - "del_Common.Views.Header.txtHeaderDeveloper": "DEVELOPER MODE", "Common.Views.Header.txtRename": "Rename", "Common.Views.History.textCloseHistory": "Close History", "Common.Views.History.textHide": "Collapse", @@ -217,6 +216,7 @@ "DE.Controllers.Main.errorMailMergeLoadFile": "Loading failed", "DE.Controllers.Main.errorMailMergeSaveFile": "Merge failed.", "DE.Controllers.Main.errorProcessSaveResult": "Saving failed.", + "DE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", "DE.Controllers.Main.errorSessionAbsolute": "The document editing session has expired. Please reload the page.", "DE.Controllers.Main.errorSessionIdle": "The document has not been edited for quite a long time. Please reload the page.", "DE.Controllers.Main.errorSessionToken": "The connection to the server has been interrupted. Please reload the page.", @@ -227,6 +227,7 @@ "DE.Controllers.Main.errorUserDrop": "The file cannot be accessed right now.", "DE.Controllers.Main.errorUsersExceed": "The number of users allowed by the pricing plan was exceeded", "DE.Controllers.Main.errorViewerDisconnect": "Connection is lost. You can still view the document,
but will not be able to download or print until the connection is restored.", + "DE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "DE.Controllers.Main.leavePageText": "You have unsaved changes in this document. Click \"Stay on This Page\", then \"Save\" to save them. Click \"Leave This Page\" to discard all the unsaved changes.", "DE.Controllers.Main.loadFontsTextText": "Loading data...", "DE.Controllers.Main.loadFontsTitleText": "Loading Data", @@ -261,6 +262,7 @@ "DE.Controllers.Main.splitMaxRowsErrorText": "The number of rows must be less than %1.", "DE.Controllers.Main.textAnonymous": "Anonymous", "DE.Controllers.Main.textBuyNow": "Visit website", + "DE.Controllers.Main.textChangesSaved": "All changes saved", "DE.Controllers.Main.textCloseTip": "Click to close the tip", "DE.Controllers.Main.textContactUs": "Contact sales", "DE.Controllers.Main.textLoadingDocument": "Loading document", @@ -268,6 +270,7 @@ "DE.Controllers.Main.textStrict": "Strict mode", "DE.Controllers.Main.textTryUndoRedo": "The Undo/Redo functions are disabled for the Fast co-editing mode.
Click the 'Strict mode' button to switch to the Strict co-editing mode to edit the file without other users interference and send your changes only after you save them. You can switch between the co-editing modes using the editor Advanced settings.", "DE.Controllers.Main.titleLicenseExp": "License expired", + "DE.Controllers.Main.titleServerVersion": "Editor updated", "DE.Controllers.Main.titleUpdateVersion": "Version changed", "DE.Controllers.Main.txtArt": "Your text here", "DE.Controllers.Main.txtBasicShapes": "Basic Shapes", @@ -298,10 +301,6 @@ "DE.Controllers.Main.warnLicenseExp": "Your license has expired.
Please update your license and refresh the page.", "DE.Controllers.Main.warnNoLicense": "You are using an open source version of ONLYOFFICE. The version has limitations for concurrent connections to the document server (20 connections at a time).
If you need more please consider purchasing a commercial license.", "DE.Controllers.Main.warnProcessRightsChange": "You have been denied the right to edit the file.", - "DE.Controllers.Main.titleServerVersion": "Editor updated", - "DE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", - "DE.Controllers.Main.textChangesSaved": "All changes saved", - "DE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "DE.Controllers.Statusbar.textHasChanges": "New changes have been tracked", "DE.Controllers.Statusbar.textTrackChanges": "The document is opened with the Track Changes mode enabled", "DE.Controllers.Statusbar.zoomText": "Zoom {0}%", @@ -925,6 +924,7 @@ "DE.Views.FileMenuPanels.Settings.strCoAuthModeDescStrict": "You will need to accept changes before you can see them", "DE.Views.FileMenuPanels.Settings.strFast": "Fast", "DE.Views.FileMenuPanels.Settings.strFontRender": "Font Hinting", + "DE.Views.FileMenuPanels.Settings.strForcesave": "Always save to server (otherwise save to server on document close)", "DE.Views.FileMenuPanels.Settings.strInputMode": "Turn on hieroglyphs", "DE.Views.FileMenuPanels.Settings.strLiveComment": "Turn on display of the comments", "DE.Views.FileMenuPanels.Settings.strShowChanges": "Realtime Collaboration Changes", @@ -940,6 +940,7 @@ "DE.Views.FileMenuPanels.Settings.textAutoRecover": "Autorecover", "DE.Views.FileMenuPanels.Settings.textAutoSave": "Autosave", "DE.Views.FileMenuPanels.Settings.textDisabled": "Disabled", + "DE.Views.FileMenuPanels.Settings.textForceSave": "Save to Server", "DE.Views.FileMenuPanels.Settings.textMinute": "Every Minute", "DE.Views.FileMenuPanels.Settings.txtAll": "View All", "DE.Views.FileMenuPanels.Settings.txtCm": "Centimeter", @@ -955,8 +956,6 @@ "DE.Views.FileMenuPanels.Settings.txtPt": "Point", "DE.Views.FileMenuPanels.Settings.txtSpellCheck": "Spell Checking", "DE.Views.FileMenuPanels.Settings.txtWin": "as Windows", - "DE.Views.FileMenuPanels.Settings.textForceSave": "Save to Server", - "DE.Views.FileMenuPanels.Settings.strForcesave": "Always save to server (otherwise save to server on document close)", "DE.Views.HeaderFooterSettings.textBottomCenter": "Bottom Center", "DE.Views.HeaderFooterSettings.textBottomLeft": "Bottom Left", "DE.Views.HeaderFooterSettings.textBottomRight": "Bottom Right", diff --git a/apps/documenteditor/main/locale/fr.json b/apps/documenteditor/main/locale/fr.json index ce66640d0..68aa3e049 100644 --- a/apps/documenteditor/main/locale/fr.json +++ b/apps/documenteditor/main/locale/fr.json @@ -141,7 +141,6 @@ "Common.Views.ExternalMergeEditor.textTitle": "Destinataires de fusion et publipostage", "Common.Views.Header.openNewTabText": "Ouvrir dans un nouvel onglet", "Common.Views.Header.textBack": "Aller aux Documents", - "Common.Views.Header.txtHeaderDeveloper": "MODE DEVELOPPEUR", "Common.Views.Header.txtRename": "Renommer", "Common.Views.History.textCloseHistory": "Fermer l'historique", "Common.Views.History.textHide": "Réduire", @@ -207,7 +206,7 @@ "DE.Controllers.Main.downloadTitleText": "Téléchargement du document", "DE.Controllers.Main.errorAccessDeny": "Vous tentez d'exéсuter une action pour laquelle vous ne disposez pas des droits.
Veuillez contacter l'administrateur de Document Server.", "DE.Controllers.Main.errorCoAuthoringDisconnect": "Connexion au serveur perdue. Le document ne peut être modifié en ce moment.", - "DE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion Document Serverhere", + "DE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion de Document Serverici", "DE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
Erreur de connexion à la base de données. Si l'erreur persiste veillez contactez l'assistance technique.", "DE.Controllers.Main.errorDataRange": "Plage de données incorrecte.", "DE.Controllers.Main.errorDefaultMessage": "Code d'erreur: %1", @@ -262,6 +261,7 @@ "DE.Controllers.Main.splitMaxRowsErrorText": "Le nombre de lignes doit être inférieure à %1.", "DE.Controllers.Main.textAnonymous": "Anonyme", "DE.Controllers.Main.textBuyNow": "Visiter le site web", + "DE.Controllers.Main.textChangesSaved": "Toutes les modifications sont enregistrées", "DE.Controllers.Main.textCloseTip": "Cliquez pour fermer le conseil", "DE.Controllers.Main.textContactUs": "Contacter l'équipe de ventes", "DE.Controllers.Main.textLoadingDocument": "Chargement du document", @@ -923,6 +923,7 @@ "DE.Views.FileMenuPanels.Settings.strCoAuthModeDescStrict": "Avant de pouvoir afficher les modifications, vous avez besoin de les accépter ", "DE.Views.FileMenuPanels.Settings.strFast": "Rapide", "DE.Views.FileMenuPanels.Settings.strFontRender": "Hinting de la police", + "DE.Views.FileMenuPanels.Settings.strForcesave": "Toujours enregistrer sur le serveur ( sinon enregistrer sur le serveur lors de la fermeture du document )", "DE.Views.FileMenuPanels.Settings.strInputMode": "Activer des hiéroglyphes", "DE.Views.FileMenuPanels.Settings.strLiveComment": "Activer l'affichage des commentaires", "DE.Views.FileMenuPanels.Settings.strShowChanges": "Changements de collaboration en temps réel", @@ -938,6 +939,7 @@ "DE.Views.FileMenuPanels.Settings.textAutoRecover": "Récupération automatique", "DE.Views.FileMenuPanels.Settings.textAutoSave": "Enregistrement automatique", "DE.Views.FileMenuPanels.Settings.textDisabled": "Désactivé", + "DE.Views.FileMenuPanels.Settings.textForceSave": "Enregistrer sur le serveur", "DE.Views.FileMenuPanels.Settings.textMinute": "Chaque minute", "DE.Views.FileMenuPanels.Settings.txtAll": "Tous", "DE.Views.FileMenuPanels.Settings.txtCm": "Centimètre", @@ -1070,6 +1072,7 @@ "DE.Views.LeftMenu.tipSearch": "Recherche", "DE.Views.LeftMenu.tipSupport": "Commentaires & assistance", "DE.Views.LeftMenu.tipTitles": "Titres", + "DE.Views.LeftMenu.txtDeveloper": "MODE DEVELOPPEUR", "DE.Views.MailMergeEmailDlg.cancelButtonText": "Annuler", "DE.Views.MailMergeEmailDlg.filePlaceholder": "PDF", "DE.Views.MailMergeEmailDlg.okButtonText": "Envoyer", @@ -1540,7 +1543,7 @@ "DE.Views.Toolbar.tipColumns": "Insérer des colonnes ", "DE.Views.Toolbar.tipCopy": "Copier", "DE.Views.Toolbar.tipCopyStyle": "Copier le style", - "DE.Views.Toolbar.tipDecFont": "Réduire taille de la police", + "DE.Views.Toolbar.tipDecFont": "Réduire la taille de la police", "DE.Views.Toolbar.tipDecPrLeft": "Réduire le retrait", "DE.Views.Toolbar.tipDropCap": "Insérer une lettrine", "DE.Views.Toolbar.tipEditHeader": "Modifier l'en-tête ou le pied de page", @@ -1549,7 +1552,7 @@ "DE.Views.Toolbar.tipFontSize": "Taille de la police", "DE.Views.Toolbar.tipHAligh": "Alignement horizontal", "DE.Views.Toolbar.tipHighlightColor": "Couleur de surlignage", - "DE.Views.Toolbar.tipIncFont": "Augmenter taille de la police", + "DE.Views.Toolbar.tipIncFont": "Augmenter la taille de la police", "DE.Views.Toolbar.tipIncPrLeft": "Augmenter le retrait", "DE.Views.Toolbar.tipInsertChart": "Insérer un graphique", "DE.Views.Toolbar.tipInsertEquation": "Insérer une équation", diff --git a/apps/documenteditor/main/locale/ru.json b/apps/documenteditor/main/locale/ru.json index 5aea5cecc..e28461b8f 100644 --- a/apps/documenteditor/main/locale/ru.json +++ b/apps/documenteditor/main/locale/ru.json @@ -141,7 +141,6 @@ "Common.Views.ExternalMergeEditor.textTitle": "Получатели слияния", "Common.Views.Header.openNewTabText": "Открыть в новой вкладке", "Common.Views.Header.textBack": "Перейти к Документам", - "Common.Views.Header.txtHeaderDeveloper": "РЕЖИМ РАЗРАБОТЧИКА", "Common.Views.Header.txtRename": "Переименовать", "Common.Views.History.textCloseHistory": "Закрыть историю", "Common.Views.History.textHide": "Свернуть", @@ -262,6 +261,7 @@ "DE.Controllers.Main.splitMaxRowsErrorText": "Число строк должно быть меньше, чем %1.", "DE.Controllers.Main.textAnonymous": "Аноним", "DE.Controllers.Main.textBuyNow": "Перейти на сайт", + "DE.Controllers.Main.textChangesSaved": "Все изменения сохранены", "DE.Controllers.Main.textCloseTip": "Щелкните, чтобы закрыть эту подсказку", "DE.Controllers.Main.textContactUs": "Связаться с отделом продаж", "DE.Controllers.Main.textLoadingDocument": "Загрузка документа", @@ -923,6 +923,7 @@ "DE.Views.FileMenuPanels.Settings.strCoAuthModeDescStrict": "Прежде чем вы сможете увидеть изменения, их надо будет принять", "DE.Views.FileMenuPanels.Settings.strFast": "Быстрый", "DE.Views.FileMenuPanels.Settings.strFontRender": "Хинтинг шрифтов", + "DE.Views.FileMenuPanels.Settings.strForcesave": "Всегда сохранять на сервере (в противном случае сохранять на сервере при закрытии документа)", "DE.Views.FileMenuPanels.Settings.strInputMode": "Включить иероглифы", "DE.Views.FileMenuPanels.Settings.strLiveComment": "Включить отображение комментариев в тексте", "DE.Views.FileMenuPanels.Settings.strShowChanges": "Отображать изменения при совместной работе", @@ -938,6 +939,7 @@ "DE.Views.FileMenuPanels.Settings.textAutoRecover": "Автовосстановление", "DE.Views.FileMenuPanels.Settings.textAutoSave": "Автосохранение", "DE.Views.FileMenuPanels.Settings.textDisabled": "Отключено", + "DE.Views.FileMenuPanels.Settings.textForceSave": "Сохранить на сервере", "DE.Views.FileMenuPanels.Settings.textMinute": "Каждую минуту", "DE.Views.FileMenuPanels.Settings.txtAll": "Все", "DE.Views.FileMenuPanels.Settings.txtCm": "Сантиметр", @@ -1070,6 +1072,7 @@ "DE.Views.LeftMenu.tipSearch": "Поиск", "DE.Views.LeftMenu.tipSupport": "Обратная связь и поддержка", "DE.Views.LeftMenu.tipTitles": "Заголовки", + "DE.Views.LeftMenu.txtDeveloper": "РЕЖИМ РАЗРАБОТЧИКА", "DE.Views.MailMergeEmailDlg.cancelButtonText": "Отмена", "DE.Views.MailMergeEmailDlg.filePlaceholder": "PDF", "DE.Views.MailMergeEmailDlg.okButtonText": "Отправить", diff --git a/apps/documenteditor/mobile/locale/de.json b/apps/documenteditor/mobile/locale/de.json index 07b1c6311..9ed00928a 100644 --- a/apps/documenteditor/mobile/locale/de.json +++ b/apps/documenteditor/mobile/locale/de.json @@ -19,10 +19,10 @@ "DE.Controllers.DocumentHolder.menuCut": "Ausschneiden", "DE.Controllers.DocumentHolder.menuDelete": "Löschen", "DE.Controllers.DocumentHolder.menuEdit": "Bearbeiten", + "DE.Controllers.DocumentHolder.menuMore": "Mehr", "DE.Controllers.DocumentHolder.menuOpenLink": "Link öffnen", - "DE.Controllers.DocumentHolder.menuMore": "More", - "DE.Controllers.DocumentHolder.sheetCancel": "Abbrechen", "DE.Controllers.DocumentHolder.menuPaste": "Einfügen", + "DE.Controllers.DocumentHolder.sheetCancel": "Abbrechen", "DE.Controllers.DocumentHolder.textGuest": "Gast", "DE.Controllers.EditContainer.textChart": "Diagramm", "DE.Controllers.EditContainer.textHyperlink": "Hyperlink", @@ -63,6 +63,7 @@ "DE.Controllers.Main.errorMailMergeLoadFile": "Fehler beim Laden\t", "DE.Controllers.Main.errorMailMergeSaveFile": "Verbinden ist fehlgeschlagen.", "DE.Controllers.Main.errorProcessSaveResult": "Fehler beim Speichern von Daten.", + "DE.Controllers.Main.errorServerVersion": "Editor-Version wurde aktualisiert. Die Seite wird neu geladen, um die Änderungen zu übernehmen.", "DE.Controllers.Main.errorStockChart": "Falsche Reihenfolge der Zeilen. Um ein Kursdiagramm zu erstellen, ordnen Sie die Daten auf dem Blatt folgendermaßen an:
Eröffnungspreis, Höchstpreis, Tiefstpreis, Schlusskurs.", "DE.Controllers.Main.errorUpdateVersion": "Die Dateiversion wurde geändert. Die Seite wird neu geladen.", "DE.Controllers.Main.errorUserDrop": "Kein Zugriff auf diese Datei ist möglich.", @@ -112,6 +113,7 @@ "DE.Controllers.Main.textTryUndoRedo": "Undo/Redo Optionen sind für den halbformalen Zusammenbearbeitungsmodus deaktiviert.", "DE.Controllers.Main.textUsername": "Benutzername", "DE.Controllers.Main.titleLicenseExp": "Lizenz ist abgelaufen", + "DE.Controllers.Main.titleServerVersion": "Editor wurde aktualisiert", "DE.Controllers.Main.titleUpdateVersion": "Version wurde geändert", "DE.Controllers.Main.txtArt": "Hier den Text eingeben", "DE.Controllers.Main.txtDiagramTitle": "Diagrammtitel", diff --git a/apps/documenteditor/mobile/locale/en.json b/apps/documenteditor/mobile/locale/en.json index 0578df115..196204f7a 100644 --- a/apps/documenteditor/mobile/locale/en.json +++ b/apps/documenteditor/mobile/locale/en.json @@ -19,10 +19,10 @@ "DE.Controllers.DocumentHolder.menuCut": "Cut", "DE.Controllers.DocumentHolder.menuDelete": "Delete", "DE.Controllers.DocumentHolder.menuEdit": "Edit", - "DE.Controllers.DocumentHolder.menuOpenLink": "Open Link", "DE.Controllers.DocumentHolder.menuMore": "More", - "DE.Controllers.DocumentHolder.sheetCancel": "Cancel", + "DE.Controllers.DocumentHolder.menuOpenLink": "Open Link", "DE.Controllers.DocumentHolder.menuPaste": "Paste", + "DE.Controllers.DocumentHolder.sheetCancel": "Cancel", "DE.Controllers.DocumentHolder.textGuest": "Guest", "DE.Controllers.EditContainer.textChart": "Chart", "DE.Controllers.EditContainer.textHyperlink": "Hyperlink", @@ -63,13 +63,14 @@ "DE.Controllers.Main.errorMailMergeLoadFile": "Loading failed", "DE.Controllers.Main.errorMailMergeSaveFile": "Merge failed.", "DE.Controllers.Main.errorProcessSaveResult": "Saving is failed.", + "DE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", "DE.Controllers.Main.errorStockChart": "Incorrect row order. To build a stock chart place the data on the sheet in the following order:
opening price, max price, min price, closing price.", "DE.Controllers.Main.errorUpdateVersion": "The file version has been changed. The page will be reloaded.", "DE.Controllers.Main.errorUserDrop": "The file cannot be accessed right now.", "DE.Controllers.Main.errorUsersExceed": "The number of users was exceeded", "DE.Controllers.Main.errorViewerDisconnect": "Connection is lost. You can still view the document,
but will not be able to download until the connection is restored.", - "DE.Controllers.Main.leavePageText": "You have unsaved changes in this document. Click 'Stay on this Page' to await the autosave of the document. Click 'Leave this Page' to discard all the unsaved changes.", "DE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", + "DE.Controllers.Main.leavePageText": "You have unsaved changes in this document. Click 'Stay on this Page' to await the autosave of the document. Click 'Leave this Page' to discard all the unsaved changes.", "DE.Controllers.Main.loadFontsTextText": "Loading data...", "DE.Controllers.Main.loadFontsTitleText": "Loading Data", "DE.Controllers.Main.loadFontTextText": "Loading data...", @@ -113,6 +114,7 @@ "DE.Controllers.Main.textTryUndoRedo": "The Undo/Redo functions are disabled for the Fast co-editing mode.", "DE.Controllers.Main.textUsername": "Username", "DE.Controllers.Main.titleLicenseExp": "License expired", + "DE.Controllers.Main.titleServerVersion": "Editor updated", "DE.Controllers.Main.titleUpdateVersion": "Version changed", "DE.Controllers.Main.txtArt": "Your text here", "DE.Controllers.Main.txtDiagramTitle": "Chart Title", @@ -130,8 +132,6 @@ "DE.Controllers.Main.warnLicenseExp": "Your license has expired.
Please update your license and refresh the page.", "DE.Controllers.Main.warnNoLicense": "You are using an open source version of ONLYOFFICE. The version has limitations for concurrent connections to the document server (20 connections at a time).
If you need more please consider purchasing a commercial license.", "DE.Controllers.Main.warnProcessRightsChange": "You have been denied the right to edit the file.", - "DE.Controllers.Main.titleServerVersion": "Editor updated", - "DE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", "DE.Controllers.Search.textNoTextFound": "Text not Found", "DE.Controllers.Search.textReplaceAll": "Replace All", "DE.Controllers.Settings.notcriticalErrorTitle": "Warning", diff --git a/apps/documenteditor/mobile/locale/fr.json b/apps/documenteditor/mobile/locale/fr.json index 98a529b40..cb984fa64 100644 --- a/apps/documenteditor/mobile/locale/fr.json +++ b/apps/documenteditor/mobile/locale/fr.json @@ -19,10 +19,10 @@ "DE.Controllers.DocumentHolder.menuCut": "Couper", "DE.Controllers.DocumentHolder.menuDelete": "Supprimer", "DE.Controllers.DocumentHolder.menuEdit": "Modifier", + "DE.Controllers.DocumentHolder.menuMore": "Plus", "DE.Controllers.DocumentHolder.menuOpenLink": "Ouvrir le lien", - "DE.Controllers.DocumentHolder.menuMore": "More", - "DE.Controllers.DocumentHolder.sheetCancel": "Annuler", "DE.Controllers.DocumentHolder.menuPaste": "Coller", + "DE.Controllers.DocumentHolder.sheetCancel": "Annuler", "DE.Controllers.DocumentHolder.textGuest": "Invité", "DE.Controllers.EditContainer.textChart": "Graphique", "DE.Controllers.EditContainer.textHyperlink": "Lien hypertexte", @@ -53,7 +53,7 @@ "DE.Controllers.Main.downloadTextText": "Téléchargement du document...", "DE.Controllers.Main.downloadTitleText": "Téléchargement du document", "DE.Controllers.Main.errorCoAuthoringDisconnect": "La connexion au serveur perdue. Désolé, vous ne pouvez plus modifier le document.", - "DE.Controllers.Main.errorConnectToServer": "Impossible d'enregistrer le document. Veuillez vérifier vos paramètres de connexion ou contactez l'administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Pour en savoir plus sur la connexion de Document Server here", + "DE.Controllers.Main.errorConnectToServer": "Impossible d'enregistrer le document. Veuillez vérifier vos paramètres de connexion ou contactez l'administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion de Document Server ici", "DE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
Erreur de connexion à la base de données.Contactez le support.", "DE.Controllers.Main.errorDataRange": "Plage de données incorrecte.", "DE.Controllers.Main.errorDefaultMessage": "Code d'erreur: %1", @@ -63,6 +63,7 @@ "DE.Controllers.Main.errorMailMergeLoadFile": "Échec du chargement", "DE.Controllers.Main.errorMailMergeSaveFile": "Fusion a échoué.", "DE.Controllers.Main.errorProcessSaveResult": "Échec de l‘enregistrement.", + "DE.Controllers.Main.errorServerVersion": "La version de l'éditeur a été mise à jour. La page sera rechargée pour appliquer les modifications.", "DE.Controllers.Main.errorStockChart": "L'ordre des lignes est incorrect. Pour créer un graphique boursier organisez vos données sur la feuille de calcul dans l'ordre suivant:
cours à l'ouverture, cours maximal, cours minimal, cours à la clôture.", "DE.Controllers.Main.errorUpdateVersion": "La version du fichier a été changée. La page sera rechargée.", "DE.Controllers.Main.errorUserDrop": "Impossible d'accéder au fichier.", @@ -112,6 +113,7 @@ "DE.Controllers.Main.textTryUndoRedo": "Les fonctions Annuler/Rétablir sont désactivées pour le mode collaboratif rapide.", "DE.Controllers.Main.textUsername": "Nom d'utilisateur", "DE.Controllers.Main.titleLicenseExp": "Licence expirée", + "DE.Controllers.Main.titleServerVersion": "L'éditeur est mis à jour", "DE.Controllers.Main.titleUpdateVersion": "Version a été modifiée", "DE.Controllers.Main.txtArt": "Entrez votre texte", "DE.Controllers.Main.txtDiagramTitle": "Titre du graphique", diff --git a/apps/documenteditor/mobile/locale/ru.json b/apps/documenteditor/mobile/locale/ru.json index 0ee1d2a2d..7a9f9d52c 100644 --- a/apps/documenteditor/mobile/locale/ru.json +++ b/apps/documenteditor/mobile/locale/ru.json @@ -3,7 +3,7 @@ "Common.UI.ThemeColorPalette.textThemeColors": "Цвета темы", "Common.Utils.Metric.txtCm": "см", "Common.Utils.Metric.txtPt": "пт", - "DE.Controllers.AddContainer.textImage": "Картинка", + "DE.Controllers.AddContainer.textImage": "Изображение", "DE.Controllers.AddContainer.textOther": "Другое", "DE.Controllers.AddContainer.textShape": "Фигура", "DE.Controllers.AddContainer.textTable": "Таблица", @@ -19,10 +19,10 @@ "DE.Controllers.DocumentHolder.menuCut": "Вырезать", "DE.Controllers.DocumentHolder.menuDelete": "Удалить", "DE.Controllers.DocumentHolder.menuEdit": "Редактировать", + "DE.Controllers.DocumentHolder.menuMore": "Ещё", "DE.Controllers.DocumentHolder.menuOpenLink": "Перейти", - "DE.Controllers.DocumentHolder.menuMore": "Еще", - "DE.Controllers.DocumentHolder.sheetCancel": "Отмена", "DE.Controllers.DocumentHolder.menuPaste": "Вставить", + "DE.Controllers.DocumentHolder.sheetCancel": "Отмена", "DE.Controllers.DocumentHolder.textGuest": "Гость", "DE.Controllers.EditContainer.textChart": "Диаграмма", "DE.Controllers.EditContainer.textHyperlink": "Гиперссылка", @@ -63,6 +63,7 @@ "DE.Controllers.Main.errorMailMergeLoadFile": "Сбой при загрузке", "DE.Controllers.Main.errorMailMergeSaveFile": "Не удалось выполнить слияние.", "DE.Controllers.Main.errorProcessSaveResult": "Не удалось завершить сохранение.", + "DE.Controllers.Main.errorServerVersion": "Версия редактора была обновлена. Страница будет перезагружена, чтобы применить изменения.", "DE.Controllers.Main.errorStockChart": "Неверный порядок строк. Чтобы создать биржевую диаграмму, расположите данные на листе в следующем порядке:
цена открытия, максимальная цена, минимальная цена, цена закрытия.", "DE.Controllers.Main.errorUpdateVersion": "Версия файла была изменена. Страница будет перезагружена.", "DE.Controllers.Main.errorUserDrop": "В настоящий момент файл недоступен.", @@ -112,6 +113,7 @@ "DE.Controllers.Main.textTryUndoRedo": "Функции отмены и повтора действий отключены в Быстром режиме совместного редактирования.", "DE.Controllers.Main.textUsername": "Имя пользователя", "DE.Controllers.Main.titleLicenseExp": "Истек срок действия лицензии", + "DE.Controllers.Main.titleServerVersion": "Редактор обновлен", "DE.Controllers.Main.titleUpdateVersion": "Версия изменилась", "DE.Controllers.Main.txtArt": "Введите ваш текст", "DE.Controllers.Main.txtDiagramTitle": "Заголовок диаграммы", @@ -251,7 +253,7 @@ "DE.Views.EditShape.textFromText": "Расстояние до текста", "DE.Views.EditShape.textInFront": "Перед текстом", "DE.Views.EditShape.textInline": "В тексте", - "DE.Views.EditShape.textOpacity": "Непрозрачность", + "DE.Views.EditShape.textOpacity": "Прозрачность", "DE.Views.EditShape.textOverlap": "Разрешить перекрытие", "DE.Views.EditShape.textRemoveShape": "Удалить фигуру", "DE.Views.EditShape.textReorder": "Порядок", @@ -319,7 +321,7 @@ "DE.Views.Search.textHighlight": "Выделить результаты", "DE.Views.Search.textReplace": "Заменить", "DE.Views.Search.textSearch": "Найти", - "DE.Views.Settings.textAbout": "О продукте", + "DE.Views.Settings.textAbout": "О программе", "DE.Views.Settings.textAddress": "адрес", "DE.Views.Settings.textAuthor": "Автор", "DE.Views.Settings.textBack": "Назад", diff --git a/apps/presentationeditor/main/locale/de.json b/apps/presentationeditor/main/locale/de.json index c052a0a5c..dcc7d224d 100644 --- a/apps/presentationeditor/main/locale/de.json +++ b/apps/presentationeditor/main/locale/de.json @@ -80,7 +80,6 @@ "Common.Views.ExternalDiagramEditor.textTitle": "Diagramm bearbeiten", "Common.Views.Header.openNewTabText": "In neuer Registerkarte öffnen", "Common.Views.Header.textBack": "Zu Dokumenten übergehen", - "Common.Views.Header.txtHeaderDeveloper": "ENTWICKLERMODUS", "Common.Views.Header.txtRename": "Umbenennen", "Common.Views.ImageFromUrlDialog.cancelButtonText": "Abbrechen", "Common.Views.ImageFromUrlDialog.okButtonText": "OK", @@ -174,6 +173,7 @@ "PE.Controllers.Main.splitMaxRowsErrorText": "Die Zeilenanzahl muss weniger als %1 sein.", "PE.Controllers.Main.textAnonymous": "Anonym", "PE.Controllers.Main.textBuyNow": "Webseite besuchen", + "PE.Controllers.Main.textChangesSaved": "Alle Änderungen werden gespeichert", "PE.Controllers.Main.textCloseTip": "Klicken Sie, um den Tipp zu schließen", "PE.Controllers.Main.textContactUs": "Verkaufsteam kontaktieren", "PE.Controllers.Main.textLoadingDocument": "Präsentation wird geladen ", @@ -796,6 +796,7 @@ "PE.Views.FileMenuPanels.Settings.strCoAuthModeDescFast": "Andere Benutzer werden Ihre Änderungen gleichzeitig sehen", "PE.Views.FileMenuPanels.Settings.strCoAuthModeDescStrict": "Sie müssen die Änderungen annehmen, bevor Sie diese sehen können", "PE.Views.FileMenuPanels.Settings.strFast": "Schnell", + "PE.Views.FileMenuPanels.Settings.strForcesave": "Immer auf dem Server speichern (ansonsten auf dem Server beim Schließen des Dokuments speichern)", "PE.Views.FileMenuPanels.Settings.strInputMode": "Hieroglyphen einschalten", "PE.Views.FileMenuPanels.Settings.strShowChanges": "Änderungen bei der Echtzeit-Zusammenarbeit zeigen", "PE.Views.FileMenuPanels.Settings.strStrict": "Formal", @@ -809,6 +810,7 @@ "PE.Views.FileMenuPanels.Settings.textAutoRecover": "Autowiederherstellen", "PE.Views.FileMenuPanels.Settings.textAutoSave": "Automatisch speichern", "PE.Views.FileMenuPanels.Settings.textDisabled": "Ausgeschaltet", + "PE.Views.FileMenuPanels.Settings.textForceSave": "Auf dem Server speichern", "PE.Views.FileMenuPanels.Settings.textMinute": "Jede Minute", "PE.Views.FileMenuPanels.Settings.txtAll": "Alle anzeigen", "PE.Views.FileMenuPanels.Settings.txtCm": "Zentimeter", @@ -872,6 +874,7 @@ "PE.Views.LeftMenu.tipSlides": "Folien", "PE.Views.LeftMenu.tipSupport": "Feedback und Support", "PE.Views.LeftMenu.tipTitles": "Titel", + "PE.Views.LeftMenu.txtDeveloper": "ENTWICKLERMODUS", "PE.Views.ParagraphSettings.strLineHeight": "Zeilenabstand", "PE.Views.ParagraphSettings.strParagraphSpacing": "Absatzabstand", "PE.Views.ParagraphSettings.strSpacingAfter": "Nach ", diff --git a/apps/presentationeditor/main/locale/en.json b/apps/presentationeditor/main/locale/en.json index fe010e70d..1c2329e49 100644 --- a/apps/presentationeditor/main/locale/en.json +++ b/apps/presentationeditor/main/locale/en.json @@ -80,7 +80,6 @@ "Common.Views.ExternalDiagramEditor.textTitle": "Chart Editor", "Common.Views.Header.openNewTabText": "Open in New Tab", "Common.Views.Header.textBack": "Go to Documents", - "del_Common.Views.Header.txtHeaderDeveloper": "DEVELOPER MODE", "Common.Views.Header.txtRename": "Rename", "Common.Views.ImageFromUrlDialog.cancelButtonText": "Cancel", "Common.Views.ImageFromUrlDialog.okButtonText": "OK", @@ -131,6 +130,7 @@ "PE.Controllers.Main.errorKeyEncrypt": "Unknown key descriptor", "PE.Controllers.Main.errorKeyExpire": "Key descriptor expired", "PE.Controllers.Main.errorProcessSaveResult": "Saving failed.", + "PE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", "PE.Controllers.Main.errorSessionAbsolute": "The document editing session has expired. Please reload the page.", "PE.Controllers.Main.errorSessionIdle": "The document has not been edited for quite a long time. Please reload the page.", "PE.Controllers.Main.errorSessionToken": "The connection to the server has been interrupted. Please reload the page.", @@ -141,6 +141,7 @@ "PE.Controllers.Main.errorUserDrop": "The file cannot be accessed right now.", "PE.Controllers.Main.errorUsersExceed": "The number of users allowed by the pricing plan was exceeded", "PE.Controllers.Main.errorViewerDisconnect": "Connection is lost. You can still view the document,
but will not be able to download or print until the connection is restored.", + "PE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "PE.Controllers.Main.leavePageText": "You have unsaved changes in this presentation. Click \"Stay on This Page\", then \"Save\" to save them. Click \"Leave This Page\" to discard all the unsaved changes.", "PE.Controllers.Main.loadFontsTextText": "Loading data...", "PE.Controllers.Main.loadFontsTitleText": "Loading Data", @@ -173,6 +174,7 @@ "PE.Controllers.Main.splitMaxRowsErrorText": "The number of rows must be less than %1.", "PE.Controllers.Main.textAnonymous": "Anonymous", "PE.Controllers.Main.textBuyNow": "Visit website", + "PE.Controllers.Main.textChangesSaved": "All changes saved", "PE.Controllers.Main.textCloseTip": "Click to close the tip", "PE.Controllers.Main.textContactUs": "Contact sales", "PE.Controllers.Main.textLoadingDocument": "Loading presentation", @@ -181,6 +183,7 @@ "PE.Controllers.Main.textStrict": "Strict mode", "PE.Controllers.Main.textTryUndoRedo": "The Undo/Redo functions are disabled for the Fast co-editing mode.
Click the 'Strict mode' button to switch to the Strict co-editing mode to edit the file without other users interference and send your changes only after you save them. You can switch between the co-editing modes using the editor Advanced settings.", "PE.Controllers.Main.titleLicenseExp": "License expired", + "PE.Controllers.Main.titleServerVersion": "Editor updated", "PE.Controllers.Main.txtArt": "Your text here", "PE.Controllers.Main.txtBasicShapes": "Basic Shapes", "PE.Controllers.Main.txtButtons": "Buttons", @@ -245,10 +248,6 @@ "PE.Controllers.Main.warnLicenseExp": "Your license has expired.
Please update your license and refresh the page.", "PE.Controllers.Main.warnNoLicense": "You are using an open source version of ONLYOFFICE. The version has limitations for concurrent connections to the document server (20 connections at a time).
If you need more please consider purchasing a commercial license.", "PE.Controllers.Main.warnProcessRightsChange": "You have been denied the right to edit the file.", - "PE.Controllers.Main.titleServerVersion": "Editor updated", - "PE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", - "PE.Controllers.Main.textChangesSaved": "All changes saved", - "PE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "PE.Controllers.Statusbar.zoomText": "Zoom {0}%", "PE.Controllers.Toolbar.confirmAddFontName": "The font you are going to save is not available on the current device.
The text style will be displayed using one of the system fonts, the saved font will be used when it is available.
Do you want to continue?", "PE.Controllers.Toolbar.textAccent": "Accents", @@ -798,6 +797,7 @@ "PE.Views.FileMenuPanels.Settings.strCoAuthModeDescFast": "Other users will see your changes at once", "PE.Views.FileMenuPanels.Settings.strCoAuthModeDescStrict": "You will need to accept changes before you can see them", "PE.Views.FileMenuPanels.Settings.strFast": "Fast", + "PE.Views.FileMenuPanels.Settings.strForcesave": "Always save to server (otherwise save to server on document close)", "PE.Views.FileMenuPanels.Settings.strInputMode": "Turn on hieroglyphs", "PE.Views.FileMenuPanels.Settings.strShowChanges": "Realtime Collaboration Changes", "PE.Views.FileMenuPanels.Settings.strStrict": "Strict", @@ -811,6 +811,7 @@ "PE.Views.FileMenuPanels.Settings.textAutoRecover": "Autorecover", "PE.Views.FileMenuPanels.Settings.textAutoSave": "Autosave", "PE.Views.FileMenuPanels.Settings.textDisabled": "Disabled", + "PE.Views.FileMenuPanels.Settings.textForceSave": "Save to Server", "PE.Views.FileMenuPanels.Settings.textMinute": "Every Minute", "PE.Views.FileMenuPanels.Settings.txtAll": "View All", "PE.Views.FileMenuPanels.Settings.txtCm": "Centimeter", @@ -820,8 +821,6 @@ "PE.Views.FileMenuPanels.Settings.txtInput": "Alternate Input", "PE.Views.FileMenuPanels.Settings.txtLast": "View Last", "PE.Views.FileMenuPanels.Settings.txtPt": "Point", - "PE.Views.FileMenuPanels.Settings.textForceSave": "Save to Server", - "PE.Views.FileMenuPanels.Settings.strForcesave": "Always save to server (otherwise save to server on document close)", "PE.Views.HyperlinkSettingsDialog.cancelButtonText": "Cancel", "PE.Views.HyperlinkSettingsDialog.okButtonText": "OK", "PE.Views.HyperlinkSettingsDialog.strDisplay": "Display", diff --git a/apps/presentationeditor/main/locale/fr.json b/apps/presentationeditor/main/locale/fr.json index b9113136d..fd99c2f90 100644 --- a/apps/presentationeditor/main/locale/fr.json +++ b/apps/presentationeditor/main/locale/fr.json @@ -80,7 +80,6 @@ "Common.Views.ExternalDiagramEditor.textTitle": "Éditeur de graphique", "Common.Views.Header.openNewTabText": "Ouvrir dans un nouvel onglet", "Common.Views.Header.textBack": "Aller aux Documents", - "Common.Views.Header.txtHeaderDeveloper": "MODE DEVELOPPEUR", "Common.Views.Header.txtRename": "Renommer", "Common.Views.ImageFromUrlDialog.cancelButtonText": "Annuler", "Common.Views.ImageFromUrlDialog.okButtonText": "OK", @@ -123,7 +122,7 @@ "PE.Controllers.Main.downloadTitleText": "Téléchargement de la présentation", "PE.Controllers.Main.errorAccessDeny": "Vous tentez d'exéсuter une action pour laquelle vous ne disposez pas des droits.
Veuillez contacter l'administrateur de Document Server.", "PE.Controllers.Main.errorCoAuthoringDisconnect": "Connexion au serveur perdue. Le document ne peut être modifié en ce moment.", - "PE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion Document Serverhere", + "PE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion de Document Serverici", "PE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
Erreur de connexion à la base de données. Si l'erreur persiste veillez contactez l'assistance technique.", "PE.Controllers.Main.errorDataRange": "Plage de données incorrecte.", "PE.Controllers.Main.errorDefaultMessage": "Code d'erreur: %1", @@ -174,6 +173,7 @@ "PE.Controllers.Main.splitMaxRowsErrorText": "Le nombre de lignes doit être inférieure à %1.", "PE.Controllers.Main.textAnonymous": "Anonyme", "PE.Controllers.Main.textBuyNow": "Visiter le site web", + "PE.Controllers.Main.textChangesSaved": "Toutes les modifications sont enregistrées", "PE.Controllers.Main.textCloseTip": "Cliquez pour fermer la conseil", "PE.Controllers.Main.textContactUs": "Contacter l'équipe de ventes", "PE.Controllers.Main.textLoadingDocument": "Chargement de présentation", @@ -796,6 +796,7 @@ "PE.Views.FileMenuPanels.Settings.strCoAuthModeDescFast": "Les autres utilisateurs pourront voir immédiatement vos modifications ", "PE.Views.FileMenuPanels.Settings.strCoAuthModeDescStrict": "Avant de pouvoir afficher les modifications, vous avez besoin de les accépter ", "PE.Views.FileMenuPanels.Settings.strFast": "Rapide", + "PE.Views.FileMenuPanels.Settings.strForcesave": "Toujours enregistrer sur le serveur ( sinon enregistrer sur le serveur lors de la fermeture du document )", "PE.Views.FileMenuPanels.Settings.strInputMode": "Activer les hiéroglyphes", "PE.Views.FileMenuPanels.Settings.strShowChanges": "Changements de collaboration en temps réel", "PE.Views.FileMenuPanels.Settings.strStrict": "Strict", @@ -809,6 +810,7 @@ "PE.Views.FileMenuPanels.Settings.textAutoRecover": "Récupération automatique", "PE.Views.FileMenuPanels.Settings.textAutoSave": "Enregistrement automatique", "PE.Views.FileMenuPanels.Settings.textDisabled": "Désactivé", + "PE.Views.FileMenuPanels.Settings.textForceSave": "Enregistrer sur le serveur", "PE.Views.FileMenuPanels.Settings.textMinute": "Chaque minute", "PE.Views.FileMenuPanels.Settings.txtAll": "Tout", "PE.Views.FileMenuPanels.Settings.txtCm": "Centimètre", @@ -872,6 +874,7 @@ "PE.Views.LeftMenu.tipSlides": "Diapositives", "PE.Views.LeftMenu.tipSupport": "Commentaires & assistance", "PE.Views.LeftMenu.tipTitles": "Titres", + "PE.Views.LeftMenu.txtDeveloper": "MODE DEVELOPPEUR", "PE.Views.ParagraphSettings.strLineHeight": "Interligne", "PE.Views.ParagraphSettings.strParagraphSpacing": "Espacement de paragraphe", "PE.Views.ParagraphSettings.strSpacingAfter": "Après", diff --git a/apps/presentationeditor/main/locale/ru.json b/apps/presentationeditor/main/locale/ru.json index 32f5aeda6..ca631efa9 100644 --- a/apps/presentationeditor/main/locale/ru.json +++ b/apps/presentationeditor/main/locale/ru.json @@ -80,7 +80,6 @@ "Common.Views.ExternalDiagramEditor.textTitle": "Редактор диаграмм", "Common.Views.Header.openNewTabText": "Открыть в новой вкладке", "Common.Views.Header.textBack": "Перейти к Документам", - "Common.Views.Header.txtHeaderDeveloper": "РЕЖИМ РАЗРАБОТЧИКА", "Common.Views.Header.txtRename": "Переименовать", "Common.Views.ImageFromUrlDialog.cancelButtonText": "Отмена", "Common.Views.ImageFromUrlDialog.okButtonText": "OK", @@ -174,6 +173,7 @@ "PE.Controllers.Main.splitMaxRowsErrorText": "Число строк должно быть меньше, чем %1.", "PE.Controllers.Main.textAnonymous": "Аноним", "PE.Controllers.Main.textBuyNow": "Перейти на сайт", + "PE.Controllers.Main.textChangesSaved": "Все изменения сохранены", "PE.Controllers.Main.textCloseTip": "Щелкните, чтобы закрыть эту подсказку", "PE.Controllers.Main.textContactUs": "Связаться с отделом продаж", "PE.Controllers.Main.textLoadingDocument": "Загрузка презентации", @@ -796,6 +796,7 @@ "PE.Views.FileMenuPanels.Settings.strCoAuthModeDescFast": "Другие пользователи будут сразу же видеть ваши изменения", "PE.Views.FileMenuPanels.Settings.strCoAuthModeDescStrict": "Прежде чем вы сможете увидеть изменения, их надо будет принять", "PE.Views.FileMenuPanels.Settings.strFast": "Быстрый", + "PE.Views.FileMenuPanels.Settings.strForcesave": "Всегда сохранять на сервере (в противном случае сохранять на сервере при закрытии документа)", "PE.Views.FileMenuPanels.Settings.strInputMode": "Включить иероглифы", "PE.Views.FileMenuPanels.Settings.strShowChanges": "Отображать изменения при совместной работе", "PE.Views.FileMenuPanels.Settings.strStrict": "Строгий", @@ -809,6 +810,7 @@ "PE.Views.FileMenuPanels.Settings.textAutoRecover": "Автовосстановление", "PE.Views.FileMenuPanels.Settings.textAutoSave": "Автосохранение", "PE.Views.FileMenuPanels.Settings.textDisabled": "Отключено", + "PE.Views.FileMenuPanels.Settings.textForceSave": "Сохранить на сервере", "PE.Views.FileMenuPanels.Settings.textMinute": "Каждую минуту", "PE.Views.FileMenuPanels.Settings.txtAll": "Все", "PE.Views.FileMenuPanels.Settings.txtCm": "Сантиметр", @@ -872,6 +874,7 @@ "PE.Views.LeftMenu.tipSlides": "Слайды", "PE.Views.LeftMenu.tipSupport": "Обратная связь и поддержка", "PE.Views.LeftMenu.tipTitles": "Заголовки", + "PE.Views.LeftMenu.txtDeveloper": "РЕЖИМ РАЗРАБОТЧИКА", "PE.Views.ParagraphSettings.strLineHeight": "Междустрочный интервал", "PE.Views.ParagraphSettings.strParagraphSpacing": "Интервал между абзацами", "PE.Views.ParagraphSettings.strSpacingAfter": "После", diff --git a/apps/presentationeditor/mobile/locale/de.json b/apps/presentationeditor/mobile/locale/de.json index 0e1449396..93c1d1a18 100755 --- a/apps/presentationeditor/mobile/locale/de.json +++ b/apps/presentationeditor/mobile/locale/de.json @@ -28,9 +28,9 @@ "PE.Controllers.DocumentHolder.menuCut": "Ausschneiden", "PE.Controllers.DocumentHolder.menuDelete": "Löschen", "PE.Controllers.DocumentHolder.menuEdit": "Bearbeiten", + "PE.Controllers.DocumentHolder.menuMore": "Mehr", "PE.Controllers.DocumentHolder.menuOpenLink": "Link öffnen", "PE.Controllers.DocumentHolder.menuPaste": "Einfügen", - "PE.Controllers.DocumentHolder.menuMore": "More", "PE.Controllers.DocumentHolder.sheetCancel": "Abbrechen", "PE.Controllers.EditContainer.textChart": "Diagramm", "PE.Controllers.EditContainer.textHyperlink": "Hyperlink", @@ -403,7 +403,7 @@ "PE.Views.EditText.textSmallCaps": "Kapitälchen", "PE.Views.EditText.textStrikethrough": "Durchgestrichen", "PE.Views.EditText.textSubscript": "Tiefgestellt", - "PE.Views.Search.textSearch": "Search", + "PE.Views.Search.textSearch": "Suche", "PE.Views.Settings.mniSlideStandard": "Standard (4:3)", "PE.Views.Settings.mniSlideWide": "Breitbildschirm (16:9)", "PE.Views.Settings.textAbout": "Über", diff --git a/apps/presentationeditor/mobile/locale/fr.json b/apps/presentationeditor/mobile/locale/fr.json index a2894967e..573838219 100755 --- a/apps/presentationeditor/mobile/locale/fr.json +++ b/apps/presentationeditor/mobile/locale/fr.json @@ -28,9 +28,9 @@ "PE.Controllers.DocumentHolder.menuCut": "Couper", "PE.Controllers.DocumentHolder.menuDelete": "Supprimer", "PE.Controllers.DocumentHolder.menuEdit": "Modifier", + "PE.Controllers.DocumentHolder.menuMore": "Plus", "PE.Controllers.DocumentHolder.menuOpenLink": "Ouvrir le lien", "PE.Controllers.DocumentHolder.menuPaste": "Coller", - "PE.Controllers.DocumentHolder.menuMore": "More", "PE.Controllers.DocumentHolder.sheetCancel": "Annuler", "PE.Controllers.EditContainer.textChart": "Graphique", "PE.Controllers.EditContainer.textHyperlink": "Lien hypertexte", @@ -68,7 +68,7 @@ "PE.Controllers.Main.downloadTextText": "Téléchargement du document...", "PE.Controllers.Main.downloadTitleText": "Téléchargement du document", "PE.Controllers.Main.errorCoAuthoringDisconnect": "La connexion au serveur perdue. Désolé, vous ne pouvez plus modifier le document.", - "PE.Controllers.Main.errorConnectToServer": "Impossible d'enregistrer le document. Veuillez vérifier vos paramètres de connexion ou contactez l'administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Pour en savoir plus sur la connexion de Document Server here", + "PE.Controllers.Main.errorConnectToServer": "Impossible d'enregistrer le document. Veuillez vérifier vos paramètres de connexion ou contactez l'administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion de Document Server ici", "PE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
Erreur de connexion à la base de données.Contactez le support.", "PE.Controllers.Main.errorDataRange": "Plage de données incorrecte.", "PE.Controllers.Main.errorDefaultMessage": "Code d'erreur: %1", @@ -403,7 +403,7 @@ "PE.Views.EditText.textSmallCaps": "Petites majuscules", "PE.Views.EditText.textStrikethrough": "Barré", "PE.Views.EditText.textSubscript": "Indice", - "PE.Views.Search.textSearch": "Search", + "PE.Views.Search.textSearch": "Rechercher", "PE.Views.Settings.mniSlideStandard": "Standard (4:3)", "PE.Views.Settings.mniSlideWide": "Écran large (16:9)", "PE.Views.Settings.textAbout": "À propos du produit", diff --git a/apps/presentationeditor/mobile/locale/ru.json b/apps/presentationeditor/mobile/locale/ru.json index 2ff1b0f7f..e0a850b95 100755 --- a/apps/presentationeditor/mobile/locale/ru.json +++ b/apps/presentationeditor/mobile/locale/ru.json @@ -3,7 +3,7 @@ "Common.UI.ThemeColorPalette.textThemeColors": "Цвета темы", "Common.Utils.Metric.txtCm": "см", "Common.Utils.Metric.txtPt": "пт", - "PE.Controllers.AddContainer.textImage": "Картинка", + "PE.Controllers.AddContainer.textImage": "Изображение", "PE.Controllers.AddContainer.textLink": "Ссылка", "PE.Controllers.AddContainer.textShape": "Фигура", "PE.Controllers.AddContainer.textSlide": "Слайд", @@ -28,9 +28,9 @@ "PE.Controllers.DocumentHolder.menuCut": "Вырезать", "PE.Controllers.DocumentHolder.menuDelete": "Удалить", "PE.Controllers.DocumentHolder.menuEdit": "Редактировать", + "PE.Controllers.DocumentHolder.menuMore": "Ещё", "PE.Controllers.DocumentHolder.menuOpenLink": "Перейти по ссылке", "PE.Controllers.DocumentHolder.menuPaste": "Вставить", - "PE.Controllers.DocumentHolder.menuMore": "Еще", "PE.Controllers.DocumentHolder.sheetCancel": "Отмена", "PE.Controllers.EditContainer.textChart": "Диаграмма", "PE.Controllers.EditContainer.textHyperlink": "Гиперссылка", @@ -403,10 +403,10 @@ "PE.Views.EditText.textSmallCaps": "Малые прописные", "PE.Views.EditText.textStrikethrough": "Зачеркнутый", "PE.Views.EditText.textSubscript": "Подстрочные", - "PE.Views.Search.textSearch": "Найти", + "PE.Views.Search.textSearch": "Поиск", "PE.Views.Settings.mniSlideStandard": "Стандартный (4:3)", "PE.Views.Settings.mniSlideWide": "Широкоэкранный (16:9)", - "PE.Views.Settings.textAbout": "О продукте", + "PE.Views.Settings.textAbout": "О программе", "PE.Views.Settings.textAddress": "адрес", "PE.Views.Settings.textAuthor": "Автор", "PE.Views.Settings.textBack": "Назад", diff --git a/apps/spreadsheeteditor/main/locale/de.json b/apps/spreadsheeteditor/main/locale/de.json index 87f7e97e0..62fc071b7 100644 --- a/apps/spreadsheeteditor/main/locale/de.json +++ b/apps/spreadsheeteditor/main/locale/de.json @@ -74,7 +74,6 @@ "Common.Views.DocumentAccessDialog.textTitle": "Freigabeeinstellungen", "Common.Views.Header.openNewTabText": "In neuer Registerkarte öffnen", "Common.Views.Header.textBack": "Zu Dokumenten übergehen", - "Common.Views.Header.txtHeaderDeveloper": "ENTWICKLERMODUS", "Common.Views.Header.txtRename": "Umbenennen", "Common.Views.ImageFromUrlDialog.cancelButtonText": "Abbrechen", "Common.Views.ImageFromUrlDialog.okButtonText": "OK", @@ -176,6 +175,23 @@ "SSE.Controllers.DocumentHolder.txtMatrixAlign": "Matrixausrichtung", "SSE.Controllers.DocumentHolder.txtNoChoices": "Es gibt keine Möglichkeit zum Füllung der Zelle.
Nur die Textwerte aus der Spalte kann für den Ersatz gewählt werden. ", "SSE.Controllers.DocumentHolder.txtOverbar": "Linie über dem Text", + "SSE.Controllers.DocumentHolder.txtPaste": "Einfügen", + "SSE.Controllers.DocumentHolder.txtPasteBorders": "Formel ohne Rahmenlinien", + "SSE.Controllers.DocumentHolder.txtPasteColWidths": "Formel + Spaltenbreite", + "SSE.Controllers.DocumentHolder.txtPasteDestFormat": "Zielformatierung", + "SSE.Controllers.DocumentHolder.txtPasteFormat": "Nur Formatierung einfügen", + "SSE.Controllers.DocumentHolder.txtPasteFormulaNumFormat": "Formel + Zahlenformat", + "SSE.Controllers.DocumentHolder.txtPasteFormulas": "Nur Formel einfügen", + "SSE.Controllers.DocumentHolder.txtPasteKeepSourceFormat": "Formel + alle Formatierungen", + "SSE.Controllers.DocumentHolder.txtPasteLink": "Hyperlink einfügen", + "SSE.Controllers.DocumentHolder.txtPasteLinkPicture": "Verknüpfte Grafik", + "SSE.Controllers.DocumentHolder.txtPasteMerge": "Bedingte Formatierung beim Verbinden", + "SSE.Controllers.DocumentHolder.txtPastePicture": "Bild", + "SSE.Controllers.DocumentHolder.txtPasteSourceFormat": "Ursprüngliche Formatierung ", + "SSE.Controllers.DocumentHolder.txtPasteTranspose": "Vertauschen", + "SSE.Controllers.DocumentHolder.txtPasteValFormat": "Wert + alle Formatierung", + "SSE.Controllers.DocumentHolder.txtPasteValNumFormat": "Wert + Zahlenformat", + "SSE.Controllers.DocumentHolder.txtPasteValues": "Nur die Werte einfügen", "SSE.Controllers.DocumentHolder.txtRemFractionBar": "Bruchstrich entfernen", "SSE.Controllers.DocumentHolder.txtRemLimit": "Grenzwert entfernen\t", "SSE.Controllers.DocumentHolder.txtRemoveAccentChar": "Akzentzeichen entfernen\t", @@ -224,7 +240,7 @@ "SSE.Controllers.Main.downloadTextText": "Kalkulationstabelle wird heruntergeladen...", "SSE.Controllers.Main.downloadTitleText": "Herunterladen der Kalkulationstabelle", "SSE.Controllers.Main.errorAccessDeny": "Sie haben versucht die Änderungen im Dokument, zu dem Sie keine Berechtigungen haben, vorzunehemen.
Wenden Sie sich an Ihren Serveradministrator.", - "SSE.Controllers.Main.errorArgsRange": "Die eingegebene Formel enthält einen Fehler.
Falscher Bereich der Argumente wurde genutzt.", + "SSE.Controllers.Main.errorArgsRange": "Die eingegebene Formel enthält einen Fehler.
Es wird falschen Argumentbereich genutzt.", "SSE.Controllers.Main.errorAutoFilterChange": "Der Vorgang ist nicht zulässig, denn es wurde versucht, Zellen in der Tabelle auf Ihrem Arbeitsblatt zu verschieben.", "SSE.Controllers.Main.errorAutoFilterChangeFormatTable": "Dieser Vorgang kann für die gewählten Zellen nicht ausgeführt werden, weil Sie ein Teil der Tabelle nicht verschieben können.
Wählen Sie den anderen Datenbereich, so dass die ganze Tabelle verschoben wurde und versuchen Sie noch einmal.", "SSE.Controllers.Main.errorAutoFilterDataRange": "Der Vorgang kann für einen ausgewählten Zellbereich nicht ausgeführt werden.
Wählen Sie einen einheitlichen Datenbereich, der sich deutlich von dem bestehenden unterscheidet und versuchen Sie es erneut.", @@ -254,7 +270,7 @@ "SSE.Controllers.Main.errorMoveRange": "Es ist unmöglich einen Teil der vereinigten Zelle zu ändern", "SSE.Controllers.Main.errorOpenWarning": "Die Länge einer der Formeln in der Datei hat
die zugelassene Anzahl von Zeichen überschritten und sie wurde entfernt.", "SSE.Controllers.Main.errorOperandExpected": "Die Syntax der eingegeben Funktion ist nicht korrekt. Bitte überprüfen Sie, ob eine der Klammern - '(' oder ')' fehlt.", - "SSE.Controllers.Main.errorPasteMaxRange": "Der Bereich Kopieren und Einfügen stimmen nicht überein.
Bitte wählen Sie einen Bereich der gleichen Größe oder klicken auf die erste Zelle der Zeile, um die kopierten Zellen einzufügen", + "SSE.Controllers.Main.errorPasteMaxRange": "Zeilen Kopieren und Einfügen stimmen nicht überein.
Bitte wählen Sie einen Bereich der gleichen Größe oder klicken auf die erste Zelle der Zeile, um die kopierten Zellen einzufügen.", "SSE.Controllers.Main.errorPrintMaxPagesCount": "Leider kann man in der aktuellen Programmversion nicht mehr als 1500 Seiten gleichzeitig drucken.
Diese Einschränkung wird in den kommenden Versionen entfernt.", "SSE.Controllers.Main.errorProcessSaveResult": "Speichern ist fehlgeschlagen", "SSE.Controllers.Main.errorServerVersion": "Editor-Version wurde aktualisiert. Die Seite wird neu geladen, um die Änderungen zu übernehmen.", @@ -916,28 +932,29 @@ "SSE.Views.DigitalFilterDialog.textUse1": "Nutzen Sie das Symbol ?, um ein einziges Zeichen darzustellen", "SSE.Views.DigitalFilterDialog.textUse2": "Nutzen Sie das Symbol *, um eine Reihe von Zeichen darzustellen", "SSE.Views.DigitalFilterDialog.txtTitle": "Benutzerdefinierter Filter", + "SSE.Views.DocumentHolder.advancedImgText": "Erweiterte Einstellungen des Bildes", "SSE.Views.DocumentHolder.advancedShapeText": "Erweiterte Einstellungen der Form", "SSE.Views.DocumentHolder.bottomCellText": "Unten ausrichten", "SSE.Views.DocumentHolder.centerCellText": "Zentriert ausrichten", "SSE.Views.DocumentHolder.chartText": "Erweiterte Einstellungen des Diagramms", - "SSE.Views.DocumentHolder.deleteColumnText": "Spalte löschen", - "SSE.Views.DocumentHolder.deleteRowText": "Zeile löschen", - "SSE.Views.DocumentHolder.deleteTableText": "Tabelle löschen", + "SSE.Views.DocumentHolder.deleteColumnText": "Spalte", + "SSE.Views.DocumentHolder.deleteRowText": "Zeile", + "SSE.Views.DocumentHolder.deleteTableText": "Tabelle", "SSE.Views.DocumentHolder.direct270Text": "Um 270° drehen", "SSE.Views.DocumentHolder.direct90Text": "Um 90° drehen ", "SSE.Views.DocumentHolder.directHText": "Horizontal", "SSE.Views.DocumentHolder.directionText": "Textausrichtung\t", "SSE.Views.DocumentHolder.editChartText": "Daten ändern", "SSE.Views.DocumentHolder.editHyperlinkText": "Hyperlink bearbeiten", - "SSE.Views.DocumentHolder.insertColumnLeftText": "Spalte links einfügen", - "SSE.Views.DocumentHolder.insertColumnRightText": "Spalte rechts einfügen", - "SSE.Views.DocumentHolder.insertRowAboveText": "Zeile oberhalb einfügen", - "SSE.Views.DocumentHolder.insertRowBelowText": "Zeile unterhalb einfügen", + "SSE.Views.DocumentHolder.insertColumnLeftText": "Spalte nach links", + "SSE.Views.DocumentHolder.insertColumnRightText": "Spalte nach rechts", + "SSE.Views.DocumentHolder.insertRowAboveText": "Zeile oberhalb", + "SSE.Views.DocumentHolder.insertRowBelowText": "Zeile unterhalb", "SSE.Views.DocumentHolder.removeHyperlinkText": "Hyperlink entfernen", - "SSE.Views.DocumentHolder.selectColumnText": "Ganze Spalte auswählen", - "SSE.Views.DocumentHolder.selectDataText": "Spaltendaten auswählen", - "SSE.Views.DocumentHolder.selectRowText": "Zeile auswählen", - "SSE.Views.DocumentHolder.selectTableText": "Tabelle auswählen", + "SSE.Views.DocumentHolder.selectColumnText": "Ganze Spalte", + "SSE.Views.DocumentHolder.selectDataText": "Spaltendaten", + "SSE.Views.DocumentHolder.selectRowText": "Zeile", + "SSE.Views.DocumentHolder.selectTableText": "Tabelle", "SSE.Views.DocumentHolder.textArrangeBack": "In den Hintergrund senden", "SSE.Views.DocumentHolder.textArrangeBackward": "Eine Ebene nach hinten", "SSE.Views.DocumentHolder.textArrangeForward": "Eine Ebene nach vorne", @@ -988,6 +1005,7 @@ "SSE.Views.DocumentHolder.txtShiftRight": "Zellen nach rechts verschieben", "SSE.Views.DocumentHolder.txtShiftUp": "Zellen nach oben verschieben", "SSE.Views.DocumentHolder.txtShow": "Anzeigen", + "SSE.Views.DocumentHolder.txtShowComment": "Kommentare anzeigen", "SSE.Views.DocumentHolder.txtSort": "Sortieren", "SSE.Views.DocumentHolder.txtSortCellColor": "Ausgewählte Zellenfarbe nach oben", "SSE.Views.DocumentHolder.txtSortFontColor": "Ausgewählte Schriftfarbe nach oben", @@ -1031,6 +1049,7 @@ "SSE.Views.FileMenuPanels.MainSettingsGeneral.strCoAuthModeDescStrict": "Sie müssen die Änderungen annehmen, bevor Sie diese sehen können", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFast": "Schnell", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFontRender": "Hinting der Schriftarten", + "SSE.Views.FileMenuPanels.MainSettingsGeneral.strForcesave": "Immer auf dem Server speichern (ansonsten auf dem Server beim Schließen des Dokuments speichern)", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFuncLocale": "Formelsprache ", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFuncLocaleEx": "Beispiel: SUM; MIN; MAX; COUNT", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strLiveComment": "Live-Kommentare einschalten", @@ -1046,6 +1065,7 @@ "SSE.Views.FileMenuPanels.MainSettingsGeneral.textAutoRecover": "Autowiederherstellen", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textAutoSave": "Automatisch speichern", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textDisabled": "Deaktiviert", + "SSE.Views.FileMenuPanels.MainSettingsGeneral.textForceSave": "Auf dem Server speichern", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textMinute": "Jede Minute", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtCm": "Zentimeter", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtDe": "Deutsch", @@ -1086,9 +1106,9 @@ "SSE.Views.FormatSettingsDialog.txtScientific": "Wissenschaftlich", "SSE.Views.FormatSettingsDialog.txtText": "Text", "SSE.Views.FormatSettingsDialog.txtTime": "Zeit", - "SSE.Views.FormatSettingsDialog.txtUpto1": "Bis zu einer Ziffer", - "SSE.Views.FormatSettingsDialog.txtUpto2": "Bis zu zwei Ziffern", - "SSE.Views.FormatSettingsDialog.txtUpto3": "Dreistellig", + "SSE.Views.FormatSettingsDialog.txtUpto1": "Bis zu einer Ziffer(1/3)", + "SSE.Views.FormatSettingsDialog.txtUpto2": "Zweistellig (12/25)", + "SSE.Views.FormatSettingsDialog.txtUpto3": "Dreistellig (131/135)", "SSE.Views.FormulaDialog.cancelButtonText": "Abbrechen", "SSE.Views.FormulaDialog.okButtonText": "OK", "SSE.Views.FormulaDialog.sCategoryAll": "Alle", @@ -1173,6 +1193,7 @@ "SSE.Views.LeftMenu.tipPlugins": "Plugins", "SSE.Views.LeftMenu.tipSearch": "Suchen", "SSE.Views.LeftMenu.tipSupport": "Feedback und Support", + "SSE.Views.LeftMenu.txtDeveloper": "ENTWICKLERMODUS", "SSE.Views.MainSettingsPrint.okButtonText": "Speichern", "SSE.Views.MainSettingsPrint.strBottom": "Unten", "SSE.Views.MainSettingsPrint.strLandscape": "Querformat", diff --git a/apps/spreadsheeteditor/main/locale/en.json b/apps/spreadsheeteditor/main/locale/en.json index 3b5842fce..cea0fad83 100644 --- a/apps/spreadsheeteditor/main/locale/en.json +++ b/apps/spreadsheeteditor/main/locale/en.json @@ -74,7 +74,6 @@ "Common.Views.DocumentAccessDialog.textTitle": "Sharing Settings", "Common.Views.Header.openNewTabText": "Open in New Tab", "Common.Views.Header.textBack": "Go to Documents", - "del_Common.Views.Header.txtHeaderDeveloper": "DEVELOPER MODE", "Common.Views.Header.txtRename": "Rename", "Common.Views.ImageFromUrlDialog.cancelButtonText": "Cancel", "Common.Views.ImageFromUrlDialog.okButtonText": "OK", @@ -176,6 +175,23 @@ "SSE.Controllers.DocumentHolder.txtMatrixAlign": "Matrix alignment", "SSE.Controllers.DocumentHolder.txtNoChoices": "There are no choices for filling the cell.
Only text values from the column can be selected for replacement.", "SSE.Controllers.DocumentHolder.txtOverbar": "Bar over text", + "SSE.Controllers.DocumentHolder.txtPaste": "Paste", + "SSE.Controllers.DocumentHolder.txtPasteBorders": "Formula without borders", + "SSE.Controllers.DocumentHolder.txtPasteColWidths": "Formula + column width", + "SSE.Controllers.DocumentHolder.txtPasteDestFormat": "Destination formatting", + "SSE.Controllers.DocumentHolder.txtPasteFormat": "Paste only formatting", + "SSE.Controllers.DocumentHolder.txtPasteFormulaNumFormat": "Formula + number format", + "SSE.Controllers.DocumentHolder.txtPasteFormulas": "Paste only formula", + "SSE.Controllers.DocumentHolder.txtPasteKeepSourceFormat": "Formula + all formatting", + "SSE.Controllers.DocumentHolder.txtPasteLink": "Paste link", + "SSE.Controllers.DocumentHolder.txtPasteLinkPicture": "Linked picture", + "SSE.Controllers.DocumentHolder.txtPasteMerge": "Merge conditional formatting", + "SSE.Controllers.DocumentHolder.txtPastePicture": "Picture", + "SSE.Controllers.DocumentHolder.txtPasteSourceFormat": "Source formatting", + "SSE.Controllers.DocumentHolder.txtPasteTranspose": "Transpose", + "SSE.Controllers.DocumentHolder.txtPasteValFormat": "Value + all formatting", + "SSE.Controllers.DocumentHolder.txtPasteValNumFormat": "Value + number format", + "SSE.Controllers.DocumentHolder.txtPasteValues": "Paste only value", "SSE.Controllers.DocumentHolder.txtRemFractionBar": "Remove fraction bar", "SSE.Controllers.DocumentHolder.txtRemLimit": "Remove limit", "SSE.Controllers.DocumentHolder.txtRemoveAccentChar": "Remove accent character", @@ -198,23 +214,6 @@ "SSE.Controllers.DocumentHolder.txtTop": "Top", "SSE.Controllers.DocumentHolder.txtUnderbar": "Bar under text", "SSE.Controllers.DocumentHolder.txtWidth": "Width", - "SSE.Controllers.DocumentHolder.txtPaste": "Paste", - "SSE.Controllers.DocumentHolder.txtPasteFormulas": "Paste only formula", - "SSE.Controllers.DocumentHolder.txtPasteFormulaNumFormat": "Formula + number format", - "SSE.Controllers.DocumentHolder.txtPasteKeepSourceFormat": "Formula + all formatting", - "SSE.Controllers.DocumentHolder.txtPasteBorders": "Formula without borders", - "SSE.Controllers.DocumentHolder.txtPasteColWidths": "Formula + column width", - "SSE.Controllers.DocumentHolder.txtPasteMerge": "Merge conditional formatting", - "SSE.Controllers.DocumentHolder.txtPasteTranspose": "Transpose", - "SSE.Controllers.DocumentHolder.txtPasteValues": "Paste only value", - "SSE.Controllers.DocumentHolder.txtPasteValNumFormat": "Value + number format", - "SSE.Controllers.DocumentHolder.txtPasteValFormat": "Value + all formatting", - "SSE.Controllers.DocumentHolder.txtPasteFormat": "Paste only formatting", - "SSE.Controllers.DocumentHolder.txtPasteLink": "Paste link", - "SSE.Controllers.DocumentHolder.txtPastePicture": "Picture", - "SSE.Controllers.DocumentHolder.txtPasteLinkPicture": "Linked picture", - "SSE.Controllers.DocumentHolder.txtPasteSourceFormat": "Source formatting", - "SSE.Controllers.DocumentHolder.txtPasteDestFormat": "Destination formatting", "SSE.Controllers.LeftMenu.newDocumentTitle": "Unnamed spreadsheet", "SSE.Controllers.LeftMenu.textByColumns": "By columns", "SSE.Controllers.LeftMenu.textByRows": "By rows", @@ -241,7 +240,7 @@ "SSE.Controllers.Main.downloadTextText": "Downloading spreadsheet...", "SSE.Controllers.Main.downloadTitleText": "Downloading Spreadsheet", "SSE.Controllers.Main.errorAccessDeny": "You are trying to perform an action you do not have rights for.
Please contact your Document Server administrator.", - "SSE.Controllers.Main.errorArgsRange": "An error in the entered formula.
Incorrect arguments range is used.", + "SSE.Controllers.Main.errorArgsRange": "An error in the entered formula.
Incorrect argument range is used.", "SSE.Controllers.Main.errorAutoFilterChange": "The operation is not allowed, as it is attempting to shift cells in a table on your worksheet.", "SSE.Controllers.Main.errorAutoFilterChangeFormatTable": "The operation could not be done for the selected cells as you cannot move a part of the table.
Select another data range so that the whole table was shifted and try again.", "SSE.Controllers.Main.errorAutoFilterDataRange": "The operation could not be done for the selected range of cells.
Select a uniform data range different from the existing one and try again.", @@ -271,9 +270,10 @@ "SSE.Controllers.Main.errorMoveRange": "Cannot change part of a merged cell", "SSE.Controllers.Main.errorOpenWarning": "The length of one of the formulas in the file exceeded
the allowed number of characters and it was removed.", "SSE.Controllers.Main.errorOperandExpected": "The entered function syntax is not correct. Please check if you are missing one of the parentheses - '(' or ')'.", - "SSE.Controllers.Main.errorPasteMaxRange": "The copy and paste area does not match.
Please select an area with the same size or click the first cell in a row to paste the copied cells.", + "SSE.Controllers.Main.errorPasteMaxRange": "The copy and paste area do not match.
Please select an area with the same size or click the first cell in a row to paste the copied cells.", "SSE.Controllers.Main.errorPrintMaxPagesCount": "Unfortunately, it is not possible to print more than 1500 pages at once in the current program version.
This restriction will be removed in the upcoming releases.", "SSE.Controllers.Main.errorProcessSaveResult": "Saving failed", + "SSE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", "SSE.Controllers.Main.errorSessionAbsolute": "The document editing session has expired. Please reload the page.", "SSE.Controllers.Main.errorSessionIdle": "The document has not been edited for quite a long time. Please reload the page.", "SSE.Controllers.Main.errorSessionToken": "The connection to the server has been interrupted. Please reload the page.", @@ -328,6 +328,7 @@ "SSE.Controllers.Main.textYes": "Yes", "SSE.Controllers.Main.titleLicenseExp": "License expired", "SSE.Controllers.Main.titleRecalcFormulas": "Calculating...", + "SSE.Controllers.Main.titleServerVersion": "Editor updated", "SSE.Controllers.Main.txtArt": "Your text here", "SSE.Controllers.Main.txtBasicShapes": "Basic Shapes", "SSE.Controllers.Main.txtButtons": "Buttons", @@ -355,8 +356,6 @@ "SSE.Controllers.Main.warnLicenseExp": "Your license has expired.
Please update your license and refresh the page.", "SSE.Controllers.Main.warnNoLicense": "You are using an open source version of ONLYOFFICE. The version has limitations for concurrent connections to the document server (20 connections at a time).
If you need more please consider purchasing a commercial license.", "SSE.Controllers.Main.warnProcessRightsChange": "You have been denied the right to edit the file.", - "SSE.Controllers.Main.titleServerVersion": "Editor updated", - "SSE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", "SSE.Controllers.Print.strAllSheets": "All Sheets", "SSE.Controllers.Print.textWarning": "Warning", "SSE.Controllers.Print.warnCheckMargings": "Margins are incorrect", @@ -933,8 +932,8 @@ "SSE.Views.DigitalFilterDialog.textUse1": "Use ? to present any single character", "SSE.Views.DigitalFilterDialog.textUse2": "Use * to present any series of character", "SSE.Views.DigitalFilterDialog.txtTitle": "Custom Filter", - "SSE.Views.DocumentHolder.advancedShapeText": "Shape Advanced Settings", "SSE.Views.DocumentHolder.advancedImgText": "Image Advanced Settings", + "SSE.Views.DocumentHolder.advancedShapeText": "Shape Advanced Settings", "SSE.Views.DocumentHolder.bottomCellText": "Align Bottom", "SSE.Views.DocumentHolder.centerCellText": "Align Center", "SSE.Views.DocumentHolder.chartText": "Chart Advanced Settings", @@ -987,7 +986,6 @@ "SSE.Views.DocumentHolder.txtDelete": "Delete", "SSE.Views.DocumentHolder.txtDescending": "Descending", "SSE.Views.DocumentHolder.txtEditComment": "Edit Comment", - "SSE.Views.DocumentHolder.txtShowComment": "Show Comment", "SSE.Views.DocumentHolder.txtFilter": "Filter", "SSE.Views.DocumentHolder.txtFilterCellColor": "Filter by cell's color", "SSE.Views.DocumentHolder.txtFilterFontColor": "Filter by font color", @@ -1007,6 +1005,7 @@ "SSE.Views.DocumentHolder.txtShiftRight": "Shift cells right", "SSE.Views.DocumentHolder.txtShiftUp": "Shift cells up", "SSE.Views.DocumentHolder.txtShow": "Show", + "SSE.Views.DocumentHolder.txtShowComment": "Show Comment", "SSE.Views.DocumentHolder.txtSort": "Sort", "SSE.Views.DocumentHolder.txtSortCellColor": "Selected Cell Color on top", "SSE.Views.DocumentHolder.txtSortFontColor": "Selected Font Color on top", @@ -1050,6 +1049,7 @@ "SSE.Views.FileMenuPanels.MainSettingsGeneral.strCoAuthModeDescStrict": "You will need to accept changes before you can see them", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFast": "Fast", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFontRender": "Font Hinting", + "SSE.Views.FileMenuPanels.MainSettingsGeneral.strForcesave": "Always save to server (otherwise save to server on document close)", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFuncLocale": "Formula Language", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFuncLocaleEx": "Example: SUM; MIN; MAX; COUNT", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strLiveComment": "Turn on display of the comments", @@ -1065,6 +1065,7 @@ "SSE.Views.FileMenuPanels.MainSettingsGeneral.textAutoRecover": "Autorecover", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textAutoSave": "Autosave", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textDisabled": "Disabled", + "SSE.Views.FileMenuPanels.MainSettingsGeneral.textForceSave": "Save to Server", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textMinute": "Every Minute", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtCm": "Centimeter", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtDe": "Deutsch", @@ -1073,12 +1074,10 @@ "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtLiveComment": "Commenting Display", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtMac": "as OS X", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtNative": "Native", - "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtPt": "Point", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtPl": "Polish", + "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtPt": "Point", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtRu": "Russian", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtWin": "as Windows", - "SSE.Views.FileMenuPanels.MainSettingsGeneral.textForceSave": "Save to Server", - "SSE.Views.FileMenuPanels.MainSettingsGeneral.strForcesave": "Always save to server (otherwise save to server on document close)", "SSE.Views.FileMenuPanels.Settings.txtGeneral": "General", "SSE.Views.FileMenuPanels.Settings.txtPageSettings": "Page Settings", "SSE.Views.FormatSettingsDialog.textCancel": "Cancel", @@ -1465,6 +1464,7 @@ "SSE.Views.TableSettings.textBanded": "Banded", "SSE.Views.TableSettings.textCancel": "Cancel", "SSE.Views.TableSettings.textColumns": "Columns", + "SSE.Views.TableSettings.textConvertRange": "Convert to range", "SSE.Views.TableSettings.textEdit": "Rows & Columns", "SSE.Views.TableSettings.textEmptyTemplate": "No templates", "SSE.Views.TableSettings.textExistName": "ERROR! A range with such a name already exists", @@ -1482,7 +1482,6 @@ "SSE.Views.TableSettings.textTableName": "Table Name", "SSE.Views.TableSettings.textTemplate": "Select From Template", "SSE.Views.TableSettings.textTotal": "Total", - "SSE.Views.TableSettings.textConvertRange": "Convert to range", "SSE.Views.TableSettingsAdvanced.cancelButtonText": "Cancel", "SSE.Views.TableSettingsAdvanced.okButtonText": "Ok", "SSE.Views.TableSettingsAdvanced.textAlt": "Alternative Text", diff --git a/apps/spreadsheeteditor/main/locale/fr.json b/apps/spreadsheeteditor/main/locale/fr.json index 0f912132e..9e675a6b8 100644 --- a/apps/spreadsheeteditor/main/locale/fr.json +++ b/apps/spreadsheeteditor/main/locale/fr.json @@ -74,7 +74,6 @@ "Common.Views.DocumentAccessDialog.textTitle": "Paramètres de partage", "Common.Views.Header.openNewTabText": "Ouvrir dans un nouvel onglet", "Common.Views.Header.textBack": "Aller aux Documents", - "Common.Views.Header.txtHeaderDeveloper": "MODE DEVELOPPEUR", "Common.Views.Header.txtRename": "Renommer", "Common.Views.ImageFromUrlDialog.cancelButtonText": "Annuler", "Common.Views.ImageFromUrlDialog.okButtonText": "OK", @@ -176,6 +175,23 @@ "SSE.Controllers.DocumentHolder.txtMatrixAlign": "Alignement de la matrice", "SSE.Controllers.DocumentHolder.txtNoChoices": "Il n’y a pas de choix pour le remplissage de la cellule.
Uniquement les valeurs de texte de la colonne peuvent être sélectionnées pour le remplacement.", "SSE.Controllers.DocumentHolder.txtOverbar": "Barre au-dessus d'un texte", + "SSE.Controllers.DocumentHolder.txtPaste": "Coller", + "SSE.Controllers.DocumentHolder.txtPasteBorders": "Formule sans bordures", + "SSE.Controllers.DocumentHolder.txtPasteColWidths": "Formule + largeur de colonne", + "SSE.Controllers.DocumentHolder.txtPasteDestFormat": "Mise en forme de destination", + "SSE.Controllers.DocumentHolder.txtPasteFormat": "Coller uniquement la mise en forme", + "SSE.Controllers.DocumentHolder.txtPasteFormulaNumFormat": "Formule + format de nombre", + "SSE.Controllers.DocumentHolder.txtPasteFormulas": "Coller uniquement la formule", + "SSE.Controllers.DocumentHolder.txtPasteKeepSourceFormat": "Formule + mise en forme", + "SSE.Controllers.DocumentHolder.txtPasteLink": "Coller avec liaison", + "SSE.Controllers.DocumentHolder.txtPasteLinkPicture": "Image liée", + "SSE.Controllers.DocumentHolder.txtPasteMerge": "Fusionner la mise en forme conditionnelle", + "SSE.Controllers.DocumentHolder.txtPastePicture": "Image", + "SSE.Controllers.DocumentHolder.txtPasteSourceFormat": "Mise en forme source", + "SSE.Controllers.DocumentHolder.txtPasteTranspose": "Transposer", + "SSE.Controllers.DocumentHolder.txtPasteValFormat": "Valeur + toute la mise en forme", + "SSE.Controllers.DocumentHolder.txtPasteValNumFormat": "Valeur + format de nombre", + "SSE.Controllers.DocumentHolder.txtPasteValues": "Coller uniquement la valeur", "SSE.Controllers.DocumentHolder.txtRemFractionBar": "Supprimer la barre de fraction", "SSE.Controllers.DocumentHolder.txtRemLimit": "Supprimer la limite", "SSE.Controllers.DocumentHolder.txtRemoveAccentChar": "Supprimer le caractère d'accent", @@ -224,18 +240,18 @@ "SSE.Controllers.Main.downloadTextText": "Téléchargement de la feuille de calcul en cours...", "SSE.Controllers.Main.downloadTitleText": "Téléchargement de la feuille de calcul", "SSE.Controllers.Main.errorAccessDeny": "Vous tentez d'exéсuter une action pour laquelle vous ne disposez pas des droits.
Veuillez contacter l'administrateur de Document Server.", - "SSE.Controllers.Main.errorArgsRange": "Une erreur dans la formule entrée.
Argument de plage utilisé est incorrect.", + "SSE.Controllers.Main.errorArgsRange": "Une erreur dans la formule entrée.
La plage des arguments utilisée est incorrecte.", "SSE.Controllers.Main.errorAutoFilterChange": "L'opération n'est pas autorisée, car elle tente de déplacer les cellules d'un tableau de votre feuille de calcul.", - "SSE.Controllers.Main.errorAutoFilterChangeFormatTable": "Opération impossible sur les cellules sélectionnées car vous ne pouvez pas déplacer une partie du tableau.
Sélectionnez une autre plage de données afin que tout le tableau soit déplacé et essayez à nouveau.", - "SSE.Controllers.Main.errorAutoFilterDataRange": "L'opération n'a pu être effectuée pour la plage de cellules spécifiée.
Sélectionnez la plage de données différentes à partir de celui existant et essayez à nouveau.\n", + "SSE.Controllers.Main.errorAutoFilterChangeFormatTable": "Impossible de réaliser l'opération sur les cellules sélectionnées car vous ne pouvez pas déplacer une partie du tableau.
Sélectionnez une autre plage de données afin que tout le tableau soit déplacé et essayez à nouveau.", + "SSE.Controllers.Main.errorAutoFilterDataRange": "Impossible de réaliser l'opération sur la plage de cellules spécifiée.
Sélectionnez la plage de données différente de la plage existante et essayez à nouveau.\n", "SSE.Controllers.Main.errorAutoFilterHiddenRange": "L'opération ne peut pas être effectuée car la zone contient des cellules filtrées.
Veuillez afficher des éléments filtrés et réessayez.", "SSE.Controllers.Main.errorBadImageUrl": "L'URL d'image est incorrecte", "SSE.Controllers.Main.errorCoAuthoringDisconnect": "Connexion au serveur perdue. Le document ne peut être modifié en ce moment.", - "SSE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion Document Serverhere", + "SSE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion de Document Serverici", "SSE.Controllers.Main.errorCopyMultiselectArea": "Impossible d'exécuter cette commande sur des sélections multiples.
Sélectionnez une seule plage et essayez à nouveau.", "SSE.Controllers.Main.errorCountArg": "Une erreur dans la formule entrée.
Nombre d'arguments utilisé est incorrect.", "SSE.Controllers.Main.errorCountArgExceed": "Une erreur dans la formule entrée.
Nombre d'arguments est dépassé.", - "SSE.Controllers.Main.errorCreateDefName": "Actuellement des plages nommées existantes ne peuvent pas être modifiées et les nouvelles ne peuvent pas être
créées car certaines d'entre eux sont en cours de modification.", + "SSE.Controllers.Main.errorCreateDefName": "Actuellement, des plages nommées existantes ne peuvent pas être modifiées et les nouvelles ne peuvent pas être créées,
car certaines d'entre eux sont en cours de modification.", "SSE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
Erreur de connexion à la base de données. Si l'erreur persiste veillez contactez l'assistance technique.", "SSE.Controllers.Main.errorDataRange": "Plage de données incorrecte.", "SSE.Controllers.Main.errorDefaultMessage": "Code d'erreur: %1", @@ -252,9 +268,9 @@ "SSE.Controllers.Main.errorLockedAll": "L'opération ne peut pas être faite car la feuille a été verrouillée par un autre utilisateur.", "SSE.Controllers.Main.errorLockedWorksheetRename": "La feuille ne peut pas être renommée pour l'instant puisque elle est renommée par un autre utilisateur", "SSE.Controllers.Main.errorMoveRange": "Impossible de modifier une partie d'une cellule fusionnée", - "SSE.Controllers.Main.errorOpenWarning": "La longueur de l'une des formules dans le fichier dépassé le nombre autorisé de caractères et il a été retiré.", + "SSE.Controllers.Main.errorOpenWarning": "La longueur de l'une des formules dans le fichier a dépassé
le nombre de caractères autorisé, et la formule a été supprimée.", "SSE.Controllers.Main.errorOperandExpected": "La syntaxe de la saisie est incorrecte. Veuillez vérifier si l'une des parenthèses - '(' ou ')' est manquante.", - "SSE.Controllers.Main.errorPasteMaxRange": "La zone de copie ne correspond pas à la zone de collage.
Pour coller les cellules copiées, veuillez sélectionner une zone avec la même taille ou cliquer sur la première cellule d'une ligne.", + "SSE.Controllers.Main.errorPasteMaxRange": "La zone de copie ne correspond pas à la zone de collage.
Sélectionnez une zone avec la même taille ou cliquez sur la première cellule d'une ligne pour coller les cellules sélectionnées.", "SSE.Controllers.Main.errorPrintMaxPagesCount": "Malheureusement, il n’est pas possible d’imprimer plus de 1500 pages à la fois en utilisant la version actuelle du programme.
Cette restriction sera supprimée dans la version future.", "SSE.Controllers.Main.errorProcessSaveResult": "Échec de l'enregistrement", "SSE.Controllers.Main.errorServerVersion": "La version de l'éditeur a été mise à jour. La page sera rechargée pour appliquer les modifications.", @@ -916,28 +932,29 @@ "SSE.Views.DigitalFilterDialog.textUse1": "Utilisez ? pour présenter un caractère unique", "SSE.Views.DigitalFilterDialog.textUse2": "Utilisez * pour présenter une série de caractères", "SSE.Views.DigitalFilterDialog.txtTitle": "Filtre personnalisé", + "SSE.Views.DocumentHolder.advancedImgText": "Paramètres avancés de l'image", "SSE.Views.DocumentHolder.advancedShapeText": "Paramètres avancés de la forme", "SSE.Views.DocumentHolder.bottomCellText": "Aligner en bas", "SSE.Views.DocumentHolder.centerCellText": "Aligner au centre", "SSE.Views.DocumentHolder.chartText": "Paramètres du graphique avancés", - "SSE.Views.DocumentHolder.deleteColumnText": "Supprimer la colonne", - "SSE.Views.DocumentHolder.deleteRowText": "Supprimer la ligne", - "SSE.Views.DocumentHolder.deleteTableText": "Supprimer le tableau", + "SSE.Views.DocumentHolder.deleteColumnText": "Colonne", + "SSE.Views.DocumentHolder.deleteRowText": "Ligne", + "SSE.Views.DocumentHolder.deleteTableText": "Tableau", "SSE.Views.DocumentHolder.direct270Text": "Rotation à 270 °", "SSE.Views.DocumentHolder.direct90Text": "Rotation à 90 °", "SSE.Views.DocumentHolder.directHText": "Horizontal", "SSE.Views.DocumentHolder.directionText": "Orientation du texte", "SSE.Views.DocumentHolder.editChartText": "Modifier les données", "SSE.Views.DocumentHolder.editHyperlinkText": "Modifier le lien hypertexte", - "SSE.Views.DocumentHolder.insertColumnLeftText": "Insérer une colonne à gauche", - "SSE.Views.DocumentHolder.insertColumnRightText": "Insérer une colonne à droite", - "SSE.Views.DocumentHolder.insertRowAboveText": "Insérer une ligne au-dessus", - "SSE.Views.DocumentHolder.insertRowBelowText": "Insérer une ligne en dessous", + "SSE.Views.DocumentHolder.insertColumnLeftText": "Colonne à gauche", + "SSE.Views.DocumentHolder.insertColumnRightText": "Colonne à droite", + "SSE.Views.DocumentHolder.insertRowAboveText": "Ligne au-dessus", + "SSE.Views.DocumentHolder.insertRowBelowText": "Ligne en dessous", "SSE.Views.DocumentHolder.removeHyperlinkText": "Supprimer le lien hypertexte", - "SSE.Views.DocumentHolder.selectColumnText": "Sélectionner la colonne entière", - "SSE.Views.DocumentHolder.selectDataText": "Sélectionner les données de la colonne", - "SSE.Views.DocumentHolder.selectRowText": "Sélectionner la ligne", - "SSE.Views.DocumentHolder.selectTableText": "Sélectionner le tableau", + "SSE.Views.DocumentHolder.selectColumnText": "Colonne entière", + "SSE.Views.DocumentHolder.selectDataText": "Données de la colonne", + "SSE.Views.DocumentHolder.selectRowText": "Ligne", + "SSE.Views.DocumentHolder.selectTableText": "Tableau", "SSE.Views.DocumentHolder.textArrangeBack": "Mettre en arrière-plan", "SSE.Views.DocumentHolder.textArrangeBackward": "Déplacer vers l'arrière", "SSE.Views.DocumentHolder.textArrangeForward": "Avancer", @@ -988,6 +1005,7 @@ "SSE.Views.DocumentHolder.txtShiftRight": "Décaler les cellules vers la droite", "SSE.Views.DocumentHolder.txtShiftUp": "Décaler les cellules vers le haut", "SSE.Views.DocumentHolder.txtShow": "Afficher", + "SSE.Views.DocumentHolder.txtShowComment": "Afficher le commentaire", "SSE.Views.DocumentHolder.txtSort": "Trier", "SSE.Views.DocumentHolder.txtSortCellColor": "Couleur sélectionnée de cellules sur le dessus", "SSE.Views.DocumentHolder.txtSortFontColor": "Couleur sélectionné de la police sur le dessus", @@ -1031,6 +1049,7 @@ "SSE.Views.FileMenuPanels.MainSettingsGeneral.strCoAuthModeDescStrict": "Avant de pouvoir afficher les modifications, vous avez besoin de les accépter ", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFast": "Rapide", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFontRender": "Hinting de la police", + "SSE.Views.FileMenuPanels.MainSettingsGeneral.strForcesave": "Toujours enregistrer sur le serveur ( sinon enregistrer sur le serveur lors de la fermeture du document )", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFuncLocale": "La formule de langue", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFuncLocaleEx": "Example: SUM; MIN; MAX; COUNT", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strLiveComment": "Activer l'affichage des commentaires", @@ -1046,6 +1065,7 @@ "SSE.Views.FileMenuPanels.MainSettingsGeneral.textAutoRecover": "Récupération automatique", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textAutoSave": "Enregistrement automatique", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textDisabled": "Désactivé", + "SSE.Views.FileMenuPanels.MainSettingsGeneral.textForceSave": "Enregistrer sur le serveur", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textMinute": "Chaque minute", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtCm": "Centimètre", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtDe": "Deutsch", @@ -1173,6 +1193,7 @@ "SSE.Views.LeftMenu.tipPlugins": "Plug-ins", "SSE.Views.LeftMenu.tipSearch": "Rechercher", "SSE.Views.LeftMenu.tipSupport": "Commentaires & assistance", + "SSE.Views.LeftMenu.txtDeveloper": "MODE DEVELOPPEUR", "SSE.Views.MainSettingsPrint.okButtonText": "Enregistrer", "SSE.Views.MainSettingsPrint.strBottom": "En bas", "SSE.Views.MainSettingsPrint.strLandscape": "Paysage", @@ -1193,7 +1214,7 @@ "SSE.Views.MainSettingsPrint.textPrintHeadings": "Imprimer les titres de lignes et de colonnes ", "SSE.Views.MainSettingsPrint.textSettings": "Paramètres pour", "SSE.Views.NamedRangeEditDlg.cancelButtonText": "Annuler", - "SSE.Views.NamedRangeEditDlg.errorCreateDefName": "Actuellement des plages nommées existantes ne peuvent pas être modifiées et les nouvelles ne peuvent pas être
créées car certaines d'entre eux sont en cours de modification.", + "SSE.Views.NamedRangeEditDlg.errorCreateDefName": "Actuellement, des plages nommées existantes ne peuvent pas être modifiées et les nouvelles ne peuvent pas être créées,
car certaines d'entre eux sont en cours de modification.", "SSE.Views.NamedRangeEditDlg.namePlaceholder": "Nom défini", "SSE.Views.NamedRangeEditDlg.notcriticalErrorTitle": "Avertissement", "SSE.Views.NamedRangeEditDlg.okButtonText": "Ok", @@ -1419,7 +1440,7 @@ "SSE.Views.Statusbar.tipZoomOut": "Zoom arrière", "SSE.Views.Statusbar.txAccessRights": "Changer les droits d'accès", "SSE.Views.Statusbar.zoomText": "Zoom {0}%", - "SSE.Views.TableOptionsDialog.errorAutoFilterDataRange": "L'opération n'a pu être effectuée pour la plage de cellules spécifiée.
Sélectionnez la plage de données différentes à partir de celui existant et essayez à nouveau.\n", + "SSE.Views.TableOptionsDialog.errorAutoFilterDataRange": "Impossible de réaliser l'opération sur la plage de cellules spécifiée.
Sélectionnez la plage de données différente de la plage existante et essayez à nouveau.\n", "SSE.Views.TableOptionsDialog.errorFTChangeTableRangeError": "L'opération n'a pas pu être achevée pour la plage de cellules sélectionnée.
Sélectionnez une plage de telle sorte que la première ligne de la table était sur la même ligne
et la table résultant chevauché l'actuel.", "SSE.Views.TableOptionsDialog.errorFTRangeIncludedOtherTables": "L'opération n'a pas pu être achevée pour la plage de cellules sélectionnée.
Sélectionnez une plage qui ne comprend pas d'autres tables.", "SSE.Views.TableOptionsDialog.textCancel": "Annuler", @@ -1588,7 +1609,7 @@ "SSE.Views.Toolbar.tipCopy": "Copier", "SSE.Views.Toolbar.tipCopyStyle": "Copier le style", "SSE.Views.Toolbar.tipDecDecimal": "Réduire les décimales", - "SSE.Views.Toolbar.tipDecFont": "Réduire taille de la police", + "SSE.Views.Toolbar.tipDecFont": "Réduire la taille de la police", "SSE.Views.Toolbar.tipDeleteOpt": "Supprimer les cellules", "SSE.Views.Toolbar.tipDigStyleAccounting": "Style comptable", "SSE.Views.Toolbar.tipDigStyleCurrency": "Style monétaire", @@ -1599,7 +1620,7 @@ "SSE.Views.Toolbar.tipFontSize": "Taille de la police", "SSE.Views.Toolbar.tipHAligh": "Alignement horizontal", "SSE.Views.Toolbar.tipIncDecimal": "Ajouter une décimale", - "SSE.Views.Toolbar.tipIncFont": "Augmenter taille de la police", + "SSE.Views.Toolbar.tipIncFont": "Augmenter la taille de la police", "SSE.Views.Toolbar.tipInsertChart": "Insérer un graphique", "SSE.Views.Toolbar.tipInsertChartSpark": "Insérer une graphique ou sparkline", "SSE.Views.Toolbar.tipInsertEquation": "Insérer une équation", diff --git a/apps/spreadsheeteditor/main/locale/ru.json b/apps/spreadsheeteditor/main/locale/ru.json index 48c625a8c..70fb959a7 100644 --- a/apps/spreadsheeteditor/main/locale/ru.json +++ b/apps/spreadsheeteditor/main/locale/ru.json @@ -74,7 +74,6 @@ "Common.Views.DocumentAccessDialog.textTitle": "Настройки совместного доступа", "Common.Views.Header.openNewTabText": "Открыть в новой вкладке", "Common.Views.Header.textBack": "Перейти к Документам", - "Common.Views.Header.txtHeaderDeveloper": "РЕЖИМ РАЗРАБОТЧИКА", "Common.Views.Header.txtRename": "Переименовать", "Common.Views.ImageFromUrlDialog.cancelButtonText": "Отмена", "Common.Views.ImageFromUrlDialog.okButtonText": "OK", @@ -176,6 +175,23 @@ "SSE.Controllers.DocumentHolder.txtMatrixAlign": "Выравнивание матрицы", "SSE.Controllers.DocumentHolder.txtNoChoices": "Нет вариантов для заполнения ячейки.
Для замены можно выбрать только текстовые значения из столбца.", "SSE.Controllers.DocumentHolder.txtOverbar": "Черта над текстом", + "SSE.Controllers.DocumentHolder.txtPaste": "Вставить", + "SSE.Controllers.DocumentHolder.txtPasteBorders": "Формула без границ", + "SSE.Controllers.DocumentHolder.txtPasteColWidths": "Формула + ширина столбца", + "SSE.Controllers.DocumentHolder.txtPasteDestFormat": "Форматы конечных ячеек", + "SSE.Controllers.DocumentHolder.txtPasteFormat": "Вставить только форматирование", + "SSE.Controllers.DocumentHolder.txtPasteFormulaNumFormat": "Формула + формат чисел", + "SSE.Controllers.DocumentHolder.txtPasteFormulas": "Вставить только формулу", + "SSE.Controllers.DocumentHolder.txtPasteKeepSourceFormat": "Формула + все форматирование", + "SSE.Controllers.DocumentHolder.txtPasteLink": "Вставить связь", + "SSE.Controllers.DocumentHolder.txtPasteLinkPicture": "Связанный рисунок", + "SSE.Controllers.DocumentHolder.txtPasteMerge": "Объединить условное форматирование", + "SSE.Controllers.DocumentHolder.txtPastePicture": "Рисунок", + "SSE.Controllers.DocumentHolder.txtPasteSourceFormat": "Форматы исходных ячеек", + "SSE.Controllers.DocumentHolder.txtPasteTranspose": "Транспонировать", + "SSE.Controllers.DocumentHolder.txtPasteValFormat": "Значение + все форматирование", + "SSE.Controllers.DocumentHolder.txtPasteValNumFormat": "Значение + формат чисел", + "SSE.Controllers.DocumentHolder.txtPasteValues": "Вставить только значение", "SSE.Controllers.DocumentHolder.txtRemFractionBar": "Удалить дробную черту", "SSE.Controllers.DocumentHolder.txtRemLimit": "Удалить предел", "SSE.Controllers.DocumentHolder.txtRemoveAccentChar": "Удалить диакритический знак", @@ -916,28 +932,29 @@ "SSE.Views.DigitalFilterDialog.textUse1": "Используйте знак ? вместо любого отдельного символа", "SSE.Views.DigitalFilterDialog.textUse2": "Используйте знак * вместо любой последовательности символов", "SSE.Views.DigitalFilterDialog.txtTitle": "Пользовательский фильтр", + "SSE.Views.DocumentHolder.advancedImgText": "Дополнительные параметры изображения", "SSE.Views.DocumentHolder.advancedShapeText": "Дополнительные параметры фигуры", "SSE.Views.DocumentHolder.bottomCellText": "По нижнему краю", "SSE.Views.DocumentHolder.centerCellText": "По центру", "SSE.Views.DocumentHolder.chartText": "Дополнительные параметры диаграммы", - "SSE.Views.DocumentHolder.deleteColumnText": "Удалить столбец", - "SSE.Views.DocumentHolder.deleteRowText": "Удалить строку", - "SSE.Views.DocumentHolder.deleteTableText": "Удалить таблицу", + "SSE.Views.DocumentHolder.deleteColumnText": "Столбец", + "SSE.Views.DocumentHolder.deleteRowText": "Строку", + "SSE.Views.DocumentHolder.deleteTableText": "Таблицу", "SSE.Views.DocumentHolder.direct270Text": "Поворот на 270°", "SSE.Views.DocumentHolder.direct90Text": "Поворот на 90°", "SSE.Views.DocumentHolder.directHText": "Горизонтальное", "SSE.Views.DocumentHolder.directionText": "Направление текста", "SSE.Views.DocumentHolder.editChartText": "Изменить данные", "SSE.Views.DocumentHolder.editHyperlinkText": "Изменить гиперссылку", - "SSE.Views.DocumentHolder.insertColumnLeftText": "Вставить столбец слева", - "SSE.Views.DocumentHolder.insertColumnRightText": "Вставить столбец справа", - "SSE.Views.DocumentHolder.insertRowAboveText": "Вставить строку выше", - "SSE.Views.DocumentHolder.insertRowBelowText": "Вставить строку ниже", + "SSE.Views.DocumentHolder.insertColumnLeftText": "Столбец слева", + "SSE.Views.DocumentHolder.insertColumnRightText": "Столбец справа", + "SSE.Views.DocumentHolder.insertRowAboveText": "Строку выше", + "SSE.Views.DocumentHolder.insertRowBelowText": "Строку ниже", "SSE.Views.DocumentHolder.removeHyperlinkText": "Удалить гиперссылку", - "SSE.Views.DocumentHolder.selectColumnText": "Выделить весь столбец", - "SSE.Views.DocumentHolder.selectDataText": "Выделить данные столбцов", - "SSE.Views.DocumentHolder.selectRowText": "Выделить строку", - "SSE.Views.DocumentHolder.selectTableText": "Выделить таблицу", + "SSE.Views.DocumentHolder.selectColumnText": "Весь столбец", + "SSE.Views.DocumentHolder.selectDataText": "Данные столбцов", + "SSE.Views.DocumentHolder.selectRowText": "Строку", + "SSE.Views.DocumentHolder.selectTableText": "Таблицу", "SSE.Views.DocumentHolder.textArrangeBack": "Переместить на задний план", "SSE.Views.DocumentHolder.textArrangeBackward": "Перенести назад", "SSE.Views.DocumentHolder.textArrangeForward": "Перенести вперед", @@ -988,6 +1005,7 @@ "SSE.Views.DocumentHolder.txtShiftRight": "Ячейки со сдвигом вправо", "SSE.Views.DocumentHolder.txtShiftUp": "Ячейки со сдвигом вверх", "SSE.Views.DocumentHolder.txtShow": "Показать", + "SSE.Views.DocumentHolder.txtShowComment": "Показать комментарий", "SSE.Views.DocumentHolder.txtSort": "Сортировка", "SSE.Views.DocumentHolder.txtSortCellColor": "Сначала ячейки с выделенным цветом", "SSE.Views.DocumentHolder.txtSortFontColor": "Сначала ячейки с выделенным шрифтом", @@ -1031,6 +1049,7 @@ "SSE.Views.FileMenuPanels.MainSettingsGeneral.strCoAuthModeDescStrict": "Прежде чем вы сможете увидеть изменения, их надо будет принять", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFast": "Быстрый", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFontRender": "Хинтинг шрифтов", + "SSE.Views.FileMenuPanels.MainSettingsGeneral.strForcesave": "Всегда сохранять на сервере (в противном случае сохранять на сервере при закрытии документа)", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFuncLocale": "Язык формул", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strFuncLocaleEx": "Пример: СУММ; МИН; МАКС; СЧЁТ", "SSE.Views.FileMenuPanels.MainSettingsGeneral.strLiveComment": "Включить отображение комментариев в тексте", @@ -1046,6 +1065,7 @@ "SSE.Views.FileMenuPanels.MainSettingsGeneral.textAutoRecover": "Автовосстановление", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textAutoSave": "Автосохранение", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textDisabled": "Отключено", + "SSE.Views.FileMenuPanels.MainSettingsGeneral.textForceSave": "Сохранить на сервере", "SSE.Views.FileMenuPanels.MainSettingsGeneral.textMinute": "Каждую минуту", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtCm": "Сантиметр", "SSE.Views.FileMenuPanels.MainSettingsGeneral.txtDe": "Немецкий", @@ -1173,6 +1193,7 @@ "SSE.Views.LeftMenu.tipPlugins": "Дополнения", "SSE.Views.LeftMenu.tipSearch": "Поиск", "SSE.Views.LeftMenu.tipSupport": "Обратная связь и поддержка", + "SSE.Views.LeftMenu.txtDeveloper": "РЕЖИМ РАЗРАБОТЧИКА", "SSE.Views.MainSettingsPrint.okButtonText": "Сохранить", "SSE.Views.MainSettingsPrint.strBottom": "Снизу", "SSE.Views.MainSettingsPrint.strLandscape": "Альбомная", diff --git a/apps/spreadsheeteditor/mobile/locale/de.json b/apps/spreadsheeteditor/mobile/locale/de.json index 8c6679d7f..2879916e9 100644 --- a/apps/spreadsheeteditor/mobile/locale/de.json +++ b/apps/spreadsheeteditor/mobile/locale/de.json @@ -9,9 +9,9 @@ "SSE.Controllers.AddChart.txtYAxis": "y-Achse", "SSE.Controllers.AddContainer.textChart": "Diagramm", "SSE.Controllers.AddContainer.textFormula": "Funktion", + "SSE.Controllers.AddContainer.textImage": "Bild", "SSE.Controllers.AddContainer.textOther": "Sonstiges", "SSE.Controllers.AddContainer.textShape": "Form", - "SSE.Controllers.AddContainer.textImage": "Image", "SSE.Controllers.AddLink.textInvalidRange": "FEHLER! Ungültiger Zellenbereich", "SSE.Controllers.AddLink.txtNotUrl": "Dieser Bereich soll ein URL in der Format \"http://www.example.com\" sein.", "SSE.Controllers.AddOther.textEmptyImgUrl": "Sie müssen eine Bild-URL angeben.", @@ -24,23 +24,15 @@ "SSE.Controllers.DocumentHolder.menuEdit": "Bearbeiten", "SSE.Controllers.DocumentHolder.menuHide": "Vergeben", "SSE.Controllers.DocumentHolder.menuMerge": "Verbinden", + "SSE.Controllers.DocumentHolder.menuMore": "Mehr", "SSE.Controllers.DocumentHolder.menuOpenLink": "Link öffnen", "SSE.Controllers.DocumentHolder.menuPaste": "Einfügen", "SSE.Controllers.DocumentHolder.menuShow": "Anzeigen", "SSE.Controllers.DocumentHolder.menuUnmerge": "Verbund aufheben", "SSE.Controllers.DocumentHolder.menuUnwrap": "Umbruch aufheben", "SSE.Controllers.DocumentHolder.menuWrap": "Umbrechen", - "SSE.Controllers.DocumentHolder.warnMergeLostData": "Vorgang kann die Daten in den markierten Zellen zerstören.
Fortsenzen? ", - "SSE.Controllers.DocumentHolder.menuMore": "More", "SSE.Controllers.DocumentHolder.sheetCancel": "Abbrechen", - "SSE.Controllers.EditContainer.textSettings": "Settings", - "SSE.Controllers.EditContainer.textCell": "Cell", - "SSE.Controllers.EditContainer.textTable": "Table", - "SSE.Controllers.EditContainer.textShape": "Shape", - "SSE.Controllers.EditContainer.textImage": "Image", - "SSE.Controllers.EditContainer.textChart": "Chart", - "SSE.Controllers.EditContainer.textText": "Text", - "SSE.Controllers.EditContainer.textHyperlink": "Hyperlink", + "SSE.Controllers.DocumentHolder.warnMergeLostData": "Vorgang kann die Daten in den markierten Zellen zerstören.
Fortsenzen? ", "SSE.Controllers.EditCell.textAuto": "Automatisch", "SSE.Controllers.EditCell.textFonts": "Schriftarten", "SSE.Controllers.EditCell.textPt": "pt", @@ -86,6 +78,14 @@ "SSE.Controllers.EditChart.textTop": "Oben", "SSE.Controllers.EditChart.textTrillions": "Billionen", "SSE.Controllers.EditChart.textValue": "Wert", + "SSE.Controllers.EditContainer.textCell": "Zelle", + "SSE.Controllers.EditContainer.textChart": "Diagramm", + "SSE.Controllers.EditContainer.textHyperlink": "Hyperlink", + "SSE.Controllers.EditContainer.textImage": "Bild", + "SSE.Controllers.EditContainer.textSettings": "Einstellungen", + "SSE.Controllers.EditContainer.textShape": "Form", + "SSE.Controllers.EditContainer.textTable": "Tabelle", + "SSE.Controllers.EditContainer.textText": "Text", "SSE.Controllers.EditHyperlink.textDefault": "Gewählter Bereich", "SSE.Controllers.EditHyperlink.textEmptyImgUrl": "Sie müssen eine Bild-URL angeben.", "SSE.Controllers.EditHyperlink.textExternalLink": "Externer Link", @@ -108,39 +108,42 @@ "SSE.Controllers.Main.downloadTextText": "Dokument wird heruntergeladen...", "SSE.Controllers.Main.downloadTitleText": "Herunterladen des Dokuments", "SSE.Controllers.Main.errorAccessDeny": "Sie haben versucht die Änderungen im Dokument, zu dem Sie keine Berechtigungen haben, vorzunehemen.
Wenden Sie sich an Ihren Serveradministrator.", - "SSE.Controllers.Main.errorArgsRange": "Die eingegebene Formel enthält einen Fehler.
Falscher Bereich der Argumente wurde genutzt.", + "SSE.Controllers.Main.errorArgsRange": "Die eingegebene Formel enthält einen Fehler.
Falscher Argumentbereich wurde genutzt.", "SSE.Controllers.Main.errorAutoFilterChange": "Der Vorgang ist nicht zulässig, denn es wurde versucht, Zellen in der Tabelle auf Ihrem Arbeitsblatt zu verschieben.", - "SSE.Controllers.Main.errorAutoFilterChangeFormatTable": "Dieser Vorgang kann für die gewählten Zellen nicht ausgeführt werden, weil Sie ein Teil der Tabelle nicht verschieben können.
Wählen Sie den anderen Datenbereich, so dass die ganze Tabelle verschoben wurde und versuchen Sie noch einmal.", + "SSE.Controllers.Main.errorAutoFilterChangeFormatTable": "Dieser Vorgang kann für die gewählten Zellen nicht ausgeführt werden, weil Sie einen Teil der Tabelle nicht verschieben können.
Wählen Sie den anderen Datenbereich, so dass die ganze Tabelle verschoben wurde und versuchen Sie noch einmal.", "SSE.Controllers.Main.errorAutoFilterDataRange": "Der Vorgang kann für einen ausgewählten Zellbereich nicht ausgeführt werden.
Wählen Sie einen einheitlichen Datenbereich, der sich deutlich von dem bestehenden unterscheidet und versuchen Sie es erneut.", "SSE.Controllers.Main.errorAutoFilterHiddenRange": "Die Operation kann nicht ausgeführt werden, weil der Bereich gefilterte Zellen enthält.
Bitte machen Sie die gefilterten Elemente sichtbar und versuchen Sie es erneut.", "SSE.Controllers.Main.errorBadImageUrl": "URL des Bildes ist falsch", - "SSE.Controllers.Main.errorCoAuthoringDisconnect": "Verbindung zum Server ist verloren gegangen. Das Dokument kann momentan nicht bearbeitet werden.", + "SSE.Controllers.Main.errorCoAuthoringDisconnect": "Verbindung zum Server ist verloren gegangen. Sie können nicht mehr editieren.", "SSE.Controllers.Main.errorConnectToServer": "Das Dokument konnte nicht gespeichert werden. Bitte überprüfen Sie die Verbindungseinstellungen, oder richten Sie an Ihren Administrator.
Wann Sie auf die Taste \"OK\" klicken, werden Sie aufgefordert, das Dokument herunterzuladen.

Finden Sie mehr Information über Verbindung des Dokument Servers hier", - "SSE.Controllers.Main.errorCopyMultiselectArea": "Bei einer Markierung von nicht angrenzenden Zellen ist die Ausführung dieses Befehls nicht möglich.
Wählen Sie nur einen einzelnen Bereich aus, und versuchen Sie es noch mal.", - "SSE.Controllers.Main.errorCountArg": "Die eingegebene Formel enthält einen Fehler.
Falsche Anzahl an Argumenten wurde genutzt.", + "SSE.Controllers.Main.errorCopyMultiselectArea": "Dieser Befehl kann nicht bei Mehrfachauswahl verwendet werden
Wählen Sie nur einen einzelnen Bereich aus, und versuchen Sie es nochmal.", + "SSE.Controllers.Main.errorCountArg": "Die eingegebene Formel enthält einen Fehler.
Es wurde falsche Anzahl an Argumenten benutzt.", "SSE.Controllers.Main.errorCountArgExceed": "Die eingegebene Formel enthält einen Fehler.
Anzahl der Argumente wurde überschritten.", - "SSE.Controllers.Main.errorCreateDefName": "Die bestehende benannte Bereiche können nicht bearbeitet werden und die neuen Bereiche können
im Moment nicht erstellt werden, weil einige von ihnen sind in Bearbeitung.", - "SSE.Controllers.Main.errorDatabaseConnection": "Externer Fehler.
Fehler beim Verbinden zur Datenbank. Bitte wenden Sie sich an den Kundendienst, falls der Fehler bestehen bleibt.", + "SSE.Controllers.Main.errorCreateDefName": "Die bestehende benannte Bereiche können nicht bearbeitet werden und neue Bereiche können
im Moment nicht erstellt werden, weil einige von ihnen sind in Bearbeitung.", + "SSE.Controllers.Main.errorDatabaseConnection": "Externer Fehler.
Datenbank-Verbindungsfehler. Wenden Sie sich an den Support.", "SSE.Controllers.Main.errorDataRange": "Falscher Datenbereich.", "SSE.Controllers.Main.errorDefaultMessage": "Fehlercode: %1", "SSE.Controllers.Main.errorFilePassProtect": "Das Dokument ist kennwortgeschützt und kann nicht geöffnet werden.", "SSE.Controllers.Main.errorFileRequest": "Externer Fehler.
Fehler bei der Dateianfrage. Bitte wenden Sie sich an den Kundendienst, falls der Fehler bestehen bleibt.", "SSE.Controllers.Main.errorFileVKey": "Externer Fehler.
Ungültiger Sicherheitsschlüssel. Bitte wenden Sie sich an den Kundendienst, falls der Fehler bestehen bleibt.", "SSE.Controllers.Main.errorFillRange": "Der gewählte Zellbereich kann nicht ausgefüllt werden.
Alle verbundenen Zellen müssen die gleiche Größe haben.", - "SSE.Controllers.Main.errorFormulaName": "Die eingegebene Formel enthält einen Fehler.
Falscher Name der Formel wurde genutzt.", + "SSE.Controllers.Main.errorFormulaName": "Die eingegebene Formel enthält einen Fehler.
Es wurde falschen Formelnamen benutzt.", "SSE.Controllers.Main.errorFormulaParsing": "Interner Fehler bei der Syntaxanalyse der Formel.", "SSE.Controllers.Main.errorFrmlWrongReferences": "Die Funktion bezieht sich auf ein Blatt, das nicht existiert.
Bitte überprüfen Sie die Daten und versuchen Sie es erneut.", - "SSE.Controllers.Main.errorInvalidRef": "Enter a correct name for the selection or a valid reference to go to.", + "SSE.Controllers.Main.errorInvalidRef": "Geben Sie einen korrekten Namen oder einen gültigen Webverweis ein.", "SSE.Controllers.Main.errorKeyEncrypt": "Unbekannter Schlüsseldeskriptor", "SSE.Controllers.Main.errorKeyExpire": "Der Schlüsseldeskriptor ist abgelaufen", "SSE.Controllers.Main.errorLockedAll": "Die Operation kann nicht durchgeführt werden, weil das Blatt von einem anderen Benutzer gesperrt ist.", - "SSE.Controllers.Main.errorLockedWorksheetRename": "Umbenennen des Blattes ist derzeit nicht möglich, denn es wird gleichzeitig von einem anderen Benutzer umbenannt.", - "SSE.Controllers.Main.errorMoveRange": "Es ist unmöglich einen Teil der vereinigten Zelle zu ändern", + "SSE.Controllers.Main.errorLockedWorksheetRename": "Umbenennen des Sheets ist derzeit nicht möglich, denn es wird gleichzeitig von einem anderen Benutzer umbenannt.", + "SSE.Controllers.Main.errorMailMergeLoadFile": "Fehler beim Laden\t", + "SSE.Controllers.Main.errorMailMergeSaveFile": "Verbinden ist fehlgeschlagen.", + "SSE.Controllers.Main.errorMoveRange": "Es ist unmöglich, den Teil einer verbundenen Zelle zu ändern", "SSE.Controllers.Main.errorOpenWarning": "Die Länge einer der Formeln in der Datei hat
die zugelassene Anzahl von Zeichen überschritten und sie wurde entfernt.", "SSE.Controllers.Main.errorOperandExpected": "Die Syntax der eingegeben Funktion ist nicht korrekt. Bitte überprüfen Sie, ob eine der Klammern - '(' oder ')' fehlt.", - "SSE.Controllers.Main.errorPasteMaxRange": "Der Bereich Kopieren und Einfügen stimmen nicht überein.
Bitte wählen Sie einen Bereich der gleichen Größe oder klicken auf die erste Zelle der Zeile, um die kopierten Zellen einzufügen", + "SSE.Controllers.Main.errorPasteMaxRange": "Zeilen Kopieren und Einfügen stimmen nicht überein.
Bitte wählen Sie einen Bereich der gleichen Größe oder klicken auf die erste Zelle der Zeile, um die kopierten Zellen einzufügen.", "SSE.Controllers.Main.errorPrintMaxPagesCount": "Leider kann man in der aktuellen Programmversion nicht mehr als 1500 Seiten gleichzeitig drucken.
Diese Einschränkung wird in den kommenden Versionen entfernt.", "SSE.Controllers.Main.errorProcessSaveResult": "Speichern ist fehlgeschlagen", + "SSE.Controllers.Main.errorServerVersion": "Editor-Version wurde aktualisiert. Die Seite wird neu geladen, um die Änderungen zu übernehmen.", "SSE.Controllers.Main.errorSessionAbsolute": "Die Bearbeitungssitzung des Dokumentes ist abgelaufen. Laden Sie die Seite neu.", "SSE.Controllers.Main.errorSessionIdle": "Das Dokument wurde lange nicht bearbeitet. Laden Sie die Seite neu.", "SSE.Controllers.Main.errorSessionToken": "Die Verbindung zum Server wurde unterbrochen. Laden Sie die Seite neu.", @@ -151,11 +154,9 @@ "SSE.Controllers.Main.errorUpdateVersion": "Die Dateiversion wurde geändert. Die Seite wird neu geladen.", "SSE.Controllers.Main.errorUserDrop": "Kein Zugriff auf diese Datei ist möglich.", "SSE.Controllers.Main.errorUsersExceed": "Die nach dem Zahlungsplan erlaubte Benutzeranzahl ist überschritten", - "SSE.Controllers.Main.errorViewerDisconnect": "Die Verbindung ist verloren. Man kann das Dokument anschauen.
Es ist aber momentan nicht möglich, ihn herunterzuladen oder auszudrücken bis die Verbindung wiederhergestellt wird.", - "SSE.Controllers.Main.errorWrongBracketsCount": "Die eingegebene Formel enthält einen Fehler.
Falsche Anzahl an Klammern wurde genutzt.", + "SSE.Controllers.Main.errorViewerDisconnect": "Die Verbindung ist verloren. Man kann das Dokument anschauen,
aber nicht herunterladen bis die Verbindung wiederhergestellt wird.", + "SSE.Controllers.Main.errorWrongBracketsCount": "Die eingegebene Formel enthält einen Fehler.
Es wurde falsche Anzahl an Klammern wurde benutzt.", "SSE.Controllers.Main.errorWrongOperator": "Die eingegebene Formel enthält einen Fehler. Falscher Operator wurde genutzt.
Bitte korrigieren Sie den Fehler.", - "SSE.Controllers.Main.errorMailMergeLoadFile": "Fehler beim Laden\t", - "SSE.Controllers.Main.errorMailMergeSaveFile": "Verbinden ist fehlgeschlagen.", "SSE.Controllers.Main.leavePageText": "Dieses Dokument enthält ungespeicherte Änderungen. Klicken Sie \"Auf dieser Seite bleiben\", um auf automatisches Speichern des Dokumentes zu warten. Klicken Sie \"Diese Seite verlassen\", um alle nicht gespeicherten Änderungen zu verwerfen.", "SSE.Controllers.Main.loadFontsTextText": "Daten werden geladen...", "SSE.Controllers.Main.loadFontsTitleText": "Daten werden geladen", @@ -202,6 +203,7 @@ "SSE.Controllers.Main.textTryUndoRedo": "Undo/Redo Optionen sind für den halbformalen Zusammenbearbeitungsmodus deaktiviert.
Klicken Sie auf den Button \"Formaler Modus\", um den formalen Zusammenbearbeitungsmodus zu aktivieren, um die Datei, ohne Störungen anderer Benutzer zu bearbeiten und die Änderungen erst nachdem Sie sie gespeichert haben, zu senden. Sie können zwischen den Zusammenbearbeitungsmodi mit der Hilfe der erweiterten Einstellungen von Editor umschalten.", "SSE.Controllers.Main.textUsername": "Benutzername", "SSE.Controllers.Main.titleLicenseExp": "Lizenz ist abgelaufen", + "SSE.Controllers.Main.titleServerVersion": "Editor wurde aktualisiert", "SSE.Controllers.Main.titleUpdateVersion": "Version wurde geändert", "SSE.Controllers.Main.txtArt": "Hier den Text eingeben", "SSE.Controllers.Main.txtBasicShapes": "Standardformen", @@ -259,8 +261,8 @@ "SSE.Views.AddFunction.sCatMathematic": "Mathematik und Trigonometrie", "SSE.Views.AddFunction.sCatStatistical": "Statistisch", "SSE.Views.AddFunction.sCatTextAndData": "Text und Daten", - "SSE.Views.AddFunction.textGroups": "Kategorien", "SSE.Views.AddFunction.textBack": "Zurück", + "SSE.Views.AddFunction.textGroups": "Kategorien", "SSE.Views.AddLink.textAddLink": "Link hinzufügen", "SSE.Views.AddLink.textAddress": "Adresse", "SSE.Views.AddLink.textDisplay": "Anzeigen", @@ -283,8 +285,7 @@ "SSE.Views.AddOther.textInsert": "Einfügen", "SSE.Views.AddOther.textInsertImage": "Bild einfügen", "SSE.Views.AddOther.textLink": "Link", - "SSE.Views.AddOther.textSortOrig": "Sortieren und Filtern", - "SSE.Views.AddOther.textSort": "Sortieren", + "SSE.Views.AddOther.textSort": "Sortieren und Filtern", "SSE.Views.EditCell.textAccounting": "Rechnungswesen", "SSE.Views.EditCell.textAlignBottom": "Unten ausrichten", "SSE.Views.EditCell.textAlignCenter": "Zentriert ausrichten", @@ -363,6 +364,7 @@ "SSE.Views.EditChart.textMajorType": "Primärer Typ", "SSE.Views.EditChart.textMaxValue": "Maximalwert", "SSE.Views.EditChart.textMinor": "Unerheblich", + "SSE.Views.EditChart.textMinorType": "Sekundärer Typ", "SSE.Views.EditChart.textMinValue": "Minimalwert", "SSE.Views.EditChart.textNone": "Kein", "SSE.Views.EditChart.textNoOverlay": "Ohne Überlagerung", @@ -443,8 +445,8 @@ "SSE.Views.Settings.textAuthor": "Autor", "SSE.Views.Settings.textBack": "Zurück", "SSE.Views.Settings.textCreateDate": "Erstellungsdatum", - "SSE.Views.Settings.textDocInfo": "Dokumentinfo", - "SSE.Views.Settings.textDocTitle": "Titel des Dokuments", + "SSE.Views.Settings.textDocInfo": "Tabelle Information", + "SSE.Views.Settings.textDocTitle": "Titel der Tabelle", "SSE.Views.Settings.textDone": "Fertig", "SSE.Views.Settings.textDownload": "Herunterladen", "SSE.Views.Settings.textDownloadAs": "Herunterladen als...", diff --git a/apps/spreadsheeteditor/mobile/locale/en.json b/apps/spreadsheeteditor/mobile/locale/en.json index d4b8535f9..2b4ae6721 100644 --- a/apps/spreadsheeteditor/mobile/locale/en.json +++ b/apps/spreadsheeteditor/mobile/locale/en.json @@ -9,9 +9,9 @@ "SSE.Controllers.AddChart.txtYAxis": "Y Axis", "SSE.Controllers.AddContainer.textChart": "Chart", "SSE.Controllers.AddContainer.textFormula": "Function", + "SSE.Controllers.AddContainer.textImage": "Image", "SSE.Controllers.AddContainer.textOther": "Other", "SSE.Controllers.AddContainer.textShape": "Shape", - "SSE.Controllers.AddContainer.textImage": "Image", "SSE.Controllers.AddLink.textInvalidRange": "ERROR! Invalid cells range", "SSE.Controllers.AddLink.txtNotUrl": "This field should be a URL in the format 'http://www.example.com'", "SSE.Controllers.AddOther.textEmptyImgUrl": "You need to specify image URL.", @@ -24,23 +24,15 @@ "SSE.Controllers.DocumentHolder.menuEdit": "Edit", "SSE.Controllers.DocumentHolder.menuHide": "Hide", "SSE.Controllers.DocumentHolder.menuMerge": "Merge", + "SSE.Controllers.DocumentHolder.menuMore": "More", "SSE.Controllers.DocumentHolder.menuOpenLink": "Open Link", "SSE.Controllers.DocumentHolder.menuPaste": "Paste", "SSE.Controllers.DocumentHolder.menuShow": "Show", "SSE.Controllers.DocumentHolder.menuUnmerge": "Unmerge", "SSE.Controllers.DocumentHolder.menuUnwrap": "Unwrap", "SSE.Controllers.DocumentHolder.menuWrap": "Wrap", - "SSE.Controllers.DocumentHolder.warnMergeLostData": "Operation can destroy data in the selected cells.
Continue?", - "SSE.Controllers.DocumentHolder.menuMore": "More", "SSE.Controllers.DocumentHolder.sheetCancel": "Cancel", - "SSE.Controllers.EditContainer.textSettings": "Settings", - "SSE.Controllers.EditContainer.textCell": "Cell", - "SSE.Controllers.EditContainer.textTable": "Table", - "SSE.Controllers.EditContainer.textShape": "Shape", - "SSE.Controllers.EditContainer.textImage": "Image", - "SSE.Controllers.EditContainer.textChart": "Chart", - "SSE.Controllers.EditContainer.textText": "Text", - "SSE.Controllers.EditContainer.textHyperlink": "Hyperlink", + "SSE.Controllers.DocumentHolder.warnMergeLostData": "Operation can destroy data in the selected cells.
Continue?", "SSE.Controllers.EditCell.textAuto": "Auto", "SSE.Controllers.EditCell.textFonts": "Fonts", "SSE.Controllers.EditCell.textPt": "pt", @@ -86,6 +78,14 @@ "SSE.Controllers.EditChart.textTop": "Top", "SSE.Controllers.EditChart.textTrillions": "Trillions", "SSE.Controllers.EditChart.textValue": "Value", + "SSE.Controllers.EditContainer.textCell": "Cell", + "SSE.Controllers.EditContainer.textChart": "Chart", + "SSE.Controllers.EditContainer.textHyperlink": "Hyperlink", + "SSE.Controllers.EditContainer.textImage": "Image", + "SSE.Controllers.EditContainer.textSettings": "Settings", + "SSE.Controllers.EditContainer.textShape": "Shape", + "SSE.Controllers.EditContainer.textTable": "Table", + "SSE.Controllers.EditContainer.textText": "Text", "SSE.Controllers.EditHyperlink.textDefault": "Selected range", "SSE.Controllers.EditHyperlink.textEmptyImgUrl": "You need to specify image URL.", "SSE.Controllers.EditHyperlink.textExternalLink": "External Link", @@ -108,7 +108,7 @@ "SSE.Controllers.Main.downloadTextText": "Downloading document...", "SSE.Controllers.Main.downloadTitleText": "Downloading Document", "SSE.Controllers.Main.errorAccessDeny": "You are trying to perform an action you do not have rights for.
Please contact your Document Server administrator.", - "SSE.Controllers.Main.errorArgsRange": "An error in the entered formula.
Incorrect arguments range is used.", + "SSE.Controllers.Main.errorArgsRange": "An error in the entered formula.
Incorrect argument range is used.", "SSE.Controllers.Main.errorAutoFilterChange": "The operation is not allowed, as it is attempting to shift cells in a table on your worksheet.", "SSE.Controllers.Main.errorAutoFilterChangeFormatTable": "The operation could not be done for the selected cells as you cannot move a part of the table.
Select another data range so that the whole table was shifted and try again.", "SSE.Controllers.Main.errorAutoFilterDataRange": "The operation could not be done for the selected range of cells.
Select a uniform data range different from the existing one and try again.", @@ -135,12 +135,15 @@ "SSE.Controllers.Main.errorKeyExpire": "Key descriptor expired", "SSE.Controllers.Main.errorLockedAll": "The operation could not be done as the sheet has been locked by another user.", "SSE.Controllers.Main.errorLockedWorksheetRename": "The sheet cannot be renamed at the moment as it is being renamed by another user", + "SSE.Controllers.Main.errorMailMergeLoadFile": "Loading failed", + "SSE.Controllers.Main.errorMailMergeSaveFile": "Merge failed.", "SSE.Controllers.Main.errorMoveRange": "Cannot change part of a merged cell", "SSE.Controllers.Main.errorOpenWarning": "The length of one of the formulas in the file exceeded
the allowed number of characters and it was removed.", "SSE.Controllers.Main.errorOperandExpected": "The entered function syntax is not correct. Please check if you are missing one of the parentheses - '(' or ')'.", - "SSE.Controllers.Main.errorPasteMaxRange": "The copy and paste area does not match.
Please select an area with the same size or click the first cell in a row to paste the copied cells.", + "SSE.Controllers.Main.errorPasteMaxRange": "The copy and paste area do not match.
Please select an area with the same size or click the first cell in a row to paste the copied cells.", "SSE.Controllers.Main.errorPrintMaxPagesCount": "Unfortunately, it is not possible to print more than 1500 pages at once in the current program version.
This restriction will be removed in the upcoming releases.", "SSE.Controllers.Main.errorProcessSaveResult": "Saving failed", + "SSE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", "SSE.Controllers.Main.errorSessionAbsolute": "The document editing session has expired. Please reload the page.", "SSE.Controllers.Main.errorSessionIdle": "The document has not been edited for quite a long time. Please reload the page.", "SSE.Controllers.Main.errorSessionToken": "The connection to the server has been interrupted. Please reload the page.", @@ -154,8 +157,6 @@ "SSE.Controllers.Main.errorViewerDisconnect": "Connection is lost. You can still view the document,
but will not be able to download or print until the connection is restored.", "SSE.Controllers.Main.errorWrongBracketsCount": "An error in the entered formula.
Wrong number of brackets is used.", "SSE.Controllers.Main.errorWrongOperator": "An error in the entered formula. Wrong operator is used.
Please correct the error.", - "SSE.Controllers.Main.errorMailMergeLoadFile": "Loading failed", - "SSE.Controllers.Main.errorMailMergeSaveFile": "Merge failed.", "SSE.Controllers.Main.leavePageText": "You have unsaved changes in this document. Click 'Stay on this Page' to await the autosave of the document. Click 'Leave this Page' to discard all the unsaved changes.", "SSE.Controllers.Main.loadFontsTextText": "Loading data...", "SSE.Controllers.Main.loadFontsTitleText": "Loading Data", @@ -202,6 +203,7 @@ "SSE.Controllers.Main.textTryUndoRedo": "The Undo/Redo functions are disabled for the Fast co-editing mode.
Click the 'Strict mode' button to switch to the Strict co-editing mode to edit the file without other users interference and send your changes only after you save them. You can switch between the co-editing modes using the editor Advanced settings.", "SSE.Controllers.Main.textUsername": "Username", "SSE.Controllers.Main.titleLicenseExp": "License expired", + "SSE.Controllers.Main.titleServerVersion": "Editor updated", "SSE.Controllers.Main.titleUpdateVersion": "Version changed", "SSE.Controllers.Main.txtArt": "Your text here", "SSE.Controllers.Main.txtBasicShapes": "Basic Shapes", @@ -233,8 +235,6 @@ "SSE.Controllers.Main.warnLicenseExp": "Your license has expired.
Please update your license and refresh the page.", "SSE.Controllers.Main.warnNoLicense": "You are using an open source version of ONLYOFFICE. The version has limitations for concurrent connections to the document server (20 connections at a time).
If you need more please consider purchasing a commercial license.", "SSE.Controllers.Main.warnProcessRightsChange": "You have been denied the right to edit the file.", - "SSE.Controllers.Main.titleServerVersion": "Editor updated", - "SSE.Controllers.Main.errorServerVersion": "The editor version has been updated. The page will be reloaded to apply the changes.", "SSE.Controllers.Search.textNoTextFound": "Text not found", "SSE.Controllers.Search.textReplaceAll": "Replace All", "SSE.Controllers.Settings.notcriticalErrorTitle": "Warning", @@ -261,8 +261,8 @@ "SSE.Views.AddFunction.sCatMathematic": "Math and trigonometry", "SSE.Views.AddFunction.sCatStatistical": "Statistical", "SSE.Views.AddFunction.sCatTextAndData": "Text and data", - "SSE.Views.AddFunction.textGroups": "Categories", "SSE.Views.AddFunction.textBack": "Back", + "SSE.Views.AddFunction.textGroups": "Categories", "SSE.Views.AddLink.textAddLink": "Add Link", "SSE.Views.AddLink.textAddress": "Address", "SSE.Views.AddLink.textDisplay": "Display", @@ -285,8 +285,7 @@ "SSE.Views.AddOther.textInsert": "Insert", "SSE.Views.AddOther.textInsertImage": "Insert Image", "SSE.Views.AddOther.textLink": "Link", - "SSE.Views.AddOther.textSortOrig": "Sort and Filter", - "SSE.Views.AddOther.textSort": "Sort", + "SSE.Views.AddOther.textSort": "Sort and Filter", "SSE.Views.EditCell.textAccounting": "Accounting", "SSE.Views.EditCell.textAlignBottom": "Align Bottom", "SSE.Views.EditCell.textAlignCenter": "Align Center", @@ -446,8 +445,8 @@ "SSE.Views.Settings.textAuthor": "Author", "SSE.Views.Settings.textBack": "Back", "SSE.Views.Settings.textCreateDate": "Creation date", - "SSE.Views.Settings.textDocInfo": "Document Info", - "SSE.Views.Settings.textDocTitle": "Document title", + "SSE.Views.Settings.textDocInfo": "Spreadsheet Info", + "SSE.Views.Settings.textDocTitle": "Spreadsheet title", "SSE.Views.Settings.textDone": "Done", "SSE.Views.Settings.textDownload": "Download", "SSE.Views.Settings.textDownloadAs": "Download As...", diff --git a/apps/spreadsheeteditor/mobile/locale/fr.json b/apps/spreadsheeteditor/mobile/locale/fr.json index a6670ed90..2525a8cd7 100644 --- a/apps/spreadsheeteditor/mobile/locale/fr.json +++ b/apps/spreadsheeteditor/mobile/locale/fr.json @@ -9,9 +9,9 @@ "SSE.Controllers.AddChart.txtYAxis": "Axe Y", "SSE.Controllers.AddContainer.textChart": "Graphique", "SSE.Controllers.AddContainer.textFormula": "Fonction", + "SSE.Controllers.AddContainer.textImage": "Image", "SSE.Controllers.AddContainer.textOther": "Autre", "SSE.Controllers.AddContainer.textShape": "Forme", - "SSE.Controllers.AddContainer.textImage": "Image", "SSE.Controllers.AddLink.textInvalidRange": "ERREUR! La plage de cellules n'est pas valide", "SSE.Controllers.AddLink.txtNotUrl": "Ce champ doit être une URL au format 'http://www.example.com'", "SSE.Controllers.AddOther.textEmptyImgUrl": "Spécifiez l'URL de l'image", @@ -24,23 +24,15 @@ "SSE.Controllers.DocumentHolder.menuEdit": "Modifier", "SSE.Controllers.DocumentHolder.menuHide": "Masquer", "SSE.Controllers.DocumentHolder.menuMerge": "Fusionner", + "SSE.Controllers.DocumentHolder.menuMore": "Plus", "SSE.Controllers.DocumentHolder.menuOpenLink": "Ouvrir le lien", "SSE.Controllers.DocumentHolder.menuPaste": "Coller", "SSE.Controllers.DocumentHolder.menuShow": "Afficher", "SSE.Controllers.DocumentHolder.menuUnmerge": "Annuler la fusion", "SSE.Controllers.DocumentHolder.menuUnwrap": "Annuler le renvoi", "SSE.Controllers.DocumentHolder.menuWrap": "Renvoi à la ligne", - "SSE.Controllers.DocumentHolder.warnMergeLostData": "Cette opération détruira les données des cellules sélectionnées.
Сontinuer ?", - "SSE.Controllers.DocumentHolder.menuMore": "More", "SSE.Controllers.DocumentHolder.sheetCancel": "Annuler", - "SSE.Controllers.EditContainer.textSettings": "Settings", - "SSE.Controllers.EditContainer.textCell": "Cell", - "SSE.Controllers.EditContainer.textTable": "Table", - "SSE.Controllers.EditContainer.textShape": "Shape", - "SSE.Controllers.EditContainer.textImage": "Image", - "SSE.Controllers.EditContainer.textChart": "Chart", - "SSE.Controllers.EditContainer.textText": "Text", - "SSE.Controllers.EditContainer.textHyperlink": "Hyperlink", + "SSE.Controllers.DocumentHolder.warnMergeLostData": "Cette opération détruira les données des cellules sélectionnées.
Сontinuer ?", "SSE.Controllers.EditCell.textAuto": "Auto", "SSE.Controllers.EditCell.textFonts": "Polices", "SSE.Controllers.EditCell.textPt": "pt", @@ -86,6 +78,14 @@ "SSE.Controllers.EditChart.textTop": "En haut", "SSE.Controllers.EditChart.textTrillions": "Trillions", "SSE.Controllers.EditChart.textValue": "Valeur", + "SSE.Controllers.EditContainer.textCell": "Cellule", + "SSE.Controllers.EditContainer.textChart": "Graphique", + "SSE.Controllers.EditContainer.textHyperlink": "Lien hypertexte", + "SSE.Controllers.EditContainer.textImage": "Image", + "SSE.Controllers.EditContainer.textSettings": "Paramètres", + "SSE.Controllers.EditContainer.textShape": "Forme", + "SSE.Controllers.EditContainer.textTable": "Tableau", + "SSE.Controllers.EditContainer.textText": "Texte", "SSE.Controllers.EditHyperlink.textDefault": "Plage sélectionnée", "SSE.Controllers.EditHyperlink.textEmptyImgUrl": "Spécifiez l'URL de l'image", "SSE.Controllers.EditHyperlink.textExternalLink": "Lien externe", @@ -108,54 +108,55 @@ "SSE.Controllers.Main.downloadTextText": "Téléchargement du document...", "SSE.Controllers.Main.downloadTitleText": "Téléchargement du document", "SSE.Controllers.Main.errorAccessDeny": "Vous tentez d'exéсuter une action pour laquelle vous ne disposez pas des droits.
Veuillez contacter l'administrateur de Document Server.", - "SSE.Controllers.Main.errorArgsRange": "Une erreur dans la formule entrée.
Argument de plage utilisé est incorrect.", + "SSE.Controllers.Main.errorArgsRange": "Une erreur dans la formule entrée.
La plage des arguments utilisée est incorrecte.", "SSE.Controllers.Main.errorAutoFilterChange": "L'opération n'est pas autorisée, car elle tente de déplacer les cellules d'un tableau de votre feuille de calcul.", - "SSE.Controllers.Main.errorAutoFilterChangeFormatTable": "Opération impossible sur les cellules sélectionnées car vous ne pouvez pas déplacer une partie du tableau.
Sélectionnez une autre plage de données afin que tout le tableau soit déplacé et essayez à nouveau.", - "SSE.Controllers.Main.errorAutoFilterDataRange": "L'opération n'a pu être effectuée pour la plage de cellules spécifiée.
Sélectionnez la plage de données différentes à partir de celui existant et essayez à nouveau.\n", + "SSE.Controllers.Main.errorAutoFilterChangeFormatTable": "Impossible de réaliser l'opération sur les cellules sélectionnées car vous ne pouvez pas déplacer une partie du tableau.
Sélectionnez une autre plage de données afin que tout le tableau soit déplacé et essayez à nouveau.", + "SSE.Controllers.Main.errorAutoFilterDataRange": "Impossible de réaliser l'opération sur la plage de cellules spécifiée.
Sélectionnez la plage de données différente de la plage existante et essayez à nouveau.\n", "SSE.Controllers.Main.errorAutoFilterHiddenRange": "L'opération ne peut pas être effectuée car la zone contient des cellules filtrées.
Veuillez afficher des éléments filtrés et réessayez.", "SSE.Controllers.Main.errorBadImageUrl": "L'URL d'image est incorrecte", - "SSE.Controllers.Main.errorCoAuthoringDisconnect": "Connexion au serveur perdue. Le document ne peut être modifié en ce moment.", - "SSE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion Document Serverhere", + "SSE.Controllers.Main.errorCoAuthoringDisconnect": "La connexion au serveur perdue. Désolé, vous ne pouvez plus modifier le document.", + "SSE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

Trouvez plus d'informations sur la connexion de Document Serverici", "SSE.Controllers.Main.errorCopyMultiselectArea": "Impossible d'exécuter cette commande sur des sélections multiples.
Sélectionnez une seule plage et essayez à nouveau.", - "SSE.Controllers.Main.errorCountArg": "Une erreur dans la formule entrée.
Nombre d'arguments utilisé est incorrect.", - "SSE.Controllers.Main.errorCountArgExceed": "Une erreur dans la formule entrée.
Nombre d'arguments est dépassé.", - "SSE.Controllers.Main.errorCreateDefName": "Actuellement des plages nommées existantes ne peuvent pas être modifiées et les nouvelles ne peuvent pas être
créées car certaines d'entre eux sont en cours de modification.", - "SSE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
Erreur de connexion à la base de données. Si l'erreur persiste veillez contactez l'assistance technique.", + "SSE.Controllers.Main.errorCountArg": "Une erreur dans la formule entrée.
Le nombre d'arguments utilisé est incorrect.", + "SSE.Controllers.Main.errorCountArgExceed": "Une erreur dans la formule entrée.
Le nombre d'arguments est dépassé.", + "SSE.Controllers.Main.errorCreateDefName": "Actuellement, des plages nommées existantes ne peuvent pas être modifiées et les nouvelles ne peuvent pas être créées,
car certaines d'entre eux sont en cours de modification.", + "SSE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
Erreur de connexion à la base de données. Si l'erreur persiste, veillez contactez l'assistance technique.", "SSE.Controllers.Main.errorDataRange": "Plage de données incorrecte.", "SSE.Controllers.Main.errorDefaultMessage": "Code d'erreur: %1", "SSE.Controllers.Main.errorFilePassProtect": "Le document est protégé par le mot de passe et ne peut être ouvert.", - "SSE.Controllers.Main.errorFileRequest": "Erreur externe.
Erreur de demande du fichier. Si l'erreur persiste veillez contactez l'assistance technique.", - "SSE.Controllers.Main.errorFileVKey": "Erreur externe.
Clé de sécurité incorrecte. Si l'erreur persiste veillez contactez l'assistance technique.", + "SSE.Controllers.Main.errorFileRequest": "Erreur externe.
Erreur de demande du fichier. Si l'erreur persiste, veillez contactez l'assistance technique.", + "SSE.Controllers.Main.errorFileVKey": "Erreur externe.
Clé de sécurité incorrecte. Si l'erreur persiste, veillez contactez l'assistance technique.", "SSE.Controllers.Main.errorFillRange": "Il est impossible de remplir la plage de cellules sélectionnée.
Toutes les cellules unies doivent être de la même taille.", - "SSE.Controllers.Main.errorFormulaName": "Une erreur dans la formule entrée.
Nom de formule utilisé est incorrect.", + "SSE.Controllers.Main.errorFormulaName": "Une erreur dans la formule entrée.
Le nom de formule utilisé est incorrect.", "SSE.Controllers.Main.errorFormulaParsing": "Une erreur interne lors de l'analyse de la formule.", - "SSE.Controllers.Main.errorFrmlWrongReferences": "La fonction fait référence à une feuille qui n'existe pas.
Veuillez vérifier les données et réessayer.", + "SSE.Controllers.Main.errorFrmlWrongReferences": "La fonction fait référence à une feuille qui n'existe pas.
Veuillez vérifier les données et réessayez.", "SSE.Controllers.Main.errorInvalidRef": "Entrez un nom correct pour la sélection ou une référence valable pour aller à.", "SSE.Controllers.Main.errorKeyEncrypt": "Descripteur de clés inconnu", "SSE.Controllers.Main.errorKeyExpire": "Descripteur de clés expiré", "SSE.Controllers.Main.errorLockedAll": "L'opération ne peut pas être faite car la feuille a été verrouillée par un autre utilisateur.", "SSE.Controllers.Main.errorLockedWorksheetRename": "La feuille ne peut pas être renommée pour l'instant puisque elle est renommée par un autre utilisateur", + "SSE.Controllers.Main.errorMailMergeLoadFile": "Échec du chargement", + "SSE.Controllers.Main.errorMailMergeSaveFile": "Fusion a échoué.", "SSE.Controllers.Main.errorMoveRange": "Impossible de modifier une partie d'une cellule fusionnée", - "SSE.Controllers.Main.errorOpenWarning": "La longueur de l'une des formules dans le fichier dépassé le nombre autorisé de caractères et il a été retiré.", - "SSE.Controllers.Main.errorOperandExpected": "La syntaxe de la saisie est incorrecte. Veuillez vérifier si l'une des parenthèses - '(' ou ')' est manquante.", - "SSE.Controllers.Main.errorPasteMaxRange": "La zone de copie ne correspond pas à la zone de collage.
Pour coller les cellules copiées, veuillez sélectionner une zone avec la même taille ou cliquer sur la première cellule d'une ligne.", + "SSE.Controllers.Main.errorOpenWarning": "La longueur de l'une des formules dans le fichier a dépassé
le nombre de caractères autorisé, et la formule a été supprimée.", + "SSE.Controllers.Main.errorOperandExpected": "La syntaxe de la saisie est incorrecte. Veuillez vérifier la présence de l'une des parenthèses - '(' ou ')'.", + "SSE.Controllers.Main.errorPasteMaxRange": "La zone de copie ne correspond pas à la zone de collage.
Sélectionnez une zone avec la même taille ou cliquez sur la première cellule d'une ligne pour coller les cellules sélectionnées.", "SSE.Controllers.Main.errorPrintMaxPagesCount": "Malheureusement, il n’est pas possible d’imprimer plus de 1500 pages à la fois en utilisant la version actuelle du programme.
Cette restriction sera supprimée dans la version future.", "SSE.Controllers.Main.errorProcessSaveResult": "Échec de l'enregistrement", - "SSE.Controllers.Main.errorSessionAbsolute": "La session de la modification de document a expiré.Veuillez recharger la page.", + "SSE.Controllers.Main.errorServerVersion": "La version de l'éditeur a été mise à jour. La page sera rechargée pour appliquer les modifications.", + "SSE.Controllers.Main.errorSessionAbsolute": "La session de la modification de document a expiré. Veuillez recharger la page.", "SSE.Controllers.Main.errorSessionIdle": "Le document n'est pas modifié depuis longtemps. Veuillez recharger la page.", "SSE.Controllers.Main.errorSessionToken": "La connexion au serveur a été interrompue. Veuillez recharger la page.", "SSE.Controllers.Main.errorStockChart": "L'ordre des lignes est incorrect. Pour créer un graphique boursier organisez vos données sur la feuille de calcul dans l'ordre suivant:
cours à l'ouverture, cours maximal, cours minimal, cours à la clôture.", "SSE.Controllers.Main.errorToken": "Le jeton de sécurité du document n’était pas formé correctement.
Veuillez contacter l'administrateur de Document Server.", "SSE.Controllers.Main.errorTokenExpire": "Le jeton de sécurité du document a expiré.
Veuillez contactez l'administrateur de Document Server.", - "SSE.Controllers.Main.errorUnexpectedGuid": "Erreur externe.
GUID non prévue. Si l'erreur persiste veillez contactez l'assistance technique.", + "SSE.Controllers.Main.errorUnexpectedGuid": "Erreur externe.
GUID non prévue. Si l'erreur persiste, veillez contactez l'assistance technique.", "SSE.Controllers.Main.errorUpdateVersion": "La version du fichier a été changée. La page sera rechargée.", - "SSE.Controllers.Main.errorUserDrop": "Impossible d'accéder au fichier", + "SSE.Controllers.Main.errorUserDrop": "Impossible d'accéder au fichier.", "SSE.Controllers.Main.errorUsersExceed": "Le nombre d'utilisateurs autorisés par le plan tarifaire a été dépassé", - "SSE.Controllers.Main.errorViewerDisconnect": "La connexion a été perdue. Vous pouvez toujours afficher le document,
mais ne pouvez pas le télécharger jusqu'à ce que la connexion soit rétablie.", - "SSE.Controllers.Main.errorWrongBracketsCount": "Une erreur dans la formule entrée.
Nombre utilisé entre parenthèses est incorrect.", - "SSE.Controllers.Main.errorWrongOperator": "Une erreur dans la formule entrée.Opérateur utilisé est incorrect.
Veuillez corriger l'erreur.", - "SSE.Controllers.Main.errorMailMergeLoadFile": "Échec du chargement", - "SSE.Controllers.Main.errorMailMergeSaveFile": "Fusion a échoué.", + "SSE.Controllers.Main.errorViewerDisconnect": "La connexion a été perdue. Vous pouvez toujours afficher le document,
mais ne pouvez pas le télécharger ou l'imprimer jusqu'à ce que la connexion soit rétablie.", + "SSE.Controllers.Main.errorWrongBracketsCount": "Une erreur dans la formule entrée.
Le nombre de crochets utilisé est incorrect.", + "SSE.Controllers.Main.errorWrongOperator": "Une erreur dans la formule entrée. L'opérateur utilisé est incorrect.
Veuillez corriger l'erreur.", "SSE.Controllers.Main.leavePageText": "Vous avez des modifications non enregistrées dans ce document. Cliquez sur 'Rester sur cette page' pour la sauvegarde automatique du document. Cliquez sur 'Quitter cette Page' pour ignorer toutes les modifications non enregistrées.", "SSE.Controllers.Main.loadFontsTextText": "Chargement des données...", "SSE.Controllers.Main.loadFontsTitleText": "Chargement des données", @@ -202,6 +203,7 @@ "SSE.Controllers.Main.textTryUndoRedo": "Les fonctions annuler/rétablir sont désactivées pour le mode de la co-édition rapide.
Cliquez sur le bouton \"Mode strict\" pour passer au mode de la co-édition stricte pour modifier le fichier sans interférence des autres utilisateurs et envoyer vos modifications seulement après leur enregistrement. Vous pouvez basculer entre les modes de la co-édition à l'aide des paramètres avancés de l'éditeur.", "SSE.Controllers.Main.textUsername": "Nom d'utilisateur", "SSE.Controllers.Main.titleLicenseExp": "Licence expirée", + "SSE.Controllers.Main.titleServerVersion": "L'éditeur est mis à jour", "SSE.Controllers.Main.titleUpdateVersion": "Version a été modifiée", "SSE.Controllers.Main.txtArt": "Entrez votre texte", "SSE.Controllers.Main.txtBasicShapes": "Formes de base", @@ -259,8 +261,8 @@ "SSE.Views.AddFunction.sCatMathematic": "Maths et trigonométrie", "SSE.Views.AddFunction.sCatStatistical": "Statistiques", "SSE.Views.AddFunction.sCatTextAndData": "Texte et données", - "SSE.Views.AddFunction.textGroups": "Catégories", "SSE.Views.AddFunction.textBack": "Retour", + "SSE.Views.AddFunction.textGroups": "Catégories", "SSE.Views.AddLink.textAddLink": "Ajouter le lien", "SSE.Views.AddLink.textAddress": "Adresse", "SSE.Views.AddLink.textDisplay": "Afficher", @@ -283,7 +285,6 @@ "SSE.Views.AddOther.textInsert": "Insérer", "SSE.Views.AddOther.textInsertImage": "Insérer une image", "SSE.Views.AddOther.textLink": "Lien", - "SSE.Views.AddOther.textSortOrig": "Trier", "SSE.Views.AddOther.textSort": "Trier et filtrer", "SSE.Views.EditCell.textAccounting": "Comptabilité", "SSE.Views.EditCell.textAlignBottom": "Aligner en bas", @@ -363,6 +364,7 @@ "SSE.Views.EditChart.textMajorType": "Type principal", "SSE.Views.EditChart.textMaxValue": "Valeur maximale", "SSE.Views.EditChart.textMinor": "Secondaires", + "SSE.Views.EditChart.textMinorType": "Type secondaire", "SSE.Views.EditChart.textMinValue": "Valeur minimale", "SSE.Views.EditChart.textNone": "Aucun", "SSE.Views.EditChart.textNoOverlay": "Sans superposition", @@ -443,8 +445,8 @@ "SSE.Views.Settings.textAuthor": "Auteur", "SSE.Views.Settings.textBack": "Retour", "SSE.Views.Settings.textCreateDate": "Date de création", - "SSE.Views.Settings.textDocInfo": "Infos du document", - "SSE.Views.Settings.textDocTitle": "Titre du document", + "SSE.Views.Settings.textDocInfo": "Infos sur tableur", + "SSE.Views.Settings.textDocTitle": "Titre du tableur", "SSE.Views.Settings.textDone": "Terminé", "SSE.Views.Settings.textDownload": "Télécharger", "SSE.Views.Settings.textDownloadAs": "Télécharger comme...", diff --git a/apps/spreadsheeteditor/mobile/locale/ru.json b/apps/spreadsheeteditor/mobile/locale/ru.json index df36a82a8..84f982db9 100644 --- a/apps/spreadsheeteditor/mobile/locale/ru.json +++ b/apps/spreadsheeteditor/mobile/locale/ru.json @@ -9,9 +9,9 @@ "SSE.Controllers.AddChart.txtYAxis": "Ось Y", "SSE.Controllers.AddContainer.textChart": "Диаграмма", "SSE.Controllers.AddContainer.textFormula": "Функция", + "SSE.Controllers.AddContainer.textImage": "Изображение", "SSE.Controllers.AddContainer.textOther": "Другое", "SSE.Controllers.AddContainer.textShape": "Фигура", - "SSE.Controllers.AddContainer.textImage": "Картинка", "SSE.Controllers.AddLink.textInvalidRange": "ОШИБКА! Недопустимый диапазон ячеек", "SSE.Controllers.AddLink.txtNotUrl": "Это поле должно быть URL-адресом в формате 'http://www.example.com'", "SSE.Controllers.AddOther.textEmptyImgUrl": "Необходимо указать URL изображения.", @@ -24,23 +24,15 @@ "SSE.Controllers.DocumentHolder.menuEdit": "Редактировать", "SSE.Controllers.DocumentHolder.menuHide": "Скрыть", "SSE.Controllers.DocumentHolder.menuMerge": "Объединить", + "SSE.Controllers.DocumentHolder.menuMore": "Ещё", "SSE.Controllers.DocumentHolder.menuOpenLink": "Перейти по ссылке", "SSE.Controllers.DocumentHolder.menuPaste": "Вставить", "SSE.Controllers.DocumentHolder.menuShow": "Показать", "SSE.Controllers.DocumentHolder.menuUnmerge": "Разбить", "SSE.Controllers.DocumentHolder.menuUnwrap": "Убрать перенос", "SSE.Controllers.DocumentHolder.menuWrap": "Перенос текста", - "SSE.Controllers.DocumentHolder.warnMergeLostData": "Операция может уничтожить данные в выделенных ячейках.
Продолжить?", - "SSE.Controllers.DocumentHolder.menuMore": "Еще", "SSE.Controllers.DocumentHolder.sheetCancel": "Отмена", - "SSE.Controllers.EditContainer.textSettings": "Настройки", - "SSE.Controllers.EditContainer.textCell": "Ячейка", - "SSE.Controllers.EditContainer.textTable": "Таблица", - "SSE.Controllers.EditContainer.textShape": "Фигура", - "SSE.Controllers.EditContainer.textImage": "Картинка", - "SSE.Controllers.EditContainer.textChart": "Диаграмма", - "SSE.Controllers.EditContainer.textText": "Текст", - "SSE.Controllers.EditContainer.textHyperlink": "Ссылка", + "SSE.Controllers.DocumentHolder.warnMergeLostData": "Операция может уничтожить данные в выделенных ячейках.
Продолжить?", "SSE.Controllers.EditCell.textAuto": "Авто", "SSE.Controllers.EditCell.textFonts": "Шрифты", "SSE.Controllers.EditCell.textPt": "пт", @@ -86,6 +78,14 @@ "SSE.Controllers.EditChart.textTop": "Сверху", "SSE.Controllers.EditChart.textTrillions": "Триллионы", "SSE.Controllers.EditChart.textValue": "Значение", + "SSE.Controllers.EditContainer.textCell": "Ячейка", + "SSE.Controllers.EditContainer.textChart": "Диаграмма", + "SSE.Controllers.EditContainer.textHyperlink": "Гиперссылка", + "SSE.Controllers.EditContainer.textImage": "Изображение", + "SSE.Controllers.EditContainer.textSettings": "Настройки", + "SSE.Controllers.EditContainer.textShape": "Фигура", + "SSE.Controllers.EditContainer.textTable": "Таблица", + "SSE.Controllers.EditContainer.textText": "Текст", "SSE.Controllers.EditHyperlink.textDefault": "Выбранный диапазон", "SSE.Controllers.EditHyperlink.textEmptyImgUrl": "Необходимо указать URL изображения.", "SSE.Controllers.EditHyperlink.textExternalLink": "Внешняя ссылка", @@ -135,12 +135,15 @@ "SSE.Controllers.Main.errorKeyExpire": "Срок действия дескриптора ключа истек", "SSE.Controllers.Main.errorLockedAll": "Операция не может быть произведена, так как лист заблокирован другим пользователем.", "SSE.Controllers.Main.errorLockedWorksheetRename": "В настоящее время лист нельзя переименовать, так как его переименовывает другой пользователь", + "SSE.Controllers.Main.errorMailMergeLoadFile": "Сбой при загрузке", + "SSE.Controllers.Main.errorMailMergeSaveFile": "Не удалось выполнить слияние.", "SSE.Controllers.Main.errorMoveRange": "Нельзя изменить часть объединенной ячейки", "SSE.Controllers.Main.errorOpenWarning": "Длина одной из формул в файле превышала
допустимое количество символов, и формула была удалена.", "SSE.Controllers.Main.errorOperandExpected": "Синтаксис введенной функции некорректен. Проверьте, не пропущена ли одна из скобок - '(' или ')'.", "SSE.Controllers.Main.errorPasteMaxRange": "Область копирования не соответствует области вставки.
Для вставки скопированных ячеек выделите область такого же размера или щелкните по первой ячейке в строке.", "SSE.Controllers.Main.errorPrintMaxPagesCount": "К сожалению, в текущей версии программы нельзя напечатать более 1500 страниц за один раз.
Это ограничение будет устранено в последующих версиях.", "SSE.Controllers.Main.errorProcessSaveResult": "Сбой при сохранении", + "SSE.Controllers.Main.errorServerVersion": "Версия редактора была обновлена. Страница будет перезагружена, чтобы применить изменения.", "SSE.Controllers.Main.errorSessionAbsolute": "Время сеанса редактирования документа истекло. Пожалуйста, обновите страницу.", "SSE.Controllers.Main.errorSessionIdle": "Документ долгое время не редактировался. Пожалуйста, обновите страницу.", "SSE.Controllers.Main.errorSessionToken": "Подключение к серверу было прервано. Пожалуйста, обновите страницу.", @@ -154,8 +157,6 @@ "SSE.Controllers.Main.errorViewerDisconnect": "Подключение прервано. Вы по-прежнему можете просматривать документ,
но не сможете скачать или напечатать его до восстановления подключения.", "SSE.Controllers.Main.errorWrongBracketsCount": "Ошибка во введенной формуле.
Использовано неверное количество скобок.", "SSE.Controllers.Main.errorWrongOperator": "Ошибка во введенной формуле. Использован неправильный оператор.
Пожалуйста, исправьте ошибку.", - "SSE.Controllers.Main.errorMailMergeLoadFile": "Сбой при загрузке", - "SSE.Controllers.Main.errorMailMergeSaveFile": "Не удалось выполнить слияние.", "SSE.Controllers.Main.leavePageText": "В документе есть несохраненные изменения. Нажмите 'Остаться на странице', чтобы дождаться автосохранения документа. Нажмите 'Уйти со страницы', чтобы сбросить все несохраненные изменения.", "SSE.Controllers.Main.loadFontsTextText": "Загрузка данных...", "SSE.Controllers.Main.loadFontsTitleText": "Загрузка данных", @@ -202,6 +203,7 @@ "SSE.Controllers.Main.textTryUndoRedo": "Функции отмены и повтора действий отключены в Быстром режиме совместного редактирования.
Нажмите на кнопку 'Строгий режим' для переключения в Строгий режим совместного редактирования, чтобы редактировать файл без вмешательства других пользователей и отправлять изменения только после того, как вы их сохраните. Переключаться между режимами совместного редактирования можно с помощью Дополнительных параметров редактора.", "SSE.Controllers.Main.textUsername": "Имя пользователя", "SSE.Controllers.Main.titleLicenseExp": "Истек срок действия лицензии", + "SSE.Controllers.Main.titleServerVersion": "Редактор обновлен", "SSE.Controllers.Main.titleUpdateVersion": "Версия изменилась", "SSE.Controllers.Main.txtArt": "Введите ваш текст", "SSE.Controllers.Main.txtBasicShapes": "Основные фигуры", @@ -259,8 +261,8 @@ "SSE.Views.AddFunction.sCatMathematic": "Математические", "SSE.Views.AddFunction.sCatStatistical": "Статистические", "SSE.Views.AddFunction.sCatTextAndData": "Текст и данные", - "SSE.Views.AddFunction.textGroups": "Категории", "SSE.Views.AddFunction.textBack": "Назад", + "SSE.Views.AddFunction.textGroups": "Категории", "SSE.Views.AddLink.textAddLink": "Добавить ссылку", "SSE.Views.AddLink.textAddress": "Адрес", "SSE.Views.AddLink.textDisplay": "Отображать", @@ -283,8 +285,7 @@ "SSE.Views.AddOther.textInsert": "Вставить", "SSE.Views.AddOther.textInsertImage": "Вставить изображение", "SSE.Views.AddOther.textLink": "Ссылка", - "SSE.Views.AddOther.textSortOrig": "Сортировка и фильтрация", - "SSE.Views.AddOther.textSort": "Сортировка", + "SSE.Views.AddOther.textSort": "Сортировка и фильтрация", "SSE.Views.EditCell.textAccounting": "Финансовый", "SSE.Views.EditCell.textAlignBottom": "По нижнему краю", "SSE.Views.EditCell.textAlignCenter": "По центру", @@ -363,6 +364,7 @@ "SSE.Views.EditChart.textMajorType": "Основной тип", "SSE.Views.EditChart.textMaxValue": "Максимум", "SSE.Views.EditChart.textMinor": "Дополнительные", + "SSE.Views.EditChart.textMinorType": "Дополнительный тип", "SSE.Views.EditChart.textMinValue": "Минимум", "SSE.Views.EditChart.textNone": "Нет", "SSE.Views.EditChart.textNoOverlay": "Без наложения", @@ -415,7 +417,7 @@ "SSE.Views.EditShape.textEffects": "Эффекты", "SSE.Views.EditShape.textFill": "Заливка", "SSE.Views.EditShape.textForward": "Перенести вперед", - "SSE.Views.EditShape.textOpacity": "Непрозрачность", + "SSE.Views.EditShape.textOpacity": "Прозрачность", "SSE.Views.EditShape.textRemoveShape": "Удалить фигуру", "SSE.Views.EditShape.textReorder": "Порядок", "SSE.Views.EditShape.textReplace": "Заменить", @@ -438,13 +440,13 @@ "SSE.Views.Search.textSearchIn": "Область поиска", "SSE.Views.Search.textSheet": "Лист", "SSE.Views.Search.textWorkbook": "Книга", - "SSE.Views.Settings.textAbout": "О продукте", + "SSE.Views.Settings.textAbout": "О программе", "SSE.Views.Settings.textAddress": "адрес", "SSE.Views.Settings.textAuthor": "Автор", "SSE.Views.Settings.textBack": "Назад", "SSE.Views.Settings.textCreateDate": "Дата создания", - "SSE.Views.Settings.textDocInfo": "Информация о документе", - "SSE.Views.Settings.textDocTitle": "Название документа", + "SSE.Views.Settings.textDocInfo": "Информация о таблице", + "SSE.Views.Settings.textDocTitle": "Название таблицы", "SSE.Views.Settings.textDone": "Готово", "SSE.Views.Settings.textDownload": "Скачать", "SSE.Views.Settings.textDownloadAs": "Скачать как...", From e4872916173d001e26b3c3d95115d5469dedd118 Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Tue, 4 Apr 2017 13:54:42 +0300 Subject: [PATCH 02/27] Update helps. --- .../resources/help/en/HelpfulHints/About.htm | 4 +- .../en/HelpfulHints/CollaborativeEditing.htm | 14 +-- .../en/HelpfulHints/KeyboardShortcuts.htm | 10 +- .../UsageInstructions/SavePrintDownload.htm | 10 +- .../help/en/UsageInstructions/ViewDocInfo.htm | 18 ++-- .../main/resources/help/en/editor.css | 7 ++ .../help/en/images/chartsettings2.png | Bin 18532 -> 19028 bytes .../help/en/images/formattingpresets.png | Bin 28577 -> 34699 bytes .../resources/help/fr/HelpfulHints/About.htm | 10 +- .../help/fr/HelpfulHints/AdvancedSettings.htm | 12 +-- .../fr/HelpfulHints/CollaborativeEditing.htm | 80 ++++++++------- .../fr/HelpfulHints/KeyboardShortcuts.htm | 15 ++- .../help/fr/HelpfulHints/Navigation.htm | 2 +- .../resources/help/fr/HelpfulHints/Search.htm | 2 +- .../help/fr/HelpfulHints/SpellChecking.htm | 2 +- .../help/fr/HelpfulHints/SupportedFormats.htm | 12 +-- .../help/fr/UsageInstructions/AddBorders.htm | 8 +- .../help/fr/UsageInstructions/AlignText.htm | 16 +-- .../fr/UsageInstructions/BackgroundColor.htm | 71 ++++++------- .../UsageInstructions/ChangeColorScheme.htm | 28 ++++++ .../UsageInstructions/CopyPasteUndoRedo.htm | 52 ++++++---- .../help/fr/UsageInstructions/CreateLists.htm | 56 ++++++----- .../UsageInstructions/FontTypeSizeColor.htm | 20 +++- .../UsageInstructions/FormattingPresets.htm | 86 ++++++++++++---- .../InsertHeadersFooters.htm | 65 ++++++------ .../fr/UsageInstructions/InsertImages.htm | 2 +- .../UsageInstructions/InsertPageNumbers.htm | 77 ++++++++------ .../help/fr/UsageInstructions/LineSpacing.htm | 23 +++-- .../NonprintingCharacters.htm | 24 +++-- .../fr/UsageInstructions/OpenCreateNew.htm | 8 +- .../help/fr/UsageInstructions/PageBreaks.htm | 14 +-- .../fr/UsageInstructions/ParagraphIndents.htm | 6 +- .../UsageInstructions/SavePrintDownload.htm | 34 ++++--- .../fr/UsageInstructions/SectionBreaks.htm | 33 ++++++ .../UsageInstructions/SetPageParameters.htm | 85 ++++++++++------ .../help/fr/UsageInstructions/SetTabStops.htm | 6 +- .../help/fr/UsageInstructions/ViewDocInfo.htm | 4 +- .../help/fr/images/backgroundcolor.png | Bin 0 -> 326 bytes .../fr/images/backgroundcolor_selected.png | Bin 0 -> 272 bytes .../help/fr/images/changecolorscheme.png | Bin 385 -> 507 bytes .../resources/help/fr/images/colorscheme.png | Bin 0 -> 3556 bytes .../resources/help/fr/images/columnbreak.png | Bin 0 -> 879 bytes .../help/fr/images/columnspacing.png | Bin 0 -> 1868 bytes .../help/fr/images/custommargins.png | Bin 0 -> 5915 bytes .../help/fr/images/custompagesize.png | Bin 0 -> 4738 bytes .../main/resources/help/fr/images/file.png | Bin 521 -> 120 bytes .../resources/help/fr/images/fontfamily.png | Bin 880 -> 323 bytes .../resources/help/fr/images/fontsize.png | Bin 489 -> 210 bytes .../help/fr/images/formattingpresets.png | Bin 18408 -> 34699 bytes .../images/highlight_color_mouse_pointer.png | Bin 0 -> 231 bytes .../help/fr/images/insertcolumns.png | Bin 0 -> 183 bytes .../resources/help/fr/images/leftcolumn.png | Bin 0 -> 105 bytes .../main/resources/help/fr/images/margins.png | Bin 0 -> 1454 bytes .../resources/help/fr/images/pagebreak1.png | Bin 266 -> 251 bytes .../resources/help/fr/images/pagemargins.png | Bin 0 -> 219 bytes .../help/fr/images/palette_custom.png | Bin 36031 -> 26210 bytes .../help/fr/images/paradvsettings_indents.png | Bin 10408 -> 16102 bytes .../help/fr/images/right heaterfooter.png | Bin 0 -> 8864 bytes .../help/fr/images/right_headerfooter.png | Bin 8721 -> 8864 bytes .../help/fr/images/right_paragraph.png | Bin 5842 -> 10025 bytes .../resources/help/fr/images/rightcolumn.png | Bin 0 -> 105 bytes .../help/fr/images/sameasprevious_label.png | Bin 0 -> 3159 bytes .../resources/help/fr/images/sectionbreak.png | Bin 0 -> 1253 bytes .../help/fr/images/tabstops_ruler.png | Bin 1910 -> 1851 bytes .../resources/help/fr/images/threecolumns.png | Bin 0 -> 105 bytes .../resources/help/fr/images/twocolumns.png | Bin 0 -> 105 bytes .../resources/help/ru/HelpfulHints/About.htm | 15 ++- .../ru/HelpfulHints/CollaborativeEditing.htm | 95 +++++++++--------- .../ru/HelpfulHints/KeyboardShortcuts.htm | 10 +- .../UsageInstructions/SavePrintDownload.htm | 32 +++--- .../help/ru/UsageInstructions/ViewDocInfo.htm | 19 ++-- .../help/ru/images/formattingpresets.png | Bin 28577 -> 34699 bytes .../UsageInstructions/SavePrintDownload.htm | 48 ++++----- .../ViewPresentationInfo.htm | 2 + .../resources/help/ru/HelpfulHints/About.htm | 2 +- .../ru/HelpfulHints/CollaborativeEditing.htm | 89 ++++++++-------- .../ru/HelpfulHints/KeyboardShortcuts.htm | 10 +- .../UsageInstructions/SavePrintDownload.htm | 23 +++-- .../ViewPresentationInfo.htm | 2 + .../en/HelpfulHints/KeyboardShortcuts.htm | 20 +++- .../ru/HelpfulHints/KeyboardShortcuts.htm | 18 +++- 81 files changed, 776 insertions(+), 525 deletions(-) create mode 100644 apps/documenteditor/main/resources/help/fr/UsageInstructions/ChangeColorScheme.htm create mode 100644 apps/documenteditor/main/resources/help/fr/UsageInstructions/SectionBreaks.htm create mode 100644 apps/documenteditor/main/resources/help/fr/images/backgroundcolor.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/backgroundcolor_selected.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/colorscheme.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/columnbreak.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/columnspacing.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/custommargins.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/custompagesize.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/highlight_color_mouse_pointer.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/insertcolumns.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/leftcolumn.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/margins.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/pagemargins.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/right heaterfooter.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/rightcolumn.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/sameasprevious_label.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/sectionbreak.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/threecolumns.png create mode 100644 apps/documenteditor/main/resources/help/fr/images/twocolumns.png diff --git a/apps/documenteditor/main/resources/help/en/HelpfulHints/About.htm b/apps/documenteditor/main/resources/help/en/HelpfulHints/About.htm index 722746f9a..3c10ae6a4 100644 --- a/apps/documenteditor/main/resources/help/en/HelpfulHints/About.htm +++ b/apps/documenteditor/main/resources/help/en/HelpfulHints/About.htm @@ -9,8 +9,8 @@

About Document Editor

-

Document Editor is an online application that lets you look through - and edit documents directly in your browser.

+

Document Editor is an online application that lets you look through + and edit documents directly in your browser.

Using Document Editor, you can perform various editing operations like in any desktop editor, print the edited documents keeping all the formatting details or download them onto your computer hard disk drive as PDF, TXT, DOCX, ODT, or HTML files.

diff --git a/apps/documenteditor/main/resources/help/en/HelpfulHints/CollaborativeEditing.htm b/apps/documenteditor/main/resources/help/en/HelpfulHints/CollaborativeEditing.htm index d584e3ce2..cb92d719a 100644 --- a/apps/documenteditor/main/resources/help/en/HelpfulHints/CollaborativeEditing.htm +++ b/apps/documenteditor/main/resources/help/en/HelpfulHints/CollaborativeEditing.htm @@ -12,12 +12,13 @@

Collaborative Document Editing

Document Editor offers you the possibility to work at a document collaboratively with other users. This feature includes:

    -
  • simultaneous multi-user access to the edited document
  • -
  • visual indication of passages that are being edited by other users
  • -
  • synchronization of changes with one button click
  • -
  • chat to share ideas concerning particular document parts
  • +
  • simultaneous multi-user access to the edited document
  • +
  • visual indication of passages that are being edited by other users
  • +
  • synchronization of changes with one button click
  • +
  • chat to share ideas concerning particular document parts
  • comments containing the description of a task or problem that should be solved
+

Co-editing

Document Editor allows to select one of the two available co-editing modes. Fast is used by default and shows the changes made by other users in realtime. Strict is selected to hide other user changes until you click the Save Save icon icon to save your own changes and accept the changes made by others. The mode can be selected in the Advanced Settings.

When a document is being edited by several users simultaneously in the Strict mode, the edited text passages are marked with dashed lines of different colors. By hovering the mouse cursor over one of the edited passages, the name of the user who is editing it at the moment is displayed. The Fast mode will show the actions and the names of the co-editors once they are editing the text.

@@ -36,7 +37,8 @@

All the messages left by users will be displayed on the panel on the left. If there are new messages you haven't read yet, the chat icon will look like this - Chat icon.

To close the panel with chat messages, click the Chat icon icon once again.

-

Comments*

+
+

Comments*

To leave a comment,

  1. select a text passage where you think there is an error or problem,
  2. @@ -57,7 +59,7 @@

    New comments added by other users will become visible only after you click the Save icon icon in the left upper corner of the top toolbar.

    To close the panel with comments, click the Comments icon icon once again.

    -

    *available for paid versions only

    +

    *available for paid versions only

\ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/en/HelpfulHints/KeyboardShortcuts.htm b/apps/documenteditor/main/resources/help/en/HelpfulHints/KeyboardShortcuts.htm index 3532bb2c9..9dab526a4 100644 --- a/apps/documenteditor/main/resources/help/en/HelpfulHints/KeyboardShortcuts.htm +++ b/apps/documenteditor/main/resources/help/en/HelpfulHints/KeyboardShortcuts.htm @@ -25,17 +25,17 @@ Open the Search panel to start searching for a character/word/phrase in the currently edited document. - Open 'Comments' panel* + Open 'Comments' panel* Ctrl+Shift+H Open the Comments panel to add your own comment or reply to other users' comments. - Open comment field* + Open comment field* Alt+H Open a data entry field where you can add the text of your comment. - - Open 'Chat' panel* + + Open 'Chat' panel* Alt+Q Open the Chat panel and send a message. @@ -347,7 +347,7 @@ Hold down the Ctrl key and use the keybord arrows to move the selected object by three pixels at a time. -

* - available for paid versions only

+

* - available for paid versions only

\ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/en/UsageInstructions/SavePrintDownload.htm b/apps/documenteditor/main/resources/help/en/UsageInstructions/SavePrintDownload.htm index c3fb90e76..878bf8b1a 100644 --- a/apps/documenteditor/main/resources/help/en/UsageInstructions/SavePrintDownload.htm +++ b/apps/documenteditor/main/resources/help/en/UsageInstructions/SavePrintDownload.htm @@ -9,29 +9,29 @@
-

Save/download/print your document

-

By default, Document Editor automatically saves your file each 2 seconds when you work on it preventing your data loss in case of the unexpected program closing. If you co-edit the file in the Fast mode, the timer requests for updates 25 times a second and saves the changes if they have been made. When the file is being co-edited in the Strict mode, changes are automatically saved at 10-minute intervals. If you need, you can easily select the preferred co-editing mode or disable the Autosave feature on the Advanced Settings page.

+

Save/download/print your document

+

By default, Document Editor automatically saves your file each 2 seconds when you work on it preventing your data loss in case of the unexpected program closing. If you co-edit the file in the Fast mode, the timer requests for updates 25 times a second and saves the changes if they have been made. When the file is being co-edited in the Strict mode, changes are automatically saved at 10-minute intervals. If you need, you can easily select the preferred co-editing mode or disable the Autosave feature on the Advanced Settings page.

To save your current document manually,

  • press the Save Save icon icon at the top toolbar, or
  • use the Ctrl+S key combination, or
  • click the File File icon icon at the left sidebar and select the Save option.
- +

To download the resulting document onto your computer hard disk drive,

  1. click the File File icon icon at the left sidebar,
  2. select the Download as... option,
  3. choose one of the available formats depending on your needs: PDF, TXT, DOCX, ODT, HTML.
- +

To print out the current document,

  • click the Print Print icon icon at the top toolbar, or
  • use the Ctrl+P key combination, or
  • click the File File icon icon at the left sidebar and select the Print option.
-

After that a PDF file will be generated on the basis of the document. You can open and print it out, or save onto your computer hard disk drive or removable medium to print it out later.

+

After that a PDF file will be generated on the basis of the document. You can open and print it out, or save onto your computer hard disk drive or removable medium to print it out later.

\ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/en/UsageInstructions/ViewDocInfo.htm b/apps/documenteditor/main/resources/help/en/UsageInstructions/ViewDocInfo.htm index b5cb6787c..2c6b5ee95 100644 --- a/apps/documenteditor/main/resources/help/en/UsageInstructions/ViewDocInfo.htm +++ b/apps/documenteditor/main/resources/help/en/UsageInstructions/ViewDocInfo.htm @@ -12,14 +12,16 @@

To access the detailed information about the currently edited document, click the File File icon icon at the left sidebar and select the Document Info... option.

General Information

The document information includes document title, author, location, creation date, and statistics: the number of pages, paragraphs, words, symbols, symbols with spaces.

-

Permission Information

-

Note: this option is not available for users with the Read Only permissions.

-

To find out, who have rights to view or edit the document, select the Access Rights... option at the left sidebar.

-

You can also change currently selected access rights by pressing the Change access rights button in the Persons who have rights section.

-

Version History

-

Note: this option is not available for free accounts as well as for users with the Read Only permissions.

-

To view all the changes made to this document, select the Version History option at the left sidebar. You'll see the list of this document versions (major changes) and revisions (minor changes) with the indication of each version/revision author and creation date and time. For document versions, the version number is also specified (e.g. ver. 2). To know exactly which changes have been made in each separate version/revision, you can view the one you need by clicking it at the left sidebar. The changes made by the version/revision author are marked with the color which is displayed next to the author name on the left sidebar. To return to the document current version, click the Back to Document link on the top of the version list.

+
+

Permission Information

+

Note: this option is not available for users with the Read Only permissions.

+

To find out, who have rights to view or edit the document, select the Access Rights... option at the left sidebar.

+

You can also change currently selected access rights by pressing the Change access rights button in the Persons who have rights section.

+

Version History

+

Note: this option is not available for free accounts as well as for users with the Read Only permissions.

+

To view all the changes made to this document, select the Version History option at the left sidebar. You'll see the list of this document versions (major changes) and revisions (minor changes) with the indication of each version/revision author and creation date and time. For document versions, the version number is also specified (e.g. ver. 2). To know exactly which changes have been made in each separate version/revision, you can view the one you need by clicking it at the left sidebar. The changes made by the version/revision author are marked with the color which is displayed next to the author name on the left sidebar. To return to the document current version, click the Back to Document link on the top of the version list.

+

To close the File panel and return to document editing, select the Back to Document option.

- + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/en/editor.css b/apps/documenteditor/main/resources/help/en/editor.css index e368fecd4..afbea2043 100644 --- a/apps/documenteditor/main/resources/help/en/editor.css +++ b/apps/documenteditor/main/resources/help/en/editor.css @@ -19,6 +19,13 @@ margin-right: 30px; margin-bottom: 10px; } +img.floatright +{ +float: right; +margin-left: 30px; +margin-bottom: 10px; +} + li { line-height: 2em; diff --git a/apps/documenteditor/main/resources/help/en/images/chartsettings2.png b/apps/documenteditor/main/resources/help/en/images/chartsettings2.png index 742130b8fb18e35b2a1689294341873447d7b796..e96ba9c2af959c8b8920b3887901d804d3a3419c 100644 GIT binary patch literal 19028 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 V;okl5Gf@mTu4!J)zibuqe*pjHSCs$& literal 18532 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%0ABPqNdQq9zO1iM#F*dfmz}pX%l$5mg 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 ZfiZ76Uvu9JJ~oI-QC974uFU;s{}01adzt_M diff --git a/apps/documenteditor/main/resources/help/en/images/formattingpresets.png b/apps/documenteditor/main/resources/help/en/images/formattingpresets.png index be198c35a6bb35edcad9f94b13453d724d932548..60c1ac73a15bbbfe54464569101362a6e15c7780 100644 GIT binary patch literal 34699 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 F`yc((HBA5j diff --git a/apps/documenteditor/main/resources/help/fr/HelpfulHints/About.htm b/apps/documenteditor/main/resources/help/fr/HelpfulHints/About.htm index 2c44c660a..4555e8c71 100644 --- a/apps/documenteditor/main/resources/help/fr/HelpfulHints/About.htm +++ b/apps/documenteditor/main/resources/help/fr/HelpfulHints/About.htm @@ -1,16 +1,16 @@  - A propos de TeamLab Document Editor + A propos de Document Editor - +
-

A propos de TeamLab Document Editor

-

TeamLab Document Editor est une application en ligne qui vous permet de parcourir et de modifier des documents directement sur le portail TeamLab.

-

En utilisant TeamLab Document Editor, vous pouvez effectuer de différentes opérations d'édition comme en utilisant n'importe quel éditeur de bureau, +

A propos de Document Editor

+

Document Editor est une application en ligne qui vous permet de parcourir et de modifier des documents directement sur le portail .

+

En utilisant Document Editor, vous pouvez effectuer de différentes opérations d'édition comme en utilisant n'importe quel éditeur de bureau, imprimer les documents modifiés en gardant la mise en forme ou les télécharger sur votre disque dur au format PDF, TXT, DOCX, DOC, ODT, RTF, HTML, et EPUB.

diff --git a/apps/documenteditor/main/resources/help/fr/HelpfulHints/AdvancedSettings.htm b/apps/documenteditor/main/resources/help/fr/HelpfulHints/AdvancedSettings.htm index fd1aab34b..834523e0c 100644 --- a/apps/documenteditor/main/resources/help/fr/HelpfulHints/AdvancedSettings.htm +++ b/apps/documenteditor/main/resources/help/fr/HelpfulHints/AdvancedSettings.htm @@ -1,15 +1,15 @@  - Paramètres avancés de TeamLab Document Editor + Paramètres avancés de Document Editor - +
-

Paramètres avancés de TeamLab Document Editor

-

TeamLab Document Editor vous permet de modifier ses paramètres avancés. Pour y accéder, cliquez sur l'icône Fichier icône Fichier sur la barre latérale gauche et sélectionez l'option Paramètres avancés... ou utilisez l'icône Paramètres avancés dans le coin supérieur droit de la barre d'outils.

+

Paramètres avancés de Document Editor

+

Document Editor vous permet de modifier ses paramètres avancés. Pour y accéder, cliquez sur l'icône Fichier icône Fichier sur la barre latérale gauche et sélectionez l'option Paramètres avancés... ou utilisez l'icône Paramètres avancés dans le coin supérieur droit de la barre d'outils.

Les paramètres avancés sont les suivants :

  • Entrée alternative sert à activer / désactiver les hiéroglyphes.
  • @@ -21,7 +21,7 @@
  • L'option Derniers sélectionnée, seulement les modificatons effectuées depuis que vous avez cliqué sur l'icône Enregistrer icône Enregistrer seront mises en surbrillance.
-
  • Hinting sert à sélectionner le type d'affichage de la police dans TeamLab Document Editor : +
  • Hinting sert à sélectionner le type d'affichage de la police dans Document Editor :
    • Choisissez comme Windows si vous aimez la façon dont les polices sont habituellement affichées sous Windows, c'est à dire en utilisant la police de Windows.
    • Choisissez comme OS X si vous aimez la façon dont les polices sont habituellement affichées sous Mac, c'est à dire sans hinting.
    • @@ -31,7 +31,7 @@
    • Enregistrement automatique sert à spécifier la fréquence de l'enregistrement des changements apportés lors de l'édition.
      • Sélectionnez un des intervalles disponibles : Toutes les 10 minutes, Toutes les 30 minutes ou Chaque heure.
      • -
      • Sélectionnez l'option Désactivé, si vous ne voulez pas que TeamLab Document Editor enregistre les changements automatiquement.
      • +
      • Sélectionnez l'option Désactivé, si vous ne voulez pas que Document Editor enregistre les changements automatiquement.
    • Unité de mesure sert à spécifier les unités de mesure utilisées sur les règles et dans les fenêtres de paramètres pour les paramètres tels que largeur, hauteur, espacement, marges etc. Vous pouvez choisir l'option Centimètre ou Point.
    • diff --git a/apps/documenteditor/main/resources/help/fr/HelpfulHints/CollaborativeEditing.htm b/apps/documenteditor/main/resources/help/fr/HelpfulHints/CollaborativeEditing.htm index 4000e3778..3ce34a193 100644 --- a/apps/documenteditor/main/resources/help/fr/HelpfulHints/CollaborativeEditing.htm +++ b/apps/documenteditor/main/resources/help/fr/HelpfulHints/CollaborativeEditing.htm @@ -9,46 +9,50 @@

      Edition collaborative des documents

      -

      TeamLab Document Editor vous offre la possibilité de travailler sur un document simultanément avec d'autres utilisateurs. Cette fonction inclut :

      +

      Document Editor vous offre la possibilité de travailler sur un document simultanément avec d'autres utilisateurs. Cette fonction inclut :

        -
      • accès simultané au document édité par plusieurs utilisateurs
      • -
      • indication visuelle des fragments qui sont en train d'être édités par d'autres utilisateurs
      • -
      • synchronisation des changements en un seul clic
      • -
      • chat pour partager des idées concernant certaines parties du document
      • +
      • accès simultané au document édité par plusieurs utilisateurs
      • +
      • indication visuelle des fragments qui sont en train d'être édités par d'autres utilisateurs
      • +
      • synchronisation des changements en un seul clic
      • +
      • chat pour partager des idées concernant certaines parties du document
      • commentaires avec la description d'une tâche ou d'un problème à résoudre
      -

      Edition collaborative

      -

      Quand un document est en train d'être édité par plusieurs utilisateurs à la fois, les fragments du texte édité sont marqués par les lignes de différentes couleurs. Placez le curseur de la souris au-dessus d'un fragment édité pour voir le nom de l'utilisateur qui est en train de l'éditer en ce moment-là. Le nombre d'utilisateurs qui travaillent sur le document courant est indiqué dans le coin gauche inférieur sur la barre d'état - Icône Nombre d'utilisateurs. Pour voir qui est en train d'éditer le fichier au présent, placez le curseur de la souris sur cette icône - les noms des utilisateurs seront affichés dans la fenêtre contextuelle. S'il y a trop d'utilisateurs, cliquez sur cette icône pour ouvrir le panneau Chat avec la liste complète.

      -

      Dès qu'un des utilisateurs enregistre ses changements, les autres vont voir une remarque sur la barre d'état signalant qu'il y a des mises à jour disponibles. Pour enregistrer les changements effectués et installer des mises à jour cliquez sur l'icône Icône Enregistrer dans le coin gauche de la barre supérieure. Les mises à jour seront marquées pour vous aider à controller ce qui a été exactement modifié.

      -

      Vous pouvez toujours spécifier les changements à marquer pendant l'édition collaborative si vous cliquez sur l'icône Fichier sur la barre latérale gauche, sélectionnez l'option Paramètres avancés... et choisissez entre tous les changements et les derniers changements de collaboration. Si vous sélectionnez Tous les changements, tous les changements effectués pendant la session courante seront marqués. Si vous sélectionnez les Derniers changements, vous n'allez voir que les changements effectués à partir du dernier clic sur l'icône Icône Enregistrer.

      -

      Chat

      -

      Pour accéder à Chat et envoyer un message à d'autres utilisateurs :

      -
        -
      1. cliquez sur l'icône Chat sur la barre latérale gauche,
      2. -
      3. saisissez le texte dans le champ correspondant,
      4. -
      5. cliquez sur le bouton Envoyer.
      6. -
      -

      Tous les messages envoyés par les utilisateurs seront affichés sur le panneau à gauche. S'il y a de nouveaux messages à lire, l'icône chat sera affichée de la manière suivante - Chat.

      -

      Pour fermer le panneau avec les messages, cliquez sur l'icône Chat encore une fois.

      -

      Commentaires

      -

      Pour laisser un commentaire :

      -
        -
      1. sélectionnez le fragment du texte que vous voulez commenter,
      2. -
      3. utilisez l'icône Commentaires sur la barre latérale gauche pour ouvrir le panneau Commentaires et cliquez sur le lien Ajouter un commentaire au document, ou
        - cliquez avec le bouton droit de la souris sur le fragment du texte à commenter et sélectionnez l'option Ajouter commentaire du menu contextuel,
      4. -
      5. saisissez le texte nécessaire,
      6. -
      7. cliquez sur le bouton Ajouter commentaire/Ajouter.
      8. -
      -

      Le commentaire sera affiché sur le panneau latéral gauche. Tout autre utilisateur peut répondre au commentaire ajouté en posant une question ou en faisant référance au travail fait. Pour le faire il suffit de cliquer sur le lien Ajouter une réponse situé au-dessus du commentaire.

      -

      Le fragment du texte commenté sera marqué dans le document. Pour voir le commentaire, cliquez à l'intérieur du fragment. Si vous avez besoin de désactiver cette fonctioin, cliquez sur l'icône Icône Fichier, sélectionnez l'option Paramètres avancés... et décochez la case Activer l'option de commentaires en temps réel. Dans ce cas-là le fragment commenté ne sera marqué que si vous cliquez sur l'icône Icône Comments.

      -

      Vous pouvez gérer les commentaires ajoutés de la manière suivante :

      -
        -
      • les modifier en cliquant sur l'icône Editer,
      • -
      • les supprimer en cliquant sur l'icône Supprimer,
      • -
      • fermer la discussion en cliquant sur le lien Résoudre si la tâche ou le problème décrit dans votre commentaire est résolu, après quoi la discussion ouverte par votre commentaire reçois le statut résolu. Pour la rouvrir, cliquez sur Résolu à côté et sélectionnez l'option Ouvrir à nouveau.
      • -
      -

      Les nouveaux commentaires ajoutés par d'autres utilisateurs ne seront visibles qu'après un clic sur l'icône Enregistrer dans le coin gauche de la barre supérieure.

      -

      Pour fermer le panneau avec les commentaires cliquez sur l'icône Commentaires encore une fois.

      -
      +
      +

      Edition collaborative

      +

      Quand un document est en train d'être édité par plusieurs utilisateurs à la fois, les fragments du texte édité sont marqués par les lignes de différentes couleurs. Placez le curseur de la souris au-dessus d'un fragment édité pour voir le nom de l'utilisateur qui est en train de l'éditer en ce moment-là. Le nombre d'utilisateurs qui travaillent sur le document courant est indiqué dans le coin gauche inférieur sur la barre d'état - Icône Nombre d'utilisateurs. Pour voir qui est en train d'éditer le fichier au présent, placez le curseur de la souris sur cette icône - les noms des utilisateurs seront affichés dans la fenêtre contextuelle. S'il y a trop d'utilisateurs, cliquez sur cette icône pour ouvrir le panneau Chat avec la liste complète.

      +

      Dès qu'un des utilisateurs enregistre ses changements, les autres vont voir une remarque sur la barre d'état signalant qu'il y a des mises à jour disponibles. Pour enregistrer les changements effectués et installer des mises à jour cliquez sur l'icône Icône Enregistrer dans le coin gauche de la barre supérieure. Les mises à jour seront marquées pour vous aider à controller ce qui a été exactement modifié.

      +

      Vous pouvez toujours spécifier les changements à marquer pendant l'édition collaborative si vous cliquez sur l'icône Fichier sur la barre latérale gauche, sélectionnez l'option Paramètres avancés... et choisissez entre tous les changements et les derniers changements de collaboration. Si vous sélectionnez Tous les changements, tous les changements effectués pendant la session courante seront marqués. Si vous sélectionnez les Derniers changements, vous n'allez voir que les changements effectués à partir du dernier clic sur l'icône Icône Enregistrer.

      +

      Chat

      +

      Pour accéder à Chat et envoyer un message à d'autres utilisateurs :

      +
        +
      1. cliquez sur l'icône Chat sur la barre latérale gauche,
      2. +
      3. saisissez le texte dans le champ correspondant,
      4. +
      5. cliquez sur le bouton Envoyer.
      6. +
      +

      Tous les messages envoyés par les utilisateurs seront affichés sur le panneau à gauche. S'il y a de nouveaux messages à lire, l'icône chat sera affichée de la manière suivante - Chat.

      +

      Pour fermer le panneau avec les messages, cliquez sur l'icône Chat encore une fois.

      +
      +

      Commentaires

      +

      Pour laisser un commentaire :

      +
        +
      1. sélectionnez le fragment du texte que vous voulez commenter,
      2. +
      3. + utilisez l'icône Commentaires sur la barre latérale gauche pour ouvrir le panneau Commentaires et cliquez sur le lien Ajouter un commentaire au document, ou
        + cliquez avec le bouton droit de la souris sur le fragment du texte à commenter et sélectionnez l'option Ajouter commentaire du menu contextuel, +
      4. +
      5. saisissez le texte nécessaire,
      6. +
      7. cliquez sur le bouton Ajouter commentaire/Ajouter.
      8. +
      +

      Le commentaire sera affiché sur le panneau latéral gauche. Tout autre utilisateur peut répondre au commentaire ajouté en posant une question ou en faisant référance au travail fait. Pour le faire il suffit de cliquer sur le lien Ajouter une réponse situé au-dessus du commentaire.

      +

      Le fragment du texte commenté sera marqué dans le document. Pour voir le commentaire, cliquez à l'intérieur du fragment. Si vous avez besoin de désactiver cette fonctioin, cliquez sur l'icône Icône Fichier, sélectionnez l'option Paramètres avancés... et décochez la case Activer l'option de commentaires en temps réel. Dans ce cas-là le fragment commenté ne sera marqué que si vous cliquez sur l'icône Icône Comments.

      +

      Vous pouvez gérer les commentaires ajoutés de la manière suivante :

      +
        +
      • les modifier en cliquant sur l'icône Editer,
      • +
      • les supprimer en cliquant sur l'icône Supprimer,
      • +
      • fermer la discussion en cliquant sur le lien Résoudre si la tâche ou le problème décrit dans votre commentaire est résolu, après quoi la discussion ouverte par votre commentaire reçois le statut résolu. Pour la rouvrir, cliquez sur Résolu à côté et sélectionnez l'option Ouvrir à nouveau.
      • +
      +

      Les nouveaux commentaires ajoutés par d'autres utilisateurs ne seront visibles qu'après un clic sur l'icône Enregistrer dans le coin gauche de la barre supérieure.

      +

      Pour fermer le panneau avec les commentaires cliquez sur l'icône Commentaires encore une fois.

      +
  • \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/HelpfulHints/KeyboardShortcuts.htm b/apps/documenteditor/main/resources/help/fr/HelpfulHints/KeyboardShortcuts.htm index c82f91e0e..f1722676d 100644 --- a/apps/documenteditor/main/resources/help/fr/HelpfulHints/KeyboardShortcuts.htm +++ b/apps/documenteditor/main/resources/help/fr/HelpfulHints/KeyboardShortcuts.htm @@ -3,7 +3,7 @@ Raccourcis clavier - + @@ -16,7 +16,7 @@ Ouvrir le volet 'Fichier' Alt+F - Ouvrir le volet Fichier pour enregistrer, télécharger, imprimer le document actuel, afficher ses informations, créer un nouveau document ou ouvrir un existant, accéder à l'aide de TeamLab Document Editor ou aux paramètres avancés. + Ouvrir le volet Fichier pour enregistrer, télécharger, imprimer le document actuel, afficher ses informations, créer un nouveau document ou ouvrir un existant, accéder à l'aide de Document Editor ou aux paramètres avancés. Ouvrir le volet 'Recherche' @@ -33,7 +33,7 @@ Alt+H Ouvrir un champ de saisie où vous pouvez ajouter le texte de votre commentaire. - + Ouvrir le volet 'Chat' Alt+Q Ouvrir le volet Chat et envoyer un message. @@ -41,7 +41,7 @@ Enregistrer le document Ctrl+S - Enregistrer toutes les modifications dans le document actuellement modifié à l'aide de TeamLab Document Editor. + Enregistrer toutes les modifications dans le document actuellement modifié à l'aide de Document Editor. Imprimer le document @@ -56,7 +56,7 @@ Plein écran F11 - Passer à l'affichage plein écran pour adapter TeamLab Document Editor à votre écran. + Passer à l'affichage plein écran pour adapter Document Editor à votre écran. Navigation @@ -310,6 +310,11 @@ Ctrl+Maj+M Diminuer le retrait gauche. + + Add page number + Ctrl+Maj+P + Add the current page number to the text or to the page footer. + Modification des objets diff --git a/apps/documenteditor/main/resources/help/fr/HelpfulHints/Navigation.htm b/apps/documenteditor/main/resources/help/fr/HelpfulHints/Navigation.htm index 1ac0b317e..2cc890517 100644 --- a/apps/documenteditor/main/resources/help/fr/HelpfulHints/Navigation.htm +++ b/apps/documenteditor/main/resources/help/fr/HelpfulHints/Navigation.htm @@ -9,7 +9,7 @@

    Paramètres d'affichage et outils de navigation

    -

    TeamLab Document Editor est doté de plusieurs outils qui vous aide à visionner et naviguer à travers votre document : les règles, le zoom, les boutons page précédente / suivante, l'affichage des numéros de page.

    +

    Document Editor est doté de plusieurs outils qui vous aide à visionner et naviguer à travers votre document : les règles, le zoom, les boutons page précédente / suivante, l'affichage des numéros de page.

    Régler les paramètres d'affichage

    Pour régler les paramètres d'affichage par défaut et définir le mode le plus convenable de travailler avec le document, cliquez sur l'icône Afficher les paramètres Afficher les paramètres dans le coin supérieur droit de la barre d'outils et sélectionnez les éléments de l'interface à afficher/masquer. Vous pouvez choisir une des options suivantes de la liste déroulante Afficher les paramètres : diff --git a/apps/documenteditor/main/resources/help/fr/HelpfulHints/Search.htm b/apps/documenteditor/main/resources/help/fr/HelpfulHints/Search.htm index 2ffab576f..94264ecd2 100644 --- a/apps/documenteditor/main/resources/help/fr/HelpfulHints/Search.htm +++ b/apps/documenteditor/main/resources/help/fr/HelpfulHints/Search.htm @@ -3,7 +3,7 @@ Fonctions de recherche et remplacement - + diff --git a/apps/documenteditor/main/resources/help/fr/HelpfulHints/SpellChecking.htm b/apps/documenteditor/main/resources/help/fr/HelpfulHints/SpellChecking.htm index 98159f1bf..6af3c63c0 100644 --- a/apps/documenteditor/main/resources/help/fr/HelpfulHints/SpellChecking.htm +++ b/apps/documenteditor/main/resources/help/fr/HelpfulHints/SpellChecking.htm @@ -9,7 +9,7 @@

    Vérification de l'orthographe

    -

    TeamLab Document Editor vous permet de vérifier l'orthographe du texte saisi dans une certaine langue et corriger des fautes lors de l'édition.

    +

    Document Editor vous permet de vérifier l'orthographe du texte saisi dans une certaine langue et corriger des fautes lors de l'édition.

    Tout d'abord, choisissez la langue pour tout le document. Cliquez sur l'icône Définir la langue du document située dans la partie droite de la Barre d'état. Dans la fenêtre ouverte sélectionnez la langue nécessaire et cliquez sur OK. La langue sélectionnée sera appliquée à tout le document.

    Définir la langue du document

    Pour sélectionner une langue différente pour un fragment, sélectionnez le fragment nécessaire avec la souris et utilisez le menu Vérification de l'orthographe - Langue du texte de la Barre d'état.

    diff --git a/apps/documenteditor/main/resources/help/fr/HelpfulHints/SupportedFormats.htm b/apps/documenteditor/main/resources/help/fr/HelpfulHints/SupportedFormats.htm index 58faf7781..1600229a4 100644 --- a/apps/documenteditor/main/resources/help/fr/HelpfulHints/SupportedFormats.htm +++ b/apps/documenteditor/main/resources/help/fr/HelpfulHints/SupportedFormats.htm @@ -3,7 +3,7 @@ Formats des documents électroniques pris en charge - + @@ -12,7 +12,7 @@

    Les documents électroniques représentent l'un des types des fichiers les plus utilisés en informatique. Grâce à l'utilisation du réseau informatique tant développé aujourd'hui, il est possible et plus pratique de distribuer des documents électroniques que des versions imprimées. Les formats de fichier ouverts et propriétaires sont bien nombreux à cause de la variété des périphériques utilisés pour la présentation des documents. - TeamLab Document Editor prend en charge les formats les plus populaires.

    + Document Editor prend en charge les formats les plus populaires.

    @@ -26,7 +26,7 @@ - + @@ -47,7 +47,7 @@ - + @@ -73,9 +73,9 @@ - - + + diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/AddBorders.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/AddBorders.htm index ce23f2eb7..33a55da14 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/AddBorders.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/AddBorders.htm @@ -9,9 +9,9 @@

    Ajouter des bordures

    -

    Pour ajouter des bordures à un paragraphe, une page ou au document entier,

    +

    Pour ajouter des bordures à un paragraphe, à une page ou à tout le document,

      -
    1. placez le curseur dans le paragraphe nécessaire, ou à l'aide de la souris sélectionnez plusieurs paragraphes ou le texte entier du document en utilisant la combinaison de touches Ctrl+A,
    2. +
    3. placez le curseur dans le paragraphe nécessaire, ou à l'aide de la souris sélectionnez plusieurs paragraphes ou tout le texte du document en utilisant la combinaison de touches Ctrl+A,
    4. cliquez avec le bouton droit de la souris et sélectionnez l'option du menu Paramètres avancés du paragraphe ou utilisez le lien Afficher les paramètres avancés de la barre latérale droite,
    5. passez à l'onglet Bordures et remplissage dans la fenêtre Paragraphe - Paramètres avancés ouverte,
    6. définissez la valeur nécessaire pour la Taille de bordure et sélectionnez une Couleur de la bordure,
    7. @@ -19,8 +19,8 @@
    8. cliquez sur le bouton OK.

    Paramètres du paragraphe avancés - Bordures et remplissage

    -

    Après avoir ajouté des bordures, vous pouvez également définir les marges c'est-à-dire la distance entre les bordures à droite, à gauche, en haut et en bas et le texte du paragraphe à l'intérieur.

    -

    Pour définir les valeurs nécessaires, passez à l'onglet Marges de la fenêtre Paragraphe - Paramètres avancés :

    +

    Après avoir ajouté des bordures, vous pouvez également définir les Marges intérieures c'est-à-dire la distance entre les bordures à droite, à gauche, en haut et en bas et le texte du paragraphe à l'intérieur.

    +

    Pour définir les valeurs nécessaires, passez à l'onglet Marges intérieures de la fenêtre Paragraphe - Paramètres avancés :

    Paramètres du paragraphe avancés - Marges

    diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/AlignText.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/AlignText.htm index af7bfa12b..e5f6a0b3d 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/AlignText.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/AlignText.htm @@ -1,23 +1,23 @@  - Alignement du texte sur la ligne ou le paragraphe + Alignement du texte d'un paragraphe - +
    -

    Alignement du texte sur la ligne ou le paragraphe

    +

    Alignement du texte d'un paragraphe

    Le texte peut être aligné de quatre façons : aligné à gauche, centré, aligné à droite et justifié. Pour ce faire,

      -
    1. placez le curseur à la position où vous voulez appliquer l'alignement (cela peut être une nouvelle ligne ou le texte déjà saisi ),
    2. +
    3. placez le curseur à la position où vous voulez appliquer l'alignement (une nouvelle ligne ou le texte déjà saisi ),
    4. sélectionnez le type d'alignement que vous allez appliquer :
        -
      • Gauche - pour aligner le texte sur le côté gauche de la page (le côté droit reste non aligné) cliquez sur l'icône Aligner à gauche l'icône Aligner à gauche située sur la barre d'outils supérieure.
      • -
      • Centré - pour aligner le texte par le centre de la page (les côté droit et gauche restent non alignés) cliquez sur l'icône Alignement centré l'icône Alignement centré située sur la barre d'outils supérieure.
      • -
      • Droit - pour aligner le texte sur le côté droit de la page (le côté gauche reste non aligné) cliquez sur l'icône Aligner à droite l'icône Aligner à droite située sur la barre d'outils supérieure.
      • -
      • Justifié - pour aligner le texte sur les deux côtés à la fois, gauche et droit ( l'espacement supplémentaire est ajouté si nécessaire pour garder l'alignement) cliquez sur l'icône Justifié l'icône Justifié située sur la barre d'outils supérieure.
      • +
      • Gauche - pour aligner du texte à gauche de la page (le côté droit reste non aligné), cliquez sur l'icône Aligner à gauche l'icône Aligner à gauche située sur la barre d'outils supérieure.
      • +
      • Centré - pour aligner du texte au centre de la page (le côté droit et le côté gauche, ils restent non alignés), cliquez sur l'icône Aligner au centre l'icône Alignement centré située sur la barre d'outils supérieure.
      • +
      • Droit - pour aligner du texte à droite de la page (le côté gauche reste non aligné), cliquez sur l'icône Aligner à droite l'icône Aligner à droite située sur la barre d'outils supérieure.
      • +
      • Justifié - pour aligner du texte à gauche et à droite à la fois ( l'espacement supplémentaire est ajouté si nécessaire pour garder l'alignement), cliquez sur l'icône Justifié l'icône Justifié située sur la barre d'outils supérieure.
    diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/BackgroundColor.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/BackgroundColor.htm index 3c0a952d6..e76430b45 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/BackgroundColor.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/BackgroundColor.htm @@ -1,41 +1,36 @@  - - Sélectionner la couleur d'arrière-plan pour un paragraphe - - - - - -
    -

    Sélectionner la couleur d'arrière-plan pour un paragraphe

    -

    La couleur d'arrière-plan est appliquée au paragraphe entier.

    -

    Pour appliquer la couleur d'arrière-plan à un certain paragraphe ou changer la couleur actuelle,

    -
      -
    1. sélectionnez un jeu de couleurs pour votre document à partir de ceux qui sont disponibles en cliquant sur l'icône Modifier le jeu de couleurs Modifier le jeu de couleurs sur la barre d'outils supérieure
    2. -
    3. placez le curseur dans le paragraphe nécessaire, ou sélectionnez plusieurs paragraphes avec la souris ou le texte entier en utilisant la combinaison de touches Ctrl+A
    4. -
    5. ouvrez la fenêtre des palettes de couleurs en cliquant sur le champ de couleurs à coté de la légende Couleur d'arrière-plan sur la barre latérale droite -

      Remarque : vous pouvez accéder à la fenêtre de la palette de couleurs en cliquant sur le lien 'Afficher les paramètres avancés' sur la barre latérale droite ou en sélectionnant l'option 'Paramètres avancés du paragraphe' du menu contextuel, ensuite en passant à l'onglet 'Bordures et remplissage' de la fenêtre 'Paragraphe - Paramètres avancés' et en cliquant sur le champ de couleur à côté de la légende Arrière-plan.

      -
    6. -
    7. choisissez une couleur dans les palettes disponibles -

      Palette

      -
        -
      • Couleurs de thème - les couleurs qui correspondent à la palette de couleurs sélectionnée du document.
      • -
      • Couleurs standart - le jeu de couleurs par défaut.
      • -
      • Couleur personnalisée - choisissez cette option si il n'y a pas de couleur nécessaire dans les palettes disponibles. Sélectionnez la gamme de couleurs nécessaire en déplacant le curseur vertical et définissez la couleur spécifique en faisant glisser le sélecteur de couleur dans le grand champ de couleur carré. Une fois que vous sélectionnez une couleur avec le sélecteur de couleur, les valeurs de couleur appropriées RGB et sRGB seront affichées dans les champs à droite. Vous pouvez également spécifier une couleur sur la base du modèle de couleur RGB (RVB) en entrant les valeurs numériques nécessaires dans les champs R, G, B (rouge, vert, bleu) ou saisir le code hexadécimal dans le champ sRGB marqué par le signe #. La couleur sélectionnée apparaît dans la case de prévisualisation Nouveau. Si l'objet a déjà été rempli avec une couleur personnalisée, cette couleur sera affichée dans la case Actuel afin que vous puissiez comparer les couleurs originales et modifiées. Lorsque la couleur est spécifiée, cliquez sur le bouton Ajouter : -

        Palette - Couleur personnalisée

        -

        La couleur personnalisée sera appliquée au paragraphe et ajoutée à la palette Couleur personnalisée.

        -
      • -
      -
    8. -
    -
    -

    Pour effacer une couleur d'arrière-plan d'un certain paragraphe,

    -
      -
    1. placez le curseur dans le paragraphe nécessaire, ou sélectionnez plusieurs paragraphes avec la souris ou tout le texte en utilisant la combinaison des touches Ctrl+A
    2. -
    3. ouvrez la fenêtre des palettes de couleur en cliquant sur le champ de couleur à côté de la légende Couleur d'arrière-plan
    4. -
    5. sélectionnez l'icône Pas de remplissage.
    6. -
    -
    - + + Sélectionner la couleur d'arrière-plan pour un paragraphe + + + + + +
    +

    Sélectionner la couleur d'arrière-plan pour un paragraphe

    +

    La couleur d'arrière-plan est appliquée au paragraphe entier et remplit complètement l’espace du paragraphe de la marge de page gauche à la marge de page droite.

    +

    Pour appliquer la couleur d'arrière-plan au paragraphe particulier ou changer la couleur actuelle,

    +
      +
    1. sélectionnez un jeu de couleurs pour votre document à partir des modèles disponibles en cliquant sur l'icône Modifier le jeu de couleurs Modifier le jeu de couleurs sur la barre d'outils supérieure
    2. +
    3. placez le curseur dans le paragraphe nécessaire, ou sélectionnez plusieurs paragraphes avec la souris ou le texte entier en utilisant la combinaison de touches Ctrl+A
    4. +
    5. ouvrez la fenêtre des palettes de couleurs. Vous pouvez l'accéder par une des façons suivantes: +
        +
      • cliquez sur la flèche vers le bas à côté de l'icône icône Сouleur d'arrière plan d'un paragraphe sur la barre d'outils supérieure, ou
      • +
      • utilisez le champ de couleurs à côté de la légende Сouleur d'arrière plan sur la barre latérale droite,
      • +
      • cliquez sur le lien 'Afficher les paramètres avancés' sur la barre latérale droite ou sélectionnez l'option 'Paramètres avancés du paragraphe' dans le menu contextuel, puis passez à l'onglet 'Bordures et remplissage' dans la fenêtre 'Paragraphe - Paramètres avancés' et cliquez sur le champ de couleurs à coté de la légende Couleur d'arrière-plan.
      • +
      +
    6. +
    7. choisissez une couleur dans les palettes disponibles
    8. +
    +

    Après avoir sélectionné la couleur nécessaire à l'aide de l'icône icône Сouleur d'arrière plan d'un paragraphe, vous pourrez appliquer cette couleur à n’importe quel paragraphe sélectionné. Pour le faire, cliquez sur l'icône Sélectionner la couleur d'arrière-plan d'un paragraphe (elle affiche la couleur séléctionnée), sans avoir à choisir cette couleur dans la palette encore une fois. Si vous utilisez l'option Couleur d'arrière plan sur la barre de droite ou dans la fenêtre 'Paragraphe - Paramètres avancés', n'oubliez pas que la couleur sélectionnée n'est pas conservée pour l'accéder rapidement. (Ces options peuvent être utiles si vous souhaitez sélectionner une couleur d’arrière-plan différente pour un paragraphe spécifique, lors de l'utilisation de la couleur de base sélectionnée à l'aide l'icône Couleur d'arrière-plan d'un paragraphe).

    +
    +

    Pour effacer la couleur d'arrière d'un paragraphe particulier,

    +
      +
    1. placez le curseur dans le paragraphe nécessaire, ou sélectionnez plusieurs paragraphes à l'aide de la souris ou le texte entier en utilisant la combinaison de touches Ctrl+A
    2. +
    3. en cliquant sur le champ de couleur à côté de la légende Сouleur d'arrière plan sur la barre latérale droite, ouvrez la fenêtre des palettes de couleurs
    4. +
    5. sélectionnez l'icône Pas de remplissage.
    6. +
    +
    + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/ChangeColorScheme.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/ChangeColorScheme.htm new file mode 100644 index 000000000..0fc6fb872 --- /dev/null +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/ChangeColorScheme.htm @@ -0,0 +1,28 @@ + + + + Modifier le jeu de couleurs + + + + + + +
    +

    Modifier le jeu de couleurs

    +

    Les jeux de couleurs s'appliquent au document entier. Utilisés pour le changement rapide de l'apparence de votre document, les jeux de couleurs définissent la palette Couleurs de thème pour les éléments du document (police, arrière-plan, tableaux, formes automatiques, graphiques). Si vous appliquez des Couleurs de thèmes aux éléments du document et sélectionnez un nouveau Jeu de couleurs, les couleurs appliquées aux éléments de votre document, par conséquent, seront modifiées.

    +

    Pour modifier le jeu de couleurs, cliquez sur la flèche vers le bas située à côté de l'icône Modifier le jeu de couleurs Modifier le jeu de couleurs dans la partie droite de la barre d'outils et sélectionnez le jeu de couleurs nécessaire parmi les variantes disponibles : Bureau, Niveaux de gris, Apex, Aspect, Civique, Rotonde, Capitaux, Flux, Fonderie, Médian, Métro, Module, Opulent, Oriel, Origine, Papier, Solstice, Technique, Promenade, Urban, Verve.

    +

    Color Schemes

    +

    Lorsque le jeu de couleurs est sélectionné, vous pouvez choisir des couleurs dans une fenêtre de palettes de couleurs qui vont correspondre à l'élément auquel vous souhaitez appliquer la couleur. Les palettes suivantes sont disponibles :

    +

    Palette

    +
      +
    • Couleurs de thème - les couleurs qui correspondent à la palette de couleurs sélectionnée du document.
    • +
    • Couleurs standard - le jeu de couleurs par défaut. Le jeu de couleurs sélectionné ne les affecte pas.
    • +
    • Couleur personnalisée - choisissez cette option si la couleur nécessaire n'est pas disponible dans la palette. Sélectionnez la plage de couleurs nécessaire en déplaçant le curseur de couleur verticale et définissez la couleur spécifique en faisant glisser le sélecteur de couleur dans le grand champ de couleur. Une fois que vous avez sélectionné une couleur à l'aide du sélecteur de couleur, les valeurs des couleurs RGB et sRGB appropriées s’afficheront dans les champs à droite. Vous pouvez aussi spécifier une couleur sur la base de la palette de couleurs RGB en saisissant les valeurs numériques nécessaires dans les champs R, G, B (rouge, vert, bleu) ou entrez le code hexadécimal sRGB dans le champ marqué du signe #. La couleur sélectionnée apparaît dans la zone d'aperçu Nouvelle. Si une couleur personnalisée a été appliquée à l'objet, cette couleur s'affiche dans la fenêtre Actuelle, ainsi vous pouvez comparer les couleurs originales et modifiées. Lorsque la couleur est spécifiée, cliquez sur le bouton Ajouter : +

      Palette - Custom Color

      +

      La couleur personnalisée sera appliquée à l’élément sélectionné et ajoutée dans la palette Couleur personnalisée.

      +
    • +
    +
    + + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/CopyPasteUndoRedo.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/CopyPasteUndoRedo.htm index 88b8dc1ba..9556e1f7a 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/CopyPasteUndoRedo.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/CopyPasteUndoRedo.htm @@ -1,23 +1,35 @@  - - Copier/coller les passages de texte, annuler/rétablir vos actions - - - - - -
    -

    Copier/coller les passages de texte, annuler/rétablir vos actions

    -

    Pour effectuer ces opérations, utilisez les icônes correspondantes de la barre d'outils supérieure :

    -
      -
    • Copier – sélectionnez le fragment du texte et utilisez l'icône Copier l'icône Copier pour copier la sélection dans le presse-papiers de l'ordinateur. Le texte copié peut être inséré plus tard dans un autre endroit dans le même document, dans un autre document, ou dans un autre programme.
    • -
    • Coller – trouvez l'endroit dans votre document où vous voulez coller le fragment de texte précédemment copié et utilisez l'icône Coller Paste icon . - Le texte sera inséré à la position actuelle du curseur. Le texte peut être copié à partir du même document, d'un autre document ou à partir de l'autre programme.
    • -
    • Annuler – utilisez l'icône Annuler l'icône annuler pour annuler la dernière opération effectuée.
    • -
    • Rétablir – utilisez l'icône Rétablir l'icône rétablir pour rétablir la dernière opération annulée.
    • -
    -

    Remarque : pour des raisons de sécurité certains navigateurs n’autorisent pas l’accès au presse-papiers de votre ordinateur. Ainsi quand vous essayez de sélectionner l'une des opérations pour couper, le programme va vous demander d'utiliser le raccourcis clavier commun pour tous les navigateurs : Ctrl+X pour couper, Ctrl+C pour copier, Ctrl+V pour coller.

    -
    - + + Copier/coller les passages de texte, annuler/rétablir vos actions + + + + + +
    +

    Copier/coller les passages de texte, annuler/rétablir vos actions

    +

    Pour couper, copier, coller des passages de texte et des objets insérés (formes automatiques, images, graphiques) dans le document actuel utilisez les options correspondantes dans le menu contextuel ou les icônes de la barre d'outils supérieure :

    +
      +
    • Couper – sélectionnez un fragment de texte ou un objet et utilisez l'option Couper dans le menu contextuel pour supprimer la sélection et l'envoyer dans le presse-papiers de l'ordinateur. Les données coupées peuvent être insérées plus tard dans un autre endroit dans le même document.
    • +
    • Copier – sélectionnez un fragment de texte ou un objet et utilisez l'option Copier dans le menu contextuel, ou l'icône Copier l'icône Copier de la barre d'outils supérieure pour copier la sélection dans le presse-papiers de l'ordinateur. Les données coupées peuvent être insérées plus tard dans un autre endroit dans le même document.
    • +
    • + Coller – trouvez l'endroit dans votre document à coller le fragment de texte / objet précédemment copié et utilisez l'option Coller dans le menu contextuel, ou l'icône Coller l'icône Coller de la barre d'outils supérieure. + Le texte / objet sera inséré à la position actuelle du curseur. Le texte peut être copié à partir du même document. Les données peuvent être précédemment copiées à partir du même document. +
    • +
    +

    Pour copier, coller les données à partir de / dans un autre document ou autre programme, utilisez les combinaisons de touches suivantes :

    +
      +
    • Ctrl+X raccourcis clavier pour couper ;
    • +
    • Ctrl+C raccourcis clavier pour copier ;
    • +
    • Ctrl+V raccourcis clavier pour coller.
    • +
    +

    Remarque : au lieu de couper et coller du texte dans le même document, vous pouvez sélectionner le passage de texte nécessaire et le faire glisser à la position nécessaire.

    +

    Pour annuler / rétablir les actions, utilisez les icônes correspondantes de la barre d'outils supérieure ou les raccourcis clavier :

    +
      +
    • Annuler – utilisez l'icône Annuler L'icône Annuler de la barre d'outils supérieure ou la combinaison de touches Ctrl+Z pour annuler la dernière opération effectuée.
    • +
    • Rétablir – utilisez l'icône Rétablir L'icône Rétablir de la barre d'outils supérieure ou la combinaison de touches Ctrl+Y pour rétablir l’opération précédemment annulée.
    • +
    +
    + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/CreateLists.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/CreateLists.htm index b743cba4f..880d81319 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/CreateLists.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/CreateLists.htm @@ -1,30 +1,32 @@  - - Créer des listes - - - - - - -
    -

    Créer des listes

    -

    Pour créer une liste dans votre document,

    -
      -
    1. placez le curseur à la position où vous voulez commencer la liste (cela peut être une nouvelle ligne ou le texte déjà saisi),
    2. -
    3. sélectionnez le type de liste à créer : -
        -
      • Liste à puces avec les marqueurs. Pour la créer, utilisez l'icône Puces Puces sur la barre d'outils supérieure
      • -
      • Liste numérotée avec les chiffres ou les lettres. Pour la créer, utilisez l'icône Numérotation Numérotation sur la barre d'outils supérieure -

        Remarque: cliquez sur la flèche vers le bas à côté de l'icône Puces ou Numérotation pour sélectionner le format de puces ou de numérotation nécessaire.

        -
      • -
      -
    4. -
    5. appuyez sur la touche Entrée à la fin de la ligne pour ajouter un nouvel élément à la liste. Pour terminer la liste, appuyez sur la touche Retour arrière et continuez le travail.
    6. -
    -

    Vous pouvez aussi changer le retrait du texte dans les listes et leur imbrication en utilisant les icônes Plan l'icône Plan, Réduire le retrait l'icône Réduire le retrait et Augmenter le retrait Augmenter le retrait sur la barre d'outils supérieure.

    -

    Remarque: les paramètres supplémentaires du retrait et de l'espacemente peuvent être modifiés à l'aide de la barre latérale droite et la fenêtre des paramètres avancés. Pour en savoir plus, consultez les pages Modifier le retrait des paragraphes et Régler l'interligne du paragraphe.

    -
    - + + Créer des listes + + + + + + +
    +

    Créer des listes

    +

    Pour créer une liste dans votre document,

    +
      +
    1. placez le curseur à la position où vous voulez commencer la liste ( une nouvelle ligne ou le texte déjà saisi),
    2. +
    3. + sélectionnez le type de liste à créer : +
        +
      • Liste à puces avec les marqueurs. Pour la créer, utilisez l'icône Puces Puces sur la barre d'outils supérieure
      • +
      • + Liste numérotée avec les chiffres ou les lettres. Pour la créer, utilisez l'icône Numérotation Numérotation sur la barre d'outils supérieure +

        Remarque: cliquez sur la flèche vers le bas à côté de l'icône Puces ou Numérotation pour sélectionner le format de puces ou de numérotation nécessaire.

        +
      • +
      +
    4. +
    5. désormais un nouveau élément d'une liste numérotée ou non-numérotée apparaît lorsque vous appuyez sur la touche Entrée. Pour terminer la liste, appuyez sur la touche Retour arrière et continuez le travail.
    6. +
    +

    Vous pouvez aussi changer le retrait du texte dans les listes et leur imbrication en utilisant les icônes Liste multiniveau Liste multiniveau, Réduire le retrait Réduire le retrait, et Augmenter le retrait Augmenter le retrait sur la barre d'outils supérieure.

    +

    Remarque: les paramètres supplémentaires du retrait et de l'espacemente peuvent être modifiés à l'aide de la barre latérale droite et la fenêtre des paramètres avancés. Pour en savoir plus, consultez les pages Modifier le retrait des paragraphes et Régler l'interligne du paragraphe.

    +
    + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/FontTypeSizeColor.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/FontTypeSizeColor.htm index f63f007d4..35c124c28 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/FontTypeSizeColor.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/FontTypeSizeColor.htm @@ -16,17 +16,27 @@
    - + - - - + + + + + + + + + + + + + - + diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/FormattingPresets.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/FormattingPresets.htm index 7b7be4684..3d39c034e 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/FormattingPresets.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/FormattingPresets.htm @@ -1,21 +1,71 @@  - - Appliquer les paramètres de mise en forme prédéfinis - - - - - -
    -

    Appliquer les paramètres de mise en forme prédéfinis

    -

    Pour appliquer une mise en forme prédéfinie,

    -
      -
    1. placez le cursor sur une ligne ou sélectionnez plusieurs lignes ou paragraphes auxquels vous voulez appliquer la mise en forme prédéfinie,
    2. -
    3. sélectionnez la mise en forme nécessaire à droite sur la barre d'outils supérieure.
    4. -
    -

    Les mises en forme prédéfinies sont les suivantes : normal, non-espacement, en-tête 1-9, titre, sous-titre, citation, citation intense, paragraphe de liste.

    -

    Paramètres de mise en forme prédéfinis

    -
    - + + Appliquer les styles de formatage + + + + + +
    +

    Appliquer les styles de formatage

    +

    Chaque style de mise en forme représente un ensemble des options de mise en forme : (taille de la police, couleur, interligne, alignment etc.). Les styles permettent de mettre en forme rapidement les parties différentes du texte (en-têtes, sous-titres, listes,texte normal, citations) au lieu d'appliquer les options de mise en forme différentes individuellement chaque fois que vous en avez besoin. Cela permet également d'assurer une apparence uniforme de tout le document. Un style n'est appliqué au'au paragraphe entier.

    +

    Appliquer des styles par défault

    +

    Pour appliquer un des styles de mise en forme disponibles,

    +
      +
    1. placez le curseur dans le paragraphe nécessaire, ou sélectionnez plusieurs paragraphes pour appliquer un des styles de mise en forme,
    2. +
    3. sélectionnez le style nécessaire à partir de la galerie de styles située à droite de la barre d'outils supérieure.
    4. +
    +

    Les styles de mise en forme disponibles sont : normal, non-espacemen, titre 1-9, title, sous-titre, citation, citation intense, paragraphe de liste.

    +

    Formatting styles

    +

    Modifier des styles disponibles et créer de nouveaux

    +

    Pour modifier le style existant :

    +
      +
    1. Appliquez le style nécessaire à un paragraphe.
    2. +
    3. Sélectionnez le texte du paragraphe et modifiez tous les paramètres de mise en forme dont vous avez besoin.
    4. +
    5. + Enregistrez les modifications effectuées : +
        +
      • cliquez avec le bouton droit de la souris sur le texte en cours de modification, sélectionnez l'option En tant que style et sélectionnez l'option Mettre à jour le style 'Nomdestyle' ('Nomdestyle' correspond au style appliqué à l'étape 1),
      • +
      • ou sélectionnez le fragment du texte en cours de modification avec la souris, ouvrez le menu déroulant de la galerie des styles, cliquez avec le bouton droit de la souris sur le style à modifier et sélectionnez l'option Mettre à jour selon la sélection.
      • +
      +
    6. +
    +

    Une fois que le style est modifié, tous les paragraphes dans le document qui a été mis en forme à l'aide de ce style vont changer leur apparence de manière correspondante.

    +

    Pour créer un tout nouveau style:

    +
      +
    1. Mettez en forme un fragment du texte d'une manière nécessaire.
    2. +
    3. + Choisissez une façon appropriée de sauvegarder le style: +
        +
      • cliquez avec le bouton droit de la souris sur le texte en cours de modification, sélectionnez l'option En tant que style et puis choisissez l'option Créer un nouveau style,
      • +
      • ou sélectionnez le fragment du texte à l'aide de la souris, ouvrez la liste déroulante de la galerie de styles et cliquez sur l'option Nouveau style à partir du fragment sélectionné.
      • +
      +
    4. +
    5. + Définissez les paramètres du nouveau style dans la fenêtre Créer un nouveau style qui s'ouvre : +

      Fenêtre Créer un nouveau style

      +
        +
      • Spécifiez un nom du nouveau style en utilisant le champ correspondant.
      • +
      • Chosissez le style nécessaire du paragraphe suivant en utilisant la liste Style du nouveau paragraphe.
      • +
      • Cliquez sur le bouton OK.
      • +
      +
    6. +
    +

    Le style créé sera ajouté à la galerie des styles.

    +

    Gérez vos styles personnalisés:

    +
      +
    • Pour restaurer les paramètres par défaut d'un style que vous avez modifié, cliquez avec le bouton droit de la souris sur le style que vous voulez restaurer et sélectionnez l'option Restaurer les paramètres par défaut.
    • +
    • + Pour restaurer les paramètres par défaut de tous les styles que vous avez modifiés, cliquez avec le bouton droit de la souris sur le style dans la galerie des styles et sélectionnez l'option Restaurer tous les styles par défaut. +

      Edited style menu

      +
    • +
    • Pour supprimer un des styles que vous avez créé, cliquez avec le bouton droit de la souris sur le style à supprimer et sélectionnez l'option Supprimer le style.
    • +
    • + Pour supprimer tous les nouveaux style que vous avez crées, cliquez avec le bouton droit de la souris sur un des nouveaux styles crées et sélectionnez l'option Supprimer tous les styles personnalisés. +

      Menu style personnalisé

      +
    • +
    +
    + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertHeadersFooters.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertHeadersFooters.htm index 466a70513..f2097369e 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertHeadersFooters.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertHeadersFooters.htm @@ -1,32 +1,39 @@  - - Insérer en-têtes et pieds de page - - - - - - -
    -

    Insérer en-têtes et pieds de page

    -

    Pour ajouter un en-tête ou un pied de page à votre document ou les modifier,

    -
      -
    1. cliquez sur l'icône Modifier en-tête ou pied de page l'icône Modifier en-tête ou pied de page sur la barre d'outils supérieure,
    2. -
    3. sélectionnez l'une des options suivantes : -
        -
      • Modifier l'en-tête pour insérer ou modifier le texte d'en-tête.
      • -
      • Modifier le pied de page pour insérer ou modifier le texte de pied de page.
      • -
      -
    4. -
    5. modifiez les paramètres actuels pour en-têtes ou pieds de page sur la barre latérale droite : la position du texte par rapport à la partie supérieure (en-têtes) ou inférieure (pour pieds) de la page. - Vous pouvez également cocher la case Première page différente pour appliquer un en-tête ou un pied de page différent pour la première page ou si vous ne voulez pas ajouter un en-tête / un pied de page . La case Pages paires et impaires différentes sert à ajouter de différents en-têtes ou pieds de page pour les pages paires et impaires. -

      Right Sidebar - Header and Footer Settings

      -
    6. -
    -

    Pour saisir un texte ou modifier le texte déjà saisi et régler l'en-tête ou le pied de page, vous pouvez également double-cliquer sur la partie supérieure ou inférieure d'une page ou y cliquer avec le bouton droit de la souris et sélectionner l'option - Modifier l'en-tête ou Modifier le pied de page du menu contextuel.

    -

    Pour passer au corps du document, double-cliquez sur la zone de travail. Le texte que vous utilisez dans l'en-tête ou le pied de page sera affiché en gris.

    -

    Remarque: consultez la section Insérer les numéros de page pour apprendre à ajouter des numéros de page à votre document.

    -
    - + + Insérer les en-têtes et pieds de page + + + + + + +
    +

    Insérer les en-têtes et pieds de page

    +

    Pour ajouter un en-tête ou un pied de page à votre document ou modifier ceux qui déjà existent ,

    +
      +
    1. cliquez sur l'icône Modifier l'en-tête ou le pied de page l'icône Modifier l'en-tête ou le pied de page sur la barre d'outils supérieure,
    2. +
    3. sélectionnez l'une des options suivantes : +
        +
      • Modifier l'en-tête pour insérer ou modifier le texte d'en-tête.
      • +
      • Modifier le pied de page pour insérer ou modifier le texte de pied de page.
      • +
      +
    4. +
    5. modifiez les paramètres actuels pour les en-têtes ou pieds de page sur la barre latérale droite +

      Right Sidebar - Header and Footer Settings

      +
        +
      • Définissez la Position du texte par rapport à la partie supérieure (en-têtes) ou inférieure (pour pieds) de la page.
      • +
      • Cochez la case Première page différente pour appliquer un en-tête ou un pied de page différent pour la première page ou si vous ne voulez pas ajouter un en-tête / un pied de page.
      • +
      • Utilisez la case Pages paires et impaires différentes pour ajouter de différents en-têtes ou pieds de page pour les pages paires et impaires.
      • +
      • L'option Lien vers Précédent est disponible si vous avez déjà des sections dans votre document. Sinon, elle sera grisée. En outre, cette option est non disponible pour toute première section (c'est-à-dire quand un en-tête ou un pied qui appartient à la première section est choisi). Par défaut, cette case est cochée, alors que les mêmes en-têtes/pieds de page sont appliqués à toutes les sections. Si vous sélectionnez une zone d’en-tête ou de pied de page, vous verrez que сette zone est marquée par l'étiquette Same as Previous. Décochez la case Lien vers Préсédent pour utiliser de différents en-têtes et pieds de page pour chaque section du document. L'étiquette Same as Previous sera indisponible.
      • +
      +

      Same as previous label

      +
    6. +
    +

    Pour saisir un texte ou modifier le texte déjà saisi et régler les paramètres de l'en-tête ou le pied de page, vous pouvez également double-cliquer sur la partie supérieure ou inférieure de la page ou cliquer avec le bouton droit de la souris et sélectionner l'option - Modifier l'en-tête ou Modifier le pied de page du menu contextuel.

    +

    Pour passer au corps du document, double-cliquez sur la zone de travail. Le texte que vous utilisez dans l'en-tête ou dans le pied de page sera affiché en gris.

    +

    Remarque : consultez la section Insérer les numéros de page pour apprendre à ajouter des numéros de page à votre document.

    +
    + + diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertImages.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertImages.htm index 4e4100d55..a5eed9d3b 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertImages.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertImages.htm @@ -9,7 +9,7 @@

    Insérer des images

    -

    TeamLab Document Editor vous permet d'insérer des images aux formats populaires. Les formats d'image pris en charge sont les suivants : BMP, GIF, JPEG, JPG, PNG.

    +

    Document Editor vous permet d'insérer des images aux formats populaires. Les formats d'image pris en charge sont les suivants : BMP, GIF, JPEG, JPG, PNG.

    Pour insérer une image dans votre document de texte,

    1. placez le curseur à la position où vous voulez insérer une image,
    2. diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertPageNumbers.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertPageNumbers.htm index c8e27d497..9a1a5ff22 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertPageNumbers.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/InsertPageNumbers.htm @@ -1,34 +1,49 @@  - - Insérer les numéros de page - - - - - -
      -

      Insérer les numéros de page

      -

      Pour insérer des numéros de page dans le document,

      -
        -
      1. cliquez sur l'icône de la barre d'outils supérieure Modifier l'en-tête ou le pied de page Modifier l'en-tête ou le pied de page,
      2. -
      3. sélectionnez l'option Insérer le numéro de page,
      4. -
      5. sélectionnez l'une des options suivantes: -
          -
        • Pour mettre un numéro de page à chaque page de votre document, sélectionnez la position de numéro de page sur la page.
        • -
        • Pour insérer un numéro de page à la position actuelle du curseur, sélectionnez l'option A la position actuelle.
        • -
        -
      6. -
      -

      Pour modifier les paramètres du numéro de page,

      -
        -
      1. double-cliquez sur le numéro de page ajouté,
      2. -
      3. modifiez les paramètres actuels sur la barre latérale droite : la position des numéros sur la page ainsi que la position par rapport à la partie supérieure et inférieure de la page. - Vous pouvez également cocher la case Première page différente pour appliquer un numéro différent à la première page ou si vous ne voulez pas ajouter le numéro du tout. La case Pages paires et impaires différentes est utilisée pour insérer des numéros de page différents pour les pages paires et impaires. -

        Paramètres d'en-têtes et de pieds de page

        -
      4. -
      -

      Pour revenir à l'édition du document, double-cliquez sur la zone de travail.

      -
      - + + Insérer les numéros de page + + + + + +
      +

      Insérer les numéros de page

      +

      Pour insérer des numéros de page dans votre document,

      +
        +
      1. cliquez sur l'icône Modifier l'en-tête ou le pied de page Modifier l'en-tête ou le pied de page de la barre d'outils supérieure,
      2. +
      3. sélectionnez l'option Insérer le numéro de page du sous-menu,
      4. +
      5. + sélectionnez l'une des options suivantes: +
          +
        • Pour mettre un numéro de page à chaque page de votre document, sélectionnez la position de numéro de page sur la page.
        • +
        • Pour insérer un numéro de page à la position actuelle du curseur, sélectionnez l'option À la position actuelle.
        • +
        +
      6. +
      +

      Pour insérer le nombre total de pages dans votre document (par ex. si vous souhaitez créer une saisie Page X de Y):

      +
        +
      1. placez le curseur où vous souhaitez insérer le nombre total de pages,
      2. +
      3. cliquez sur l'icône Modifier l'en-tête ou le pied de page icône Modifier l'en-tête ou le pied de page de la barre d'outils supérieure,
      4. +
      5. sélectionnez l'option Insérer le nombre de pages.
      6. +
      +
      +

      Pour modifier les paramètres de la numérotation des pages,

      +
        +
      1. double-cliquez sur le numéro de page ajouté,
      2. +
      3. + modifiez les paramètres actuels en utilisant la barre latérale droite : +

        Barre latérale droite - Paramètres de l'en-tête ou du pied de page

        +
          +
        • Définissez la Position des numéros de page ainsi que la position par rapport à la partie supérieure et inférieure de la page.
        • +
        • Cochez la case Première page différente pour appliquer un numéro différent à la première page ou si vous ne voulez pas du tout ajouter le numéro.
        • +
        • Utilisez la case Pages paires et impaires différentes pour insérer des numéros de page différents pour les pages paires et impaires.
        • +
        • L'option Lier au précédent est disponible si vous avez déjà ajouté des sections dans votre document. Sinon, elle sera grisée. En outre, cette option est non disponible pour toute première section (c'est-à-dire quand un en-tête ou un pied qui appartient à la première section est choisi). Par défaut, cette case est cochée, de sorte que la numérotation unifiée est appliquée à toutes les sections. Si vous sélectionnez une zone d’en-tête ou de pied de page, vous verrez que сette zone est marquée par l'étiquette Same as Previous. Décochez la case Lier au précédent pour utiliser la numérotation des pages différente pour chaque section du document, par exemple, pour commencer la numérotation de chaque section à 1. L'étiquette Same as Previous sera indisponible.
        • +
        +

        Same as previous label

        +
      4. +
      +

      Pour retourner à l'édition du document, double-cliquez sur la zone de travail.

      +
      + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/LineSpacing.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/LineSpacing.htm index 0ada989d3..3b1f99083 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/LineSpacing.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/LineSpacing.htm @@ -9,22 +9,25 @@

      Régler l'interligne du paragraphe

      -

      En utilisant TeamLab Document Editor, vous pouvez définir la hauteur de la ligne pour les lignes de texte dans le paragraphe ainsi que les marges entre le paragraphe courant et précédent ou suivant.

      -

      Pour faire cela,

      +

      En utilisant Document Editor, vous pouvez définir la hauteur de la ligne pour les lignes de texte dans le paragraphe ainsi que les marges entre le paragraphe courant et précédent ou suivant.

      +

      Pour ce faire,

      1. placez le curseur dans le paragraphe nécessaire, ou sélectionnez plusieurs paragraphes avec la souris ou tout le texte dans le document en utilisant la combinaison de touches Ctrl+A,
      2. utilisez les champs correspondants de la barre latérale droite pour obtenir les résultats nécessaires :
          -
        • Interligne - réglez la hauteur de la ligne pour les lignes de texte dans le paragraphe. Vous pouvez choisir parmi trois options : Au moins (sert à régler l'interligne minimale qui est nécessaire pour adapter la plus grande police ou le graphique à la ligne), Multiple (sert à régler l'interligne exprimée en nombre supérieur à 1), Exactement (sert à définir l'interligne fixe). Spécifiez la valeur nécessaire dans le champ A.
        • -
        • Avant - réglez la taille de l'espace avant le paragraphe.
        • -
        • Après - réglez la taille de l'espace après le paragraphe.
        • -
        • N'ajoutez pas l'intervalle entre les paragraphes du même style - cochez cette case si vous n'avez pas besoin d'espace entre les paragraphes du même style. -

          Barre latérale droite - Paramètres du paragraphe

          -
        • -
        +
      3. Interligne - réglez la hauteur de la ligne pour les lignes de texte dans le paragraphe. Vous pouvez choisir parmi trois options : Au moins (sert à régler l'interligne minimale qui est nécessaire pour adapter la plus grande police ou le graphique à la ligne), Multiple (sert à régler l'interligne exprimée en nombre supérieur à 1), Exactement (sert à définir l'interligne fixe). Spécifiez la valeur nécessaire dans le champ situé à droite.
      4. +
      5. Espacement de paragraphe - définissez l'espace entre les paragraphes. +
          +
        • Avant - réglez la taille de l'espace avant le paragraphe.
        • +
        • Après - réglez la taille de l'espace après le paragraphe.
        • +
        • + N'ajoutez pas l'intervalle entre les paragraphes du même style - cochez cette case si vous n'avez pas besoin d'espace entre les paragraphes du même style. +

          Barre latérale droite - Paramètres du paragraphe

          +
        • +
      -

      Pour modifier rapidement l'interligne du paragraphe, vous pouvez cliquer sur l'icône de la barre d'outils supérieure Interligne du paragraphe Interligne du paragraphe et sélectionner la valeur nécessaire de la liste: 1.0, 1.15, 1.5, 2.0, 2.5, ou 3.0 lignes.

      +

      Pour modifier rapidement l'interligne du paragraphe, vous cliquez sur l'icône Interligne du paragraphe Interligne du paragraphe de la barre d'outils supérieure et sélectionnez la valeur nécessaire dans la liste: 1.0, 1.15, 1.5, 2.0, 2.5, ou 3.0 lignes.

      \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/NonprintingCharacters.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/NonprintingCharacters.htm index 7b315b0d9..ff2a2ebd1 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/NonprintingCharacters.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/NonprintingCharacters.htm @@ -16,7 +16,7 @@
    - + @@ -26,7 +26,7 @@ - + @@ -41,22 +41,32 @@ - + + + + + + + + + + + - - + + - + - +
    FormatsL'extension de nom de fichier pour les documents du traitement textuel créé avec Microsoft Word + ++
    DOCXRich Text Format
    Le format de fichier du document développé par Microsoft pour la multiplateforme d'échange des documents
    + ++
    TXT
    EPUB Electronic Publication
    Le format ebook standardisé, gratuit et ouvert créé par l'International Digital Publishing Forum
    +
    XPS
    Nom de la police Nom de la policeSert à sélectionner l'une des polices disponibles de la liste.Sert à sélectionner l'une des polices disponibles dans la liste.
    Taille de policeTaille de policeSert à sélectionner la taille de police parmi les valeurs disponibles dans la liste déroulante, ou à saisir manuellement dans le champ de la taille de police.Taille de la policeTaille de la policeSert à sélectionner la taille de la police parmi les valeurs disponibles dans la liste déroulante, ou entrer la valeur nécessaire dans le champ de la taille de police.
    Augmenter la taille de la policeAugmenter la taille de la policeSert à modifier la taille de la police en la randant plus grande à un point chaque fois que vous appuyez sur le bouton.
    Réduire la taille de la policeRéduire la taille de la policeSert à modifier la taille de la police en la randant plus petite à un point chaque fois que vous appuyez sur le bouton.
    Couleur de surlignage Couleur de surlignageSert à changer l'arrière-plan utilisé pour des phrases séparées, des expressions, des mots, ou même des caractères. Cliquez sur la flèche à côté de l'icône pour choisir la couleur. Pour supprimer la couleur de surlignage, sélectionnez l'option Pas de remplissage. Couleur de surlignage est différente de Couleur d'arrière-plan car ce dernier est appliqué au paragraphe entier.Sert à marquer les phrases, les mots ou les caractères séparés à l'aide de la bande couleur ajoutée qui imite l'effet du surligneur autour du texte. Vous pouvez sélectionner la partie nécessaire du texte et puis cliquer sur la flèche vers le bas située à côté de l'icône pour sélectionner une couleur dans la palette (cette couleur ne depend pas du Jeu de couleurs sélectionnée et inclut 16 couleurs) - cette couleur sera appliquée à la sélection de texte. Vous pouvez également choisir une couleur de surlignage et puis commencer à sélectionner le texte avec la souris - le pointeur de souris prendra l'apparence suivante Mouse pointer while highlighting et vous pourrez surligner de différentes parties de votre texte de manière séquentielle. To stop highlighting just click the icon once again. To clear the highlight color, choose the No Fill option. Highlight Color is different from the Background Color Paragraph Background Color Icon as the latter is applied to the whole paragraph and completely fills all the paragraph space from the left page margin to the right page margin.
    Couleur de police
    Espaces EspacesIl est inséré lorsque vous appuyez sur la Barre d'espacement sur le clavier. Il crée un espace entre les caractères.Il est inséré lorsque vous appuyez sur la Barre d'espacement sur le clavier. Il crée un espace entre les caractères.
    Tabulations
    Marques de paragraphe Retour chariot Il est inséré lorsque vous appuyez sur la touche Entrée et utilisé pour terminer un paragraphe et ajouter un peu d'espace après.Il est inséré lorsque vous appuyez sur la touche Entrée. Il est utilisé pour terminer un paragraphe et ajouter un peu d'espace après. Il contient des informations sur la mise en forme du paragraphe.
    Sauts de ligne
    Sauts de page Sauts de pageIl est inséré lorsque vous utilisez l'icône de la la barre d'outils supérieure Sauts de page Page Break, sélectionnez l'option Saut de page avant du menu contextuel ou dans la fenêtre Paramètres avancés.Il est inséré lorsque vous utilisez l'icône de la barre d'outils supérieure Insérer un saut de page ou de section Insérer un saut de page ou de section et sélectionnez l'option Insérer un saut de page ou sélectionnez l'option Saut de page avant du menu contextuel ou de la fenêtre des paramètres avancés.
    Sauts de sectionSection breakIl est inséré lorsque vous utilisez l'icône de la barre d'outils supérieure Insérer un saut de page ou de section Insérer un saut de page ou de section et sélectionnez une des options du sous-menu Insérer un saut de section (l’indicateur de saut de section diffère selon l'option choisie: Page suivante, Page continue, Page paire ou Page impaire).
    Sauts de colonneColumn breakIl est inséré lorsque vous utilisez l'icône de la barre d'outils supérieure Insérer un saut de page ou de section Insérer un saut de page ou de section et sélectionnez l'option Insérer un saut de colonne.
    Marques de fin de cellule des tableaux et Fin ligne tableauMarqueurs dans les tableauMarquers des tableaux Fin de cellule et Fin de ligneMarqueurs dans les tableaux Ces marqueurs contiennent des codes de mise en forme de la cellule individuelle et de la ligne respectivement.
    Petit carré noir dans la marge à gauche d'un paragraphePetit carré noir dans la marge gauche d'un paragraphe Carré noir Il indique qu'au moins une des options de paragraphe a été appliquée, par exemple Lignes solidaires, Saut de page avant.
    Symboles d'ancre Symbole d'ancreIls indiquent la position des objets, par exemple des images, des formes automatiques, des graphiques.Ils indiquent la position des objets flottants ( valables pour tout style d'habillage sauf le style En ligne), par exemple images, formes automatiques, graphiques. Vous devez sélectionner un objet pour faire son ancre visible.
    diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/OpenCreateNew.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/OpenCreateNew.htm index 130d915d0..0d9a624ef 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/OpenCreateNew.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/OpenCreateNew.htm @@ -9,17 +9,17 @@

    Créer un nouveau document ou ouvrir un document existant

    -

    Une fois que vous avez terminé de travailler sur un document, vous pouvez procéder immédiatement à un document déjà existant que vous avez récemment édité, créer un nouveau, ou retourner à la liste des documents existants.

    +

    Une fois que vous avez fini de travailler sur un document, vous pouvez immédiatement passer au document existant que vous avez récemment édité, créer un nouveau, ou revenir à la liste des documents existants.

    Pour créer un nouveau document,

    1. cliquez sur l'icône Fichier l'icône Fichier sur la barre latérale gauche,
    2. -
    3. sélectionnez l'option Créer nouveau....
    4. +
    5. sélectionnez l'option Nouveau document.
    -

    Pour ouvrir un document récemment édité à l'aide de TeamLab Document Editor,

    +

    Pour ouvrir un document récemment édité dans Document Editor,

    1. cliquez sur l'icône Fichier l'icône Fichier sur la barre latérale gauche,
    2. sélectionnez l'option Ouvrir récent...,
    3. -
    4. sélectionnez le document nécessaire de la liste des documents récemment édités.
    5. +
    6. sélectionnez le document nécessaire dans la liste des documents récemment édités.

    Pour revenir à la liste des documents existants, cliquez sur le lien Aller aux Documents situé dans le coin supérieur droit ou l'icône Fichier l'icône Fichier sur la barre latérale gauche et sélectionnez l'option Aller aux Documents.

    diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/PageBreaks.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/PageBreaks.htm index 7c3d32bac..6bc6ea8e1 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/PageBreaks.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/PageBreaks.htm @@ -9,22 +9,22 @@

    Insérer des sauts de page

    -

    En utilisant TeamLab Document Editor, vous pouvez ajouter le saut de page et régler les options de pagination.

    -

    Pour insérer un un saut de page à la position actuelle du curseur, cliquez sur l'icône de la barre d'outils supérieure Insérer un saut de page Insérer un saut de page

    -

    Pour insérer un un saut de page avant le paragraphe sélectionné c'est-à-dire pour commencer ce paragraphe du haut de la nouvelle page :

    +

    En utilisant Document Editor, vous pouvez ajouter le saut de page pour commencer une nouvelle page et régler les options de pagination.

    +

    Pour insérer un saut de page à la position actuelle du curseur, cliquez sur l'icône Insérer un saut de page Insérer un saut de page de la barre d'outils supérieure ou cliquez sur la flèche à côté de cette icône et sélectionnez l'option Insérer un saut de page dans le menu

    +

    Pour insérer un saut de page avant le paragraphe sélectionné c'est-à-dire pour commencer ce paragraphe en haut d'une nouvelle page :

    • cliquez avec le bouton droit de la souris et sélectionnez l'option Saut de page avant du menu contextuel, ou
    • cliquez avec le bouton droit de la souris, sélectionnez l'option Paramètres avancés du paragraphe du menu contextuel ou utilisez le lien Afficher les paramètres avancés sur la barre latérale droite et cochez la case Saut de page avant dans la fenêtre Paragraphe - Paramètres avancés ouverte.
    -

    Pour garder les lignes ensemble de sorte que seuleument des paragraphes entiers seront déplacés vers la nouvelle page (c'est-à-dire il n'y aura aucun saut de page entre les lignes dans un seul paragraphe),

    +

    Pour garder les lignes solidaires de sorte que seuleument des paragraphes entiers seront placés sur la nouvelle page (c'est-à-dire il n'y aura aucun saut de page entre les lignes dans un seul paragraphe),

    • cliquez avec le bouton droit de la souris et sélectionnez l'option Lignes solidaires du menu contextuel, ou
    • cliquez avec le bouton droit de la souris, sélectionnez l'option Paramètres avancés du paragraphe du menu contextuel ou utilisez le lien Afficher paramètres avancés sur la barre latérale droite et cochez la case Lignes solidaires dans la fenêtre Paragraphe - Paramètres avancés ouverte.
    -

    La fenêtre Paragraphe - Paramètres avancés vous permet de régler deux options de plus :

    +

    La fenêtre Paragraphe - Paramètres avancés vous permet de définir deux autres options de pagination :

      -
    • Paragraphes solidaires sert à empêcher de sauts de page d'apparaître entre le paragraphe sélectionné et celui qui le suit.
    • -
    • Éviter orphelines sert à empêcher des lignes (la première ou la dernière) d'un paragraphe d'apparaître seules dans le haut ou le bas d'une page.
    • +
    • Paragraphes solidaires sert à empêcher l’application du saut de page entre le paragraphe sélectionné et celui-ci qui le suit.
    • +
    • Éviter orphelines est sélectionné par défaut et sert à empêcher l’application d'une ligne (première ou dernière) d'un paragraphe en haut ou en bas d'une page.

    Paramètres du paragraphe avancés - Retraits et emplacement

    diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/ParagraphIndents.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/ParagraphIndents.htm index a8f8c564e..6cfcfe7ac 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/ParagraphIndents.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/ParagraphIndents.htm @@ -9,12 +9,12 @@

    Changer les retraits de paragraphe

    -

    En utilisant TeamLab Document Editor, vous pouvez changer le premier décalage de la ligne de la partie gauche de la page aussi bien que le décalage du paragraphe du côté gauche et du côté droit de la page.

    -

    Pour faire cela,

    +

    En utilisant Document Editor, vous pouvez changer le premier décalage de la ligne sur la partie gauche de la page aussi bien que le décalage du paragraphe du côté gauche et du côté droit de la page.

    +

    Pour ce faire,

    1. placez le curseur dans le paragraphe nécessaire, ou sélectionnez plusieurs paragraphes avec la souris ou tout le texte en utilisant la combinaison de touches Ctrl+A,
    2. cliquez sur le bouton droit de la souris et sélectionnez l'option Paramètres avancés du paragraphe du menu contextuel ou utilisez le lien Afficher les paramètres avancés sur la barre latérale droite,
    3. -
    4. dans la fenêtre ouverte Paragraphe - Paramètres avancés, définissez le retrait nécessaire pour la Première ligne et le décalage du paragraphe du côté gauche et droit de la page,
    5. +
    6. dans la fenêtre ouverte Paragraphe - Paramètres avancés, définissez le retrait nécessaire pour la Première ligne et le décalage du paragraphe du côté gauche et du côté droit de la page,
    7. cliquez sur le bouton OK .

      Paramètres du paragraphe avancés - Retraits et emplacement

    8. diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/SavePrintDownload.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/SavePrintDownload.htm index ac7e00151..1b1da2dd5 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/SavePrintDownload.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/SavePrintDownload.htm @@ -9,27 +9,29 @@
      -

      Enregistrer / télécharger / imprimer votre document

      -

      Par défaut, TeamLab Document Editor enregistre automatiquement votre fichier chaque 10 minutes quand vous travaillez visant à prévenir la perte des données au cas d'une fermeture inespérée de l'éditeur. Si nécessaire, vous pouvez facilement changer la périodicité de l'enregistrement automatique ou même désactiver cette fonction sur la page Paramètres avancés.

      +

      Enregistrer / télécharger / imprimer votre document

      +

      Par défaut, Document Editor enregistre automatiquement votre fichier chaque 10 minutes quand vous travaillez visant à prévenir la perte des données au cas d'une fermeture inespérée de l'éditeur. Si nécessaire, vous pouvez facilement changer la périodicité de l'enregistrement automatique ou même désactiver cette fonction sur la page Paramètres avancés.

      Pour enregistrer votre document actuel à la main,

      • cliquez sur l'icône Enregistrer l'icône Enregistrer sur la barre d'outils supérieure, ou
      • utilisez la combinaison des touches Ctrl+S, ou
      • cliquez sur l'icône Fichier l'icône Fichier sur la barre latérale gauche et sélectionnez l'option Enregistrer .
      -

      Pour télécharger le document résultant sur le disque dur de l'ordinateur,

      -
        -
      1. cliquez sur l'icône Fichier l'icône Fichier sur la barre latérale gauche,
      2. -
      3. sélectionnez l'option Télécharger comme ,
      4. -
      5. choisissez un des formats disponibles selon vos besoins: PDF, TXT, DOCX, DOC, ODT, RTF, HTML, EPUB.
      6. -
      -

      Pour imprimer le document actif,

      -
        -
      • cliquez sur l'icône Imprimer l'icône Imprimer sur la barre d'outils supérieure, ou
      • -
      • utilisez la combinaison des touches Ctrl+P , ou
      • -
      • cliquez sur l'icône Fichier l'icône Fichier sur la barre latérale gauche et sélectionnez l'option Imprimer .
      • -
      -

      Après cela, un fichier PDF sera généré sur la base du document édité. Vous pouvez l'ouvrir et l'imprimer, ou l'enregistrer sur votre disque dur ou sur un support amovible pour l'imprimer plus tard.

      -
      +
      +

      Pour télécharger le document résultant sur le disque dur de l'ordinateur,

      +
        +
      1. cliquez sur l'icône Fichier l'icône Fichier sur la barre latérale gauche,
      2. +
      3. sélectionnez l'option Télécharger comme ,
      4. +
      5. choisissez un des formats disponibles selon vos besoins: PDF, TXT, DOCX, DOC, ODT, RTF, HTML, EPUB.
      6. +
      +
      +

      Pour imprimer le document actif,

      +
        +
      • cliquez sur l'icône Imprimer l'icône Imprimer sur la barre d'outils supérieure, ou
      • +
      • utilisez la combinaison des touches Ctrl+P , ou
      • +
      • cliquez sur l'icône Fichier l'icône Fichier sur la barre latérale gauche et sélectionnez l'option Imprimer .
      • +
      +

      Après cela, un fichier PDF sera généré sur la base du document édité. Vous pouvez l'ouvrir et l'imprimer, ou l'enregistrer sur votre disque dur ou sur un support amovible pour l'imprimer plus tard.

      +
    \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/SectionBreaks.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/SectionBreaks.htm new file mode 100644 index 000000000..0db87c73e --- /dev/null +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/SectionBreaks.htm @@ -0,0 +1,33 @@ + + + + Insérer les sauts de section + + + + + + +
    +

    Insérer les sauts de section

    +

    Les sauts de section vous permettent d'appliquer des mises en page et mises en formes différentes pour de certaines parties de votre document. Par exemple, vous pouvez utiliser des en-têtes et pieds de page, des numérotations des pages, des marges, la taille, l'orientation, ou le numéro de colonne individuels pour chaque section séparée.

    +

    Remarque : un saut de section inséré définit la mise en page de la partie précédente du document.

    +

    Pour insérer un saut de section à la position actuelle du curseur :

    +
      +
    1. cliquez sur l'icône Insérer un saut de page ou de section Insérer un saut de page ou de section de la barre d'outils supérieure
    2. +
    3. sélectionnez l'option Insérer un saut de section
    4. +
    5. sélectionnez le type du saut de section nécessaire: +
        +
      • Page suivante - pour commencer une nouvelle section sur la page suivante
      • +
      • Page continue - pour commencer une nouvelle section sur la page actuelle
      • +
      • Page paire - pour commencer une nouvelle section sur la page suivante paire
      • +
      • Page impaire - pour commencer une nouvelle section sur la page suivante impaire
      • +
      +
    6. +
    +

    Des sauts d'une section ajoutés sont indiqués dans votre document par un double trait pointillé: Section break

    +

    Si vous ne voyez pas de sauts de section insérés, cliquez sur l'icône Icône de caractères non imprimables de la barre d'outils supérieure pour les afficher.

    +

    Pour supprimer un saut de section, sélectionnez-le avec le souris et appuyez sur la touche Supprimer. Lorsque vous supprimez un saut de section, la mise en forme de cette section sera également supprimée, car un saut de section définit la mise en forme de la section précédente. La partie du document qui précède le saut de section supprimé acquiert la mise en forme de la partie qui la suive.

    +
    + + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/SetPageParameters.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/SetPageParameters.htm index 762cc1b9c..144a7da66 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/SetPageParameters.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/SetPageParameters.htm @@ -1,35 +1,56 @@  - - Régler les paramètres de page - - - - - -
    -

    Régler les paramètres de page

    -

    Pour régler l'orientation de page et sa taille, utilisez les icônes correspondantes de la barre d'outils supérieure :

    -
      -
    • Orientation de page - modifiez le type d'orientation actuel en cliquant sur l'icône Orientation de page l'icône Orientation de page . Le type d'orientation par défaut est Portrait qui peut être commuté sur Album.
    • -
    • Taille de la page - changez le format A4 par défaut en cliquant sur l'icône Taille de la page l'icône Taille de la page et sélectionnez le format nécessaire de la liste. Les formats offerts sont les suivants : -
        -
      • US Letter (21,59cm x 27,94cm)
      • -
      • US Legal (21,59cm x 35,56cm)
      • -
      • A4 (21cm x 29,7cm)
      • -
      • A5 (14,81cm x 20,99cm)
      • -
      • B5 (17,6cm x 25,01cm)
      • -
      • Envelope #10 (10,48cm x 24,13cm)
      • -
      • Envelope DL (11,01cm x 22,01cm)
      • -
      • Tabloid (27,94cm x 43,17cm)
      • -
      • AЗ (29,7cm x 42,01cm)
      • -
      • Tabloid Oversize (30,48cm x 45,71cm)
      • -
      • ROC 16K (19,68cm x 27,3cm)
      • -
      • Envelope Choukei 3 (11,99cm x 23,49cm)
      • -
      • Super B/A3 (33,02cm x 48,25cm)
      • -
      -
    • -
    -
    - + + Régler les paramètres de page + + + + + + +
    +

    Régler les paramètres de page

    +

    Pour modifier la mise en page, c-à-d définir l'orientation et la taille de la page, ajuster les marges et insérer des colonnes, utilisez les icônes correspondantes de la barre d'outils supérieure.

    +

    Remarque: tous ces paramètres sont appliqués au document entier. Si vous voulez définir de différentes marges de page, l'orientation, la taille, ou le numéro de colonne pour les parties différentes de votre document, consultez cette page.

    +

    Orientation de page

    +

    Changez l'orientation de page actuelle en cliquant sur l'icône Orientation de page icône Orientation de page. Le type d'orientation par défaut est Portrait qui peut être commuté sur Album.

    +

    Taille de la page

    +

    Changez le format A4 par défaut en cliquant sur l'icône Taille de la page icône Taille de la page et sélectionnez la taille nécessaire dans la liste. Les formats offerts sont les suivants :

    +
      +
    • US Letter (21,59cm x 27,94cm)
    • +
    • US Legal (21,59cm x 35,56cm)
    • +
    • A4 (21cm x 29,7cm)
    • +
    • A5 (14,81cm x 20,99cm)
    • +
    • B5 (17,6cm x 25,01cm)
    • +
    • Envelope #10 (10,48cm x 24,13cm)
    • +
    • Envelope DL (11,01cm x 22,01cm)
    • +
    • Tabloid (27,94cm x 43,17cm)
    • +
    • AЗ (29,7cm x 42,01cm)
    • +
    • Tabloid Oversize (30,48cm x 45,71cm)
    • +
    • ROC 16K (19,68cm x 27,3cm)
    • +
    • Envelope Choukei 3 (11,99cm x 23,49cm)
    • +
    • Super B/A3 (33,02cm x 48,25cm)
    • +
    +

    Vous pouvez définir une taille de la page particulière en utilisant l'option Taille personnalisée dans la liste. Dans la fenêtre Taille de la page vous pouvez définir les valeurs nécessaires Largeur et Hauteur. Entrez vos nouvelles valeurs dans les champs d'entrées ou ajustez les valeurs existantes en utilisant les boutons de direction. Lorsque tout est prêt, cliquez sur OK pour appliquer les changements.

    +

    Custom Page Size

    +

    Marges de la page

    +

    Modifiez les marges par défaut, c-à-d l’espace entre les bords de la page et le texte du paragraphe, en cliquant sur l'icône Page Margins icône Marges de page et sélectionnez un des paramètres prédéfinis : Normal, US Normal, Étroit, Modérer, Large. Vous pouvez aussi utiliser l'option Marges personnalisées pour définir les valeurs nécessaires dans la fenêtre Marges qui s'ouvre. Entrez les valeurs des marges Haut, Bas, Gauche et Droite de la page dans les champs d'entrées ou ajustez les valeurs existantes en utilisant les boutons de direction. Lorsque tout est prêt, cliquez sur OK. Les marges personnalisées seront appliquées au document actuel et l'option Dernière mesure avec les paramètres spécifiés apparaît dans la liste des Marges de la page icône Marges de page pour que vous puissiez les appliquer à d'autres documents.

    +

    Marges personnalisées

    +

    Vous pouvez également modifier les marges manuellement en faisant glisser la bordure entre les zones grises et blanches sur les règles (les zones grises des règles indiquent les marges de page):

    +

    Ajustement des marges

    +

    Colonnes

    +

    Pour appliquez une disposition multicolonne, cliquez sur l'icône Insérer des colonnes icône Insérer des colonnes et sélectionnez le type de la colonne nécessaire dans la liste déroulante. Les options suivantes sont disponibles :

    +
      +
    • Deux icône deux colonnes - pour ajouter deux colonnes de la même largeur,
    • +
    • Trois icône trois colonnes - pour ajouter trois colonnes de la même largeur,
    • +
    • A gauche icône colonne gauche - pour ajouter deux colonnes: une étroite colonne à gauche et une large colonne à droite,
    • +
    • A droite icône colonne droite - pour ajouter deux colonnes: une étroite colonne à droite et une large colonne à gauche.
    • +
    +

    Pour spécifier exactement la position d'une nouvelle colonne, placez le curseur avant le texte à déplacer dans une nouvelle colonnen, cliquez sur l'icône Insérer un saut de page ou de section Insérer un saut de page ou de section de la barre d'outils supérieure et sélectionnez l'option Insérer un saut de colonne. Le texte sera déplacé vers la colonne suivante.

    +

    Les sauts de colonne ajoutés sont indiqués dans votre document par une ligne pointillée: Saut de colonne. Si les sauts de colonnes insérés ne sont pas visibles, cliquez sur l'icône icône Caractères non imprimables icon de la barre d'outils supérieure pour les afficher. Pour supprimer un saut de colonne,sélectionnez-le avec le souris et appuyez sur une touche Supprimer.

    +

    Pour modifier manuellement la largeur et l'espacement entre les colonnes, vous pouvez utiliser la règle horizontale.

    +

    Espacemment entre les colonnes

    +

    Pour annuler les colonnes et revenir à la disposition en une seule colonne, cliquez sur l'icône Insérer des colonnes icône Insérer des colonnes de la barre d'outils supérieure et sélectionnez l'option Une icône Une colonne dans la liste.

    +
    + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/fr/UsageInstructions/SetTabStops.htm b/apps/documenteditor/main/resources/help/fr/UsageInstructions/SetTabStops.htm index 4321139c8..33c91ac86 100644 --- a/apps/documenteditor/main/resources/help/fr/UsageInstructions/SetTabStops.htm +++ b/apps/documenteditor/main/resources/help/fr/UsageInstructions/SetTabStops.htm @@ -9,10 +9,10 @@

    Définir des taquets de tabulation

    -

    TeamLab Document Editor vous permet de changer des taquets de tabulation c'est-à-dire l'emplacement où le curseur s'arrête quand vous appuyez sur la touche Tab du clavier.

    +

    Document Editor vous permet de changer des taquets de tabulation c'est-à-dire l'emplacement où le curseur s'arrête quand vous appuyez sur la touche Tab du clavier.

    Pour définir les taquets de tabulation vous pouvez utiliser la règle horizontale :

      -
    1. Sélectionnez le type du taquet de tabulation en cliquant sur le bouton Taquet de tabulation de droite dans le coin supérieur gauche de la zone de travail. Trois types sont disponibles : +
    2. Sélectionnez le type du taquet de tabulation en cliquant sur le bouton Taquet de tabulation de droite dans le coin supérieur gauche de la zone de travail. Trois types de taquets de tabulationsont disponibles :
      • De gauche Taquet de tabulation de gauche sert à aligner le texte sur le côté gauche du taquet de tabulation; le texte se déplace à droite du taquet de tabulation quand vous saisissez le texte. Le taquet de tabulation sera indiqué sur la règle horizontale par le marqueur Marqueur du taquet de tabulation de gauche.
      • Du centre Taquet de tabulation du centre sert à centrer le texte à l'emplacement du taquet de tabulation. Le taquet de tabulation sera indiqué sur la règle horizontale par le marqueur Marqueur du taquet de tabulation du centre.
      • @@ -24,7 +24,7 @@

    -

    Vous pouvez également utiliser la fenêtre des paramètres avancés du paragraphe pour régler les taquets de tabulation. Cliquez droit, sélectionnez l'option Paramètres avancés du paragraphe du menu ou utilisez le lien Afficher les paramètres avancés sur la barre latérale droite, et passez à l'onglet Tabulation de la fenêtre Paragraphe - Paramètres avancés.

    +

    Vous pouvez également utiliser la fenêtre des paramètres avancés du paragraphe pour régler les taquets de tabulation. Cliquez avec le bouton droit de la souris, sélectionnez l'option Paramètres avancés du paragraphe du menu ou utilisez le lien Afficher les paramètres avancés sur la barre latérale droite, et passez à l'onglet Tabulation de la fenêtre Paragraphe - Paramètres avancés.

    Paramètres du paragraphe - onglet Tabulation

    Vous y pouvez définir les paramètres suivants :

    +
    +

    Совместное редактирование

    +

    В редакторе документов можно выбрать один из двух доступных режимов совместного редактирования. Быстрый используется по умолчанию, в нем изменения, вносимые другими пользователями, отображаются в реальном времени. Строгий режим позволяет скрывать изменения, внесенные другими пользователями, до тех пор, пока вы не нажмете значок Сохранить Значок Сохранить, чтобы сохранить ваши изменения и принять изменения, внесенные другими. Режим можно выбрать в Дополнительных настройках.

    +

    Когда документ редактируют одновременно несколько пользователей в Строгом режиме, редактируемые фрагменты текста помечаются пунктирными линиями разных цветов. При наведении курсора мыши на один из редактируемых фрагментов отображается имя того пользователя, который в данный момент его редактирует. В Быстром режиме действия и имена участников совместного редактирования отображаются непосредственно в процессе редактирования текста.

    +

    + Количество пользователей, которые в данный момент работают над текущим документом, отображается в левом нижнем углу в строке состояния - Значок Количество пользователей. + Чтобы увидеть, кто именно редактирует файл в настоящий момент, можно открыть панель Чата с полным списком пользователей. +

    +

    Если файл не просматривают или не редактируют другие пользователи, значок в строке состояния будет выглядеть следующим образом: Значок Управление правами доступа к документу, с его помощью можно непосредственно из документа управлять пользователями, имеющими доступ к файлу: приглашать новых пользователей, предоставляя им полный доступ или доступ только для чтения, или запрещать доступ к файлу для некоторых пользователей. Нажмите на этот значок для управления доступом к файлу; это можно сделать и в отсутствие других пользователей, которые просматривают или совместно редактируют документ в настоящий момент, и при наличии других пользователей, когда значок выглядит так: Значок Количество пользователей.

    +

    Как только один из пользователей сохранит свои изменения, нажав на значок Значок Сохранить, все остальные увидят в строке состояния примечание, которое сообщает о наличии обновлений. Чтобы сохранить внесенные вами изменения и сделать их доступными для других пользователей, а также получить обновления, сохраненные другими пользователями, нажмите на значок Значок Сохранить и получить обновления в левом верхнем углу верхней панели инструментов. Обновления будут подсвечены, чтобы Вы могли проверить, что конкретно изменилось.

    +

    Можно указать, какие изменения требуется подсвечивать во время совместного редактирования: для этого нажмите на значок Значок Файл на левой боковой панели, выберите опцию Дополнительные параметры..., а затем укажите, отображать ли все или последние изменения, внесенные при совместной работе. При выборе опции Все будут подсвечиваться все изменения, внесенные за время текущей сессии. При выборе опции Последние будут подсвечиваться только те изменения, которые были внесены с момента, когда Вы последний раз нажимали на значок Значок Сохранить и получить изменения. При выборе опции Никакие изменения, внесенные во время текущей сессии, подсвечиваться не будут.

    +

    Чат*

    +

    Этот инструмент можно использовать для оперативного согласования процесса совместного редактирования, например, для того, чтобы договориться с другими участниками, кто и что должен делать, какой абзац вы собираетесь сейчас отредактировать и т.д.

    +

    Сообщения в чате хранятся только в течение одной сессии. Для обсуждения содержания документа лучше использовать комментарии, которые хранятся до тех пор, пока вы не решите их удалить.

    +

    Чтобы войти в чат и оставить сообщение для других пользователей:

    +
      +
    1. нажмите на значок Значок Чат на левой боковой панели,
    2. +
    3. введите текст в соответствующем поле ниже,
    4. +
    5. нажмите кнопку Отправить.
    6. +
    +

    Все сообщения, оставленные пользователями, будут отображаться на панели слева. Если есть новые сообщения, которые Вы еще не прочитали, значок чата будет выглядеть так - Значок Чат.

    +

    Чтобы закрыть панель с сообщениями чата, нажмите на значок Значок Чат еще раз.

    +
    +

    Комментарии*

    +

    Чтобы оставить комментарий:

    +
      +
    1. выделите фрагмент текста, в котором, по Вашему мнению, содержится какая-то ошибка или проблема,
    2. +
    3. + используйте значок Значок Комментарии на левой боковой панели, чтобы открыть панель Комментарии, и нажмите на ссылку Добавить комментарий к документу или
      + щелкните правой кнопкой мыши по выделенному фрагменту текста и выберите в меню команду Добавить комментарий, +
    4. +
    5. введите нужный текст,
    6. +
    7. нажмите кнопку Добавить.
    8. +
    +

    Комментарий появится на панели слева. Любой другой пользователь может ответить на добавленный комментарий, чтобы дать ответ на вопросы или отчитаться о проделанной работе. Для этого надо нажать на ссылку Добавить ответ, расположенную под комментарием.

    +

    Фрагмент текста, который Вы прокомментировали, будет подсвечен в документе. Для просмотра комментария щелкните по этому фрагменту. Если требуется отключить эту функцию, нажмите на значок Значок Файл, выберите опцию Дополнительные параметры... и снимите флажок Включить опцию комментирования в реальном времени. В этом случае прокомментированные фрагменты будут подсвечиваться, только когда Вы нажмете на значок Значок Комментарии.

    +

    Вы можете управлять добавленными комментариями следующим образом:

    +
      +
    • отредактировать их, нажав значок Значок Редактировать,
    • +
    • удалить их, нажав значок Значок Удалить,
    • +
    • закрыть обсуждение, нажав на значок Значок Решить, если задача или проблема, обозначенная в комментарии, решена; после этого обсуждение, которое Вы открыли своим комментарием, приобретет статус решенного. Чтобы вновь его открыть, нажмите на значок Значок Открыть снова.
    • +
    +

    Новые комментарии, добавленные другими пользователями, станут видимыми только после того, как Вы нажмете на значок Значок Сохранить и получить обновления в левом верхнем углу верхней панели инструментов.

    +

    Чтобы закрыть панель с комментариями, нажмите на значок Значок Комментарии еще раз.

    +

    *доступно только для платных версий

    +
    \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm b/apps/documenteditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm index d01eb7c35..b3d758b4e 100644 --- a/apps/documenteditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm +++ b/apps/documenteditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm @@ -25,17 +25,17 @@ Открыть панель Поиск, чтобы начать поиск символа/слова/фразы в редактируемом документе. - Открыть панель 'Комментарии'* + Открыть панель 'Комментарии'* Ctrl+Shift+H Открыть панель Комментарии, чтобы добавить свой комментарий или ответить на комментарии других пользователей. - Открыть поле комментария* + Открыть поле комментария* Alt+H Открыть поле ввода данных, в котором можно добавить текст комментария. - - Открыть панель 'Чат'* + + Открыть панель 'Чат'* Alt+Q Открыть панель Чат и отправить сообщение. @@ -345,7 +345,7 @@ Удерживайте клавишу Ctrl и используйте стрелки на клавиатуре, чтобы перемещать выбранный объект на три пикселя за раз. -

    * - доступно только для платных версий

    +

    * - доступно только для платных версий

    \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/ru/UsageInstructions/SavePrintDownload.htm b/apps/documenteditor/main/resources/help/ru/UsageInstructions/SavePrintDownload.htm index ad0b72220..5f58149a5 100644 --- a/apps/documenteditor/main/resources/help/ru/UsageInstructions/SavePrintDownload.htm +++ b/apps/documenteditor/main/resources/help/ru/UsageInstructions/SavePrintDownload.htm @@ -9,7 +9,7 @@
    -

    Сохранение / загрузка / печать документа

    +

    Сохранение / загрузка / печать документа

    По умолчанию онлайн-редактор документов автоматически сохраняет файл каждые 2 секунды, когда вы работаете над ним, чтобы не допустить потери данных в случае непредвиденного закрытия программы. Если вы совместно редактируете файл в Быстром режиме, таймер запрашивает наличие изменений 25 раз в секунду и сохраняет их, если они были внесены. При совместном редактировании файла в Строгом режиме изменения автоматически сохраняются каждые 10 минут. При необходимости можно легко выбрать предпочтительный режим совместного редактирования или отключить функцию автоматического сохранения на странице Дополнительные параметры.

    Чтобы сохранить текущий документ вручную,

      @@ -17,19 +17,21 @@
    • используйте сочетание клавиш Ctrl+S, или
    • нажмите значок Файл Значок Файл на левой боковой панели и выберите опцию Сохранить.
    -

    Чтобы скачать готовый документ и сохранить его на жестком диске компьютера,

    -
      -
    1. нажмите значок Файл Значок Файл на левой боковой панели,
    2. -
    3. выберите опцию Скачать как...,
    4. -
    5. выберите один из доступных форматов в зависимости от того, что вам нужно: PDF, TXT, DOCX, ODT, HTML.
    6. -
    -

    Чтобы распечатать текущий документ,

    -
      -
    • нажмите значок Печать Значок Печать на верхней панели инструментов, или
    • -
    • используйте сочетание клавиш Ctrl+P, или
    • -
    • нажмите значок Файл Значок Файл на левой боковой панели и выберите опцию Печать.
    • -
    -

    После этого на основе данного документа будет сгенерирован файл PDF. Вы можете открыть и распечатать его, или сохранить его на жестком диске компьютера или съемном носителе чтобы распечатать позже.

    -
    +
    +

    Чтобы скачать готовый документ и сохранить его на жестком диске компьютера,

    +
      +
    1. нажмите значок Файл Значок Файл на левой боковой панели,
    2. +
    3. выберите опцию Скачать как...,
    4. +
    5. выберите один из доступных форматов в зависимости от того, что вам нужно: PDF, TXT, DOCX, ODT, HTML.
    6. +
    +
    +

    Чтобы распечатать текущий документ,

    +
      +
    • нажмите значок Печать Значок Печать на верхней панели инструментов, или
    • +
    • используйте сочетание клавиш Ctrl+P, или
    • +
    • нажмите значок Файл Значок Файл на левой боковой панели и выберите опцию Печать.
    • +
    +

    После этого на основе данного документа будет сгенерирован файл PDF. Вы можете открыть и распечатать его, или сохранить его на жестком диске компьютера или съемном носителе чтобы распечатать позже.

    + \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/ru/UsageInstructions/ViewDocInfo.htm b/apps/documenteditor/main/resources/help/ru/UsageInstructions/ViewDocInfo.htm index c35e860c5..17cba6f2c 100644 --- a/apps/documenteditor/main/resources/help/ru/UsageInstructions/ViewDocInfo.htm +++ b/apps/documenteditor/main/resources/help/ru/UsageInstructions/ViewDocInfo.htm @@ -12,14 +12,15 @@

    Чтобы получить доступ к подробным сведениям о редактируемом документе, нажмите значок Файл Значок Файл на левой боковой панели и выберите опцию Сведения о документе....

    Общие сведения

    Сведения о документе включают название документа, автора, размещение, дату создания, а также статистику: количество страниц, абзацев, слов, символов, символов с пробелами.

    -

    Сведения о правах доступа

    -

    Примечание: эта опция недоступна для пользователей с правами доступа Только чтение.

    -

    Чтобы узнать, у кого есть права на просмотр и редактирование этого документа, выберите опцию Права доступа... на левой боковой панели.

    -

    Вы можете также изменить выбранные в настоящий момент права доступа, нажав на кнопку Изменить права доступа в разделе Люди, имеющие права.

    -

    Журнал версий

    -

    Примечание: эта опция недоступна для бесплатных аккаунтов, а также для пользователей с правами доступа Только чтение.

    -

    Чтобы просмотреть все внесенные в документ изменения, выберите опцию Журнал версий на левой боковой панели. Вы увидите список версий (существенных изменений) и ревизий (незначительных изменений) этого документа с указанием автора и даты и времени создания каждой версии/ревизии. Для версий документа также указан номер версии (например, вер. 2). Чтобы точно знать, какие изменения были внесены в каждой конкретной версии/ревизии, можно просмотреть нужную, нажав на нее на левой боковой панели. Изменения, внесенные автором версии/ревизии, помечены цветом, который показан рядом с именем автора на левой боковой панели. Чтобы вернуться к текущей версии документа, нажмите на ссылку Вернуться к документу над списком версий.

    -

    Чтобы закрыть панель Файл и вернуться к редактированию документа, выберите опцию Вернуться к документу.

    - +
    +

    Сведения о правах доступа

    +

    Примечание: эта опция недоступна для пользователей с правами доступа Только чтение.

    +

    Чтобы узнать, у кого есть права на просмотр и редактирование этого документа, выберите опцию Права доступа... на левой боковой панели.

    +

    Вы можете также изменить выбранные в настоящий момент права доступа, нажав на кнопку Изменить права доступа в разделе Люди, имеющие права.

    +

    Журнал версий

    +

    Примечание: эта опция недоступна для бесплатных аккаунтов, а также для пользователей с правами доступа Только чтение.

    +

    Чтобы просмотреть все внесенные в документ изменения, выберите опцию Журнал версий на левой боковой панели. Вы увидите список версий (существенных изменений) и ревизий (незначительных изменений) этого документа с указанием автора и даты и времени создания каждой версии/ревизии. Для версий документа также указан номер версии (например, вер. 2). Чтобы точно знать, какие изменения были внесены в каждой конкретной версии/ревизии, можно просмотреть нужную, нажав на нее на левой боковой панели. Изменения, внесенные автором версии/ревизии, помечены цветом, который показан рядом с именем автора на левой боковой панели. Чтобы вернуться к текущей версии документа, нажмите на ссылку Вернуться к документу над списком версий.

    +

    Чтобы закрыть панель Файл и вернуться к редактированию документа, выберите опцию Вернуться к документу.

    +
    \ No newline at end of file diff --git a/apps/documenteditor/main/resources/help/ru/images/formattingpresets.png b/apps/documenteditor/main/resources/help/ru/images/formattingpresets.png index be198c35a6bb35edcad9f94b13453d724d932548..60c1ac73a15bbbfe54464569101362a6e15c7780 100644 GIT binary patch literal 34699 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 F`yc((HBA5j diff --git a/apps/presentationeditor/main/resources/help/en/UsageInstructions/SavePrintDownload.htm b/apps/presentationeditor/main/resources/help/en/UsageInstructions/SavePrintDownload.htm index 9e46dd35c..3c95f4964 100644 --- a/apps/presentationeditor/main/resources/help/en/UsageInstructions/SavePrintDownload.htm +++ b/apps/presentationeditor/main/resources/help/en/UsageInstructions/SavePrintDownload.htm @@ -8,28 +8,30 @@ -
    -

    Save/print/download your presentation

    -

    By default, Рresentation Editor automatically saves your file each 2 seconds when you work on it preventing your data loss in case of the unexpected program closing. If you co-edit the file in the Fast mode, the timer requests for updates 25 times a second and saves the changes if they have been made. When the file is being co-edited in the Strict mode, changes are automatically saved at 10-minute intervals. If you need, you can easily select the preferred co-editing mode or disable the Autosave feature on the Advanced Settings page.

    -

    To save your current presentation manually,

    -
      -
    • press the Save Save icon icon at the top toolbar, or
    • -
    • use the Ctrl+S key combination, or
    • -
    • click the File File icon icon at the left sidebar and select the Save option.
    • -
    -

    To print out the current presentation,

    -
      -
    • click the Print Print icon icon at the top toolbar, or
    • -
    • use the Ctrl+P key combination, or
    • -
    • click the File File icon icon at the left sidebar and select the Print option.
    • -
    -

    After that a PDF file will be generated on the basis of the presentation. You can open and print it out, or save onto your computer hard disk drive or removable medium to print it out later.

    -

    To download the resulting presentation onto your computer hard disk drive,

    -
      -
    1. click the File File icon icon at the left sidebar,
    2. -
    3. select the Download as option,
    4. -
    5. choose one of the available formats depending on your needs: PDF, PPTX.
    6. -
    -
    +
    +

    Save/print/download your presentation

    +

    By default, Рresentation Editor automatically saves your file each 2 seconds when you work on it preventing your data loss in case of the unexpected program closing. If you co-edit the file in the Fast mode, the timer requests for updates 25 times a second and saves the changes if they have been made. When the file is being co-edited in the Strict mode, changes are automatically saved at 10-minute intervals. If you need, you can easily select the preferred co-editing mode or disable the Autosave feature on the Advanced Settings page.

    +

    To save your current presentation manually,

    +
      +
    • press the Save Save icon icon at the top toolbar, or
    • +
    • use the Ctrl+S key combination, or
    • +
    • click the File File icon icon at the left sidebar and select the Save option.
    • +
    +

    To print out the current presentation,

    +
      +
    • click the Print Print icon icon at the top toolbar, or
    • +
    • use the Ctrl+P key combination, or
    • +
    • click the File File icon icon at the left sidebar and select the Print option.
    • +
    +
    +

    After that a PDF file will be generated on the basis of the presentation. You can open and print it out, or save onto your computer hard disk drive or removable medium to print it out later.

    +

    To download the resulting presentation onto your computer hard disk drive,

    +
      +
    1. click the File File icon icon at the left sidebar,
    2. +
    3. select the Download as option,
    4. +
    5. choose one of the available formats depending on your needs: PDF, PPTX.
    6. +
    +
    +
    \ No newline at end of file diff --git a/apps/presentationeditor/main/resources/help/en/UsageInstructions/ViewPresentationInfo.htm b/apps/presentationeditor/main/resources/help/en/UsageInstructions/ViewPresentationInfo.htm index 52583729a..2c91fb27e 100644 --- a/apps/presentationeditor/main/resources/help/en/UsageInstructions/ViewPresentationInfo.htm +++ b/apps/presentationeditor/main/resources/help/en/UsageInstructions/ViewPresentationInfo.htm @@ -12,10 +12,12 @@

    To access the detailed information about the currently edited presentation, click the File File icon icon at the left sidebar and select the Presentation Info option.

    General Information

    The presentation information includes presentation title, author, location and creation date.

    +

    Permission Information

    Note: this option is not available for users with the Read Only permissions.

    To find out, who have rights to view or edit the presentation, select the Access Rights... option at the left sidebar.

    You can also change currently selected access rights by pressing the Change access rights button in the Persons who have rights section.

    +

    To close the File pane and return to presentation editing, select the Return to Presentation option.

    diff --git a/apps/presentationeditor/main/resources/help/ru/HelpfulHints/About.htm b/apps/presentationeditor/main/resources/help/ru/HelpfulHints/About.htm index 36b512d39..ef55f0448 100644 --- a/apps/presentationeditor/main/resources/help/ru/HelpfulHints/About.htm +++ b/apps/presentationeditor/main/resources/help/ru/HelpfulHints/About.htm @@ -9,7 +9,7 @@

    О редакторе презентаций

    -

    Онлайн-редактор презентаций - это онлайн-приложение, которое позволяет просматривать и редактировать презентации непосредственно в браузере.

    +

    Онлайн-редактор презентаций - это онлайн-приложение, которое позволяет просматривать и редактировать презентации непосредственно в браузере.

    Используя онлайн-редактор презентаций, можно выполнять различные операции редактирования, как в любом десктопном редакторе, распечатывать отредактированные презентации, сохраняя все детали форматирования, или сохранять их на жесткий диск компьютера как файлы в формате PDF или PPTX.

    diff --git a/apps/presentationeditor/main/resources/help/ru/HelpfulHints/CollaborativeEditing.htm b/apps/presentationeditor/main/resources/help/ru/HelpfulHints/CollaborativeEditing.htm index 92d9e79fe..254c50273 100644 --- a/apps/presentationeditor/main/resources/help/ru/HelpfulHints/CollaborativeEditing.htm +++ b/apps/presentationeditor/main/resources/help/ru/HelpfulHints/CollaborativeEditing.htm @@ -12,50 +12,53 @@

    Совместное редактирование презентаций

    В онлайн-редакторе презентаций вы можете работать над презентацией совместно с другими пользователями. Эта возможность включает в себя следующее:

      -
    • одновременный многопользовательский доступ к редактируемой презентации
    • -
    • визуальная индикация объектов, которые редактируются другими пользователями
    • -
    • синхронизация изменений одним нажатием кнопки
    • -
    • чат для обмена идеями по поводу отдельных частей презентации
    • +
    • одновременный многопользовательский доступ к редактируемой презентации
    • +
    • визуальная индикация объектов, которые редактируются другими пользователями
    • +
    • синхронизация изменений одним нажатием кнопки
    • +
    • чат для обмена идеями по поводу отдельных частей презентации
    • комментарии, содержащие описание задачи или проблемы, которую необходимо решить
    -

    Совместное редактирование

    -

    В редакторе презентаций можно выбрать один из двух доступных режимов совместного редактирования. Быстрый используется по умолчанию, в нем изменения, вносимые другими пользователями, отображаются в реальном времени. Строгий режим позволяет скрывать изменения, внесенные другими пользователями, до тех пор, пока вы не нажмете значок Сохранить Значок Сохранить, чтобы сохранить ваши изменения и принять изменения, внесенные другими. Режим можно выбрать в Дополнительных настройках.

    -

    Когда презентацию редактируют одновременно несколько пользователей в Строгом режиме, редактируемые объекты (автофигуры, текстовые объекты, таблицы, изображения, диаграммы) помечаются пунктирными линиями разных цветов. Объект, который редактируете Вы, окружен зеленой пунктирной линией. Красные пунктирные линии означают, что объекты редактируются другими пользователями. При наведении курсора мыши на один из редактируемых объектов отображается имя того пользователя, который в данный момент его редактирует. В Быстром режиме действия и имена участников совместного редактирования отображаются непосредственно в процессе редактирования.

    -

    Количество пользователей, которые в данный момент работают над текущей презентацией, отображается в левом нижнем углу в строке состояния - Значок Количество пользователей. Чтобы увидеть, кто именно редактирует файл в настоящий момент, откройте панель Чата с полным списком пользователей.

    -

    Если файл не просматривают или не редактируют другие пользователи, значок в строке состояния будет выглядеть следующим образом: Значок Управление правами доступа к документу, с его помощью можно непосредственно из документа управлять пользователями, имеющими доступ к файлу: приглашать новых пользователей, предоставляя им полный доступ или доступ только для чтения, или запрещать доступ к файлу для некоторых пользователей. Нажмите на этот значок для управления доступом к файлу; это можно сделать и в отсутствие других пользователей, которые просматривают или совместно редактируют документ в настоящий момент, и при наличии других пользователей, когда значок выглядит так: Значок Количество пользователей.

    -

    Как только один из пользователей сохранит свои изменения, нажав на значок Значок Сохранить, все остальные увидят в строке состояния примечание, которое сообщает о наличии обновлений. Чтобы сохранить внесенные вами изменения и сделать их доступными для других пользователей, а также получить обновления, сохраненные другими пользователями, нажмите на значок Значок Сохранить и получить обновления в левом верхнем углу верхней панели инструментов. Обновления будут подсвечены, чтобы Вы могли проверить, что конкретно изменилось.

    -

    Чат*

    -

    Этот инструмент можно использовать для оперативного согласования процесса совместного редактирования, например, для того, чтобы договориться с другими участниками, кто и что должен делать, какой абзац вы собираетесь сейчас отредактировать и т.д.

    -

    Сообщения в чате хранятся только в течение одной сессии. Для обсуждения содержания документа лучше использовать комментарии, которые хранятся до тех пор, пока вы не решите их удалить.

    -

    Чтобы войти в чат и оставить сообщение для других пользователей:

    -
      -
    1. нажмите на значок Значок Чат на левой боковой панели,
    2. -
    3. введите текст в соответствующем поле ниже,
    4. -
    5. нажмите кнопку Отправить.
    6. -
    -

    Все сообщения, оставленные пользователями, будут отображаться на панели слева. Если есть новые сообщения, которые Вы еще не прочитали, значок чата будет выглядеть так - Значок Чат.

    -

    Чтобы закрыть панель с сообщениями чата, нажмите на значок Значок Чат еще раз.

    -

    Комментарии*

    -

    Чтобы оставить комментарий:

    -
      -
    1. выделите объект, в котором, по Вашему мнению, содержится какая-то ошибка или проблема,
    2. -
    3. используйте значок Значок Комментарии на левой боковой панели, чтобы открыть панель Комментарии, и нажмите на ссылку Добавить комментарий к документу или
      - щелкните правой кнопкой мыши по выделенному объекту и выберите в меню команду Добавить комментарий, -
    4. -
    5. введите нужный текст,
    6. -
    7. нажмите кнопку Добавить.
    8. -
    -

    Комментарий появится на панели слева. Объект, который Вы прокомментировали, будет помечен значком Значок рядом с прокомментированным объектом. Для просмотра комментария щелкните по этому значку.

    -

    Любой другой пользователь может ответить на добавленный комментарий, чтобы дать ответ на вопросы или отчитаться о проделанной работе. Для этого надо нажать на ссылку Добавить ответ, расположенную под комментарием.

    -

    Вы можете управлять добавленными комментариями следующим образом:

    -
      -
    • отредактировать их, нажав значок Значок Редактировать,
    • -
    • удалить их, нажав значок Значок Удалить,
    • -
    • закрыть обсуждение, нажав на значок Значок Решить, если задача или проблема, обозначенная в комментарии, решена; после этого обсуждение, которое Вы открыли своим комментарием, приобретет статус решенного. Чтобы вновь его открыть, нажмите на значок Значок Открыть снова.
    • -
    -

    Новые комментарии, добавленные другими пользователями, станут видимыми только после того, как Вы нажмете на значок Значок Сохранить и получить обновления в левом верхнем углу верхней панели инструментов.

    -

    Чтобы закрыть панель с комментариями, нажмите на значок Значок Комментарии еще раз.

    -

    *доступно только для платных версий

    -
    +
    +

    Совместное редактирование

    +

    В редакторе презентаций можно выбрать один из двух доступных режимов совместного редактирования. Быстрый используется по умолчанию, в нем изменения, вносимые другими пользователями, отображаются в реальном времени. Строгий режим позволяет скрывать изменения, внесенные другими пользователями, до тех пор, пока вы не нажмете значок Сохранить Значок Сохранить, чтобы сохранить ваши изменения и принять изменения, внесенные другими. Режим можно выбрать в Дополнительных настройках.

    +

    Когда презентацию редактируют одновременно несколько пользователей в Строгом режиме, редактируемые объекты (автофигуры, текстовые объекты, таблицы, изображения, диаграммы) помечаются пунктирными линиями разных цветов. Объект, который редактируете Вы, окружен зеленой пунктирной линией. Красные пунктирные линии означают, что объекты редактируются другими пользователями. При наведении курсора мыши на один из редактируемых объектов отображается имя того пользователя, который в данный момент его редактирует. В Быстром режиме действия и имена участников совместного редактирования отображаются непосредственно в процессе редактирования.

    +

    Количество пользователей, которые в данный момент работают над текущей презентацией, отображается в левом нижнем углу в строке состояния - Значок Количество пользователей. Чтобы увидеть, кто именно редактирует файл в настоящий момент, откройте панель Чата с полным списком пользователей.

    +

    Если файл не просматривают или не редактируют другие пользователи, значок в строке состояния будет выглядеть следующим образом: Значок Управление правами доступа к документу, с его помощью можно непосредственно из документа управлять пользователями, имеющими доступ к файлу: приглашать новых пользователей, предоставляя им полный доступ или доступ только для чтения, или запрещать доступ к файлу для некоторых пользователей. Нажмите на этот значок для управления доступом к файлу; это можно сделать и в отсутствие других пользователей, которые просматривают или совместно редактируют документ в настоящий момент, и при наличии других пользователей, когда значок выглядит так: Значок Количество пользователей.

    +

    Как только один из пользователей сохранит свои изменения, нажав на значок Значок Сохранить, все остальные увидят в строке состояния примечание, которое сообщает о наличии обновлений. Чтобы сохранить внесенные вами изменения и сделать их доступными для других пользователей, а также получить обновления, сохраненные другими пользователями, нажмите на значок Значок Сохранить и получить обновления в левом верхнем углу верхней панели инструментов. Обновления будут подсвечены, чтобы Вы могли проверить, что конкретно изменилось.

    +

    Чат*

    +

    Этот инструмент можно использовать для оперативного согласования процесса совместного редактирования, например, для того, чтобы договориться с другими участниками, кто и что должен делать, какой абзац вы собираетесь сейчас отредактировать и т.д.

    +

    Сообщения в чате хранятся только в течение одной сессии. Для обсуждения содержания документа лучше использовать комментарии, которые хранятся до тех пор, пока вы не решите их удалить.

    +

    Чтобы войти в чат и оставить сообщение для других пользователей:

    +
      +
    1. нажмите на значок Значок Чат на левой боковой панели,
    2. +
    3. введите текст в соответствующем поле ниже,
    4. +
    5. нажмите кнопку Отправить.
    6. +
    +

    Все сообщения, оставленные пользователями, будут отображаться на панели слева. Если есть новые сообщения, которые Вы еще не прочитали, значок чата будет выглядеть так - Значок Чат.

    +

    Чтобы закрыть панель с сообщениями чата, нажмите на значок Значок Чат еще раз.

    +
    +

    Комментарии*

    +

    Чтобы оставить комментарий:

    +
      +
    1. выделите объект, в котором, по Вашему мнению, содержится какая-то ошибка или проблема,
    2. +
    3. + используйте значок Значок Комментарии на левой боковой панели, чтобы открыть панель Комментарии, и нажмите на ссылку Добавить комментарий к документу или
      + щелкните правой кнопкой мыши по выделенному объекту и выберите в меню команду Добавить комментарий, +
    4. +
    5. введите нужный текст,
    6. +
    7. нажмите кнопку Добавить.
    8. +
    +

    Комментарий появится на панели слева. Объект, который Вы прокомментировали, будет помечен значком Значок рядом с прокомментированным объектом. Для просмотра комментария щелкните по этому значку.

    +

    Любой другой пользователь может ответить на добавленный комментарий, чтобы дать ответ на вопросы или отчитаться о проделанной работе. Для этого надо нажать на ссылку Добавить ответ, расположенную под комментарием.

    +

    Вы можете управлять добавленными комментариями следующим образом:

    +
      +
    • отредактировать их, нажав значок Значок Редактировать,
    • +
    • удалить их, нажав значок Значок Удалить,
    • +
    • закрыть обсуждение, нажав на значок Значок Решить, если задача или проблема, обозначенная в комментарии, решена; после этого обсуждение, которое Вы открыли своим комментарием, приобретет статус решенного. Чтобы вновь его открыть, нажмите на значок Значок Открыть снова.
    • +
    +

    Новые комментарии, добавленные другими пользователями, станут видимыми только после того, как Вы нажмете на значок Значок Сохранить и получить обновления в левом верхнем углу верхней панели инструментов.

    +

    Чтобы закрыть панель с комментариями, нажмите на значок Значок Комментарии еще раз.

    +

    *доступно только для платных версий

    + \ No newline at end of file diff --git a/apps/presentationeditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm b/apps/presentationeditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm index 52fbc2925..6315dcf37 100644 --- a/apps/presentationeditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm +++ b/apps/presentationeditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm @@ -25,17 +25,17 @@ Открыть панель Поиск, чтобы начать поиск символа/слова/фразы в редактируемой презентации. - Открыть панель 'Комментарии'* + Открыть панель 'Комментарии'* Ctrl+Shift+H Открыть панель Комментарии, чтобы добавить свой комментарий или ответить на комментарии других пользователей. - Открыть поле комментария* + Открыть поле комментария* Alt+H Открыть поле ввода данных, в котором можно добавить текст комментария. - - Открыть панель 'Чат'* + + Открыть панель 'Чат'* Alt+Q Открыть панель Чат и отправить сообщение. @@ -370,7 +370,7 @@ Remove a paragraph indent from the left incrementally. --> -

    *доступно только для платных версий

    +

    *доступно только для платных версий

    \ No newline at end of file diff --git a/apps/presentationeditor/main/resources/help/ru/UsageInstructions/SavePrintDownload.htm b/apps/presentationeditor/main/resources/help/ru/UsageInstructions/SavePrintDownload.htm index 8ea45100a..afd60d1ae 100644 --- a/apps/presentationeditor/main/resources/help/ru/UsageInstructions/SavePrintDownload.htm +++ b/apps/presentationeditor/main/resources/help/ru/UsageInstructions/SavePrintDownload.htm @@ -9,10 +9,9 @@
    -

    Сохранение / печать / загрузка презентации

    +

    Сохранение / печать / загрузка презентации

    - По умолчанию онлайн-редактор презентаций автоматически сохраняет файл каждые 2 секунды, когда Вы работаете над ним, чтобы не допустить потери данных в случае непредвиденного закрытия программы. Если вы совместно редактируете файл в Быстром режиме, таймер запрашивает наличие изменений 25 раз в секунду и сохраняет их, если они были внесены. При совместном редактировании файла в Строгом режиме изменения автоматически сохраняются каждые 10 минут. При необходимости можно легко выбрать предпочтительный режим совместного редактирования или отключить функцию автоматического сохранения на странице Дополнительные параметры. -

    + По умолчанию онлайн-редактор презентаций автоматически сохраняет файл каждые 2 секунды, когда Вы работаете над ним, чтобы не допустить потери данных в случае непредвиденного закрытия программы. Если вы совместно редактируете файл в Быстром режиме, таймер запрашивает наличие изменений 25 раз в секунду и сохраняет их, если они были внесены. При совместном редактировании файла в Строгом режиме изменения автоматически сохраняются каждые 10 минут. При необходимости можно легко выбрать предпочтительный режим совместного редактирования или отключить функцию автоматического сохранения на странице Дополнительные параметры.

    Чтобы сохранить текущую презентацию вручную,

    • щелкните по значку Сохранить Значок Сохранить на верхней панели инструментов, или
    • @@ -25,13 +24,15 @@
    • используйте сочетание клавиш Ctrl+P, или
    • щелкните по значку Файл Значок Файл на левой боковой панели и выберите опцию Печать.
    -

    После этого на основе данной презентации будет сгенерирован файл PDF. Его можно открыть и распечатать или сохранить на жестком диске компьютера или съемном носителе, чтобы распечатать позже.

    -

    Чтобы скачать готовую презентацию и сохранить ее на жестком диске компьютера,

    -
      -
    1. щелкните по значку Файл Значок Файл на левой боковой панели,
    2. -
    3. выберите опцию Скачать как...,
    4. -
    5. выберите один из доступных форматов в зависимости от того, что вам нужно: PDF, PPTX.
    6. -
    -
    +
    +

    После этого на основе данной презентации будет сгенерирован файл PDF. Его можно открыть и распечатать или сохранить на жестком диске компьютера или съемном носителе, чтобы распечатать позже.

    +

    Чтобы скачать готовую презентацию и сохранить ее на жестком диске компьютера,

    +
      +
    1. щелкните по значку Файл Значок Файл на левой боковой панели,
    2. +
    3. выберите опцию Скачать как...,
    4. +
    5. выберите один из доступных форматов в зависимости от того, что вам нужно: PDF, PPTX.
    6. +
    +
    + \ No newline at end of file diff --git a/apps/presentationeditor/main/resources/help/ru/UsageInstructions/ViewPresentationInfo.htm b/apps/presentationeditor/main/resources/help/ru/UsageInstructions/ViewPresentationInfo.htm index a379c5b71..6f6811178 100644 --- a/apps/presentationeditor/main/resources/help/ru/UsageInstructions/ViewPresentationInfo.htm +++ b/apps/presentationeditor/main/resources/help/ru/UsageInstructions/ViewPresentationInfo.htm @@ -12,10 +12,12 @@

    Чтобы получить доступ к подробным сведениям о редактируемой презентации, щелкните по значку Файл Значок Файл на левой боковой панели и выберите опцию Сведения о презентации.

    Общие сведения

    Сведения о презентации включают название презентации, автора, размещение и дату создания.

    +

    Сведения о правах доступа

    Примечание: эта опция недоступна для пользователей с правами доступа Только чтение.

    Чтобы узнать, у кого есть права на просмотр и редактирование этой презентации, выберите опцию Права доступа... на левой боковой панели.

    Вы можете также изменить выбранные в настоящий момент права доступа, нажав на кнопку Изменить права доступа в разделе Люди, имеющие права.

    +

    Чтобы закрыть панель Файл и вернуться к редактированию презентации, выберите опцию Вернуться к презентации.

    diff --git a/apps/spreadsheeteditor/main/resources/help/en/HelpfulHints/KeyboardShortcuts.htm b/apps/spreadsheeteditor/main/resources/help/en/HelpfulHints/KeyboardShortcuts.htm index 9c2891803..fcdab000a 100644 --- a/apps/spreadsheeteditor/main/resources/help/en/HelpfulHints/KeyboardShortcuts.htm +++ b/apps/spreadsheeteditor/main/resources/help/en/HelpfulHints/KeyboardShortcuts.htm @@ -20,10 +20,15 @@ Open the File panel to save, download, print the current spreadsheet, view its info, create a new spreadsheet or open an existing one, access Spreadsheet Editor help or advanced settings. - Open 'Search' window + Open 'Find and Replace' window Ctrl+F - Open the Search window to start searching for a cell containing the characters you need. + Open the Find and Replace window to start searching for a cell containing the characters you need. + + Open 'Find and Replace' window with replacement field + Ctrl+H + Open the Find and Replace window with the replacement field to replace one or more occurrences of the found characters. + Open 'Comments' panel* Ctrl+Shift+H @@ -39,7 +44,7 @@ Alt+Q Open the Chat panel and send a message. - + Save spreadsheet Ctrl+S Save all the changes to the spreadsheet currently edited with Spreadsheet Editor. @@ -79,7 +84,7 @@ Jump to the end of the row - End + End, or Ctrl+Right Outline the last cell of the current row. @@ -172,10 +177,15 @@ Select a fragment from the cursor to the end of the current row. - Extend the selection + Extend the selection to beginning of worksheet Ctrl+Shift+Home Select a fragment from the current selected cells to the beginning of the worksheet. + + Extend the selection to the last used cell + Ctrl+Shift+End + Select a fragment from the current selected cells to the last used cell on the worksheet. + Undo and Redo diff --git a/apps/spreadsheeteditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm b/apps/spreadsheeteditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm index 174ea3ff8..eb19860c8 100644 --- a/apps/spreadsheeteditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm +++ b/apps/spreadsheeteditor/main/resources/help/ru/HelpfulHints/KeyboardShortcuts.htm @@ -20,10 +20,15 @@ Открыть панель Файл, чтобы сохранить, загрузить, распечатать текущую электронную таблицу, просмотреть сведения о ней, создать новую таблицу или открыть существующую, получить доступ к Справке по онлайн-редактору электронных таблиц или дополнительным параметрам. - Открыть панель 'Поиск' + Открыть окно 'Поиск и замена' Ctrl+F - Открыть панель Поиск, чтобы начать поиск ячейки, содержащей требуемые символы. + Открыть окно Поиск и замена, чтобы начать поиск ячейки, содержащей требуемые символы. + + Открыть окно 'Поиск и замена' с полем замены + Ctrl+H + Открыть окно Поиск и замена с полем замены, чтобы заменить одно или более вхождений найденных символов. + Открыть панель 'Комментарии'* Ctrl+Shift+H @@ -79,7 +84,7 @@ Перейти в конец строки - End + End или Ctrl+Right Выделить последнюю ячейку текущей строки. @@ -172,10 +177,15 @@ Выделить фрагмент с позиции курсора до конца текущей строки. - Расширить выделение + Расширить выделение до начала рабочего листа Ctrl+Shift+Home Выделить фрагмент начиная с выделенных в данный момент ячеек до начала рабочего листа. + + Расширить выделение до последней используемой ячейки + Ctrl+Shift+End + Выделить фрагмент начиная с выделенных в данный момент ячеек до последней используемой ячейки на рабочем листе. + Отмена и повтор From b6a339c8547624f18e40476e2721cc6650de78f5 Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Thu, 6 Apr 2017 09:46:13 +0300 Subject: [PATCH 03/27] Fix Bug 34681. --- apps/common/main/resources/less/plugins.less | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/common/main/resources/less/plugins.less b/apps/common/main/resources/less/plugins.less index 361eef56d..23e713e1e 100644 --- a/apps/common/main/resources/less/plugins.less +++ b/apps/common/main/resources/less/plugins.less @@ -54,7 +54,7 @@ margin-right: 10px; margin-top: -1px; - .background-ximage('@{common-image-path}/plugin/icon_add_on_default.png', '@{common-image-path}/plugin/icon_add_on_default@2x.png', 31px); + .background-ximage('@{common-image-path}/plugin/icon_add_on_default.png', '@{common-image-path}/plugin/icon_add_on_default@2x.png', 40px); } .plugin-caret { From 16477c3927b175c6f32c1dde2360b95331d63d09 Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Thu, 6 Apr 2017 09:47:47 +0300 Subject: [PATCH 04/27] [SSE] Added id-ID Indonesian (Bahasa Indonesia). --- .../main/resources/img/toolbar-menu.png | Bin 26636 -> 19386 bytes .../main/resources/img/toolbar-menu@2x.png | Bin 62754 -> 49132 bytes .../main/resources/less/statusbar.less | 1 + 3 files changed, 1 insertion(+) diff --git a/apps/documenteditor/main/resources/img/toolbar-menu.png b/apps/documenteditor/main/resources/img/toolbar-menu.png index 71c02c69889338495c737616de79f1550e586c55..1fbee3e239617ff95ab9f1dfdd78c643c422738e 100644 GIT binary patch literal 19386 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

    gm${c6b$w z2JetUzzZ7nU4qie0F(q5fX})Q3V1>^M`C3jWHlP&%6Q$3Rtzrbt;@~98G9;XY|Mcw z+SJlzxHwN}yeCw91ANzV6@bYYmhL=lgk5^xc#Xr2HH_Vj5T2!OJ3Im*3dyk#z1b$> z;`!NPtaCjaW?V2zOX+n5z}wl77Hh_d3XuDvB2=9vw0Pq6N2#QAsTg3!4$|XRL&g6y z;GOM}Oqe6|&-m%w;(TOK$`R5B4uH_211PQ^U|Is;Q2y?NdgsY(Y#H4Xa9v;p}Ri_jZzN|V~GQoufreIZYPCmUTZ;$HdN3-e+kutxD7YC%n-h=$b2uW8xqXcW7#Bj3rs? zJerF+Z`0p$ME1HO^ov|GRr-^t%gHB+<7@h|iftE8v2fM!k0#rrVJGx=n#2Jx5BDRT5y(?hJk= zWtJJ`^EnTHtMpXd_7+>JzBk{`0HSM1ti*7Rh>9=2N9?ME9jHbX4p>4m)r~mVW@~cF zv7+DCq36aWLrCg0fxeNhjrf25|3bPbg-OT(x{-NGI*<264KQq;eT9 zcI#X5tCGSWN8CeEr;`gOt98y^9^H@XtOeh#m3#{j*uejqf>k)u%9WR@h`zfsp8!t+ zk#B7m0vAAaXhijx!t7TTKXH&}srr@{W*N!02Xcgma3SBq7cHoEv+4eY8fQjn0%SY` zu^n?9^wz&VT($k5&p_eRf0zGJf$%StrRJp>d{DQ@|3^#l8!Y{Y=Rw;6;s93QcpAgQ zxLz20alMd?V7(BxvKal|x%IivSY4RU*m(@#0|4SaW0!z$mpB>DD;`H&v?8#xL@O;X z$^G$e4H|*UZ+d?joERD1Yy8d3XF`{N#m zNonrkh=flEsXEh=tlmzS06C~`eO@L>z1vgQ0X>dm#u>sZhH(xdE&1;9DB*$Lq}C$c z#f#ukfrKj?_<+IT5>a9@Iz)Uj#*hCzwjLZP?#C5LUTFz0{}jL)B_KH9gz3wZN0L(* zy(7e{R&B|)lqV!O7+HTO|He*WrI7%eR%3=dey%U%b6e z_x@NEX2|A6I|aLiO>Bpb+szp>@B+ti{m&K(qa}bhZ)7%LLml_C(%9uuOx@NA$i>RB z_cI74?)%q#XtLhG&+`UOmy58AX|QhWR82K~3m}N{V_}nmV*|kr8Tmrtmw{DR$)N7a zG$R6b&&YR)$b^%-Ih^X$3(imsh}{0t-t;!060q++13WLTu?NOs$27{uAH43+`CK z^c%e1TEagk*@ROHEpopdWAhy%IPDQTy;y1?zdbn=-?`phs3?alG=(H*bkQ?IVVBzR(Y3=%atdG zgyZ@pJlfP4&uJC>rn|SBH3v22Z*k6VX-oVM=US^#lpI4J=XMS7>#8{Z?f}FWjCB_ek!w&@0e9`k43x+zi+9(}G%Uz1K; zEc9VlRymk?H%`I_`~%v`Hr`K{KarvdUA>0Q3Z=(bQ~u%p8tL`%H%`ne9sqb zk-{rr&}oonP6xeSU--_INRfTH_WRzFUW4KyB4{#oAUzWkaa)0kog;?-kVHlQOF#7& z^%Nlc*`l;W@)*-KiUhv`dSTsKUOu%;)miCmu=-YNq94sr`NMvqJknSqJb2=jCqh)x z%kHo=Ek!2nw}k0`i|+0l@(QZdd>$NMQUnm+_(hri-D|3}WkR*M!QB&&0^^&}i>$1j zBBj$RMf~Pt#U!;juLt(Lj2mMyvXLCq#({!-E9k-{PwOtg(p?bMqQAp74Dvp&Tp}f2vG$@QS1| ztZ>35XrtPdCPcMt16*33%o`s2ux6*V@1bma;e_vPBcM7 zz)Wl| zGT#u@z#+ZB$^Oo7U_gK4_{O@njP;bhv4XC*A4tQ7QN*`a}Z8bOxNno9cZPsJ=Y=Ip>AdUgK&u-T)Y4fLo5by<<)3MX+9eMRZPj1YdiSyQQr z5?i?Tax?+q8zSQTSa|%Eo%zXqx%!x z_(SdY55N8!G?%zJX^Jwm*R)hCN~4`zJ72J|i!Z06vEpE2gsb|UY?qre=3ut;d}$Nj znG^6-a;pU<;7ntmDe5fEH>Bm6uNR6E9I>*cz++m~9_Ku26b_cLsR<7e4X6+1^iq#HaxLnnI3rUiCwt}#9 zT~H)R+I@5?fK4Vuw`+MByy^?NGs8l47uzDp0sD!cyXy*A!++865Fs;#h0o1`{0SHtfJc~MJiKdWKcSmn^Gc%7EIQizI?q&>~bG-b6Xia!5tCL)oc||)UM}Y z&D@ywPITsKV$p>Syzk4b7e#yKD0-^yc*281ca3kRpp1c~<`qj+*(*?1-tsT>movE; z0}25CO|(d1dF2;aR(sQ|+R|;FR)Od=o7A^9CzD-ZX*=eH_q7dty+mh1U$eN1D>I!I z*`np5JUF|ta8dD>G5i*g4kXGd`(-ZC;_55Lz6x+{+q_CM`3y5*ROAL?^Q`ar-xnrV z@U5>T2o{W9px?6QOp)N{`TS466)rjC41D;}VsoRl=RS}|2`}$;eOrYuf|n0UmNIh| zaE~LySWb1xgV_E%U-L)KaxFSj+xG*yV*Z=#aosNpN^~-(X-8}qEX*8{SMk*Z4=O99eGe_xF4oD4Q5ZMBHZd8yR>&KL(52@+jd$@= zb<&b;xA$BgrbHz61E4=#Z>4!l0lKuHTP1Fj+@E<=-QTcA*)(MRGis?jup8de;K-?Z zjvX~c=PS9h z@~(m+-;g{F#W1lCJ<`%|rOZFWA5lyKgwwjmO_Uh_kFZBPWdj%grAe*ppBwnl3~4D( zB0U!}Pk@vGPaxR(NjG_Soo_wj`roy~PdlC_@?^H#&QH^b#xg~L$SVtS*h0l8+LiiB z=j+JSx1ADB$M_oi_ui6M7Ue9+J3Z?UOI(?a1OfuXAhS|^M@*l6B(k!+0si>aZyWhn zvEbhlQ6-KawD-Io6uzOemg>}RnSa!tUchJ0jUNxkV+x-c2yZ{%Yu2e^mzxaTki9{a?i|ZlP)v=LSkDr<2L$pN!0nbq*e;&z(^7n9lCE>PwmH?O)cr5~0k}9({I)lZh)a!b-&>Hg@fq zMy_qRK&dO3IQb+Bh5jCE6UBK3Ekj+JxlV1gHm7Vst}q!8(o8xjtysY437~&n0U<9* zCv8WO0>llbt*zAk)3B2Mz^`4~pYR(>s|@e=!)o-%3nF1ZPBwQ?nj3o!BM zo(vTA%H1fAvPz5xfq#|s-O8;U?@z!DvQM9g+a3AH7K1|o=>?@%zby3BD>K3*SjnJo z(W1Fm?Q+gRk3laIO5j5;sNk8$jLo!B*E@>@HCU{S!~9Y~W7Tjxp`7|GazxQXYXwSr zBUwHNtE=DnqF;3F^8Fs<{I%snLwO>uyWLTil3VeN^7 z!3&XhWi%lx?fcK7b%#3JPv$>iTxvT}VwH!WE!C~s)hH2>n!E<^{@6+B(KV)87f2dD zXvLO(j&^D{JwkH3EF}WzF)$h^nkjSndm!9+h(r>kIJImmywC=C*j#Q@Y)2rGi4P_s zFN(RGo>@&>|aCm znW(ZHuC8Hm(cPWL)(9C`C+XsW793vD#2Zxg}$rN0nuwc+F%oDQDZH{>Vf=w8TSVycQqijY4+? z^A@Y`DA&#K5El#lr&LABE`Kxa8ow4(a1{1&ve#Q@(iER7JAyGZr^}*;eOcMsPU}a= z7?NCYII`7^OV;SCE;5I0itMYTHGX59r!vzqA^c#4?MYxydDDyXK@`MUdW!(y=kFUX zt&!D3po*yuZrZSurGDsF6s23T z9!&`^$USMb`y7dQCef*{A&ysv1qZD(l2>O9s14{MxZFy(Js*XY!0keWLPuXtU7nC` z<6P=^gTuDR0vM6mF6^t!+sIw>1;##HX@8D$G~~>xJeivTtg7O_V{XgKCW;BJd5+@& zITO_3)*-6c=R9v9#$+wAA*I<3!PlU&z+;!S((ut~s`%T^PIy%_msXH0zyPo(g-X@B zRR*k=E=b-*Y~{p6XWa=0*+5;pp}t#k>@i)8F@Erfw&fXEd`BYgy8()S5~9wda%2Eg zuy}@Uif(k`^beCR8UE4Iy|>%=BtR)9du<%3>z)xU)&0XVUgMh(xjw>6wkF7*F-&%) zP#hdB*6B>JVR=9(c>9B=HRty7nt$#J-uFhc!V}2_cGen>DR^TZIm-{>cY~&{WNUC* zm6TTXjbur+sZFSMi`w z5uNgLRekU#r)ZSiSQa3xOpSBPN@;HytKI;%dK?4TG4yX69sU_IDlIywm->wH*DLLc z_?{ZfiMVvVzS5Z81>4gTc|_*K9-@lq3MCv+%i2uwp}W!-6?-_UO5{ZvX)=^)4_6z2 zAp3Ep`GCjS#|&rMCtv;m$xFkeNRB=7VM=&@ZRkgUk)^m@CZ2H0=o{dYdo6@N7(4j1 z3RQj=)9%i>pVG0hbklcU)4>NpeR(S(i?4f*E9t}Ya~7tfs(#8~U0&qaqefyvdAI&{ z1$k!LM>EaFJDx5x!+8!NghFD;w>~+ePP|dhws)^!vQ5;~i|-x@$C9u_MSb$F4N~(^|E zhtHUa4(eP@``&CmGhC6g(0BXt8Ucp)1&fidXuyk=wgZFtR#$FWU`s$-QdYYqrV+CW z)tDg=eQ{S>*7M$nPZG%cYn1#MY04g%l_Iz(k%f` zm$w9Grkg@g)=<$(aRt5^19*mxdvX(ew78 z_J1es9ByjaDW*_d^ZRaff3|Y0kW@E@I|dE42ymoBD}MeAVNULo7U(smVF5$|?)OgL z=JeU^3aM|rY#BTLen`pGk;Rn$pOd`55VC)_gc|TdAC&*X=uOy&wt-Z?a+h6>5HCKa z@cEg)je!kN=z#RxXy>3D%Bu5^wM(0xEyiE7j308>;OYp-$YvS}k{oL(iHePG;j14q zGK_ii;AjWP!@%c!J*&V84Q2Ylk%v>>V9({uE1^Zmi$^;OuN1(eF?(WP4tP@1BJ4Wm zBtlS3vjzpQ?=4iF`mhjeah52HH?)?e}xLK@oyR%N&(VaFnGF|h2GQ&tdT;X|Dt#+_*1aHYe`FeH_e-b9~Xf9hC>&3`&01f zmK@AAlG*TWhh@Mwb+N4zjrlTj+hM$##*-h_=?ARI2Wm##9$C^+vV;wNEuqaVQUz+_ z{+Xm7=y!yu-Gi(bpg4fw2U_c%K&Jx)KD*?uR9eU{lsCh1BTYK-lI1O|9ktDFzTteJ z?4yq(&qBBp)>sbmw4-Y|l8G~quDzV!<>hJPdM^iauZD}V4RvktN)b#QEPlAOY(<{z z&*NBymaNkvEa&dCW?EM2{XV(Z3(9RBA98)tGLf+j!~l+~B)!1z3BGeRuK)%;u8_j-rSIHd6p9bi{@MUqFl z^844nwg@h?TDC<)41=c-=LAE$q`9WAeU6qB&;Ki(3lM-%}l$ zWH%ZQ$lb+8pwvAD0AaR3o@8UA3B?L}V#&p}HUxV?hYmIGoP*fIn%>HE5k;1XYC`ec zo>)qR!?pxG!70h6F+;BN0Wg4UQm?Z#V~>ntmh+PAq+%sL6>$fEJn6N>6~sx{M_ z{kN7-_x~ze|8v`(k`{P2YvqkK9a3IFKcOie3o$Iw3tqsTnMLP8YY)FXXce+%YOP^D zqnl}6X}4G4l-8CPSZF?AMA)PTn9cjq_p+C(@4dsfR<}%MEGj)mehG~47cEcQE5LuT zvCc}ndF%b)W)~Out@^6hfpyXDpn<3$ZnLY9Kz;o|q~zO4%|;w3ZJ37GD@--*_pjDk zKQt9D^ScN3)ULhn_LNgM=1Zh%);GDmH5P!wWAdH4wKhP%#*rtv%H46>K4E%GSU&fU5pHxGUf>7GTr{ zuvudPW^WNv#Pk*FtALh_wjym+GTiEO(e4+3WWy9%r%sXhK1lok^wR5r=pclsxm10X z>|8jXwyJ7t0`&FHXy)&4x{Fn;6I#g?z@O!>^s0<6Y)nPV2Ek=%6Dd;h`R6+uA^#C3 z{p;SRztc?y9Jt?F(dUD=EDUmYzwZE3>B|2-^h1y6`<+OoQ?dgNpI36Pt?*M}Bmh(M zA5fiXHB$#s-IL-lgx3c^<{`K$=1P0>INN|I#EA9N%Zu69K_yomG4!c!8ivIlDl5Q= z(+IiK7cM7^eHEg8)0meR0Ujc?@5>JrC>y9(p@b%kH!J_=$9f()hXJYS;^LhiyoM9NNBHlY^*tY3?0IyD*`$bhdFKb1>T8p1rdbUI9w5}~!B>LvNPwSR;sP$k=0_K*_h?niB- zJ0S%~%E&{xn-_@ej}_tQ3tp?O>l|op_~LcTiaq+rj6l{(c2t`#wL&TF{-^?F;_bLP zm5Y-r|90W=rn%8Oh}+HZzx$V`9e;5>zqbwlWfGpsc8g@?0DJmgzbe+tnf<6Yv2qTC z#o3YpNLQ9j-&M}zIMVtUtX4goG^8TaWHVG_Kb7>3{ufPO`5ERYJfP{3HxQi!Ik=*P zpYfi>9m{}&b*p&&n@H;`TlDW_VI`?u%Ds5DQ+P~w&e{Q+b8jCBR?pgOUdBQwsOG}3 zT&O(6Nh-jyY)PM!Ajf3w^pEG6IGCmM9W^af9qX_419A5UsjFKRzou1rA;KsdL~u_) z1=|caSX`&-T8-cH(DZ|rTmee2SA^$|X=xJ>zm?v3LMMQh$>i4Etu9)1Ko1#<&iXC| zfc;(s*Vvw}y*VnAs1{M3>>s7eaKAZU`L30}o<64N*L}^4k(rKja5VF&B1(+3Z<{sU zV!(~7Im(4+h6e>526=KZ0VCw8ARLs91sA}*h zE{fo}Wes3?&0~WjG;JL1UwhA1BC-qYcwh%0LeQ9SvKADvct9`oL z8=7NjOQFri0TXjs&%#AiFRhUe3tk$r5ovum0?)%O0@0^~cI9wv%6RQ40*$q_-5dy> z=(NJy-;ikp0?sUz=l5YBxsxT{2a%u!TBi#2R@SyfSpk`#O{~sy8rqDkb(3HplzBJ2 zgsx@eTCR*3AgB?yE+VsD_OT&|(!-|^@-ilJK-Qjsb!2DvR0Cgo#5n*-@Pnp^`_EoC zg>6OrU$0(1+P6}9O{KD9-}a<-3kA+-kDyxfWH?DwvTl`>!xx#pv{nKG0CtpzMa(_q znNGBHFA9j7B1w39@b-E8Xc^_~(a!1o_CdtN>Y@UF&eY>lvj#vp57C0k#zpu{z=#yA zpkN7t_G)_-H_EW~{1!z37J4`vrP(at2dq?OfIJqUJ$$fwmh2mXt0lt+t^%s*E&B}u zuc;H#9^i9g*|*^-p<8KWi?>4)1$}^PEOE>3_VDb6Jhz*VV z9`U6oRFJRYC%+=9W@XC_zuQ%C_B=VB>&%t+beS!6%@tJxWaV)rxhmaA?V;T6PXCI&iIQ;O(T8uo8|rlI;KA4 zPT)`c)0m!CC6$d{ee1GJw%9qILOrq0N|kzkJYzfWm=(OEz7boiw=N%|0H0sRU<9vA zrRK>g+JrRSykG@8yTGglmT) z0}MCcm3q0!L(4{*zH|#ODNa!a-H++F+Dm-FqS|Nu(9<1M(U`Z(DVV&lO@m+x9~5&{ z>r*f+?pR2c9oh_@cU+>+&`*-}(t?xYb7^~gjt1;9@V>NX!&TPHs3>UcVjuj>dT&x? zPFJ0QovQUw)T-L%$ROlS*Ocf>u_qan==eO9?~M2rA0+-UUdpmi3VWYLtl{mu; zQPW{0BUJe83;QV(=AI=RC7w8=gQO=1>|D~=;6Z)R}Pdn-ZxvmcZ;5k*@VMCHn-iLUC7^ zM}*n9yav%`hrtq<=fia*!Ne!Q9-1RrTuZEApWG0(WUqGHRq&=pN{?u^1*?eHpc+r$ zyH#{m6J5a*bs6N_F~O)tdedRi(3^>KOP-r--KCk`rahwUn1`LCx!vjPt*h)m{VYd< zXQbaiW-<*e12=sQUYRMF4O}&Tq7zlB6{6`ixXdw(`V{`P0@NcZwv5(%d`}}(! zlbwuFyUEQ;_zlbmNfspS6yn3>#vmreAB~oVmaU|zp>Gu~c$|FQ^W45G*(M#mhW2o4 z!e+dE)wJq3;#wnPWdF!NUX;8LB$aKEYgOFQ*Eb{_T^1dvi%e z*lUp9?8xU@$Q6!*vF*W#=zir7*QIoM*G1^=2aN9@6sv1S<^OQl5~18C_;eEZrD&zv zIYh2!nqU6=aQUN;4oiy2rDDqIexl2=yFnRmA3sh*=<+uy8$+1!{EFJ9)(%CHHoQka zTm#U?4yDtfqc2*2yi=wn{;&Iao)U0{%q&*j({bi|Q@?!G{#bOt4-h;uc80$n;7NAH ze#ad_YX7q7Uw?9ey1fSss0cEoh>yK<5fFV558(d+DD+5rZk)ubczlzyX{VbszJK#K z?I5=ULHZH06WgT|0Ya!pfn&ZRD6gEdcB?h5VL&(8G(E9!z8OZQ*8kU>@hiYfeEq%! zn7V&%rzgDL*muEeY<2s+rNn(#B0G_H41i+yUxGgf`I=8BQ{Xq_+4Mhl@Oz#Kwa#bf zA!Nh#1rI4E-wRO-35}9%;5Xm$D%}m@IzQnq7DAs&52ze1XEx7w@Ly;AXOn*w-d|x~ z7k%n=siA%Flk)A{fGj!IA5Dc;k3tj%5N8Q5O0vLmd&005?$|SckywuBQ8Ev?IRYzSuRCe2fb{*CGfXFjpufC@r@$=-ZEXE5Ih^D;77 z@j?NRW&;Q1EaJ{R@r2lgd!me#%(2Qz0j(e}%WiU{(87d9tUocdBg4&}QEHeNekECXVvWM1D>x}djau(iwcuWpoeQR)nL zKSZQm){0t8c*frK2&|swUsJlKe#*oEgZEfv-rSVoIW%h-)*kG9mWBn{1t{d^Dq7mn zVJaG#=|`Y}(JN^jl+w9E;);o_-$&=XdVdD-$%jgPolfkYWT1%hLOXFbX^@{P!G4X6 zxSQQzOKL29|Le~8sa=2P)On_yc0u`{C0?7_{)+f9fO3fYCV6NtcV-v@KHzRyh|e21 za~&t!_4iXdyY`hXbtEHsJLItF<=kYr9^rWO+Y#&%q9AldgdYbVc<+^_e0rRgvZl~~VoVzJ0hfEQn&BU9mlbyZSt#WzY zKRPL=xer5OWeeg;P=^g z#=<|sH0Q*U=?eZ2VQ(D})wj2gDk32z(%lRV(%m2pN_T@WbV^7HNXO9KA|28V3JfVA zjdV8y2qSejem&>+o^#*dy?^YPJ$q)>T6?dx*Z#!wJS*h*Z0mDp{Gn;kdx*rt;R+h1{gac#$8L@?o%kmw$ro>5I1mo#vJk{CF&ImYv! zTGc)oV57bUjm3*zm{D z@gmg4SKfs<7A%)y7M=C^f%5+G2Vjd&5PR14SxdTzvNYo|-z5~B10nA|M3O}Uo!9O4 zC!{pJt@=vC$GGOEsu5r@pxio*65m z*cr@K%JJr*ieH3?=-ugbJ|1pVZr3bW4`i1*SA5z?-F(5iAV7Q%Cu zj{#u}AD3Z;@GS{TX%0`FY3gfZNaZXc#Ti&&`^$@bDWT5s1lMLx@pQAdXB)Hl0*%#O ztf=^F9A0gLy*yPhnfizTp`KRfqw?ly5&DcTqf6xs3OQq}ZilqL=svnK&KX*mq;AIs zRxH_0KEY2-8W-3oOUR7dxFUdsY?rmz-Xt)SA;K1(9Hg^(DN8=Y5CI^0CBfwx_^UOM z2GUN73f30gj4=L@*O=48RZENKJOY)?^}?e}VA9N|2331y6Kw`I=MQ8~?k^RE@!NIM z9RA&HHS+iEw)2bK5F~VQji)K}4AWWoz&QUP?+>~2S^lyt7_yAKY!W2n z6{e6YO2TL5P{C!CxqhM|6aesB7NEbVu2JWt=o!$xL308EeRb*ZpET612xvp!)MGij zBXEpZ2AJ8Jop$Zg<~Vh!|0J53%*Db#KB2Cn0}ibD`;~aP1|yrrwbSwBRq@9e0Th2mSk!)He?GghcDd?Z;hFR-g$dnyntXH z-p(rdi9|40%hL5pn@NIvKFTUrt0`g>pQ@eH}kJ9CHuI}KDc*_H2tPy)zIVg>rMk|p~Ij2?ovjW)*v z{~WsXbc((e7OZ}CdPe0$fjpx#RPe+HSB!#3u*Gz((c@yd`z?##g{>R^X4r9X2I*3N zH|Ft;)#}V^da-}0dHqGnDa5R_xtBC?49o_#O_aL;Q<7sA%cmRPAJE^(wLZ!xbnaRw z0&TuqXpAva|K~gJ#qw;lO`+73E>c2QPgj}duxE=!^YG0I$Yx^BG3l^$lVFBnNHjsh z`^wz2E_S+3PC?z90_-;8YIWGAwsI)b%N6Y?mtZpAZyZ@9$flTPgy%1sdT=!V8|7@w?sRet631nU&2O*ZD``L9CS7tBflGFjS z#jE%c*anbOn10!9_KW2L4~|60)KGyM0rA_AS`ke?Golt6Llh&gsUG!uqI}}IM=;cE z(2-JB&VKQ{IUQcFw+5>W0URp5ok>k7SdPnENngYyx%wE3y z*4f*xtO%$WjQd_ z;)Q!*Ydz9ITVElM`hpPl^po<&D2dKW-AsL7HlXZb>{D3Rz&SCoA#p56D-^v_{ciFD z0wPv?|0j{frg`{CUEiKpO}H={11F|Rdd6+A64FiFK@6$L)WzoFiJliwz{KL|5T|a9 z{@|&j0fjl=zTPO!)w5cV1#d{}?|uk|ob%EnfnQVtm-5ZYU;vfkUcNOH?M6B$r|zH5 zX_x$JP{8-dr&wjAvW-huxX!hZNB=y9bmpC8WzCOZAZxIm2FkWVx2}WTM7OXY&d{Q_eHUy|M>`Zs!X!Ag+|zBx|%F2AlV$*RihGN<(nW zgteP1S1&|s6P_evEsbdR836#u-=BOP(Q0v&PtF8xGUW7a_GHdr6N*L6+s4=PQ3sb3 zh=*pSVE}G#)Yfs_EdFHEm`A9ueChG)&s|CpDgL5zyb3N9+g(8La^)~J0;rav3cOWO zc${wXbo*yQ0bfG0wJXNDiP5ENB`SWv?L1=Zwjb#A8QxQXz)7+0w+dz4AjV+{vwYt$ zMS0>#Cn7G=?f} z8{J}Ps7rX?soJIQYBroezo{m`<$K%u01{^$QHZ6-5UpYY+rW5fy9Q=Vqug?dg4x|Z zbv$_HTBzIC^i(eFB$%ye@X@N}VyX~&banp3 z{oXh+vwvi0&Z4deRj}xxJ&hS(7^|~sN`>DRsX4?L*Yyo!4V|6ZyeVf6>||0|+gktRg3M{e z2X=^V(_7l!f(0aG0wkpds|&x4Ps4jKY~Nm)$gFnS|$GLSn6j;Fg`fW8?AIy^C|rm?G|J=E;m@fbp+) zI$WKvlzinw-HYUx1|tr3=}YP~)4Q++qvA0}O_yKv!_HzY%~s94jOYPUDBo7-9$jy7 z)+QXp+2*rYxZtp-wBl6Bw-`B3J?>N^jodkkv%4+tmIxpWdrae_D`;(d%{bT@Fw78vdbg7_V3b8wrBPVuZrs5kyF19 zS*8Cr_*^@y{8+Xxjp}73k$_5O|5r9hE5|`rUq#yqkhUO?i3*NJ-#$uO^5>^t>PIxD z3N=prT7eFwJE?FPUq$tF)nQvzwHCc&4N_( zqP2b9%yeUShRG|J@rI0TLM-uUeB-Mr8>?q?@t67*-p8`LU)l^@?k0c>{6fr^ zoKLYKC2EL*j>vzPnW=+7_C-_!x^^2(SfiyPvMQqunc@p#CLyd|a`mC=3;@Bjb;;3n{Rl|<~P z{*0N#jB8jA%sp?iShN}1N+&*@MKN~R0>s;#HDZXFreziGJ!S8V(cFIvZ2@pC6YbX~ z1u|(^09`TmChIegIKbZjkK9%6AV5rN#f$O8?tb7YqMv^x1&I3`Gt@!j(*IINO}xz4 zCv86h@^J3_-elP5g;kKvy3ycBL0vs~hqgg11vVlPr;`37uYav$|7%+7oF8n)D;w&br;<{~d_=TZh`Cna+$K`8W4~iT(up z4;_gH8BGm@=3e{kF<=k^8xzu=#pAy)%KuJJJ#w#Y1AYQ1vFZSJWC=tQK*VXH1KUfW zdl3FUdZ;+QsdN zb5NfE{r@0F`~4s7vwvqHa;0(abhGlm$)*>bDB#I}C$6xuqv!#tNbyOu8}(W``m6a$ z#q4{H@%ztN%h~i-)1`(78eYmFU9FHTsp%Q-imJATRs=u^fiKYxxwgK1W;$UeG?|Ix z0v8#QrOf-)+bc-;t_yKUOMj#JmOOY&Aj7Wul{H54*!RIIin(7fLbDITqEXj*7b)QV`nb?{0Ma4^!+>?6UoL*+My{87p z91gVxuk;5UFpuRlNDFaTJ&$sYzlm`?Y<7mS&hhv+-j+!J1Vfa%rK!F1jFF_P#?4k*PiWt z=*udoz~vB+Wc+pW#})OS){L(r##YLN{gabb5D}VK<_gyAJH;Bh+VGKi(zc z339fX%rboSD`py+-~CF!^0e0ZNZhs~bXysuPZ|4bg4oZGV$KKlE+f-?gV>bTQS-QW zpKUH4bIJgI1^|KtYDm@Ha9FJ!-;#Ncjc}?VG{Xqpv}sgf~XP$v~M589uPVNzF7+R*rY#DFF5Jz zR3$hvN`CHCea`Dydvszvc;*l3bWEN(O1`OyZSxD=DUP+}mg(vD z0`3LJNUAw7DeLW{7GWOqOzK>Yn_0s5BZ2O(jyM_9dh;cc1o$kpd0hV-7Dy$%Up{wo z7}mE=pzJKXRL$M?$KQVcZ7`S$NiK#z`M{!9EP{qTGDD#deCH~=O$WnynFXV{-i{J@ zrklaZia=>o9p=fQz88tB4;SrCe=Hbor=Klp?o4xh z&)MfC3o|^Q9kL{L&~M(t+S^cpBpDA|YkhMp=aJ%xK2Wvjtj*{1y`TGF&?3A#YYeR~ z*5xuXKjR+1rM)$R_36SsA7S}~+kMBDS$=hJ>@P61?PLo1cm7gd<@Fa< zDjTy-C>MdVO0|a6XS49_{J=h`D#4%Eeu?^bhFcC^rN@>LJ4w@7&~QJoS`wVfnjB$K z(&o<%35;K}D#ix6ZgxOis%2NYIK-M~`c(1t8E&ClL+F~byJ^g1983Ba-aS0LwK0ZM zj?^TCyG<1XINhdxzq7eD*-t{`PPk}}b4*+OeQ?awXt6@ca`9X$s^N9ZV?jE8TCq*G zex`SeG7do6^;VrWClp?1+=FYqSaVN1lTJC)usp^Kkuf`>uagoQA{#ktQB}u1raU%g zQ)$u6bc$Wf^BLNIHME~MnZwzwZN#qd)p_Hm?2HesH5%^F*j~Hfsy0pRbOv-@%__U+ zt-fk8#XZaMF-?BE4F-g;cjIDY^WwT3H3Nh8KC}X1ENVnK9lED!9kd>Qk^^y!X$W5& zA<%v8@=h}GmIHQaIYH}JGmWdSAZ#YEjMf>H3g)Roj_C6EB4R7ERg7BEWgg#o8Si|Y zWrI?9+Y;BwzA1mZFBdmdL9$}se61SiaE+W`?U2%XI6Sb(oI~O_Y74Qs2kL??Dv;1hRD^-HO z%esJlSI+%&-2!ugFwB@Bo6SF4^?qP%c#r)@RWiBtxx@S$KrPoiCdJjyO5pe$m`{Qo zZZ^2MPxZ}4QfoNcD);2DFX?s(2#FTb2hOlIQsZ4ljgp%#iGjjAbX0`($=M5MI^Swl zxzCRa(%o2XqwYo<5elSKZPs#Fvxvd%FmAHzq?e4yg^EC=h9=fxIqdyzvZL+VmAl0R z_Tj_I_*MGEOv~6Lbz9HAl{|@d)8S2Y4I>oO)NVbk6ExDh5&CaNLerkhqt76S1-&;OT%Hlt*zP+OA=Ce8j3l-plH}NDjTlLO_0OlHc zV4y6LttNHQAH>T4P_h)C%w78R`WP|2i14%4zr<>Ia(4GF{QwHZ#gMhTcJ>3|Qsvm9 zKzKq)^FcHHk4pT1vO^za;{WQ$qf-Ln?0<#P|3hm2kI?+Zw#mVXqs#w?n0#=97SLYa@Vq=JN zqjmbzYQ&gvJ*RCU$F4>9Hj=|j+s|!dr2A$+qd#?Jrja*_Kh}7iLZ{f) zNXV+Je0;Qtx<4iSlxWD}KM?o2Rq;9`5-8ru$_7Gr&Ku;GK{*g>3CF~l*yI`f zftT(y<0)M?y-LV3nQvLP$p|O8lTiTa}$|{0P;I5?ms1pHO_(w#HbvgaXXez^JGk1Wb1J)mJ?Xt~}jR`l~7HHxrIKxcb zIspW6Nwf%w=HW8m+Fz&dKTRIEEsa`%qLSeYZ7V0|Dg5D8-r@*qc&Kcd-x+CQiYg9B zXS7tCyA~+XYCHv!a1i%yce;j z{XGci=@u9vrcAYuk?20~f4%~?W)zBtXmEi*tzB|7q81k2$K6AAK@ zk1K#6;K*03zsno|T&DlasC+mtjFjNlpfc$HzaHRGjrhwqs=x*)uBdeB;Nac~XWG%~ zE)QIAsXFO@#A|nBVQlA;-*JJ@|xpH_q7iQs@AmXT% zV(vokL{WQBFGOSgqpqO;<9o<~w#9aOu1W|R5QobAl}N%d*22+&Jx#D+B<&Y)fJ1;# zcjiD3$jC+=_z&EP|N5e9h%`eht?dQU3zh=4^{5r1CU#d13YLK`Tk6!88Qv6i>p}E07noXDzKagKB>n{C}9TV z1b)467Nc&BIq<{Lc%+xTN<6fgdA&*OTd_L9gbV2IgFLyGzZo>`J0st77_ZAg0d_zc z+Q+Sj)C}#smV#f-alo=~^Yk+yks*U9syu(sOe(xTm6%-A>AB0BxNd z2soB4As7qNzYJ>2@p$cS&>vI<$ACwuiE_B*4=$#cjrXcT7S5(MIYWjwN@tS3rdi&Ia!4>wOQ5CA@+ zW1cYPDYuQ;UNrvR=1Qmhi~AsSnu(3dFZ{aXpYR?4S&oAnU$r=gI*>0>6s0!hnq;=Io=tM7x< z_vJ9tagMtGkOI;URi0(Kx0b!Xw2;{tK{Lb@p7I-_CVRtD8R(wa_XP&T_bC?lv?C)7 zV20ON!k)j=rRS5LX0*$0^h+wG5V4MCWei;L9C9Re^88lQ3)|Xwm330V5 z0brkvl*)Csq{%MgsA2TT$p`<25dXzR{oBR+`*it3>3>2!Q&p*y$$!dqeqwBOv9>Rn zD(GIiPsgnSC+b)99q`xx-b-FQ+L4cb=X%#F&z80QWF9VnGoy&8KRWqaNE$%9t$AGC zQ9h9MS`Nk9N@o2(nxI1facgcz%!Zmn-i4Fm3a52k$4UO%5CY@2Y46Tx^2(Rp9eFnA zMss+oGb^t=DlYu^{Ddu7A*66?W1Is+Gbf@?KG0WO6_?F#x)66m#_}&6*o1-(gXj#* zR*12apcu881R@PA<=v{%_4kztIU>AUw@@5ujbFybiUxfF6B%#A2O!k!#x$`6k&}V~ zoIA6u%uXh@eilV%kdINxQK^)%>oNwFv#wlZ*ZqJ-H8*s7?zNQXGS39}D6aFTw7qI1 zF>Eh9G4*n`SFi71hJ_uL(BCV^75n6U6e^og!N#+m-)sP%YBhBusrx!*|`ZVq+lg+`L`P8s-{y@@%q zg}F{?K$hpB^DbBHq{lAS`$lywv}>PUXBK@{!RaQ5yQRaBcSwd>p-3D>-|2XdY|G?Z z+wwnNdz1qk`p)9H4vGbz)X|F@6b6Me-tv7o0NB*KYfCVk39uhO=VKRX{>N`LR!-1d z@4kb<@r78vGMNN|Tjx%4wC3wEa*h5-S?b(?cdWfV%X6hGm9OK6>EiNE`R-{ds{75Q z7hFto^#~(Onv>U$jdR^yIE!mZ1`)tc+L6T*=){8NT)#`g&gBZ5B>DV!V)pykw{(ne z1j{pulLt)4>x(#-vWQNX+vDX_wllpg6H~uSVTqjEh$g30lJGmNIIqGZI!=tgjUjH{ z*_|>6Z+2Fn{UU}tMylNmcAgNpG}Ofavr$6MW+z8NBg5I5{$#?B7ubw4yz|mA+%qkR zF%fM}tCAlL#5Xu^XVM|G(hkq#UzgQF19^1+JCO4~aK_?Pv`1Lk0M58cn-|Rr5X-7B z7pZT)N=x{4lW^bD-lls(1wY<;gV;d&Vq3>ZZ%|(>pQL_psi`PyIqYRs%}LvNQe&|+ zUjnPUGUeH2x0`in>jr4zr`9I#ciQqbYowPNcYp@H%sfT z`P@mSlkZr6*32-$JI&!~zazJYB3z85L=@6t)w^4FBg2CCE?Zj@F^54}-aviE^undgF5Q}*`y25UK%?cz zW5m3qx7V@66m7?Zmja*+5uT+XO}|+nAvMW7BA>x;pXg>akITU||GXMhXA% znA*;`&U?nRNZ?&il+-Kv?IQK`uW1vAMz3qIG|zBMFY#Wd3|b6Grg38>gLiZR5?5}kpFeaalYg1Yu~6341Zmht0~L_!!3jD$-0v>lbp zpxMQ#?2}x0qyxq`Z8JC&tao*x{PF0eCYrEvdS~R}mQMCUYbI!bPt(#IuT##o4XHCi z(4el+i`hiSM$}*4#JxzhOW%jF=2gU#nbNb7@zA;3+$3ISJ?%(T) z>f8w@_|A;duleZ}ym3HKq5C}aYXi<3o>3YB$RxKWpb9O6)ICi82W?`! zfBJN{<~92<|2tOsbD#KVnuRDk^;-9V->JDhBY-(WM<1Gi z!PT9((PGyA;riL`BQ|<{+RP=o40}$teJIn(yoGfnMwRoTAZ%cxv9SeyFw1B`kRWp0 z4X9aOB#fEw=SZgbS&$514C=`LDn%{US@vY*xMXvrC1LjOl=Xo4&MI0s4u#c1yd!ba zNK;PNj|DD43$am(zsNvR$z`N~@T1t#ExGN*l9wMGqqdn7U5{nF`OR_8kQihiQlB+wI~WY;zTieOZq2BJ#gxoZ?hGu;&1&3D*0SEKDGmVq8YY zwhUl#+I6={@vmuWFE^|(^vP8O76vhz<C#0EMn5x00ezt(h3kSj^r8XLn3`Z<$Kg$Tr8C(3f zj?f4}H^IPq2+^_cwQvrg4S_xVXm^+}?U$DkAPdLAXbqXB-Zom%H5j>(jGrGA?9|5G zHe`s^bv1Z5N~;>5pOwR^@(C|Q*Tu7Dhmr2r+?)#5Fhv~B^c?pv09L=y9s zE1wFk%(=4`|zZ2veHBiO_HL42ceLogGSK-c;+^OKg%yS`&pB$iQ z2;4|&pdlI{{14Rn6d<V^Pu{cnITga_jS9Hsy!SEog?Lj+M86|PQ>H3Le`2W$eO zPO|2E>>9GkBK~{PxBqnU9u{n4bJf>; z`@!(=(9##>K^7@jKFTAJUYf*WmM z|0)AOZ;G%6apm&O<@rt~9Ln-W*uD2UeqWnr5qp9m^UJe#lkVOTaG;#*sT-)~#A`)i zOPsYK7Ov2x7y7!%T?d7(1EaWR&V=+0pt(7VvjH}mR_V-r96$u-qfngvt7-Pt)cS9! z>%Rbf&}=Fng|CG-bBU5aMM)eDTgV%1vyj_62 zs(!*D-P%i0N*`rvw?Gsd=_`pBKlS4(tb-^*twt{)?Q41~^#gtq9-%iQA&sM;R=(A% z#&19<8*{G1f*>t7dy-<{7tJ-&+_P6P024dq#UU#~NY20-rE5xL55GdF2}h&#$Kx!NKNzIYMs6}>Si zPx2toB@@%h_CN>Q$Tw^YEdHx7ia78o&sf6P007o)NF4nEqA&4;$2{>n9;g?M2Ro)| zn*gSoLAJlTOCYl_?F=`JXn*^F6}&amAHi`%*uEdP-0Ct?9kK&?Wpbp9nS?oHu}%)z zoR`|HdjPtU6*dl&m2Qii_!w2TH~4DN2VM{GC!ALGjn)2OYi@`i+k?Cm@@U53YIW>8{R(gQv5gui^ZGFJnm|?g7X?wu>95Oac z%VDm7s_wCBO`851;IB$rcj_T@o^bJwP@hyzUw!903iq`lehX+#M{q2s;=qSB5FE|v zT$bNSkT;Gx#d!GrN+IsR_UrR@cBEI<3`HJOjh~!oGXnQn`mqLK46+}@vC7B0P69_> zttAXtTcz(jA|{o`z6m@hMIO>&MrB2fkGIS@GTG;v1kToSqRz6c{d5-N z)gFqUJo&2>Cx|@>e3<+D=y9+d+pq8o@Xr8w1kb_s4!Y;)V(a7R+dvDo$@!Q9jjxE# zYEgUvw6ifnHx6}98WI2*ql=|cT;bz{rG0+7wY08cacyx`og4+Xr+}k(tlwupocB_~ z(&0+f>&5t&_YSl<-8o6SDP#Pm#!O@PKd%@adS=?>vW*>TD|jYGV+jUCXvRFx2;0{; zPKhAZ3{}${;hl2ZRYmWJhQz!m`)7p3^Vbtdfz{Xg(KbYRyJ6`s$*)d%!W7R%libw6 zSwsnSn{ZXAho_u_XLE7<*Z|jhK^Da;->WRvswL9BxMj6wdUNlJ>7pg+{v4(1%*3*< ziw6VYtwZ&{hw3BCO#nI%o{gwi$12AwYOEyiMFM!nz%TqX=9jX4PxWL=hydht(Yezr3e1>Y3I)s5U4(o3%)7*m~Zm* zagF)U`tqlS?)I(V+p?1V`RRt~Z)}5m5r(h3f^hL@xj#!%ic?adQniE5I<1$yphFy7 zXeB4eu&_&tU2dm{n@i>LAD*3_)EIcc<51kl2Z~Y{qq8IJeR$o=BJzsX zg<{k`#~kmy)x^`;xT-|Bzv83*i4@Vl`dq=Y!DX4sb}aib_|tcb6!ys9Wi%3}_OffT z>aZ}|iTP-g(+RvO@Am<}b4nho=Xp76Tel#kn3hDg;;Wa2d>8kCp9p}(iGFJkaL@wv6~qx zpC(1B4ULUsm$3OS!*PzWUQ4VUVD{xalgVVMf=LXqE=e)wSZA`(y79bYVaMC#%NWE; z`LdFb@+5ayFhp2oI{djWX(@!#4X7t!Oh2=HOrnQhS0^MDFKmw^l>(%w1^>aXJL{o9 z0zxyC`3@N?=hv9*UgX!U9sCpxseeuz192Bhr3l;O{cR_k$wIU_*$4gobT}4rQCA$w zfeQi)Xc}6rYYr{|y`j6QzHVW9-TVy!-tcBAblu`7+Vr=)mK0Hva0p$2?>xV58S8<1 znb&>{q#nCG+M+HqJC9E#xg7S{xi+HfH@~h7rB`No><$AslSKp;!l^RNCb$;?sb@h% zAO=b(^R*o=U3@;U4g9Ano<+*JBy*IeuA-{y(p>=P5-mT!bqn~!iuk%3yr+q>vYt~V z*%0=W7&JuCg5+#3w{G!XH5hXlS@&aJ&;g7r_iUl^B zQ1~$Hv$}VU#NZ`e{pZ&bailPQU4Dxz)8`?C4`VcLOfIuL#DWM}p_bLTmJL1sIcV}_ z;@=Tj?ifrtr zfV+RPmLmb}WB+CWu2efLgh^%kr5iUXXkQTRLu)$s*C>8n&WG;f8AgDI#vx_A0PWjw zh+!t}v6}v?4+ZgoJ}esL0ls678NOUsdVa(|I5hmX-$RV3KhqDSD0rojgUZm{8HYbg zO+9q_Lmy<86LPStXrb%ig3w+mb0q(mpuEn7>||>*V3n;|KpAp)RkUci`=QT@V;8fI z(xkm~$U$Jv_rKg&w1pNJoeIqb#CL$-(dRVs>sm`V#T-B4{R$<1>~|B$UCpeLYzOn| zgYM;1jZpx19?8QrkcRkYvS2>wM(av5LHdu0h`vdrdtWX&3wvSSrBH}yD3+&5#I_o# zX=U%F8g&b8+KpY*MyPALr-YNrroDWJMFIv$&`c>A3q^rY!ssc#!tH0jZty0dUC^%Wf#0KeEB=BX|8fK-eE?9u?a+&bwD0- zn?gwgS>`pqt~TB$)Y3L1QS*FTdKg~L z2jU%;DRTy?eM#dS;sYgK)*;m)h23BvLwUYCU?5GKAT@R`B5^fgZ1L*c@y^;&_#A$;lNep0!~((2r3SAb|%R zV-ks@2xsdi3_q-xW2bJhUeF?GBN?G~hp;~**JL2Jdt@n*D#qF<(;-h2If=m1iQERY zwjoi%T1E)0I5|UHIwMEyG!b;N*BDq>gBXA}G71WALidu`akak0;~h%rh?>LTlP2eH zIo$hX?bU}l*yaO#cbu`!8-4FkWVPeYB<=B#9?xwqSgKts#RIF#zJ@*fx`*88u)9%o zDvO0+yn5jz!G*+0w8NOFg5i9%6Pik$)1Ysf?5p9|Vc_AkT;M5PdJ-=(t)Tr#rWtR{ zyNd~>ChV&Qf&~3I za6Qs>9RdaA{t@omrI9jZJeJS}|L0XSfd=?C)uwPcA4A}sJyZH~fE?SWGqRF}5J}c= z7P9c&UHXyn^HvRI=Qt~Fg6s?839U{2QYyVEi!fQ&6x+&|EIE^flG5nEUcV#MEKV8S z-U#{ZtCvt!IL0VCz(Z^NK|FXi=>R9b5D!GO9a;Rm@1tA@w~wz}{t2qhHT>F8xJF ze*+CS|Hg@&;8H89QBL$6N%z^{^oh|>6%R&3?aA}^;?4kQYi_eH!2l5{OhC603H)rV zv|fl`CEa1gZ*Ef^ngx#c-9eA*vgc^*&O@%p!8{k&SU?vl`0_j83po1o@K%GCANGr9 zBOEn1=eE`H0)4BwS1jO)kuY)8(s2FawKt=&N^gafA*x+=a4V9gODVO6t;ytNezluY zXQ4JJ+E4Ao)9 zgsF5FY?6Fkm6^oKHGc=Kc|g2QSt zJl>YKpUL#}T+`hCVk0}|tGTz8-q#$dR}dAkKQT~-U*MoZH3`N)WADxkDZO_l~eZB^G9o>n^{zLlR*hsi2@ozn;D0ybuw@&_JWHq z&3O|mh)^CW4|sl*Z6%$3wUy|L-X}k;gRKzfkrr5&7qtefR0?0pa?dbyS3K^$vJ7a8 zGwhI!U*e;oDMP75rc2Bzk24gdZTb-vH49=M9#2Qw4q9t|yUBDa{N!?wjY3u`7TKJf zG*DQo5y{y61gjzRmfFz%VL0ed6F)|JEiratZ|{Nzd^i6^!5G}6r#qQJiTKl`$+MzJCM(1%a$G1!E z7z8fs3f&{;x_sy(^x=D(-p=pz3i&BgG{W6l<*H^iWJr?7*7m#z8NlA}8Hv5KZxCL| zl2&&_;P)6ZVX~{Qwkrnpy%tO!{Wi{%GOy&GclNXK?7{YobjzQ#utj6hecpWGX-F3r>%759(iRD zcGb(fv0$v;Cm41J3d%lpvgalPOUYO7F%rtrYPv_B?LEiOJ%CLizr@6r?+;abVNmiC zUr$O~Fw_O~a&EKi*SqE$s?h!9(hTUwR}E zdAOpNTI75#Sz+9gTSa{rhPwt%fbmnlw7n%XrU z<2ki}KbI7lgLiy}ksP0gmrQ;4jGp+Qx`^UNJH7+Svd=1E>xR~L0e^zWL&5{@hi^mC z?mOQOYEfTkO{uK0&XsBldE4T6GLamWyDro6ZKe+$e)+A1(P zt<>J<-Ri|k;nRd1Y3VtfJfTX7(f6vhaA~;ATl8{C_S-Am0#c$hEEQU*rx+iya+Q2O z>qu;$@MUf(ROj6kCIcspOY}xWf{1>q(si`J^=iS!r0a}c@{`lmR>7xkCC(S$$%++R zzGYqnWW_hoO1(%+Tm^WCNMAILqd$_McdM3LJS zwId~B8+U>+B)Fsi`T@reW!rD59ByJ&Y+o8~{yYz@A^*$sFmT;s4ju6Es_nobt?Pmc-j3gviz{T8A**H3p zV*BnWv@?IShy^&>N=_z#-`SVj*A{`fGEXQt{UPCxW8IA?sou}DCne7TI6m7?qtI6L zF6b&y=>GWG)dF4Lv74-*If`s|)8Un@XheSwGc?Gvy%!lBE825Jru>?R{uuC1UzaGp za(?QM#ol?DyZn4eYDswwXns8*2*)q4iomwqa>}J6PIR58b72Y#II+HJ!o+wcJ8K{C z=w+U(&<^oWCt_bTl47N_Am&A<+XFc!4rz7LQaaU&i)9ZxbdEQ-15)178h~4ts4Bdi z$*(K8(VLo-e}fqj`$R^8P7?d)&O0?=Zi$Dj$Cq=2Z93N~FlYA1;Wq~<@f-5?t=xk} zRJ1Gz7B+A;!+uiod@!xt0$sT3DcgVL_fBSmv&2F)4M|Q}udveg(R89ataEP=A|ZI# zj*-5Fqy*uKj}_W5?YqX?-Pj5$yH6Q+GO#>`tN{EtF4y&{gZXAISd_%Zt!Si#h9 zRO=%T7iJP3F?GFf{*7#WpT|@tAP5~040x6k{JQwy>Qms=wwgigVBs#XgPgM02IET< z6Pkx@^{#V?()4p+cynE2qDRq@B#lvY7x(9Jnh4j+z%@Y4${vH(HkmBG>s!l>s4X6) zQyDVZNwDU!sfv6wgU01&@E<3gjf9Mys&_Sc8g5Am=YtQI!&>wJ;~LGJQtlGmM%-_C zZ&C02{fsCe^=<|9Bhd)1M1+*x$bT3)@od|)eeAgIB=w2PlrAUhtotb?Uyr0{j3&{RNi61HlOR;&L~zlX+XI6AL28jD zZSaqm=k(|@7l$M!U+K{|Gz!n#u58i1oStUY#ql!&l6%N7Ebq+)ee?J3_9xJ&eTcK$ zGwTv3=4@Xfls+m>gOVa7++NGLx!kDmwIz|A((sN~jp1JsVmd>sPb^X#A2GzcYN(Ft zQRBrnJty_h@t2q5~KnrOgNxFmQi!wsU$FKF z4SakzU;);(f>RIVO1|lQl0`)*x9*?k;hUL;xW3f5Q+`hG&K|Ww#>wO~kHr}p0kuOjDKS7QzW=a8c1~+JO_Ylk>-zAb>Is|-aj8LCr<)x8m z1LFxE%B2{PU~QMTA9`PZSu>vqhWVB~A%hKiK) zMXr!p6w#;Bu22d(@G*+bZ88ULg(Gr(A1^3=sdVzJv~oCoF;l)jiGCB*>g%PEl|EH` zIlTdX)=lyKQ~`pHNgCB<%2#J~ZkfV9RA9w1pN*B~5D$jvnt?P8jfzA-Lgtd+2q{^-7!;do|<4XR-&JXNDHORJ%?~M(S zZ|Hao3%a)sebp(P9@QNHaQr^>e=O(Ez5gIbp?)2fhvW2)Jp+lfC4nlyeyzywi#+Ss z$z02K5;TAlU@V7pj-8=!$Q8r7{T(j2$7QZvf(1?6+1`{+==|^qq2--n#zy}kjy2a- zz>6T&);vV3wUeDE9VmmOI|#}YqprDYA0@eWc)`uSfnBO_y4Qzs;|ho2!L&QYt^uVx zZz*O-C2FnVPZg^pa7#sFiul;lWdk^zE?o9(OIlM&r8M&zC54szDJN-zSX0I*yP3>B zBkjtj6U*xvNQP>}^ywYR-hU&3zCHCXqMKt03YO^#c?(%@{Y{7vRx04(2%H_{4IM%3+?663|+7#?ZFw->YxZT<8Ru+s1rn}0a zE$m&>`zA9cC^m2n%xS{$95ZHctI4`#M6g8`5U4W$6R2Vr`2f54VJ-s-QR|W>y9917 zH@=|K6ld^+Dv|gtZh3b(5UFw{8D8N{ zT`+M7CbDnBz%0^&vMnE|*@^%^2Hbrhl2sTY7NjV}@T&PwT#WYvY-*S4YT@~_CY^qf z+M!0V53tQ0o8aT8-weM^GTmY%&PI0p`q2xuUUaiH3FyI_tP2V`fUTBZyRmC=h4Ov9 z(wY)I>a7kV+$lGI&hxFxo13sPO~RNqiu4mkRxAm~>`{$_(UCk>{>dQ2vH7lUH;~9U z`6h1s`6m$d3RMxtNvCPrr)!LX{o0Mi`-m4EW0l<@=PKHvSjXkKH?6VF+| z-Su>E&Whp8Fs5EoQoxt)CLaFl2LDO8(CA!|?o`vn$?+qa)Rf)$CXs=ie5Ong97?WQ zEqcD`hi|?kHZc%r!XUoKj;y)FMUonCZT`yc=De zJ5`&ISZY9vuoj3`0$py;5&Y2Q%@|fr4Zrc`2aM$4O?obfi zB?JrZ5Zv9}9YTTyg1cLAE!^FqaF@c}UVZ1@?t8k=d%x~~tM=Z@$DC`dG3WY^|NeU- ztdOS}(WYTe*s)iQ%R%ozvam>G1^JkzqA#NLhg^32)bRb`FoWZ|33wsO!&#^!traKXge`oC& z*d13ifoWAH+bj8{=sH4j1!|-yzWH|L9PkPO{@%Enr{KEm?-q&Qj|9Osp7==$boEk< zh&(`i{WIf4+Q$c>I4pat$7b14saEu)8lT)i$V99kjF|%so?j+8R`(<3jHPYCCADY} zX+MN_K#kac|8>aHs~pgFbkC9$IMJcF_%+YRV7k3f12Q<2N7@!xEj&54zNPk6UTIiU zEq1O79<3Hwd&Y>sW*b`Yy9bHTjY?uXt`$AK7_SrZ0^Aq$sg_*VagoxMmiVi~?_yiS ztcED`t4pXf1Pd`YnCjz*1-YzWY!3A-TFo8i;2kCjwN^1V^@xa4rC*1C zF|0~kzEK=@k3(o`pJxj2@Yxz!?hbNFV%#KXFO~JVvf2f08!lOS8r>w zM1o%mun)HqtR~`en@CZUic6+gX@M#=*OJx+tO!WX(4hRzs07t4I%R)v2x1Hq+IV&B z>qW~l4F#^qvRT5Z11&{w^9iMR`ZT75G8qzX2~YT!BjK`T2H&muR}W^0x&psG-lm`M z&Nvmw2)9Xk%M~DtE)O<^UbQD?)rwhs9APyS;Rk;zO%cAQiR-gin9hlb)^^p~OjKg% zuzeloD=!p`nB_t{n%|To43V^9f5|&)gAL|vJ@6cFaX92#4h)ANxC-A){2r5axQ!zbP#V+9WM|?Y)*Ue;%U{-E< zw8qK3M~Tiy>p@4~dwLP|pG~E4D6Jk*gY~~q8=W{ui{qm+VaChZwoB6Q0BJKp@fMzs zEKy+f02fce-YL-t?RrIx&&H#y*}bffe?F=qTg`20TIe}jsqF#S7R#k$n}(+>h9Xr0 zPNZJDCE(s_POsEwBbPYiFe6`YY!m(OoI9*d{r2{nc7}$&1;~Fo`sN)D<_*Y5bwA|I z)QA78*W~Hygt3bH3BrTn_*l`!xN1CmP8ugP>OG_A!<3Vp%<>%g=Y0%D6GK=G0&ACk z<{jT5@P@d*$34wi=`QeAB^->AzIlgPA?Yq1koLxGCLDPp(*K%`R4SoP|%A63AOm`AiS10>MO2VL&6W?wCaSiwtJ_kyV1(sRR5%>;s9|L}sfhUq!^IUdh zRiCGY+UOH3M^yi*i9?RcC*PsWbQiw1OHMbXw=X*t=d(5B8e%b4H;0O?Z&Ix?l~Cse zf|~gFl9tGjTklX~axuAX^(bz)CX4Y<;)qEf5r9z0q(iy$E_kJ&?$6*X< zaigv6m_Ae%kZ8_gl{K#25aI?!Cz~ z=k9mDvu-lP=oh!%*mguZM;gmrKNkv_H^P69ohVJ{ZzuKDbo;QDLEX=;xhuuzas?VQ z@_eBN>y`ds51x=rK_OHu-{Kj z$iBb^T<9~ykigZwHU$SEh1D;3CBH&hUE!B$M-P1`t8DQ{$z;m2i7)cOqEB$(o^Q3H z5?xBmW`=C5yj$Qu@%~~l{q*u{{Y|z`@qXIjx2)vzor?vNV+9aSd8OHEy7$k#vegX$8j+my}%@5@c3P&&wq&fG9 zAO&`M0_mofETnh87$hb4N5{lfNz3@AAbFiSISF)$`X)H6jo*LU*yuY~Oyta6m4`Sp zj9lHkNRyJU5&o#jY|t8Jy|*NFLibPN>kD(Fqk8LZZd>blJmV?*ri?=jbuQDyjd72(l$36H;=g1$YVW>BiM%onXWpcoTGS^)9l=_R|-i= zZjqWck(NR*`8>;UC@G5}CHqj7q=v=qZPWj9l;g;42vEyJg?KF_HZ!#C^V@fF%zP6A z5#o#dskh&|(=XsyM~Du!r^T!z#;&B4ZxKydbYXeUx7HtSWG>VQsg+YL;Y>K-uz!i` z=Pl#3S5sJ*U)>gTp5~HQ?zLd7;a(hL&1hZJ4V3Z(W0FM}4JrB!9Eo8u*ZW!raN?hP8yt6M^vn(Au{~qLmhL975mfXzvFtczWgIwQ&DLj7^Q8W=+=llv1Tcc|8 zU6DgA&lS!$VTUQlBSQ)bjPONV3g1&jtvkjT(mcBy%Yoy^x5Ix!(17-!i4cu9O|XE2 zf_SdQH{T`54J#l8P7)Yuf?P(ykPQF5`;cC-74E!*P}f@K zkIqA_3XNJZ^EGb|6@EaBSkyb@8*R`-kF8M_`s_g}If7w6fSinf$gXH|hlSdcn1nY3 zueg1oRgNz)e)ikpfU_M&7?!26NfuqAyI}dw8KE%46-y&u`1N1einrJUd>!{ov(KH8|sykPJ zLE7YXq(Jr9 zjuVN}s%AB%T|_`8ZG$H-s_Ym_UhADY%Va^jyNxx11WZ02nY3L$M#rI0j{O!t2Lh1x z8?Zw>{gQLfDqOb=;s~GONGF5Db&BmdG6*F}OC?XKM@*3Sjy%lA2uvyRG@%tUg=E4} z7hd@?t_j>I8pY)Yoqbmw(Qa+WBUOd1!j|LrAP_B?b1ll+@zv@^&vv!GR978^g=E7i z^oyIXrc8NRH|3xQ&ch%FKCQG#Mi!E_ zje1;_HJXdHZ%&0M^1ea|E6%b;2O=kd>C=vP{nzhbzy+=Q_LE+Vkei#xqojF1%<2cZKK9V!V3ZHwlk zR~$v??ehaZkSLnjDGPH|TI+d+CkBj^bb&cTjOs7&>8mOZ(V|ZrVyI_PAexr&zj&`e zC)+yd6aapLl7!3YAQ44#^0l45FxQHao@>!Sk)*|wI9D8V{yZIjYPrg?6ZbJ{)L#gq zVKkHL#}Ou!0~#~zq)zO1*qxtjDD~wwrdQgADSw)#I8+6_hxKM#f8d*2N!D=vb%RS9 ze`4@u!|C@BGlIg88pj{#!~ISNA8$I+S3nwg0}vrbi{MYj53El;Gz5#DdI{5u>x-XA zb@BqJN9T}+ST}4F3bvVl+C}2UE6)Fjc|U@$`vXJ@_QN?IPueJ_6cWp>y%46hQ@+HY zkLeYM5N*(9_Low9AOqSdizLhr_$QkxU1Wpc%FsMi1En)$Li&JWUbMwy#aX@U2J@P3 zGqJ7)mZ5`k)B$s2CIV&r`C=*_k`)Z#3NUs#=Q_2yDm?hnKnQSUoXOND>gi1!6LeIsZx~6_^Wc}n z>FzjzX@9{Z_h<{BuHt%HWh393noEh`m-DS*f$3WB8ZgjtA+}%* zUHo+1ACQHQ7!ChgFH_VG^Hygb=PuUbkG1 zmF#(o_`R)m|GVu=dn?x>D6=gS%sD+%4CuH^LZ4nj<1J^$xZSE@> z2A;?LVS_d+=XLEBvuG~Ez{`Tb48*L6TNpd--4v}xu%`1>_+(lzUkI(e>;w8g75@PJq0l){$S5a+lH>3wc|DZ5W z8hXVI5785!$FMeE21!9Y(zYN-iv2rxDQWp<*%LXZm5S`j=LazyE@C&%^)wpuXL%Pak=sL0EP>Gx>KO}0L# zEtAWl3!2t?V9OL}A{lK8iWSjhVrw8I5Cx&MCC0J}E-r#)Jw(-!z@~A~jTXwfAsY_G zimzQgUxn*;Wb!Y=bSJlQ8#;!kR+Pd`(6tBh=0S~Yc{{ZH@kWnO!7E=)r!Lt^+J?ky z2r1M_f(2v;)l%5VOBMn+N#<#|oa!LAt8iG)xx--gU%`gLk&UZ{GQ*UsgQOM6FjV>= zKe3Wt_07~u;|5O_??ku~D-P;28;TwtP$*gOr|1ya;f7eg3k9BV)U7ge>IoOR6i+*8 z-)Jy}Ff0|lMy$k(sIKDXPNzAn)cR7D z65&pNNp-{`(VQARp0d2@G~!;GjvlZK=+Y`^l4||45)sq={QVG)kYWZ39^LWD1{tR5 zB<=KS+)gULq07VrK0*~<#V7g|>`nH5hMeHyCl>WHLcj1;!;1ZmX#NuPo7yP#1GqOX zi?n#srMvY41v09yI&Lp5yr?yOFpgx5r!aPl6C;dI0%hMFeLqnp?YL@GYkI04aOfY*NCo{uj!QctMqAL2gpigCL_@HJ zgTHT+@<@H8U?qm&7IM%y`@Bk%tp)q<2=7Il3VwE47uJ7Z#-^Z3yZf@~6u+#ms((Wi zvMDBm;p-_>GlbBY?ZBAH|2SVy`F*w!$ZsFo1Zb_uNzaKPnk!K_Z+SXEWb)O z@tC!y#5=X?&6DM%9KF0AH+y<|s-Ew_1et&S=uInc)C_Jp<|x^RP@D1LCv8&WgAwi@ zSD~XPYm%-Idq7u+c|--|d=~F-Hz8hLN6X90aS{<|Cp?auIfYJ6m!rg+^vMMuW*Ktx z37-VmV9j`p+I}kW0?lY>E+JxVioc$Ee)@i2BqSsj2L=W@-Q3&+h~p!fKfuF5mp0UX z;D8}-9sQ0_@M$Kclk)6?*;xXlPzC#+N|r8_b}%xMlFfc`R8&;?E-vUdY%EL)im?gV zGaWJsueG()hLTcJo$8tgYc?hy%Q~mFPWyC^z^TN!f`=;iXoD+?G^bRI(iyu);VKhqV! z`238MQ=9#fnt?1OB_+{hzwF#$nJRMwp7jVLZyDN$y6IC+glNC+29P*Ad%g*)BH%7? z6BEI1=n3&mCXG7iDK+3ReXP%jE94h3Ge(df5-Gk(QZ(KK-4N@HUkV*OBuAE znpzT*^o>ux$@i1*JKtj{sYnXNTSjRZ1?OkG;<=$K7iy9xKLx9}BpTT!2vF0g7Zgw* zxSpl7ws}+=Fy-gxYsdqJl*Q&j_R0&;5e>eI`F-DrPO)|uWMpJCUC-BFoAL)^A9EeL z{L8J&Z2;Qbr*m zKNr`pU(J1eUych33K}v6+@6H!ECQ0LZL%Hx&dim!HFBgkr}oY4Gou$3MZ!4aT~K+rD6&(Yl$7LfcUB6D+99)4FHdP0pErPY z<8rJH%fVBM&=Jn9EgCJ(-Xs;W_Pz)I68gS7w79*!(+TQgD`w{4m`+Sj-?uJt_#wqr zC*>W-ogZ6#d&G=WO36uCr(;=BQ#h4wpCl#mr6eXu zCwTfhkr?aWA4(WDQNHaT1jm4+B>#JB|Nqcp|K}q{!K)up`iGAfn+(B`D)|TfeUjB1 z*}qgf?N&90CMP3+65-t!`O>0Ra)`vN{HF#3SIfoO%_KD<51X&bENVRvRlF2PFmXZR z#KyMc?2|nKJ>)tx-+J?LMu$D`Cf{v-nG$Zk1=y-I>C{n%Zv~#-1*VEO%{<;H9es&O z5Y-w6K0`9x!QMWpx{AtlbwR=3cBO1#t4w}39U?Nkrrx7i+rF?(Bavm9sJR4gJm`$g zpI=c_s7J9KaPb*vmLBoH`M@H6f^EgXa=d)ePQOTFy>1-5{mXtI{RA9wH z;obD6Awp(PE(2*(qC%N%fXKw6#KLTX32kXt%YB1)X&=YPb$NlPsHm99$;r)2%se&_ z8kdKsfg~^Q&(2OLu7{kWHX#W~TSd{6GHPQNfar(T51AN9P|jsRzq}Y?t{PQPkWHy? zE8>8)mX!-tZeCuy{K5o@%wS(%>@lcVNnP7YyX)%eD*dqJ5H(qO`NQ2{#Hn}~A5zl% z&1=a8;D_xu$Tf7!HIOf8;CWFI5)o0Am6w|wA0OA)#p`w)AE@4g$K4Bx3j^?o@S5cH zG_71dO8*cGiNroqt4ry^w!(v{-E!9bHThT6Qu6O1E{>R-oZKSx=g$@?=nV2i9N*`7 z#aGeg&4J(bhPZ~CfywX2OxadJQ$I{m#=bHAZLX@DQPEZXrL3<$58861iOepeVG^B- zhe>Rm<*?o3V!=pXVU2Nb?v#k=i&o5R`&8(z^sJ7-R)#NAD;ZWC&eTJ0()A4ozwzqx zI3?bWn)F~_vv)!9IVmLT!jnOVcbf#`{KCR%!?MmTapj5vm9F(rUQNp6CscR-%($qi zD6`8;L~Z>xcl{_JFam|7uC=|L(!#+Vz`Y`J81y>Kc??}$V2^;C#r#pW0}?aA&ctl} zi~wHm@BwcEC2*qwo>mHW;~Gw5R+g?fx#}#4j$bzVeYl_rfkXeDotoOl$$KxFN!a%7=Us(z0S)Ci-C4=6$R(;>Y#p02$WVZn%kc`_ zXjk?!%tMt1{tIJn)R8^T&w?0|{^^UD=lVjJYcDXY2Bw&!oh3L!nH=fN*nG@pU z2khLesiPCax>;=4N%x$UX$h^VshYxkgZ1QhcOWrXXm^MI7OC~$M)u!r?SDma{ReRf zhrNcDvwHdZiv9+#-|8A<(DB?2)^E!xD;w$fQ<$3zoX(Yi*Bp$Odv!lw_uC0aJ$YAC zRaL!zFiCJr-4!Ws3p7a!C6ao=re`88_@M-X)g#s&qML9-X8`I*%EcYiN&LE%`R;Hpw!pPM~ zr-1t@AApV5!(2mSVH&dd6y=5#K9xqu`bqI?6#UMSY;YPO|1+gEy33UmM2?gC;1L-a ziBBP%{U^g`GxTirOMF=g{kev!>PcTS31DFOThL{(bfjyYEZq;t)-_U3evj*?++Q{E@czKA;{6vew2|!88q2nk4PG`4Du0Ue$p7eQ;*Ry!j|F>;ikk)H&Gc5{4rz^Xr!6_ zb9;N*=gt(Jg^kTaW=YJh-$XK0B3P?nDxzX4_ousx1Z6fj2+deP9b&%mXlQ8m==ug1 zO*Dr|%rq>ksKar>Vft`x9&|zOU*`Vn&WhjOT4BeFTd=k>bEZ^$JQcIaWR#0 zN=hPSGCDfd!NEPTA@Jx$^K)wbl9G}uW^$S;Jxin6fmr!F^n|iQA861D?4%bfwCmG% zv2h#Bt_Y58Ze^9=#aM~?T`gs#ydIe#GxlhqT=P6CZ$jAawbgA97l#CFXlRH>6C@YM zk!DC?gTt~oYbly5VkN&cVgbn>WO&#(M@f0iVV*Lz0768J+Jn?U2%jvwhD*R_GPbAZ zMJx^#)g4`WQMHFXXm1XD_Q?v4etN+!8RDw_@)gfeNS;iZ{uw?1cRz+TL61Ym%1Fp= zdBK?r@=C4 z9+O~V;?H#H)Ir%&?-XXObkw9kqMpJ&FXjJNu;st6-T#Px`cGGy;H#s6sB-){p1y1C zYSdE*`nreFp9*2WXYpdiOe0MV+|xWQ?Mwj=Clo?%1b%9_!v;Em=Ww@^1p~-xRwc{h z(=Pkf)HQE^?9e=P0+(3zv442Y`4N{{Ycs`Rp+Xx?WYIHua)D19{4F0Rlr@#9uC|t= zuBwVvu|V>&C0~}4r8~YLbd-m~R$S(XO;B^`)o*b}NmFWKVc#VZPnypy@~&1^!@F@r zykCbW*8RRfP-P-K9PT%+flNrRV3u0llB(($;@M&D(ZVps5gkLDRI3YyF@OrRXpG`&F0< zW-z9Vx3i|EM#f6+2m&2A#k&ynF;sKqQ?PVl4hQrtB?A~81Pe*kJheb1=p!SKT5VA{ z1>usAR}(5qN`@Zx4ziUdgd8{NRaows85oXKbYqyLL0hPx6`P&o{=}S6@oofXe8OBh zoFeF5QQ2KC@ry_Za>$)5RAp-X2w<0DKNjbl_tZ93S=A22Cw2TOJ1hC+BO+z}G6lq8 zBqE}y&F!o*F*EZzC_841m?hX#hR)qm33eN_#>?lj3X|XrhM+;BtcQ<)>uK6@wc-2j z(o%D7UMpBnUwzN=Q(voI+%~MEimb$HAO!}e)E79KbUlk9Rxh%gZQTI?@1f zbI69LWwpscz(aFkf;9piuWeXL+m050N6D^GTrl#}m=p&t~w-gnj| zSa2E^i32fZ{5m&b0-LfaB}Hc_n28cHC;$y9Le4BWEdD9kL_0#3RvCpW#F!P%N0i%; z+RA>%S4y*m`FY{QpEC^*ERvAkClmC1X{^+Z&{WeXFzWM=kqiZ)a#{~#=fJFy%7GHM zOxFTo;L^;lesiiZ=zy@q?#lZ5Le>4AZa_)tD?&R_;1FPd#$Kl-4^eCc)7a znF6$n3-iO=Y1u3@;>y8!>VceKqQnqc zp<$;`9-yJAeFVtO=>lk1Z=^L@Md9aeZeH}v$6a)bIxrARvbx|>s8xL1_zA}CRRrkbbP6je55su%MEfWeZ6p^@dH30&uDoR5{I&KJFn#~e24jjLK60U@CWT z1W-RRfXO&17Awk5%QWhVEd|YT){1(kX;z<15>8l=S6ha3x8?+2Ayi=at0wA#*km8D zR0Pn9@t>!;c&H+y$S+G%=U(G}_?lbmai$MjOWUu}tDP%q;5Z5Ju#*mFV0&2lLu?;{ zDNB##v!BbXG3q#LRQ0pB85Pj>#T+#SMvo@KiD*pS86QGY4&!1}+*WKdejY<0eU?e> z00P%LC*}>+h7+8UiL=yic7GB#J3rHw*x7z)WBe)FP9#JJ*BI7KHG`6Kw?Z_wyysk# z)zEAIt%Tk4VZ*g+^FFoR;o(t3p!0eLC#dKslM9u#UP%J@9S*} zVuL5~`&!wdvY`Le3dp|h#^|-W3Evw~Re>+yZtKaW??mNhG&|_DGr-VSgsc6&79tQ& zZhP}^pd-{sGn5M?4Ay^7FGIL%>Su}L;CG-^4q@$h>1yS9h|3EgYFuwYMZ=ss224@e zI0kCfo1->LR3abePMfPwZ>_`0R8WMU$=a-x;5IR!;)b0P+RXMk*;*3+quO&JT0`Sr zDzpE#)C8<(TZ7Xr17hUh*bQGp6kmVcmW0(lgO?2@iF}8$B4PlZ;{vJpC1SDa%M{wa zaMz-XSD31STrV+Nqdhtyi)-E$adwW*-SV0hqVy?M5WN^7@xEQn1nj0cIk#O6%0*hL zStus3%8Pg9*!A9EGONnbJ|y!cK7y9Mq>_-icLc7@=vMY8sK)PHyn^z#5i-w`({jv5 zJoYp2+J_kbK}UHTP&(nACnNRY*UCpE}um2*dC?*jwhir@PP|2^HoKNi?UgwGT| z;&tkSh0E7LAQB`__kRMB4;%8J^|S+H8lc2#nL9OTb!UHnWaUX@n5#LnuLpYa=zLuC zA?jVV(`Zj_#Jy+}!Clt*@Ec9W=sjSZ(4!f?@5O@^n3G;47*NrFSqD=8o5{h6;r=Am z_8uVmH8!mI0msPP%Pf3T&?R za=iih+aDH(7(7W6#!r3kj>~53XQONsl>Dvz$ob>)$g8h;3dfU;3{>)!-y1?-gdIpF z&CSHVm=;WqUGUCNhCHEF;|RH5qPR+nPsC+6#9plrc{@2u=bk0iOt-(a zGao7g-X)tadZPTVzb|P?`6zxWd0kAyr)z&2nRk3uaOkPh@5eZbjfjk-UwdEFXUX3; zM{qX8Ta=WNA>rWMFK4S59O6K>+#Pdp08vk>FrR(>=IxF5aM8uvn;9Sg3ui7FQhrI! zr}qe+^aSUl+Xq#ZX6m#vA2)NOtAEJ@W>G0+ZJ3zkZ(fbkw)|o%X$Wi*#0S{aq+oPh zx32FedU4C=+5b#2?U)VI`8ofaCS3B>$nY0hP+<8UfO9H0a}gC+8f=#K)sbFWP3NR) zDJt--gPuxVv2Ape8x;+fvufk}$@SnHjbqm?E;clP4Id%esO>ec4dwLv=yiJt@aQz^l=m;-E-OwLj?^_FEPgzxL$Sm(G3n4fp2>TYWD7fN8~RC1J?}d zW?fU3wLi^xPkiN|jb~{MVArURguev0u6iY^>rgz$bm4{LU~Ah(tBIu0T3R!DxU{;cz8?&;mM~1G!7N!F znyUu$C2555Q?ow1%?u!oK=T9ipX3X1FE)CsUAx=@wjf~eTo;0K;_l0~XY*rWX^l{E z;r#0=*dBbdJQ=dN!OI*@k;%B37%u>#b|{y(T!fL=mC9G}mDZzJ%S<|>#p78dYGHXQ z-EQiNw6Q*+0^P6CfcpiBzt-p}ug=EoT&J(hCTO5s%2!2i^z)Ea{2IO2c}xw(!YPQ2$iKd$^=4zCHN$fVUiPd;Y&E=hz-RLJi)F888i`7C zeif`j*K^zBE2*dmsm8O5@HnUr5%P&Ke(t0S=`V=J+Yc&W_AJkXXD&Zl^G9g(iE(Lm zF%>=kwQOGKWt1&Anz>OXHtky5<|p2J@(`%V zE99#C5%~Otle`{K+W$zfuyes~)>0tz5nD^oI6w*1k(>dt8tE(gV zmXxmi-UL8VpT3TOYb4Z`>#^_2I5@-XPS>t(sAB8y;sLy5u`uiB#+W+?@aO}GUo9>CTz53rfpu-)>C#M78IgigCHoWT~ECwXr5FTnj3{C8t;tOvwR#X*CSuQ zT8+hoSrm3wlDh~ocSsz#WR)ApgQ=+X0{}IUyxX0nxi6;Thnn=+B!`lM!2j!T;7X*dd1WP$w(Pc2=5sSP z)z`{H<&VWLIvF@uW-N4F4~<*)I#K;DgA;qtwB1@6NO*K7S0~TCk%xS`S?{`bIC_F_ z(KY@-UI2zMh_VFue`)8#)nAR;^2tm*`W62I><&k_I#|B)@^{J}Ta_E$Nc{r;ZTRr^ zbX(pi{GJPu^E^u>IIdua?lpOvNlBSriRxx;I9f(LFAXMIj$Yq4k3jrGNPZ|<2rUun z!dt4YD!`9~Ro(sxYeED&F9OXuyBzc+pyqo6o7BZarq|%6_tP$UbLg+y;oZg}xp(Nc zx7O|Wz4Fr)k#fC#{x?|HQv_7lFvvrRMss>BE&U0Cs5DAlb9Ln^Du{>&|l8)OrsoXZ;yz>Nd&0z|xpZ1+-dN?@Bj{T@p%36JeD;fxjX>J;`2Q#VbeB1XE z_wOY9dM*TfSlgftAA3VoocCuGr1zIafa(W}YX9Cl*uzHc>1nD6UcV~+_f^cURKa(~ zxwDnon+|^mv`?VV5%Wcr+S8p=p3V4WU4{#4acyN~xwU_pO9WH&U+_TkGAK4be6Wla zp3mSH?QIP{;R<<&di)GabUWN{+v-N-%JJ@J0|S0fOLI4W2OeN>+5dfZnaQsHhHz6+ zlpBm0o~B_8xE5mVZ?u=%(Q;W1Sa+tA##f*Izb8k7bjmTv5)TUXuiajtL~~EAc~-}B z?!m_N#sbt~O55ndL4sS0I0l<{Nk_WP0hHtYdsF%THUQ!kcI4fVlfmFurVRQ2*> ziuachW!P$Rrk4+?QQ)<+A}_)t6EzMh&UZ_(`%Ek|0!X)gl`b|xJS*Eo;v8K&jlQyBM`}5rvCukA_BCX;kaia>33qr8U9%=fnE7ZUPl&H}4;58Y|gYxch80^iguL-D@)u`={9EgCD(ns>&|lvz zSidg5>{GsnUXAliJuk2-YtDV>XJXXW?`Q zg@=t8i5{I~1Jf#)?=i9SZ0&eXo2kfVs*n1V;6K=mJ9PX4~SC>!znAWX?; zE4XwBs1dx}*WCcFI)I>Tj(kiSMk^t4QRY$}Q7)o!;Z463`H`xcuRim($fw#{^OK2? zBbB*$>i<+i9f%4M9ZXRl*MC1@DH0@>SRoTRAgb=VJ=qOBeo8rbbKNUdFfAt}7U7eo zHptjr9YS)|7%Ia#6E^?#){;k>62&^xV9029u@Z|S+mpYa4rFtUXzo<0d2jd*ne*Dy z)usy)YSi6UuT)#1a^n;+n#`JameJt-&@`~((sdsNUwbUeAz&HZpr-I6t^pzRFK;Q) zzdu|TA{sE}X|TNYK#2T`ks&KR-t^jbwof(S0R7(!}}peI{CPYK-x-9uH;%1_5juX0g0f9Z{LI{#wF?7iOzy|I{R1~@3=@A z1?IS^H2}Akw>!0zB(>u!UR6m|+m3m%R8q$@-((UU`v?_Id=nkv$M?ttkkGxo)Ks%b zaI0-*vNGLx!0*``E$Fc=&*q0h&L4IUQhSiuSVIgo&V(s{^>4iN`@LALEVE1~y-5Gw zO6Na&q#_f{C0lddF+E&hLAPlC1Al7PIwI-g zpf>y2jj^(}9vqp03z_Si6~)&+k8f*i2379Ma8w_gFew5sO(B;i;YE2eAHhz>JH8M%zd*pU9%AQlq=;uA@7aA}LR}Z+{5E)ztH5bF2zb@f4N$u{_4Z-HR)`nN#fwdhyauzpEYp7I>oHy>-R+kI z_pkhuuVZxCPmO+9U+uu-=VC8aG*wIegZ$R4ZMcjnKf;BX5kNa$=$R4R_gBU1NHm^ z|MW0XcF<&x1*`V=S9h+7`i(<)lL)t(u%R&);SP#gr=GO1>tg=0_YB18Y6TZHef=$G z+dt@+7D#o*s3oj@P^r#5F6q3J(%kOfejyu@^_f}it*b-Y zjQ!09gub5)6w&u0jwQUMU-NtwV86IGV<%~hxy#KhT?y}ccHjlgbNCG$KiJ@-bfN!& zVS7D5!WV2NYQNl9y4aLJ&Mhi3=nRT)d4J>iw6Yo(TR^GyRq#HN$!hqZs(DO1egsoo zpjcVp`R6|1#TWT4d@<5yQF!+lBJ|5vHuy#FP~8nB6Z2SBz7Yln33R6#ZXj{s>7(5P z=WyrUbGm&l490fK9qe5WqP%vv#HH+2c^NP&UYx0@qtS+&$Q(jvCy1;Hzd|zOqT)qa zYo5vcc>z$r9N3mbXY7~w@0*r9uF_QPvh_a}AKhoXo$$FDpftbH2yd`fRpzix!>2`% zyC**QBJOo`*BhRUnjN)Ruzu+N1T*(`>dJ|v*4Hlu%y7`#=WtI#e6(O~QVJnDjy7Ir z=mPFL0J^JJ%QjQ9ZF2Jd;-<7Wz=1oq>B>zB#Rd&!^QXTn;vH$uw@OpOI?ZyQooh>0 zS+ndk9JxXQ`xK--sd^Q2P&7uiG()H|NsO+rX9^S1YNzhFc0}%sZ9fX;kb)* z0qqffm~(n=_@${UYSyjFkrq-uZBnnN(>ULHac_!Lsz6Bxn6JddiC4sbrv!?KnH_UX zY{lZ^)c^K=v;ft9;(v1ue^{fx0U<^pUFF_QH8}2P9z-I8YsHJG2{WJcEOYK&e9t_@ zz#6A;#`MJk7BOR6-@8*;lV;D%OX~Et$3PoxjE= zpo3Jyx?#m-P{!Mqs52G2tCb`0Nact3-k_t_kEs2F;FyE%U6Ju{eM5gJ%niSf9Whn4 z;4?sMoU^`t7`s4Oaza#Sl_aMIqS}BW3Fb-%=;&kQH}3eAdav&++ix@f$jaf&va2GMK&-UnAuC5==~BN*eSSlC zh8IH5Mma|;-02Ek+@Ex@0qfG;hZ~wW1Rr#XEF%2|%s4@55Ak``7#5hQIootQ6+`X= z<@P&J>TeNGQ#7Wqp14ruSQ_ho_8j1;|;{9ZujuWaJjnZIj+6E@=Q!qT*897*YM}+F8a{2*av(UxA=|_7PjZO zA8V2O6ykM(t~DoV&;G9!3w_NBO$*Cux;yx&^spLziX(H3z zm)zvyn4SB)aVh9i7qL0_8O7T|QNujHZJ(fKbLaN*%gv+sOk@3rW02C(x<#hRu! zNVvWmx7AK@e zPrnkxtAl?{ebi@V?XlLPu3*c?2qn#@z!MQs>@r^Una2K+6ndYP;Khd|^XxrA5|9n` zxecvHgjNr2(EnB~=GZe}QH*`J7k%q0AuB|v1;=rs<=sjhTtDg6@5izp0-=>wtp713 zE2+uw0q?6x?(#J&IIXurk%JR}_?Yda4P?sP$#n^*J;%jP*W8WwFI2)#*#K9Rl+F0$|41oW28FIOUUJ}nA?Z7#EJ zrX@gLMh0ocZIyVC!LQ8nFM3`+OWeec)Vmpb;PtX!lf`wk@MzLJqVoApPR}Ksd9*^* z*C8Ta|BU`qaQV7uNogazs*0+7XZF0g=ggJ&sIPddKh;0<>8X2g6w;LzS8_^i5LZC3 z+z#S?2vCEl$=2iCKSGBa@gCwO03PL3aJpc}?d$mxrxIBiazA37N>pPk`slF9URhsk zN5^!F1vIUm>hl6GR6;ffv$}UdyQ_8?Vi2zJrBH+clg_7@tKie>hF8jkhKCjwKo` zICa-FqbeqxAhZe4yUS;K zz~g_aRX$2W&zLi9Adfe=kDj4Bt@bnkXZmJb^|rIUn*V9rqPXX6J{6uj8U?75(DDNmxNRqb*IZGq3geI@|rB@EkMFbxSO}YcsUe1^oI|_CSdGg zhD-2H*5in;_l83EF!{xKl%dj)Y7qYR!|YQp>IgzQmGq^s(?^y~>w@cP*Uq0F zki97+$+qn+G}Qc>=S8(wi{^{!@HyyRl`&1GT6N-GC2H4^+>Wq*m~icFQPeW1@A`UgV5C5b= z6I#p&{9kRIb9g1qx9?-y&V(})CllNDWa4B;6Wg}!WMbPkC$?>K$Hv|7`@84fbDn$q zzwXu5UDdt1*3-4>`zb*$A7bum4k#rO2MD?tF83->>xT4(TtHZr{|nUs!7`Elg>Hc0 zz^T3egR=o*uz(KyYX=qh7at6QvizsOf3EPKGydC@qW?1j_+Ob!5IqBs%1uf{XPJtM zfJ&a%LH1v_K;IzsMCS>W`{kMP2ESU_=vuF)l1a* z`S9tf;>i0Jevx|t+Tx2Duy)imp)>rSJ%cZ91m?Zm2hegg!^>wTe2>i%%Sztspky)Z zf2+~|fq(v2jX<_17=@=0)h;f6{2W>0eePv|7zv_ zhs@xAPj8e2%T7SflEF@))5y#EEar^FHY*Q4o_LLs%O+Nj4ZJA6sIYS!vP zqUq-1AGJXNJ^6oLe={N!-wxwy=+~3Itp=;wq6;0_BZcM@fyP+WXvYFASqUu=%dTJ* z4zuwe<3!%=Ey&-3UwWsehqUh1YjrD53h#`wEaT6E_iqj47z_7CsLf7UUtB9lXxAdx zGUW~)aV(f9a=dHG@S_FA6*MC}O@uz57FA!`C6);*->jQF%MK^uYu*kUl)c+thAMh5 z)B_rAV!AnEWR!MqB5IUfgWnHuLtz+iYWRibvXR_X#ox%fpG)f%g?dz;>4t$Ul};6Y z9*dF%Kz}-ce+|ZFH7oTj7xdjEsz*l(Al#8=d6ueuj%sZRjcHljo9D>Ufb^mTwgTJt zZa~9gLva#O$*=RmHx~a95S@;P$_9ids+KgT)$AZ+oE^e#v{9~6%^pnX-MQW3+Bm$cGL^WP3#69Ix&od05RqKxSt^bZ&oSAt(A7~wMZ?XqXydiLL5%wLK( zE7s{U4~{ql>e)Ew_HnB^4_taq-yrZuxHrbHl@WRQjHYa|MhgF$>tRGU(>M|kv?Hnhr^~val)YWo4*OW@lYos0D1Vo z1F5TBaR);}mbsF1Vx#D#DezVyFCE^LLvR5aX*Ag|LL*1TZy#S~1>>Z}+*kkJoXLi# znX3dSA%qN-UP>eog#aIBTbV-mvzLh!y0}vcSPvBAZge`hmZoJM%BJ2=CeC zA9|AUIRs~WOyQVy25;-kPqfX*`Y)JYO2pjSG#ZSH#`QY1OZ*Dz?G289NHxU$`Z~y|&5M!fnt5ffsG+0}s z!*MvBax}akiLUMs`}>;gv&+KI9UCK=h-hPt8J}E33;h6r?9Fq@9@ME^)sA4BTF*}O3eBn<#0(#6Ju{81uSHP z7_Ng0hhWtJIY!m+BF#x^`b_x%BEgI2Xt1@@!?r9#$3Twd-y#1Wz1V*X5uk+AmaVa| z@hUff)A?K-c21A1S7o?~SGz-d987Hnsh_~|Yjm4om&L7LAjPE5=e;H9&+VCIEK5fh zzUD~jGppqN^O1^`on7^GHY+srNe6fG&SL^vqcysQ+V%EQ+_#EP-{6#n(`g%_J3F9aw5}L^7dFn_UUWTQr)+)JRXB0tX&!oJq&#}i zFA_CrX;eSppsTy!4ro*S%G(f}Kd*TuTTk7OZAVoW^?X93D?;!}LNue)RhxRB{hE4@ zc`i-ao%3Js0{lTVwq^cIGID-!gw0o|*-N)!Am2sSHuJ_}?_XVuo*6C#Z(4lH6au(9 zge_NSTQTW{)rHuH>AmTTG@0C|{Ks5o8U$tI2{4mKR+l7V9?KSp?!yKuX#6h=ap4zt3*M2fM+Z0J{>^{dZA6;xL#jfc<3SXZOjT+-#V^0zNy z$@n{9^V}OS!v0z{y z6ba@kKNDuI(BUTJK22edhOTtpm~i4M{^aftG-~Rg_50v)(;^zjr@So-ZyX)ZEU_g4 zN`DEOU>VVPugs>wZ8&1w{DmYBO`-^675&(U8-d6v9PC*E5+><|RzqSiKv9@m3Ikm& zUSXv=Z%J##hJ)PE)%*fRgt3U`ULaD>!J%Y_yECsgIxb6vVzgNK;Ci&8O)qS!#ARS9 zgutKOtGTIoF`bYmb{s_?+LFMRrA1WSHdbaPGW= z*$?lwvskFkeE&O=+m-0ao<1D68z*)$Mzf+fdskO9_h*_$30^v|D>|aO4o7VbM!d&Gf_{l(F9TCE{%!U_@2! zSS&LVYQ91mlVLN8+5M#PMvKuBiFK>qatM<7;-gHTXmU47_+XJNHZRGRE|KanNg_pU zIucQ=r6D%VWA`S6P%4xSvQ(cT#{i7c)$Q$V<@?J$;_M290?2iTa33$7dPn67NUhTkc^cowah#(4O;kG#AibSDedTdvtZVfv-lt0zEWxWWTZ{KVHuYM9VrRHboW8e8#e@eeOS^Z1-r4(sZtnqQ;eya6rMHp z@N1)3CF~_9ho)dq6$(}-na&lP?eL8_kb~>|@aS`-!ReGM8oy3toNu8ZeBPBT$fX!? z2XRd+@l{pT(Y^+VkU`1BceEE1ShNR;?A+uWX`KD`;+jdgeQ$cZXl6*Zt}B+0A54PG zXTI-Zwfy4CC(MnR3D5=pbs(g@x>GA0&fuD8)RS#eJTtH>89&Z8WyvKqs`3GV!h?-y(D4;HP^ zYTVg^OWOWjdNWsZfXPdsZ$bi$(WHz+ouvQoA0LrxyDYvFT8eMGKfCrSM&@0M#yHn# z7Fr*>fcAsgFrQ3jzE|da!7b06=oGyG$-Yfe43T%`E$eF3$aKUgS7!$*YND!uOqlgG zS@;hHaXg%;$bJQl9q*cB?V7ghX%gZ?>pAlB(O@0cFsd!f-GX6>I(95VCJ!z6Q5r%p zHmUlSFjg%cs4h4W45X)o0z4n8)BE>gr=-3}gbsW8jfDLXIA!ws&Huv7g!!cR42|)&P8_B#U52Mm?yJI3 z3V%n2%;aN%O-)h~5c2VGHRbUewgrAs)IYt6bTlfpvk|XYl8?gQvO0B+h85A!T3CKl z78Fu2jKz^KGxWrCc{|EdyAm+$*`M;h+{FZ0i*7=8@I-`sJtP@|+_11)YQ|qKrCRu2 zKPOki-&;+YV*VC>WUKI?SWxU}l|enxJfN#QL*>@ae2bC2GP3v{?2F&l3io+THK@f^ zJ54piIem={DW)#u-}r6L!#e6CX=*c~HDnVSW(YD^ndOp>oQ5t{q0IGE2zEcyUeJOQs89HeS zu`3MtGThW@o(r4~EROZZXYvb_{j5!xMlG?^Y$1ra5)GbmGJw;;z)Uq|uiP3+-AKJ; zTFlZaoE|;3UD0S_(9<`*GFcW;jJ1?D#H&dMOy*h-71LI!3G{YM(vfMWNNgTwYC}hF z04Efy)$>y7;GX5L(#rY0a>D_y_5N5^dq02U-H~ktIF>aDc ztP~-vA~7>ha^o1u{qMV>9=pu(h@Gco<8b zE23BR-IzO{EpiqG=IfngkN|?vbIwq7nG0{+vKc3W#a38%D(V-~ezPzES%K+HID$A@ zo7%NmX=-DE1#dy3oeHUMR)_-2i z`}&gKHuCP_`VndtJl4%PLCAqVLVyFg0Us1&M3#YfQ}zDj#F1wb^9r6^r4v()f-y3B zB0@Pf@|(G4-nh5p8zc#3JoaQ}^V~f=2e$@C+Ypw%%m!44iz#e0o;w(eh_n0E&L)DVn!(@r3@lm6pRlCcxL+f#!foa_*{lJXVKz}T5RK^T)Zj55Ebe;| z-x0kCKK|f7=-sDLl#oAuXRm`k3{>fOIA>ltg0EGrRoPGHAe#K{Bp*65ps^)$fv9qR zMskVjZtHCtfEjaM+8gNuvGQ{=`?U5aFOnAY+pl50m2D$IDq$;x2mzEM{RumYtW9C- z*<5bUz$M)+rUD5hlKXgC=Vc@}O-LhQ3Ir5RyCTL7o}2AWnTK!voTPaey$e2l-%#sFiw|Hm$Y|KBOV{b*-GQ0e0qWrh-ws&8XjiuB zorSfesTntZUB1VefS3k@>+GzwOMK(A^cZpXIPvo9lem?8oFXSUi*<+YJH~DES&m>n z>DxiE;v5yjdk5Yl⁢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 diff --git a/apps/documenteditor/main/resources/less/statusbar.less b/apps/documenteditor/main/resources/less/statusbar.less index 5ba4ba559..4c23b4571 100644 --- a/apps/documenteditor/main/resources/less/statusbar.less +++ b/apps/documenteditor/main/resources/less/statusbar.less @@ -273,6 +273,7 @@ &.en-GB {background-position: -32px -1608px;} &.en-AU {background-position: 0 -1620px;} &.az-Latn-AZ {background-position: -16px -1620px;} + &.id, &.id-ID {background-position: -32px -1620px;} &.bg, &.bg-BG {background-position: 0 -1720px;} &.ca-ES-valencia {background-position: -16px -1720px;} From 2c441b6fecd16b3ba76cc3c2822ff07c220321fe Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Thu, 6 Apr 2017 10:08:00 +0300 Subject: [PATCH 05/27] Update translations + [DE] Fix Bug 34430. --- apps/documenteditor/main/locale/de.json | 4 +++- apps/documenteditor/main/locale/en.json | 5 +++-- apps/documenteditor/main/locale/fr.json | 8 +++++--- apps/documenteditor/main/locale/ru.json | 4 +++- apps/documenteditor/mobile/locale/de.json | 1 + apps/documenteditor/mobile/locale/en.json | 2 +- apps/documenteditor/mobile/locale/fr.json | 3 ++- apps/documenteditor/mobile/locale/ru.json | 1 + apps/presentationeditor/main/locale/de.json | 1 + apps/presentationeditor/main/locale/en.json | 2 +- apps/presentationeditor/main/locale/fr.json | 3 ++- apps/presentationeditor/main/locale/ru.json | 1 + apps/presentationeditor/mobile/locale/de.json | 1 + apps/presentationeditor/mobile/locale/en.json | 4 ++-- apps/presentationeditor/mobile/locale/fr.json | 3 ++- apps/presentationeditor/mobile/locale/ru.json | 1 + apps/spreadsheeteditor/main/locale/fr.json | 4 ++-- 17 files changed, 32 insertions(+), 16 deletions(-) diff --git a/apps/documenteditor/main/locale/de.json b/apps/documenteditor/main/locale/de.json index 17f1b0f35..3292d4abd 100644 --- a/apps/documenteditor/main/locale/de.json +++ b/apps/documenteditor/main/locale/de.json @@ -205,6 +205,7 @@ "DE.Controllers.Main.downloadTextText": "Dokument wird heruntergeladen...", "DE.Controllers.Main.downloadTitleText": "Herunterladen des Dokuments", "DE.Controllers.Main.errorAccessDeny": "Sie haben versucht die Änderungen im Dokument, zu dem Sie keine Berechtigungen haben, vorzunehemen.
    Wenden Sie sich an Ihren Serveradministrator.", + "DE.Controllers.Main.errorBadImageUrl": "URL des Bildes ist falsch", "DE.Controllers.Main.errorCoAuthoringDisconnect": "Verbindung zum Server ist verloren gegangen. Das Dokument kann momentan nicht bearbeitet werden.", "DE.Controllers.Main.errorConnectToServer": "Das Dokument konnte nicht gespeichert werden. Bitte überprüfen Sie die Verbindungseinstellungen, oder richten Sie an Ihren Administrator.
    Wann Sie auf den Button \"OK\" klicken, werden Sie aufgefordert das Dokument herunterzuladen.

    Mehr Information zur Verbindung des Dokument Servers finden Sie hier", "DE.Controllers.Main.errorDatabaseConnection": "Externer Fehler.
    Fehler beim Verbinden zur Datenbank. Bitte wenden Sie sich an den Kundendienst, falls der Fehler bestehen bleibt.", @@ -266,6 +267,7 @@ "DE.Controllers.Main.textContactUs": "Verkaufsteam kontaktieren", "DE.Controllers.Main.textLoadingDocument": "Dokument wird geladen...\t", "DE.Controllers.Main.textNoLicenseTitle": "ONLYOFFICE Open Source Version", + "DE.Controllers.Main.textShape": "Form", "DE.Controllers.Main.textStrict": "Formaler Modus", "DE.Controllers.Main.textTryUndoRedo": "Undo/Redo Optionen sind für den halbformalen Zusammenbearbeitungsmodus deaktiviert.
    Klicken Sie auf den Button \"Formaler Modus\", um den formalen Zusammenbearbeitungsmodus zu aktivieren, um die Datei, ohne Störungen anderer Benutzer zu bearbeiten und die Änderungen erst nachdem Sie sie gespeichert haben, zu senden. Sie können zwischen den Zusammenbearbeitungsmodi mit der Hilfe der erweiterten Einstellungen von Editor umschalten.", "DE.Controllers.Main.titleLicenseExp": "Lizenz ist abgelaufen", @@ -1345,7 +1347,7 @@ "DE.Views.TableSettings.textSelectBorders": "Wählen Sie Rahmenlinien, auf die ein anderer Stil angewandt wird", "DE.Views.TableSettings.textTemplate": "Vorlage auswählen", "DE.Views.TableSettings.textTotal": "Insgesamt", - "DE.Views.TableSettings.textWrap": "Textumbruch", + "DE.Views.TableSettings.textWrap": "Umbruch", "DE.Views.TableSettings.textWrapNoneTooltip": "Inline-Tabelle", "DE.Views.TableSettings.textWrapParallelTooltip": "Flow-Tabelle", "DE.Views.TableSettings.tipAll": "Äußere Rahmenlinie und alle inneren Linien festlegen", diff --git a/apps/documenteditor/main/locale/en.json b/apps/documenteditor/main/locale/en.json index 1301956cf..4ab3363d7 100644 --- a/apps/documenteditor/main/locale/en.json +++ b/apps/documenteditor/main/locale/en.json @@ -205,6 +205,7 @@ "DE.Controllers.Main.downloadTextText": "Downloading document...", "DE.Controllers.Main.downloadTitleText": "Downloading Document", "DE.Controllers.Main.errorAccessDeny": "You are trying to perform an action you do not have rights for.
    Please contact your Document Server administrator.", + "DE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "DE.Controllers.Main.errorCoAuthoringDisconnect": "Server connection lost. The document cannot be edited right now.", "DE.Controllers.Main.errorConnectToServer": "The document could not be saved. Please check connection settings or contact your administrator.
    When you click the 'OK' button, you will be prompted to download the document.

    Find more information about connecting Document Server here", "DE.Controllers.Main.errorDatabaseConnection": "External error.
    Database connection error. Please contact support in case the error persists.", @@ -227,7 +228,6 @@ "DE.Controllers.Main.errorUserDrop": "The file cannot be accessed right now.", "DE.Controllers.Main.errorUsersExceed": "The number of users allowed by the pricing plan was exceeded", "DE.Controllers.Main.errorViewerDisconnect": "Connection is lost. You can still view the document,
    but will not be able to download or print until the connection is restored.", - "DE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "DE.Controllers.Main.leavePageText": "You have unsaved changes in this document. Click \"Stay on This Page\", then \"Save\" to save them. Click \"Leave This Page\" to discard all the unsaved changes.", "DE.Controllers.Main.loadFontsTextText": "Loading data...", "DE.Controllers.Main.loadFontsTitleText": "Loading Data", @@ -267,6 +267,7 @@ "DE.Controllers.Main.textContactUs": "Contact sales", "DE.Controllers.Main.textLoadingDocument": "Loading document", "DE.Controllers.Main.textNoLicenseTitle": "ONLYOFFICE open source version", + "DE.Controllers.Main.textShape": "Shape", "DE.Controllers.Main.textStrict": "Strict mode", "DE.Controllers.Main.textTryUndoRedo": "The Undo/Redo functions are disabled for the Fast co-editing mode.
    Click the 'Strict mode' button to switch to the Strict co-editing mode to edit the file without other users interference and send your changes only after you save them. You can switch between the co-editing modes using the editor Advanced settings.", "DE.Controllers.Main.titleLicenseExp": "License expired", @@ -1346,7 +1347,7 @@ "DE.Views.TableSettings.textSelectBorders": "Select borders you want to change applying style chosen above", "DE.Views.TableSettings.textTemplate": "Select From Template", "DE.Views.TableSettings.textTotal": "Total", - "DE.Views.TableSettings.textWrap": "Text Wrapping", + "DE.Views.TableSettings.textWrap": "Wrapping Style", "DE.Views.TableSettings.textWrapNoneTooltip": "Inline table", "DE.Views.TableSettings.textWrapParallelTooltip": "Flow table", "DE.Views.TableSettings.tipAll": "Set Outer Border and All Inner Lines", diff --git a/apps/documenteditor/main/locale/fr.json b/apps/documenteditor/main/locale/fr.json index 68aa3e049..81a6bfaa0 100644 --- a/apps/documenteditor/main/locale/fr.json +++ b/apps/documenteditor/main/locale/fr.json @@ -205,6 +205,7 @@ "DE.Controllers.Main.downloadTextText": "Téléchargement du document...", "DE.Controllers.Main.downloadTitleText": "Téléchargement du document", "DE.Controllers.Main.errorAccessDeny": "Vous tentez d'exéсuter une action pour laquelle vous ne disposez pas des droits.
    Veuillez contacter l'administrateur de Document Server.", + "DE.Controllers.Main.errorBadImageUrl": "L'URL de l'image est incorrecte", "DE.Controllers.Main.errorCoAuthoringDisconnect": "Connexion au serveur perdue. Le document ne peut être modifié en ce moment.", "DE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
    Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

    Trouvez plus d'informations sur la connexion de Document Serverici", "DE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
    Erreur de connexion à la base de données. Si l'erreur persiste veillez contactez l'assistance technique.", @@ -266,6 +267,7 @@ "DE.Controllers.Main.textContactUs": "Contacter l'équipe de ventes", "DE.Controllers.Main.textLoadingDocument": "Chargement du document", "DE.Controllers.Main.textNoLicenseTitle": "La version open source de ONLYOFFICE", + "DE.Controllers.Main.textShape": "Forme", "DE.Controllers.Main.textStrict": "Mode strict", "DE.Controllers.Main.textTryUndoRedo": "Les fonctions annuler/rétablir sont désactivées pour le mode de co-édition rapide.
    Cliquez sur le bouton \"Mode strict\" pour passer au mode de la co-édition stricte pour modifier le fichier sans interférence d'autres utilisateurs et envoyer vos modifications seulement après que vous les enregistrez. Vous pouvez basculer entre les modes de co-édition à l'aide de paramètres avancés d'éditeur.", "DE.Controllers.Main.titleLicenseExp": "Licence expirée", @@ -1178,7 +1180,7 @@ "DE.Views.ParagraphSettingsAdvanced.strAllCaps": "Toutes en majuscules", "DE.Views.ParagraphSettingsAdvanced.strBorders": "Bordures et remplissage", "DE.Views.ParagraphSettingsAdvanced.strBreakBefore": "Saut de page avant", - "DE.Views.ParagraphSettingsAdvanced.strDoubleStrike": "Double biffés", + "DE.Views.ParagraphSettingsAdvanced.strDoubleStrike": "Barré double", "DE.Views.ParagraphSettingsAdvanced.strIndentsFirstLine": "Première ligne", "DE.Views.ParagraphSettingsAdvanced.strIndentsLeftText": "A gauche", "DE.Views.ParagraphSettingsAdvanced.strIndentsRightText": "A droite", @@ -1190,7 +1192,7 @@ "DE.Views.ParagraphSettingsAdvanced.strParagraphIndents": "Retraits et emplacement", "DE.Views.ParagraphSettingsAdvanced.strParagraphPosition": "Emplacement", "DE.Views.ParagraphSettingsAdvanced.strSmallCaps": "Petites majuscules", - "DE.Views.ParagraphSettingsAdvanced.strStrike": "Biffés", + "DE.Views.ParagraphSettingsAdvanced.strStrike": "Barré", "DE.Views.ParagraphSettingsAdvanced.strSubscript": "Indice", "DE.Views.ParagraphSettingsAdvanced.strSuperscript": "Exposant", "DE.Views.ParagraphSettingsAdvanced.strTabs": "Tabulation", @@ -1345,7 +1347,7 @@ "DE.Views.TableSettings.textSelectBorders": "Sélectionnez les bordures à modifier en appliquant le style choisi ci-dessus", "DE.Views.TableSettings.textTemplate": "Sélectionner à partir d'un modèle", "DE.Views.TableSettings.textTotal": "Total", - "DE.Views.TableSettings.textWrap": "Habillage du texte", + "DE.Views.TableSettings.textWrap": "Style d'habillage", "DE.Views.TableSettings.textWrapNoneTooltip": "Tableau aligné", "DE.Views.TableSettings.textWrapParallelTooltip": "Tableau flottant", "DE.Views.TableSettings.tipAll": "Bordure extérieure et la totalité des lignes intérieures", diff --git a/apps/documenteditor/main/locale/ru.json b/apps/documenteditor/main/locale/ru.json index e28461b8f..12ee32989 100644 --- a/apps/documenteditor/main/locale/ru.json +++ b/apps/documenteditor/main/locale/ru.json @@ -205,6 +205,7 @@ "DE.Controllers.Main.downloadTextText": "Загрузка документа...", "DE.Controllers.Main.downloadTitleText": "Загрузка документа", "DE.Controllers.Main.errorAccessDeny": "Вы пытаетесь выполнить действие, на которое у вас нет прав.
    Пожалуйста, обратитесь к администратору Сервера документов.", + "DE.Controllers.Main.errorBadImageUrl": "Неправильный URL-адрес изображения", "DE.Controllers.Main.errorCoAuthoringDisconnect": "Потеряно соединение с сервером. В данный момент нельзя отредактировать документ.", "DE.Controllers.Main.errorConnectToServer": "Не удается сохранить документ. Проверьте параметры подключения или обратитесь к вашему администратору.
    Когда вы нажмете на кнопку 'OK', вам будет предложено скачать документ.

    Дополнительную информацию о подключении Сервера документов можно найти здесь", "DE.Controllers.Main.errorDatabaseConnection": "Внешняя ошибка.
    Ошибка подключения к базе данных. Если ошибка повторяется, пожалуйста, обратитесь в службу поддержки.", @@ -266,6 +267,7 @@ "DE.Controllers.Main.textContactUs": "Связаться с отделом продаж", "DE.Controllers.Main.textLoadingDocument": "Загрузка документа", "DE.Controllers.Main.textNoLicenseTitle": "Open source версия ONLYOFFICE", + "DE.Controllers.Main.textShape": "Фигура", "DE.Controllers.Main.textStrict": "Строгий режим", "DE.Controllers.Main.textTryUndoRedo": "Функции отмены и повтора действий отключены в Быстром режиме совместного редактирования.
    Нажмите на кнопку 'Строгий режим' для переключения в Строгий режим совместного редактирования, чтобы редактировать файл без вмешательства других пользователей и отправлять изменения только после того, как вы их сохраните. Переключаться между режимами совместного редактирования можно с помощью Дополнительных параметров редактора.", "DE.Controllers.Main.titleLicenseExp": "Истек срок действия лицензии", @@ -1345,7 +1347,7 @@ "DE.Views.TableSettings.textSelectBorders": "Выберите границы, к которым надо применить выбранный стиль", "DE.Views.TableSettings.textTemplate": "По шаблону", "DE.Views.TableSettings.textTotal": "Итоговая", - "DE.Views.TableSettings.textWrap": "Обтекание текстом", + "DE.Views.TableSettings.textWrap": "Стиль обтекания", "DE.Views.TableSettings.textWrapNoneTooltip": "Встроенная таблица", "DE.Views.TableSettings.textWrapParallelTooltip": "Плавающая таблица", "DE.Views.TableSettings.tipAll": "Задать внешнюю границу и все внутренние линии", diff --git a/apps/documenteditor/mobile/locale/de.json b/apps/documenteditor/mobile/locale/de.json index 9ed00928a..cbc8c7e85 100644 --- a/apps/documenteditor/mobile/locale/de.json +++ b/apps/documenteditor/mobile/locale/de.json @@ -52,6 +52,7 @@ "DE.Controllers.Main.downloadMergeTitle": "Wird heruntergeladen\t", "DE.Controllers.Main.downloadTextText": "Dokument wird heruntergeladen...", "DE.Controllers.Main.downloadTitleText": "Herunterladen des Dokuments", + "DE.Controllers.Main.errorBadImageUrl": "URL des Bildes ist falsch", "DE.Controllers.Main.errorCoAuthoringDisconnect": "Verbindung zum Server ist verloren gegangen. Sie können nicht mehr editieren.", "DE.Controllers.Main.errorConnectToServer": "Das Dokument konnte nicht gespeichert werden. Bitte überprüfen Sie die Verbindungseinstellungen, oder richten Sie an Ihren Administrator.
    Wann Sie auf den Button \"OK\" klicken, werden Sie aufgefordert, das Dokument herunterzuladen.

    Mehr Information zur Verbindung des Dokument Servers finden Sie hier", "DE.Controllers.Main.errorDatabaseConnection": "Externer Fehler.
    Datenbank-Verbindungsfehler. Wenden Sie sich an den Support.", diff --git a/apps/documenteditor/mobile/locale/en.json b/apps/documenteditor/mobile/locale/en.json index 196204f7a..22dc3ea1f 100644 --- a/apps/documenteditor/mobile/locale/en.json +++ b/apps/documenteditor/mobile/locale/en.json @@ -52,6 +52,7 @@ "DE.Controllers.Main.downloadMergeTitle": "Downloading", "DE.Controllers.Main.downloadTextText": "Downloading document...", "DE.Controllers.Main.downloadTitleText": "Downloading Document", + "DE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "DE.Controllers.Main.errorCoAuthoringDisconnect": "Server connection lost. You can't edit anymore.", "DE.Controllers.Main.errorConnectToServer": " The document could not be saved. Please check connection settings or contact your administrator.
    When you click the 'OK' button, you will be prompted to download the document.

    Find more information about connecting Document Server here", "DE.Controllers.Main.errorDatabaseConnection": "External error.
    Database connection error. Please, contact support.", @@ -69,7 +70,6 @@ "DE.Controllers.Main.errorUserDrop": "The file cannot be accessed right now.", "DE.Controllers.Main.errorUsersExceed": "The number of users was exceeded", "DE.Controllers.Main.errorViewerDisconnect": "Connection is lost. You can still view the document,
    but will not be able to download until the connection is restored.", - "DE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "DE.Controllers.Main.leavePageText": "You have unsaved changes in this document. Click 'Stay on this Page' to await the autosave of the document. Click 'Leave this Page' to discard all the unsaved changes.", "DE.Controllers.Main.loadFontsTextText": "Loading data...", "DE.Controllers.Main.loadFontsTitleText": "Loading Data", diff --git a/apps/documenteditor/mobile/locale/fr.json b/apps/documenteditor/mobile/locale/fr.json index cb984fa64..66c5de88f 100644 --- a/apps/documenteditor/mobile/locale/fr.json +++ b/apps/documenteditor/mobile/locale/fr.json @@ -52,6 +52,7 @@ "DE.Controllers.Main.downloadMergeTitle": "Téléchargement en cours", "DE.Controllers.Main.downloadTextText": "Téléchargement du document...", "DE.Controllers.Main.downloadTitleText": "Téléchargement du document", + "DE.Controllers.Main.errorBadImageUrl": "L'URL de l'image est incorrecte", "DE.Controllers.Main.errorCoAuthoringDisconnect": "La connexion au serveur perdue. Désolé, vous ne pouvez plus modifier le document.", "DE.Controllers.Main.errorConnectToServer": "Impossible d'enregistrer le document. Veuillez vérifier vos paramètres de connexion ou contactez l'administrateur.
    Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

    Trouvez plus d'informations sur la connexion de Document Server ici", "DE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
    Erreur de connexion à la base de données.Contactez le support.", @@ -299,7 +300,7 @@ "DE.Views.EditText.textAutomatic": "Automatique", "DE.Views.EditText.textBack": "Retour", "DE.Views.EditText.textBullets": "Puces", - "DE.Views.EditText.textDblStrikethrough": "Double biffés", + "DE.Views.EditText.textDblStrikethrough": "Barré double", "DE.Views.EditText.textDblSuperscript": "Exposant", "DE.Views.EditText.textFontColor": "Couleur de police", "DE.Views.EditText.textFontColors": "Couleurs de police", diff --git a/apps/documenteditor/mobile/locale/ru.json b/apps/documenteditor/mobile/locale/ru.json index 7a9f9d52c..efac5c8f2 100644 --- a/apps/documenteditor/mobile/locale/ru.json +++ b/apps/documenteditor/mobile/locale/ru.json @@ -52,6 +52,7 @@ "DE.Controllers.Main.downloadMergeTitle": "Загрузка", "DE.Controllers.Main.downloadTextText": "Загрузка документа...", "DE.Controllers.Main.downloadTitleText": "Загрузка документа", + "DE.Controllers.Main.errorBadImageUrl": "Неправильный URL-адрес изображения", "DE.Controllers.Main.errorCoAuthoringDisconnect": "Подключение к серверу прервано. Редактирование недоступно.", "DE.Controllers.Main.errorConnectToServer": "Не удается сохранить документ. Проверьте параметры подключения или обратитесь к вашему администратору.
    Когда вы нажмете на кнопку 'OK', вам будет предложено скачать документ.

    Дополнительную информацию о подключении Сервера документов можно найти здесь", "DE.Controllers.Main.errorDatabaseConnection": "Внешняя ошибка.
    Ошибка подключения к базе данных. Пожалуйста, обратитесь в службу технической поддержки.", diff --git a/apps/presentationeditor/main/locale/de.json b/apps/presentationeditor/main/locale/de.json index dcc7d224d..52b389339 100644 --- a/apps/presentationeditor/main/locale/de.json +++ b/apps/presentationeditor/main/locale/de.json @@ -121,6 +121,7 @@ "PE.Controllers.Main.downloadTextText": "Präsentation wird heruntergeladen...", "PE.Controllers.Main.downloadTitleText": "Präsentation wird heruntergeladen", "PE.Controllers.Main.errorAccessDeny": "Sie haben versucht die Änderungen im Dokument, zu dem Sie keine Berechtigungen haben, vorzunehemen.
    Wenden Sie sich an Ihren Serveradministrator.", + "PE.Controllers.Main.errorBadImageUrl": "URL des Bildes ist falsch", "PE.Controllers.Main.errorCoAuthoringDisconnect": "Verbindung zum Server verloren. Das Dokument kann momentan nicht bearbeitet werden.", "PE.Controllers.Main.errorConnectToServer": "Das Dokument konnte nicht gespeichert werden. Bitte überprüfen Sie die Verbindungseinstellungen, oder richten Sie an Ihren Administrator.
    Wann Sie auf den Button \"OK\" klicken, werden Sie aufgefordert das Dokument herunterzuladen.

    Mehr Information zur Verbindung des Dokument Servers finden Sie hier", "PE.Controllers.Main.errorDatabaseConnection": "Externer Fehler.
    Fehler beim Verbinden zur Datenbank. Bitte wenden Sie sich an den Kundendienst, falls der Fehler bestehen bleibt.", diff --git a/apps/presentationeditor/main/locale/en.json b/apps/presentationeditor/main/locale/en.json index 1c2329e49..c123204d5 100644 --- a/apps/presentationeditor/main/locale/en.json +++ b/apps/presentationeditor/main/locale/en.json @@ -121,6 +121,7 @@ "PE.Controllers.Main.downloadTextText": "Downloading presentation...", "PE.Controllers.Main.downloadTitleText": "Downloading Presentation", "PE.Controllers.Main.errorAccessDeny": "You are trying to perform an action you do not have rights for.
    Please contact your Document Server administrator.", + "PE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "PE.Controllers.Main.errorCoAuthoringDisconnect": "Server connection lost. The document cannot be edited right now.", "PE.Controllers.Main.errorConnectToServer": "The document could not be saved. Please check connection settings or contact your administrator.
    When you click the 'OK' button, you will be prompted to download the document.

    Find more information about connecting Document Server here", "PE.Controllers.Main.errorDatabaseConnection": "External error.
    Database connection error. Please contact support in case the error persists.", @@ -141,7 +142,6 @@ "PE.Controllers.Main.errorUserDrop": "The file cannot be accessed right now.", "PE.Controllers.Main.errorUsersExceed": "The number of users allowed by the pricing plan was exceeded", "PE.Controllers.Main.errorViewerDisconnect": "Connection is lost. You can still view the document,
    but will not be able to download or print until the connection is restored.", - "PE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "PE.Controllers.Main.leavePageText": "You have unsaved changes in this presentation. Click \"Stay on This Page\", then \"Save\" to save them. Click \"Leave This Page\" to discard all the unsaved changes.", "PE.Controllers.Main.loadFontsTextText": "Loading data...", "PE.Controllers.Main.loadFontsTitleText": "Loading Data", diff --git a/apps/presentationeditor/main/locale/fr.json b/apps/presentationeditor/main/locale/fr.json index fd99c2f90..a0ca2a6d0 100644 --- a/apps/presentationeditor/main/locale/fr.json +++ b/apps/presentationeditor/main/locale/fr.json @@ -121,6 +121,7 @@ "PE.Controllers.Main.downloadTextText": "Téléchargement de la présentation...", "PE.Controllers.Main.downloadTitleText": "Téléchargement de la présentation", "PE.Controllers.Main.errorAccessDeny": "Vous tentez d'exéсuter une action pour laquelle vous ne disposez pas des droits.
    Veuillez contacter l'administrateur de Document Server.", + "PE.Controllers.Main.errorBadImageUrl": "L'URL de l'image est incorrecte", "PE.Controllers.Main.errorCoAuthoringDisconnect": "Connexion au serveur perdue. Le document ne peut être modifié en ce moment.", "PE.Controllers.Main.errorConnectToServer": "Le document n'a pas pu être enregistré. Veuillez vérifier les paramètres de connexion ou contactez votre administrateur.
    Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

    Trouvez plus d'informations sur la connexion de Document Serverici", "PE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
    Erreur de connexion à la base de données. Si l'erreur persiste veillez contactez l'assistance technique.", @@ -889,7 +890,7 @@ "PE.Views.ParagraphSettingsAdvanced.noTabs": "The specified tabs will appear in this field", "PE.Views.ParagraphSettingsAdvanced.okButtonText": "OK", "PE.Views.ParagraphSettingsAdvanced.strAllCaps": "Majuscules", - "PE.Views.ParagraphSettingsAdvanced.strDoubleStrike": "Double biffés", + "PE.Views.ParagraphSettingsAdvanced.strDoubleStrike": "Barré double", "PE.Views.ParagraphSettingsAdvanced.strIndentsFirstLine": "Première ligne", "PE.Views.ParagraphSettingsAdvanced.strIndentsLeftText": "A gauche", "PE.Views.ParagraphSettingsAdvanced.strIndentsRightText": "A droite", diff --git a/apps/presentationeditor/main/locale/ru.json b/apps/presentationeditor/main/locale/ru.json index ca631efa9..f21b58013 100644 --- a/apps/presentationeditor/main/locale/ru.json +++ b/apps/presentationeditor/main/locale/ru.json @@ -121,6 +121,7 @@ "PE.Controllers.Main.downloadTextText": "Загрузка презентации...", "PE.Controllers.Main.downloadTitleText": "Загрузка презентации", "PE.Controllers.Main.errorAccessDeny": "Вы пытаетесь выполнить действие, на которое у вас нет прав.
    Пожалуйста, обратитесь к администратору Сервера документов.", + "PE.Controllers.Main.errorBadImageUrl": "Неправильный URL-адрес изображения", "PE.Controllers.Main.errorCoAuthoringDisconnect": "Потеряно соединение с сервером. В данный момент нельзя отредактировать документ.", "PE.Controllers.Main.errorConnectToServer": "Не удается сохранить документ. Проверьте параметры подключения или обратитесь к вашему администратору.
    Когда вы нажмете на кнопку 'OK', вам будет предложено скачать документ.

    Дополнительную информацию о подключении Сервера документов можно найти здесь", "PE.Controllers.Main.errorDatabaseConnection": "Внешняя ошибка.
    Ошибка подключения к базе данных. Если ошибка повторяется, пожалуйста, обратитесь в службу поддержки.", diff --git a/apps/presentationeditor/mobile/locale/de.json b/apps/presentationeditor/mobile/locale/de.json index 93c1d1a18..ee96429e8 100755 --- a/apps/presentationeditor/mobile/locale/de.json +++ b/apps/presentationeditor/mobile/locale/de.json @@ -67,6 +67,7 @@ "PE.Controllers.Main.downloadErrorText": "Herinterladen ist fehlgeschlagen.", "PE.Controllers.Main.downloadTextText": "Dokument wird heruntergeladen...", "PE.Controllers.Main.downloadTitleText": "Herunterladen des Dokuments", + "PE.Controllers.Main.errorBadImageUrl": "URL des Bildes ist falsch", "PE.Controllers.Main.errorCoAuthoringDisconnect": "Verbindung zum Server ist verloren gegangen. Sie können nicht mehr editieren.", "PE.Controllers.Main.errorConnectToServer": "Das Dokument konnte nicht gespeichert werden. Bitte überprüfen Sie die Verbindungseinstellungen, oder kontaktieren Sie Ihren Administrator.
    Wann Sie auf den Button \"OK\" klicken, werden Sie aufgefordert, das Dokument herunterzuladen.

    Mehr Information zur Verbindung des Dokument Servers finden Sie hier", "PE.Controllers.Main.errorDatabaseConnection": "Externer Fehler.
    Datenbank-Verbindungsfehler. Wenden Sie sich an den Support.", diff --git a/apps/presentationeditor/mobile/locale/en.json b/apps/presentationeditor/mobile/locale/en.json index 9e5280b5e..e1b9446f8 100755 --- a/apps/presentationeditor/mobile/locale/en.json +++ b/apps/presentationeditor/mobile/locale/en.json @@ -28,9 +28,9 @@ "PE.Controllers.DocumentHolder.menuCut": "Cut", "PE.Controllers.DocumentHolder.menuDelete": "Delete", "PE.Controllers.DocumentHolder.menuEdit": "Edit", + "PE.Controllers.DocumentHolder.menuMore": "More", "PE.Controllers.DocumentHolder.menuOpenLink": "Open Link", "PE.Controllers.DocumentHolder.menuPaste": "Paste", - "PE.Controllers.DocumentHolder.menuMore": "More", "PE.Controllers.DocumentHolder.sheetCancel": "Cancel", "PE.Controllers.EditContainer.textChart": "Chart", "PE.Controllers.EditContainer.textHyperlink": "Hyperlink", @@ -67,6 +67,7 @@ "PE.Controllers.Main.downloadErrorText": "Download failed.", "PE.Controllers.Main.downloadTextText": "Downloading document...", "PE.Controllers.Main.downloadTitleText": "Downloading Document", + "PE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "PE.Controllers.Main.errorCoAuthoringDisconnect": "Server connection lost. You can't edit anymore.", "PE.Controllers.Main.errorConnectToServer": " The document could not be saved. Please check connection settings or contact your administrator.
    When you click the 'OK' button, you will be prompted to download the document.

    Find more information about connecting Document Server here", "PE.Controllers.Main.errorDatabaseConnection": "External error.
    Database connection error. Please, contact support.", @@ -83,7 +84,6 @@ "PE.Controllers.Main.errorUsersExceed": "The number of users was exceeded", "PE.Controllers.Main.errorViewerDisconnect": "Connection is lost. You can still view the document,
    but will not be able to download or print until the connection is restored.", "PE.Controllers.Main.leavePageText": "You have unsaved changes in this document. Click 'Stay on this Page' to await the autosave of the document. Click 'Leave this Page' to discard all the unsaved changes.", - "PE.Controllers.Main.errorBadImageUrl": "Image URL is incorrect", "PE.Controllers.Main.loadFontsTextText": "Loading data...", "PE.Controllers.Main.loadFontsTitleText": "Loading Data", "PE.Controllers.Main.loadFontTextText": "Loading data...", diff --git a/apps/presentationeditor/mobile/locale/fr.json b/apps/presentationeditor/mobile/locale/fr.json index 573838219..86c95856d 100755 --- a/apps/presentationeditor/mobile/locale/fr.json +++ b/apps/presentationeditor/mobile/locale/fr.json @@ -67,6 +67,7 @@ "PE.Controllers.Main.downloadErrorText": "Échec du téléchargement.", "PE.Controllers.Main.downloadTextText": "Téléchargement du document...", "PE.Controllers.Main.downloadTitleText": "Téléchargement du document", + "PE.Controllers.Main.errorBadImageUrl": "L'URL de l'image est incorrecte", "PE.Controllers.Main.errorCoAuthoringDisconnect": "La connexion au serveur perdue. Désolé, vous ne pouvez plus modifier le document.", "PE.Controllers.Main.errorConnectToServer": "Impossible d'enregistrer le document. Veuillez vérifier vos paramètres de connexion ou contactez l'administrateur.
    Lorsque vous cliquez sur le bouton 'OK', vous serez invité à télécharger le document.

    Trouvez plus d'informations sur la connexion de Document Server ici", "PE.Controllers.Main.errorDatabaseConnection": "Erreur externe.
    Erreur de connexion à la base de données.Contactez le support.", @@ -389,7 +390,7 @@ "PE.Views.EditText.textBack": "Retour", "PE.Views.EditText.textBefore": "Avant", "PE.Views.EditText.textBullets": "Puces", - "PE.Views.EditText.textDblStrikethrough": "Double biffés", + "PE.Views.EditText.textDblStrikethrough": "Barré double", "PE.Views.EditText.textDblSuperscript": "Exposant", "PE.Views.EditText.textFontColor": "Couleur de police", "PE.Views.EditText.textFontColors": "Couleurs de police", diff --git a/apps/presentationeditor/mobile/locale/ru.json b/apps/presentationeditor/mobile/locale/ru.json index e0a850b95..62bdb8478 100755 --- a/apps/presentationeditor/mobile/locale/ru.json +++ b/apps/presentationeditor/mobile/locale/ru.json @@ -67,6 +67,7 @@ "PE.Controllers.Main.downloadErrorText": "Загрузка не удалась.", "PE.Controllers.Main.downloadTextText": "Загрузка документа...", "PE.Controllers.Main.downloadTitleText": "Загрузка документа", + "PE.Controllers.Main.errorBadImageUrl": "Неправильный URL-адрес изображения", "PE.Controllers.Main.errorCoAuthoringDisconnect": "Подключение к серверу прервано. Редактирование недоступно.", "PE.Controllers.Main.errorConnectToServer": "Не удается сохранить документ. Проверьте параметры подключения или обратитесь к вашему администратору.
    Когда вы нажмете на кнопку 'OK', вам будет предложено скачать документ.

    Дополнительную информацию о подключении Сервера документов можно найти здесь", "PE.Controllers.Main.errorDatabaseConnection": "Внешняя ошибка.
    Ошибка подключения к базе данных. Пожалуйста, обратитесь в службу технической поддержки.", diff --git a/apps/spreadsheeteditor/main/locale/fr.json b/apps/spreadsheeteditor/main/locale/fr.json index 9e675a6b8..1613f08bc 100644 --- a/apps/spreadsheeteditor/main/locale/fr.json +++ b/apps/spreadsheeteditor/main/locale/fr.json @@ -1269,14 +1269,14 @@ "SSE.Views.ParagraphSettingsAdvanced.noTabs": "Les onglets spécifiés s'affichent dans ce champ", "SSE.Views.ParagraphSettingsAdvanced.okButtonText": "OK", "SSE.Views.ParagraphSettingsAdvanced.strAllCaps": "Majuscules", - "SSE.Views.ParagraphSettingsAdvanced.strDoubleStrike": "Double biffés", + "SSE.Views.ParagraphSettingsAdvanced.strDoubleStrike": "Barré double", "SSE.Views.ParagraphSettingsAdvanced.strIndentsFirstLine": "Première ligne", "SSE.Views.ParagraphSettingsAdvanced.strIndentsLeftText": "A gauche", "SSE.Views.ParagraphSettingsAdvanced.strIndentsRightText": "A droite", "SSE.Views.ParagraphSettingsAdvanced.strParagraphFont": "Police", "SSE.Views.ParagraphSettingsAdvanced.strParagraphIndents": "Retraits et emplacement", "SSE.Views.ParagraphSettingsAdvanced.strSmallCaps": "Petites majuscules", - "SSE.Views.ParagraphSettingsAdvanced.strStrike": "Biffés", + "SSE.Views.ParagraphSettingsAdvanced.strStrike": "Barré", "SSE.Views.ParagraphSettingsAdvanced.strSubscript": "Indice", "SSE.Views.ParagraphSettingsAdvanced.strSuperscript": "Exposant", "SSE.Views.ParagraphSettingsAdvanced.strTabs": "Tabulation", From 65ccd925972e3fea87814945b3eea094e369000e Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Fri, 7 Apr 2017 12:38:58 +0300 Subject: [PATCH 06/27] Fix Bug 34708. --- apps/documenteditor/main/app/controller/Toolbar.js | 2 +- apps/presentationeditor/main/app/controller/Toolbar.js | 2 +- apps/spreadsheeteditor/main/app/controller/Toolbar.js | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/documenteditor/main/app/controller/Toolbar.js b/apps/documenteditor/main/app/controller/Toolbar.js index 255e609d5..e27085edc 100644 --- a/apps/documenteditor/main/app/controller/Toolbar.js +++ b/apps/documenteditor/main/app/controller/Toolbar.js @@ -2516,7 +2516,7 @@ define([ store: this.getApplication().getCollection('Common.Collections.TextArt'), parentMenu: this.toolbar.mnuInsertTextArt.menu, showLast: false, - itemTemplate: _.template('

    ') + itemTemplate: _.template('
    ') }); this.toolbar.mnuTextArtPicker.on('item:click', function(picker, item, record, e) { diff --git a/apps/presentationeditor/main/app/controller/Toolbar.js b/apps/presentationeditor/main/app/controller/Toolbar.js index 8fc9a531a..44cb29368 100644 --- a/apps/presentationeditor/main/app/controller/Toolbar.js +++ b/apps/presentationeditor/main/app/controller/Toolbar.js @@ -1718,7 +1718,7 @@ define([ store: this.getApplication().getCollection('Common.Collections.TextArt'), parentMenu: this.toolbar.mnuInsertTextArt.menu, showLast: false, - itemTemplate: _.template('
    ') + itemTemplate: _.template('
    ') }); this.toolbar.mnuTextArtPicker.on('item:click', function(picker, item, record, e) { diff --git a/apps/spreadsheeteditor/main/app/controller/Toolbar.js b/apps/spreadsheeteditor/main/app/controller/Toolbar.js index 8cc7b10c8..2e7dc6f1b 100644 --- a/apps/spreadsheeteditor/main/app/controller/Toolbar.js +++ b/apps/spreadsheeteditor/main/app/controller/Toolbar.js @@ -1436,7 +1436,7 @@ define([ restoreHeight: 300, style: 'max-height: 300px;', store: me.getCollection('TableTemplates'), - itemTemplate: _.template('
    ') + itemTemplate: _.template('
    ') }); picker.on('item:click', function(picker, item, record) { @@ -2222,7 +2222,7 @@ define([ store: this.getApplication().getCollection('Common.Collections.TextArt'), parentMenu: this.toolbar.mnuInsertTextArt.menu, showLast: false, - itemTemplate: _.template('
    ') + itemTemplate: _.template('
    ') }); this.toolbar.mnuTextArtPicker.on('item:click', function(picker, item, record, e) { From b23cbb32af7f4583782007e8c6224dafe85caa8e Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Tue, 11 Apr 2017 12:40:53 +0300 Subject: [PATCH 07/27] [DE] Bugs with document without styles: update from style, create new style, delete all styles. --- apps/common/main/lib/component/ComboDataView.js | 4 ++++ apps/documenteditor/main/app/controller/Toolbar.js | 7 ++++--- apps/documenteditor/main/app/view/StyleTitleDialog.js | 9 +++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/apps/common/main/lib/component/ComboDataView.js b/apps/common/main/lib/component/ComboDataView.js index e51f05f27..421f85653 100644 --- a/apps/common/main/lib/component/ComboDataView.js +++ b/apps/common/main/lib/component/ComboDataView.js @@ -456,6 +456,10 @@ define([ } }, + clearComboView: function() { + this.fieldPicker.store.reset([]); + }, + selectByIndex: function(index) { if (index < 0) this.fieldPicker.deselectAll(); diff --git a/apps/documenteditor/main/app/controller/Toolbar.js b/apps/documenteditor/main/app/controller/Toolbar.js index e27085edc..ca2da17ad 100644 --- a/apps/documenteditor/main/app/controller/Toolbar.js +++ b/apps/documenteditor/main/app/controller/Toolbar.js @@ -733,7 +733,7 @@ define([ var styleRec = listStyle.menuPicker.store.findWhere({ title: name }); - this._state.prstyle = (listStyle.menuPicker.store.length>0) ? name : undefined; + this._state.prstyle = (listStyle.menuPicker.store.length>0 || window.styles_loaded) ? name : undefined; listStyle.menuPicker.selectRecord(styleRec); listStyle.resumeEvents(); @@ -1892,7 +1892,7 @@ define([ me._state.prstyle = title; style.put_Name(title); characterStyle.put_Name(title + '_character'); - style.put_Next(nextStyle.asc_getName()); + style.put_Next((nextStyle) ? nextStyle.asc_getName() : null); me.api.asc_AddNewStyle(style); } Common.NotificationCenter.trigger('edit:complete', me.toolbar); @@ -2630,7 +2630,8 @@ define([ if (self._state.prstyle) styleRec = listStyles.menuPicker.store.findWhere({title: self._state.prstyle}); listStyles.fillComboView((styleRec) ? styleRec : listStyles.menuPicker.store.at(0), true); Common.NotificationCenter.trigger('edit:complete', this); - } + } else if (listStyles.rendered) + listStyles.clearComboView(); window.styles_loaded = true; }, diff --git a/apps/documenteditor/main/app/view/StyleTitleDialog.js b/apps/documenteditor/main/app/view/StyleTitleDialog.js index 14e605088..3e9ff6363 100644 --- a/apps/documenteditor/main/app/view/StyleTitleDialog.js +++ b/apps/documenteditor/main/app/view/StyleTitleDialog.js @@ -107,10 +107,11 @@ define([ menuStyle : 'width: 100%; max-height: 290px;', editable : false, cls : 'input-group-nr', - data : this.options.formats + data : this.options.formats, + disabled : (this.options.formats.length==0) }); - - this.cmbNextStyle.setValue(this.options.formats[0].value); + if (this.options.formats.length>0) + this.cmbNextStyle.setValue(this.options.formats[0].value); }, show: function() { @@ -129,7 +130,7 @@ define([ getNextStyle: function () { var me = this; - return me.cmbNextStyle.getValue(); + return (me.options.formats.length>0) ? me.cmbNextStyle.getValue() : null; }, onBtnClick: function(event) { From 4f1f55179622ef96c7c74a44c6d85b92830d397c Mon Sep 17 00:00:00 2001 From: SergeyLuzyanin Date: Wed, 12 Apr 2017 11:14:36 +0300 Subject: [PATCH 08/27] Notes --- apps/presentationeditor/sdk_dev_scripts.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/presentationeditor/sdk_dev_scripts.js b/apps/presentationeditor/sdk_dev_scripts.js index 1fd27741d..b28e390be 100644 --- a/apps/presentationeditor/sdk_dev_scripts.js +++ b/apps/presentationeditor/sdk_dev_scripts.js @@ -111,6 +111,8 @@ var sdk_dev_scrpipts = [ "../../../../sdkjs/slide/Editor/Format/SlideMaster.js", "../../../../sdkjs/slide/Editor/Format/Layout.js", "../../../../sdkjs/slide/Editor/Format/Comments.js", + "../../../../sdkjs/slide/Editor/Format/NotesMaster.js", + "../../../../sdkjs/slide/Editor/Format/Notes.js", "../../../../sdkjs/word/Editor/ParagraphContent.js", "../../../../sdkjs/word/Editor/Paragraph/ParaTextPr.js", "../../../../sdkjs/word/Editor/Paragraph/ParaTextPrChanges.js", From cfa585bd7e8f00b358c6dba614b66821fb180b74 Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Wed, 12 Apr 2017 17:03:21 +0300 Subject: [PATCH 09/27] Delete unused jsrsasign. --- apps/documenteditor/embed/index.html | 1 - apps/documenteditor/embed/index.html.deploy | 1 - apps/documenteditor/main/app.js | 4 +--- apps/documenteditor/main/app_dev.js | 2 -- apps/documenteditor/mobile/app-dev.js | 2 -- apps/documenteditor/mobile/app.js | 4 +--- apps/presentationeditor/embed/index.html | 1 - apps/presentationeditor/embed/index.html.deploy | 1 - apps/presentationeditor/main/app.js | 4 +--- apps/presentationeditor/main/app_dev.js | 2 -- apps/presentationeditor/mobile/app-dev.js | 2 -- apps/presentationeditor/mobile/app.js | 4 +--- apps/spreadsheeteditor/embed/index.html | 1 - apps/spreadsheeteditor/embed/index.html.deploy | 1 - apps/spreadsheeteditor/main/app.js | 4 +--- apps/spreadsheeteditor/main/app_dev.js | 2 -- apps/spreadsheeteditor/mobile/app-dev.js | 2 -- apps/spreadsheeteditor/mobile/app.js | 4 +--- build/Gruntfile.js | 2 -- build/common.json | 14 +------------- build/documenteditor.json | 8 ++------ build/presentationeditor.json | 8 ++------ build/spreadsheeteditor.json | 8 ++------ 23 files changed, 13 insertions(+), 69 deletions(-) diff --git a/apps/documenteditor/embed/index.html b/apps/documenteditor/embed/index.html index 9bf9f202b..b5bbca2db 100644 --- a/apps/documenteditor/embed/index.html +++ b/apps/documenteditor/embed/index.html @@ -323,7 +323,6 @@ - - diff --git a/apps/documenteditor/main/app.js b/apps/documenteditor/main/app.js index 5c2a4049d..3cb20c44f 100644 --- a/apps/documenteditor/main/app.js +++ b/apps/documenteditor/main/app.js @@ -56,7 +56,6 @@ require.config({ sockjs : '../vendor/sockjs/sockjs.min', jszip : '../vendor/jszip/jszip.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/word/sdk-all-min', api : 'api/documents/api', @@ -112,8 +111,7 @@ require.config({ 'xregexp', 'sockjs', 'jszip', - 'jsziputils', - 'jsrsasign' + 'jsziputils' ] }, gateway: { diff --git a/apps/documenteditor/main/app_dev.js b/apps/documenteditor/main/app_dev.js index d15f17707..49d9e8fa0 100644 --- a/apps/documenteditor/main/app_dev.js +++ b/apps/documenteditor/main/app_dev.js @@ -56,7 +56,6 @@ require.config({ sockjs : '../vendor/sockjs/sockjs.min', jszip : '../vendor/jszip/jszip.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', api : 'api/documents/api', core : 'common/main/lib/core/application', notification : 'common/main/lib/core/NotificationCenter', @@ -125,7 +124,6 @@ require([ 'locale', 'jszip', 'jsziputils', - 'jsrsasign', 'sockjs', 'underscore' ], function (Backbone, Bootstrap, Core) { diff --git a/apps/documenteditor/mobile/app-dev.js b/apps/documenteditor/mobile/app-dev.js index 8f9d3a3be..43c8bce3c 100644 --- a/apps/documenteditor/mobile/app-dev.js +++ b/apps/documenteditor/mobile/app-dev.js @@ -53,7 +53,6 @@ require.config({ sockjs : '../vendor/sockjs/sockjs.min', jszip : '../vendor/jszip/jszip.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', api : 'api/documents/api', core : 'common/main/lib/core/application', extendes : 'common/mobile/utils/extendes', @@ -120,7 +119,6 @@ require([ 'locale', 'jszip', 'jsziputils', - 'jsrsasign', 'sockjs' ], function (Backbone, Framework7, Core) { Backbone.history.start(); diff --git a/apps/documenteditor/mobile/app.js b/apps/documenteditor/mobile/app.js index ebf92413d..0c8542859 100644 --- a/apps/documenteditor/mobile/app.js +++ b/apps/documenteditor/mobile/app.js @@ -53,7 +53,6 @@ require.config({ sockjs : '../vendor/sockjs/sockjs.min', jszip : '../vendor/jszip/jszip.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/word/sdk-all-min', api : 'api/documents/api', @@ -105,8 +104,7 @@ require.config({ 'xregexp', 'sockjs', 'jszip', - 'jsziputils', - 'jsrsasign' + 'jsziputils' ] }, gateway: { diff --git a/apps/presentationeditor/embed/index.html b/apps/presentationeditor/embed/index.html index aa4c5f04b..ceabd4062 100644 --- a/apps/presentationeditor/embed/index.html +++ b/apps/presentationeditor/embed/index.html @@ -325,7 +325,6 @@ - - diff --git a/apps/presentationeditor/main/app.js b/apps/presentationeditor/main/app.js index 941fc47b2..8e30eb1c2 100644 --- a/apps/presentationeditor/main/app.js +++ b/apps/presentationeditor/main/app.js @@ -55,7 +55,6 @@ require.config({ xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/slide/sdk-all-min', api : 'api/documents/api', @@ -109,8 +108,7 @@ require.config({ 'allfonts', 'xregexp', 'sockjs', - 'jsziputils', - 'jsrsasign' + 'jsziputils' ] }, gateway: { diff --git a/apps/presentationeditor/main/app_dev.js b/apps/presentationeditor/main/app_dev.js index 56aea9616..46dc5c451 100644 --- a/apps/presentationeditor/main/app_dev.js +++ b/apps/presentationeditor/main/app_dev.js @@ -55,7 +55,6 @@ require.config({ xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', api : 'api/documents/api', core : 'common/main/lib/core/application', notification : 'common/main/lib/core/NotificationCenter', @@ -122,7 +121,6 @@ require([ 'gateway', 'locale', 'jsziputils', - 'jsrsasign', 'sockjs', 'xregexp', 'underscore' diff --git a/apps/presentationeditor/mobile/app-dev.js b/apps/presentationeditor/mobile/app-dev.js index 020c907f2..013a3cf16 100644 --- a/apps/presentationeditor/mobile/app-dev.js +++ b/apps/presentationeditor/mobile/app-dev.js @@ -54,7 +54,6 @@ require.config({ sockjs : '../vendor/sockjs/sockjs.min', jszip : '../vendor/jszip/jszip.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', api : 'api/documents/api', core : 'common/main/lib/core/application', extendes : 'common/mobile/utils/extendes', @@ -121,7 +120,6 @@ require([ 'locale', 'jszip', 'jsziputils', - 'jsrsasign', 'sockjs' ], function (Backbone, Framework7, Core) { Backbone.history.start(); diff --git a/apps/presentationeditor/mobile/app.js b/apps/presentationeditor/mobile/app.js index c4606d588..4df2708c1 100644 --- a/apps/presentationeditor/mobile/app.js +++ b/apps/presentationeditor/mobile/app.js @@ -54,7 +54,6 @@ require.config({ sockjs : '../vendor/sockjs/sockjs.min', jszip : '../vendor/jszip/jszip.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/slide/sdk-all-min', api : 'api/documents/api', @@ -106,8 +105,7 @@ require.config({ 'xregexp', 'sockjs', 'jszip', - 'jsziputils', - 'jsrsasign' + 'jsziputils' ] }, gateway: { diff --git a/apps/spreadsheeteditor/embed/index.html b/apps/spreadsheeteditor/embed/index.html index 0695c7f3d..99a46ebed 100644 --- a/apps/spreadsheeteditor/embed/index.html +++ b/apps/spreadsheeteditor/embed/index.html @@ -324,7 +324,6 @@ - - diff --git a/apps/spreadsheeteditor/main/app.js b/apps/spreadsheeteditor/main/app.js index db740f9e6..c397378b5 100644 --- a/apps/spreadsheeteditor/main/app.js +++ b/apps/spreadsheeteditor/main/app.js @@ -55,7 +55,6 @@ require.config({ xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/cell/sdk-all-min', api : 'api/documents/api', @@ -109,8 +108,7 @@ require.config({ 'allfonts', 'xregexp', 'sockjs', - 'jsziputils', - 'jsrsasign' + 'jsziputils' ] }, gateway: { diff --git a/apps/spreadsheeteditor/main/app_dev.js b/apps/spreadsheeteditor/main/app_dev.js index 3d8301bbb..2caaafbfe 100644 --- a/apps/spreadsheeteditor/main/app_dev.js +++ b/apps/spreadsheeteditor/main/app_dev.js @@ -55,7 +55,6 @@ require.config({ xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', api : 'api/documents/api', core : 'common/main/lib/core/application', notification : 'common/main/lib/core/NotificationCenter', @@ -122,7 +121,6 @@ require([ 'gateway', 'locale', 'jsziputils', - 'jsrsasign', 'sockjs', 'underscore' ], function (Backbone, Bootstrap, Core) { diff --git a/apps/spreadsheeteditor/mobile/app-dev.js b/apps/spreadsheeteditor/mobile/app-dev.js index 70e500048..d3a031a77 100644 --- a/apps/spreadsheeteditor/mobile/app-dev.js +++ b/apps/spreadsheeteditor/mobile/app-dev.js @@ -53,7 +53,6 @@ require.config({ sockjs : '../vendor/sockjs/sockjs.min', jszip : '../vendor/jszip/jszip.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', api : 'api/documents/api', core : 'common/main/lib/core/application', extendes : 'common/mobile/utils/extendes', @@ -102,7 +101,6 @@ require([ 'locale', 'jszip', 'jsziputils', - 'jsrsasign', 'sockjs' ], function (Backbone, Framework7) { Backbone.history.start(); diff --git a/apps/spreadsheeteditor/mobile/app.js b/apps/spreadsheeteditor/mobile/app.js index 518035953..f87754175 100644 --- a/apps/spreadsheeteditor/mobile/app.js +++ b/apps/spreadsheeteditor/mobile/app.js @@ -53,7 +53,6 @@ require.config({ sockjs : '../vendor/sockjs/sockjs.min', jszip : '../vendor/jszip/jszip.min', jsziputils : '../vendor/jszip-utils/jszip-utils.min', - jsrsasign : '../vendor/jsrsasign/jsrsasign-latest-all-min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/cell/sdk-all-min', api : 'api/documents/api', @@ -82,8 +81,7 @@ require.config({ 'xregexp', 'sockjs', 'jszip', - 'jsziputils', - 'jsrsasign' + 'jsziputils' ] }, backbone: { diff --git a/build/Gruntfile.js b/build/Gruntfile.js index 05a499ce5..e4021d4b2 100644 --- a/build/Gruntfile.js +++ b/build/Gruntfile.js @@ -114,7 +114,6 @@ module.exports = function(grunt) { doRegisterTask('bootstrap'); doRegisterTask('jszip'); doRegisterTask('jsziputils'); - doRegisterTask('jsrsasign'); doRegisterTask('requirejs', function(defaultConfig, packageFile) { return { uglify: { @@ -374,7 +373,6 @@ module.exports = function(grunt) { grunt.registerTask('deploy-bootstrap', ['bootstrap-init', 'clean', 'copy']); grunt.registerTask('deploy-jszip', ['jszip-init', 'clean', 'copy']); grunt.registerTask('deploy-jsziputils', ['jsziputils-init', 'clean', 'copy']); - grunt.registerTask('deploy-jsrsasign', ['jsrsasign-init', 'clean', 'copy']); grunt.registerTask('deploy-requirejs', ['requirejs-init', 'clean', 'uglify']); grunt.registerTask('deploy-app-main', ['main-app-init', 'clean', 'imagemin', 'less', 'requirejs', 'concat', diff --git a/build/common.json b/build/common.json index c5fc67f31..0a7255f78 100644 --- a/build/common.json +++ b/build/common.json @@ -162,17 +162,6 @@ } } }, - "jsrsasign": { - "clean": [ - "../deploy/web-apps/vendor/jsrsasign" - ], - "copy": { - "script": { - "src": "../vendor/jsrsasign/jsrsasign-latest-all-min.js", - "dest": "../deploy/web-apps/vendor/jsrsasign/jsrsasign-latest-all-min.js" - } - } - }, "underscore": { "clean": [ "../deploy/web-apps/vendor/underscore" @@ -206,8 +195,7 @@ "deploy-underscore", "deploy-bootstrap", "deploy-jszip", - "deploy-jsziputils", - "deploy-jsrsasign" + "deploy-jsziputils" ] } } \ No newline at end of file diff --git a/build/documenteditor.json b/build/documenteditor.json index a9ee525c2..ac7be5be4 100644 --- a/build/documenteditor.json +++ b/build/documenteditor.json @@ -30,7 +30,6 @@ "sockjs": "empty:", "jszip": "empty:", "jszip-utils": "empty:", - "jsrsasign": "empty:", "coapisettings": "empty:", "allfonts": "empty:", "sdk": "empty:", @@ -85,8 +84,7 @@ "xregexp", "sockjs", "jszip", - "jszip-utils", - "jsrsasign" + "jszip-utils" ] }, "gateway": { @@ -197,7 +195,6 @@ "sockjs": "empty:", "jszip": "empty:", "jszip-utils": "empty:", - "jsrsasign": "empty:", "coapisettings": "empty:", "allfonts": "empty:", "sdk": "empty:", @@ -255,8 +252,7 @@ "xregexp", "sockjs", "jszip", - "jszip-utils", - "jsrsasign" + "jszip-utils" ] }, "gateway": { diff --git a/build/presentationeditor.json b/build/presentationeditor.json index 7073edbb6..b3512c847 100644 --- a/build/presentationeditor.json +++ b/build/presentationeditor.json @@ -30,7 +30,6 @@ "sockjs": "empty:", "jszip": "empty:", "jszip-utils": "empty:", - "jsrsasign": "empty:", "coapisettings": "empty:", "allfonts": "empty:", "sdk": "empty:", @@ -85,8 +84,7 @@ "xregexp", "sockjs", "jszip", - "jszip-utils", - "jsrsasign" + "jszip-utils" ] }, "gateway": { @@ -197,7 +195,6 @@ "sockjs": "empty:", "jszip": "empty:", "jszip-utils": "empty:", - "jsrsasign": "empty:", "coapisettings": "empty:", "allfonts": "empty:", "sdk": "empty:", @@ -255,8 +252,7 @@ "xregexp", "sockjs", "jszip", - "jszip-utils", - "jsrsasign" + "jszip-utils" ] }, "gateway": { diff --git a/build/spreadsheeteditor.json b/build/spreadsheeteditor.json index e3655845b..2ff9bd594 100644 --- a/build/spreadsheeteditor.json +++ b/build/spreadsheeteditor.json @@ -30,7 +30,6 @@ "sockjs": "empty:", "jszip": "empty:", "jszip-utils": "empty:", - "jsrsasign": "empty:", "coapisettings": "empty:", "allfonts": "empty:", "sdk": "empty:", @@ -85,8 +84,7 @@ "xregexp", "sockjs", "jszip", - "jszip-utils", - "jsrsasign" + "jszip-utils" ] }, "gateway": { @@ -208,7 +206,6 @@ "sockjs": "empty:", "jszip": "empty:", "jszip-utils": "empty:", - "jsrsasign": "empty:", "coapisettings": "empty:", "allfonts": "empty:", "sdk": "empty:", @@ -266,8 +263,7 @@ "xregexp", "sockjs", "jszip", - "jszip-utils", - "jsrsasign" + "jszip-utils" ] }, "gateway": { From a79bfcabc52b7b5e557cd14782d15b0da8e5d984 Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Thu, 13 Apr 2017 10:16:59 +0300 Subject: [PATCH 10/27] [Mobile] New licence type SuccessLimit. --- apps/documenteditor/mobile/app/controller/Main.js | 4 ++-- apps/presentationeditor/mobile/app/controller/Main.js | 4 ++-- apps/spreadsheeteditor/mobile/app/controller/Main.js | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/documenteditor/mobile/app/controller/Main.js b/apps/documenteditor/mobile/app/controller/Main.js index ecb792aa4..e6be4a38f 100644 --- a/apps/documenteditor/mobile/app/controller/Main.js +++ b/apps/documenteditor/mobile/app/controller/Main.js @@ -588,7 +588,7 @@ define([ me.permissions.review = (me.permissions.review === undefined) ? (me.permissions.edit !== false) : me.permissions.review; me.appOptions.canAnalytics = params.asc_getIsAnalyticsEnable(); - me.appOptions.canLicense = (licType === Asc.c_oLicenseResult.Success); + me.appOptions.canLicense = (licType === Asc.c_oLicenseResult.Success || licType === Asc.c_oLicenseResult.SuccessLimit); me.appOptions.isLightVersion = params.asc_getIsLight(); /** coauthoring begin **/ me.appOptions.canCoAuthoring = !me.appOptions.isLightVersion; @@ -617,7 +617,7 @@ define([ me._state.licenseWarning = (licType===Asc.c_oLicenseResult.Connections) && me.appOptions.canEdit && me.editorConfig.mode !== 'view'; - me.appOptions.canBranding = params.asc_getCanBranding() && (typeof me.editorConfig.customization == 'object'); + me.appOptions.canBranding = (licType === Asc.c_oLicenseResult.Success) && (typeof me.editorConfig.customization == 'object'); me.applyModeCommonElements(); me.applyModeEditorElements(); diff --git a/apps/presentationeditor/mobile/app/controller/Main.js b/apps/presentationeditor/mobile/app/controller/Main.js index c7db0bd76..23c75f241 100644 --- a/apps/presentationeditor/mobile/app/controller/Main.js +++ b/apps/presentationeditor/mobile/app/controller/Main.js @@ -543,7 +543,7 @@ define([ me.permissions.review = (me.permissions.review === undefined) ? (me.permissions.edit !== false) : me.permissions.review; me.appOptions.canAnalytics = params.asc_getIsAnalyticsEnable(); - me.appOptions.canLicense = (licType === Asc.c_oLicenseResult.Success); + me.appOptions.canLicense = (licType === Asc.c_oLicenseResult.Success || licType === Asc.c_oLicenseResult.SuccessLimit); me.appOptions.isLightVersion = params.asc_getIsLight(); /** coauthoring begin **/ me.appOptions.canCoAuthoring = !me.appOptions.isLightVersion; @@ -572,7 +572,7 @@ define([ me._state.licenseWarning = (licType===Asc.c_oLicenseResult.Connections) && me.appOptions.canEdit && me.editorConfig.mode !== 'view'; - me.appOptions.canBranding = params.asc_getCanBranding() && (typeof me.editorConfig.customization == 'object'); + me.appOptions.canBranding = (licType === Asc.c_oLicenseResult.Success) && (typeof me.editorConfig.customization == 'object'); me.applyModeCommonElements(); me.applyModeEditorElements(); diff --git a/apps/spreadsheeteditor/mobile/app/controller/Main.js b/apps/spreadsheeteditor/mobile/app/controller/Main.js index b16b005a3..82d129827 100644 --- a/apps/spreadsheeteditor/mobile/app/controller/Main.js +++ b/apps/spreadsheeteditor/mobile/app/controller/Main.js @@ -573,7 +573,7 @@ define([ me.appOptions.canAnalytics = params.asc_getIsAnalyticsEnable(); me.appOptions.isOffline = me.api.asc_isOffline(); - me.appOptions.canLicense = (licType === Asc.c_oLicenseResult.Success); + me.appOptions.canLicense = (licType === Asc.c_oLicenseResult.Success || licType === Asc.c_oLicenseResult.SuccessLimit); me.appOptions.isLightVersion = params.asc_getIsLight(); /** coauthoring begin **/ me.appOptions.canCoAuthoring = !me.appOptions.isLightVersion; @@ -582,7 +582,7 @@ define([ me.appOptions.canChat = me.appOptions.canLicense && !me.appOptions.isOffline && !((typeof (me.editorConfig.customization) == 'object') && me.editorConfig.customization.chat===false); me.appOptions.canRename = !!me.permissions.rename; - me.appOptions.canBranding = (licType!==Asc.c_oLicenseResult.Error) && (typeof me.editorConfig.customization == 'object'); + me.appOptions.canBranding = (licType === Asc.c_oLicenseResult.Success) && (typeof me.editorConfig.customization == 'object'); me.appOptions.canBrandingExt = params.asc_getCanBranding() && (typeof me.editorConfig.customization == 'object'); } From cbcee82ff063a727c005ce65047bb23c5edd3672 Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Thu, 13 Apr 2017 11:18:16 +0300 Subject: [PATCH 11/27] Update to jszip v3.1.3 --- vendor/jszip/jszip.js | 6114 +++++++++++++++++++++++++------------ vendor/jszip/jszip.min.js | 11 +- 2 files changed, 4181 insertions(+), 1944 deletions(-) diff --git a/vendor/jszip/jszip.js b/vendor/jszip/jszip.js index 1546d7fcb..6d8dcc9d9 100644 --- a/vendor/jszip/jszip.js +++ b/vendor/jszip/jszip.js @@ -1,60 +1,98 @@ /*! -JSZip - A Javascript class for generating and reading zip files +JSZip v3.1.3 - A Javascript class for generating and reading zip files -(c) 2009-2014 Stuart Knightley +(c) 2009-2016 Stuart Knightley Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/master/LICENSE.markdown. JSZip uses the library pako released under the MIT license : https://github.com/nodeca/pako/blob/master/LICENSE */ -!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.JSZip=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o> 2; enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; + enc3 = remainingBytes > 1 ? (((chr2 & 15) << 2) | (chr3 >> 6)) : 64; + enc4 = remainingBytes > 2 ? (chr3 & 63) : 64; - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } - else if (isNaN(chr3)) { - enc4 = 64; - } - - output = output + _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4); + output.push(_keyStr.charAt(enc1) + _keyStr.charAt(enc2) + _keyStr.charAt(enc3) + _keyStr.charAt(enc4)); } - return output; + return output.join(""); }; // public method for decoding -exports.decode = function(input, utf8) { - var output = ""; +exports.decode = function(input) { var chr1, chr2, chr3; var enc1, enc2, enc3, enc4; - var i = 0; + var i = 0, resultIndex = 0; + + var dataUrlPrefix = "data:"; + + if (input.substr(0, dataUrlPrefix.length) === dataUrlPrefix) { + // This is a common error: people give a data url + // (data:image/png;base64,iVBOR...) with a {base64: true} and + // wonders why things don't work. + // We can detect that the string input looks like a data url but we + // *can't* be sure it is one: removing everything up to the comma would + // be too dangerous. + throw new Error("Invalid base64 input, it looks like a data url."); + } input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + var totalLength = input.length * 3 / 4; + if(input.charAt(input.length - 1) === _keyStr.charAt(64)) { + totalLength--; + } + if(input.charAt(input.length - 2) === _keyStr.charAt(64)) { + totalLength--; + } + if (totalLength % 1 !== 0) { + // totalLength is not an integer, the length does not match a valid + // base64 content. That can happen if: + // - the input is not a base64 content + // - the input is *almost* a base64 content, with a extra chars at the + // beginning or at the end + // - the input uses a base64 variant (base64url for example) + throw new Error("Invalid base64 input, bad content length."); + } + var output; + if (support.uint8array) { + output = new Uint8Array(totalLength|0); + } else { + output = new Array(totalLength|0); + } + while (i < input.length) { enc1 = _keyStr.indexOf(input.charAt(i++)); @@ -66,178 +104,1662 @@ exports.decode = function(input, utf8) { chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); chr3 = ((enc3 & 3) << 6) | enc4; - output = output + String.fromCharCode(chr1); + output[resultIndex++] = chr1; - if (enc3 != 64) { - output = output + String.fromCharCode(chr2); + if (enc3 !== 64) { + output[resultIndex++] = chr2; } - if (enc4 != 64) { - output = output + String.fromCharCode(chr3); + if (enc4 !== 64) { + output[resultIndex++] = chr3; } } return output; - }; -},{}],2:[function(_dereq_,module,exports){ +},{"./support":30,"./utils":32}],2:[function(require,module,exports){ 'use strict'; -function CompressedObject() { - this.compressedSize = 0; - this.uncompressedSize = 0; - this.crc32 = 0; - this.compressionMethod = null; - this.compressedContent = null; + +var external = require("./external"); +var DataWorker = require('./stream/DataWorker'); +var DataLengthProbe = require('./stream/DataLengthProbe'); +var Crc32Probe = require('./stream/Crc32Probe'); +var DataLengthProbe = require('./stream/DataLengthProbe'); + +/** + * Represent a compressed object, with everything needed to decompress it. + * @constructor + * @param {number} compressedSize the size of the data compressed. + * @param {number} uncompressedSize the size of the data after decompression. + * @param {number} crc32 the crc32 of the decompressed file. + * @param {object} compression the type of compression, see lib/compressions.js. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the compressed data. + */ +function CompressedObject(compressedSize, uncompressedSize, crc32, compression, data) { + this.compressedSize = compressedSize; + this.uncompressedSize = uncompressedSize; + this.crc32 = crc32; + this.compression = compression; + this.compressedContent = data; } CompressedObject.prototype = { /** - * Return the decompressed content in an unspecified format. - * The format will depend on the decompressor. - * @return {Object} the decompressed content. + * Create a worker to get the uncompressed content. + * @return {GenericWorker} the worker. */ - getContent: function() { - return null; // see implementation + getContentWorker : function () { + var worker = new DataWorker(external.Promise.resolve(this.compressedContent)) + .pipe(this.compression.uncompressWorker()) + .pipe(new DataLengthProbe("data_length")); + + var that = this; + worker.on("end", function () { + if(this.streamInfo['data_length'] !== that.uncompressedSize) { + throw new Error("Bug : uncompressed data size mismatch"); + } + }); + return worker; }, /** - * Return the compressed content in an unspecified format. - * The format will depend on the compressed conten source. - * @return {Object} the compressed content. + * Create a worker to get the compressed content. + * @return {GenericWorker} the worker. */ - getCompressedContent: function() { - return null; // see implementation + getCompressedWorker : function () { + return new DataWorker(external.Promise.resolve(this.compressedContent)) + .withStreamInfo("compressedSize", this.compressedSize) + .withStreamInfo("uncompressedSize", this.uncompressedSize) + .withStreamInfo("crc32", this.crc32) + .withStreamInfo("compression", this.compression) + ; } }; -module.exports = CompressedObject; - -},{}],3:[function(_dereq_,module,exports){ -'use strict'; -exports.STORE = { - magic: "\x00\x00", - compress: function(content, compressionOptions) { - return content; // no compression - }, - uncompress: function(content) { - return content; // no compression - }, - compressInputType: null, - uncompressInputType: null -}; -exports.DEFLATE = _dereq_('./flate'); - -},{"./flate":8}],4:[function(_dereq_,module,exports){ -'use strict'; - -var utils = _dereq_('./utils'); - -var table = [ - 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, - 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, - 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, - 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, - 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, - 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, - 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, - 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, - 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, - 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, - 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, - 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, - 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, - 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, - 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, - 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, - 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, - 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, - 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, - 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, - 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, - 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, - 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, - 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, - 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, - 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, - 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, - 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, - 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, - 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, - 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, - 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, - 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, - 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, - 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, - 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, - 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, - 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, - 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, - 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, - 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, - 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, - 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, - 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, - 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, - 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, - 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, - 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, - 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, - 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, - 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, - 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, - 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, - 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, - 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, - 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, - 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, - 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, - 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, - 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, - 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, - 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, - 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, - 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D -]; /** - * - * Javascript crc32 - * http://www.webtoolkit.info/ - * + * Chain the given worker with other workers to compress the content with the + * given compresion. + * @param {GenericWorker} uncompressedWorker the worker to pipe. + * @param {Object} compression the compression object. + * @param {Object} compressionOptions the options to use when compressing. + * @return {GenericWorker} the new worker compressing the content. */ -module.exports = function crc32(input, crc) { +CompressedObject.createWorkerFrom = function (uncompressedWorker, compression, compressionOptions) { + return uncompressedWorker + .pipe(new Crc32Probe()) + .pipe(new DataLengthProbe("uncompressedSize")) + .pipe(compression.compressWorker(compressionOptions)) + .pipe(new DataLengthProbe("compressedSize")) + .withStreamInfo("compression", compression); +}; + +module.exports = CompressedObject; + +},{"./external":6,"./stream/Crc32Probe":25,"./stream/DataLengthProbe":26,"./stream/DataWorker":27}],3:[function(require,module,exports){ +'use strict'; + +var GenericWorker = require("./stream/GenericWorker"); + +exports.STORE = { + magic: "\x00\x00", + compressWorker : function (compressionOptions) { + return new GenericWorker("STORE compression"); + }, + uncompressWorker : function () { + return new GenericWorker("STORE decompression"); + } +}; +exports.DEFLATE = require('./flate'); + +},{"./flate":7,"./stream/GenericWorker":28}],4:[function(require,module,exports){ +'use strict'; + +var utils = require('./utils'); + +/** + * The following functions come from pako, from pako/lib/zlib/crc32.js + * released under the MIT license, see pako https://github.com/nodeca/pako/ + */ + +// Use ordinary array, since untyped makes no boost here +function makeTable() { + var c, table = []; + + for(var n =0; n < 256; n++){ + c = n; + for(var k =0; k < 8; k++){ + c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1)); + } + table[n] = c; + } + + return table; +} + +// Create table on load. Just 255 signed longs. Not a problem. +var crcTable = makeTable(); + + +function crc32(crc, buf, len, pos) { + var t = crcTable, end = pos + len; + + crc = crc ^ (-1); + + for (var i = pos; i < end; i++ ) { + crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; +} + +// That's all for the pako functions. + +/** + * Compute the crc32 of a string. + * This is almost the same as the function crc32, but for strings. Using the + * same function for the two use cases leads to horrible performances. + * @param {Number} crc the starting value of the crc. + * @param {String} str the string to use. + * @param {Number} len the length of the string. + * @param {Number} pos the starting position for the crc32 computation. + * @return {Number} the computed crc32. + */ +function crc32str(crc, str, len, pos) { + var t = crcTable, end = pos + len; + + crc = crc ^ (-1); + + for (var i = pos; i < end; i++ ) { + crc = (crc >>> 8) ^ t[(crc ^ str.charCodeAt(i)) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; +} + +module.exports = function crc32wrapper(input, crc) { if (typeof input === "undefined" || !input.length) { return 0; } var isArray = utils.getTypeOf(input) !== "string"; - if (typeof(crc) == "undefined") { - crc = 0; + if(isArray) { + return crc32(crc|0, input, input.length, 0); + } else { + return crc32str(crc|0, input, input.length, 0); } - var x = 0; - var y = 0; - var b = 0; - - crc = crc ^ (-1); - for (var i = 0, iTop = input.length; i < iTop; i++) { - b = isArray ? input[i] : input.charCodeAt(i); - y = (crc ^ b) & 0xFF; - x = table[y]; - crc = (crc >>> 8) ^ x; - } - - return crc ^ (-1); }; // vim: set shiftwidth=4 softtabstop=4: -},{"./utils":21}],5:[function(_dereq_,module,exports){ +},{"./utils":32}],5:[function(require,module,exports){ 'use strict'; -var utils = _dereq_('./utils'); +exports.base64 = false; +exports.binary = false; +exports.dir = false; +exports.createFolders = true; +exports.date = null; +exports.compression = null; +exports.compressionOptions = null; +exports.comment = null; +exports.unixPermissions = null; +exports.dosPermissions = null; + +},{}],6:[function(require,module,exports){ +/* global Promise */ +'use strict'; + +// load the global object first: +// - it should be better integrated in the system (unhandledRejection in node) +// - the environment may have a custom Promise implementation (see zone.js) +var ES6Promise = null; +if (typeof Promise !== "undefined") { + ES6Promise = Promise; +} else { + ES6Promise = require("lie"); +} + +/** + * Let the user use/change some implementations. + */ +module.exports = { + Promise: ES6Promise +}; + +},{"lie":58}],7:[function(require,module,exports){ +'use strict'; +var USE_TYPEDARRAY = (typeof Uint8Array !== 'undefined') && (typeof Uint16Array !== 'undefined') && (typeof Uint32Array !== 'undefined'); + +var pako = require("pako"); +var utils = require("./utils"); +var GenericWorker = require("./stream/GenericWorker"); + +var ARRAY_TYPE = USE_TYPEDARRAY ? "uint8array" : "array"; + +exports.magic = "\x08\x00"; + +/** + * Create a worker that uses pako to inflate/deflate. + * @constructor + * @param {String} action the name of the pako function to call : either "Deflate" or "Inflate". + * @param {Object} options the options to use when (de)compressing. + */ +function FlateWorker(action, options) { + GenericWorker.call(this, "FlateWorker/" + action); + + this._pako = new pako[action]({ + raw:true, + level : options.level || -1 // default compression + }); + // the `meta` object from the last chunk received + // this allow this worker to pass around metadata + this.meta = {}; + + var self = this; + this._pako.onData = function(data) { + self.push({ + data : data, + meta : self.meta + }); + }; +} + +utils.inherits(FlateWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +FlateWorker.prototype.processChunk = function (chunk) { + this.meta = chunk.meta; + this._pako.push(utils.transformTo(ARRAY_TYPE, chunk.data), false); +}; + +/** + * @see GenericWorker.flush + */ +FlateWorker.prototype.flush = function () { + GenericWorker.prototype.flush.call(this); + this._pako.push([], true); +}; +/** + * @see GenericWorker.cleanUp + */ +FlateWorker.prototype.cleanUp = function () { + GenericWorker.prototype.cleanUp.call(this); + this._pako = null; +}; + +exports.compressWorker = function (compressionOptions) { + return new FlateWorker("Deflate", compressionOptions); +}; +exports.uncompressWorker = function () { + return new FlateWorker("Inflate", {}); +}; + +},{"./stream/GenericWorker":28,"./utils":32,"pako":59}],8:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var GenericWorker = require('../stream/GenericWorker'); +var utf8 = require('../utf8'); +var crc32 = require('../crc32'); +var signature = require('../signature'); + +/** + * Transform an integer into a string in hexadecimal. + * @private + * @param {number} dec the number to convert. + * @param {number} bytes the number of bytes to generate. + * @returns {string} the result. + */ +var decToHex = function(dec, bytes) { + var hex = "", i; + for (i = 0; i < bytes; i++) { + hex += String.fromCharCode(dec & 0xff); + dec = dec >>> 8; + } + return hex; +}; + +/** + * Generate the UNIX part of the external file attributes. + * @param {Object} unixPermissions the unix permissions or null. + * @param {Boolean} isDir true if the entry is a directory, false otherwise. + * @return {Number} a 32 bit integer. + * + * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute : + * + * TTTTsstrwxrwxrwx0000000000ADVSHR + * ^^^^____________________________ file type, see zipinfo.c (UNX_*) + * ^^^_________________________ setuid, setgid, sticky + * ^^^^^^^^^________________ permissions + * ^^^^^^^^^^______ not used ? + * ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only + */ +var generateUnixExternalFileAttr = function (unixPermissions, isDir) { + + var result = unixPermissions; + if (!unixPermissions) { + // I can't use octal values in strict mode, hence the hexa. + // 040775 => 0x41fd + // 0100664 => 0x81b4 + result = isDir ? 0x41fd : 0x81b4; + } + return (result & 0xFFFF) << 16; +}; + +/** + * Generate the DOS part of the external file attributes. + * @param {Object} dosPermissions the dos permissions or null. + * @param {Boolean} isDir true if the entry is a directory, false otherwise. + * @return {Number} a 32 bit integer. + * + * Bit 0 Read-Only + * Bit 1 Hidden + * Bit 2 System + * Bit 3 Volume Label + * Bit 4 Directory + * Bit 5 Archive + */ +var generateDosExternalFileAttr = function (dosPermissions, isDir) { + + // the dir flag is already set for compatibility + return (dosPermissions || 0) & 0x3F; +}; + +/** + * Generate the various parts used in the construction of the final zip file. + * @param {Object} streamInfo the hash with informations about the compressed file. + * @param {Boolean} streamedContent is the content streamed ? + * @param {Boolean} streamingEnded is the stream finished ? + * @param {number} offset the current offset from the start of the zip file. + * @param {String} platform let's pretend we are this platform (change platform dependents fields) + * @param {Function} encodeFileName the function to encode the file name / comment. + * @return {Object} the zip parts. + */ +var generateZipParts = function(streamInfo, streamedContent, streamingEnded, offset, platform, encodeFileName) { + var file = streamInfo['file'], + compression = streamInfo['compression'], + useCustomEncoding = encodeFileName !== utf8.utf8encode, + encodedFileName = utils.transformTo("string", encodeFileName(file.name)), + utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)), + comment = file.comment, + encodedComment = utils.transformTo("string", encodeFileName(comment)), + utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)), + useUTF8ForFileName = utfEncodedFileName.length !== file.name.length, + useUTF8ForComment = utfEncodedComment.length !== comment.length, + dosTime, + dosDate, + extraFields = "", + unicodePathExtraField = "", + unicodeCommentExtraField = "", + dir = file.dir, + date = file.date; + + + var dataInfo = { + crc32 : 0, + compressedSize : 0, + uncompressedSize : 0 + }; + + // if the content is streamed, the sizes/crc32 are only available AFTER + // the end of the stream. + if (!streamedContent || streamingEnded) { + dataInfo.crc32 = streamInfo['crc32']; + dataInfo.compressedSize = streamInfo['compressedSize']; + dataInfo.uncompressedSize = streamInfo['uncompressedSize']; + } + + var bitflag = 0; + if (streamedContent) { + // Bit 3: the sizes/crc32 are set to zero in the local header. + // The correct values are put in the data descriptor immediately + // following the compressed data. + bitflag |= 0x0008; + } + if (!useCustomEncoding && (useUTF8ForFileName || useUTF8ForComment)) { + // Bit 11: Language encoding flag (EFS). + bitflag |= 0x0800; + } + + + var extFileAttr = 0; + var versionMadeBy = 0; + if (dir) { + // dos or unix, we set the dos dir flag + extFileAttr |= 0x00010; + } + if(platform === "UNIX") { + versionMadeBy = 0x031E; // UNIX, version 3.0 + extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir); + } else { // DOS or other, fallback to DOS + versionMadeBy = 0x0014; // DOS, version 2.0 + extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir); + } + + // date + // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html + // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html + // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html + + dosTime = date.getUTCHours(); + dosTime = dosTime << 6; + dosTime = dosTime | date.getUTCMinutes(); + dosTime = dosTime << 5; + dosTime = dosTime | date.getUTCSeconds() / 2; + + dosDate = date.getUTCFullYear() - 1980; + dosDate = dosDate << 4; + dosDate = dosDate | (date.getUTCMonth() + 1); + dosDate = dosDate << 5; + dosDate = dosDate | date.getUTCDate(); + + if (useUTF8ForFileName) { + // set the unicode path extra field. unzip needs at least one extra + // field to correctly handle unicode path, so using the path is as good + // as any other information. This could improve the situation with + // other archive managers too. + // This field is usually used without the utf8 flag, with a non + // unicode path in the header (winrar, winzip). This helps (a bit) + // with the messy Windows' default compressed folders feature but + // breaks on p7zip which doesn't seek the unicode path extra field. + // So for now, UTF-8 everywhere ! + unicodePathExtraField = + // Version + decToHex(1, 1) + + // NameCRC32 + decToHex(crc32(encodedFileName), 4) + + // UnicodeName + utfEncodedFileName; + + extraFields += + // Info-ZIP Unicode Path Extra Field + "\x75\x70" + + // size + decToHex(unicodePathExtraField.length, 2) + + // content + unicodePathExtraField; + } + + if(useUTF8ForComment) { + + unicodeCommentExtraField = + // Version + decToHex(1, 1) + + // CommentCRC32 + decToHex(crc32(encodedComment), 4) + + // UnicodeName + utfEncodedComment; + + extraFields += + // Info-ZIP Unicode Path Extra Field + "\x75\x63" + + // size + decToHex(unicodeCommentExtraField.length, 2) + + // content + unicodeCommentExtraField; + } + + var header = ""; + + // version needed to extract + header += "\x0A\x00"; + // general purpose bit flag + header += decToHex(bitflag, 2); + // compression method + header += compression.magic; + // last mod file time + header += decToHex(dosTime, 2); + // last mod file date + header += decToHex(dosDate, 2); + // crc-32 + header += decToHex(dataInfo.crc32, 4); + // compressed size + header += decToHex(dataInfo.compressedSize, 4); + // uncompressed size + header += decToHex(dataInfo.uncompressedSize, 4); + // file name length + header += decToHex(encodedFileName.length, 2); + // extra field length + header += decToHex(extraFields.length, 2); + + + var fileRecord = signature.LOCAL_FILE_HEADER + header + encodedFileName + extraFields; + + var dirRecord = signature.CENTRAL_FILE_HEADER + + // version made by (00: DOS) + decToHex(versionMadeBy, 2) + + // file header (common to file and central directory) + header + + // file comment length + decToHex(encodedComment.length, 2) + + // disk number start + "\x00\x00" + + // internal file attributes TODO + "\x00\x00" + + // external file attributes + decToHex(extFileAttr, 4) + + // relative offset of local header + decToHex(offset, 4) + + // file name + encodedFileName + + // extra field + extraFields + + // file comment + encodedComment; + + return { + fileRecord: fileRecord, + dirRecord: dirRecord + }; +}; + +/** + * Generate the EOCD record. + * @param {Number} entriesCount the number of entries in the zip file. + * @param {Number} centralDirLength the length (in bytes) of the central dir. + * @param {Number} localDirLength the length (in bytes) of the local dir. + * @param {String} comment the zip file comment as a binary string. + * @param {Function} encodeFileName the function to encode the comment. + * @return {String} the EOCD record. + */ +var generateCentralDirectoryEnd = function (entriesCount, centralDirLength, localDirLength, comment, encodeFileName) { + var dirEnd = ""; + var encodedComment = utils.transformTo("string", encodeFileName(comment)); + + // end of central dir signature + dirEnd = signature.CENTRAL_DIRECTORY_END + + // number of this disk + "\x00\x00" + + // number of the disk with the start of the central directory + "\x00\x00" + + // total number of entries in the central directory on this disk + decToHex(entriesCount, 2) + + // total number of entries in the central directory + decToHex(entriesCount, 2) + + // size of the central directory 4 bytes + decToHex(centralDirLength, 4) + + // offset of start of central directory with respect to the starting disk number + decToHex(localDirLength, 4) + + // .ZIP file comment length + decToHex(encodedComment.length, 2) + + // .ZIP file comment + encodedComment; + + return dirEnd; +}; + +/** + * Generate data descriptors for a file entry. + * @param {Object} streamInfo the hash generated by a worker, containing informations + * on the file entry. + * @return {String} the data descriptors. + */ +var generateDataDescriptors = function (streamInfo) { + var descriptor = ""; + descriptor = signature.DATA_DESCRIPTOR + + // crc-32 4 bytes + decToHex(streamInfo['crc32'], 4) + + // compressed size 4 bytes + decToHex(streamInfo['compressedSize'], 4) + + // uncompressed size 4 bytes + decToHex(streamInfo['uncompressedSize'], 4); + + return descriptor; +}; + + +/** + * A worker to concatenate other workers to create a zip file. + * @param {Boolean} streamFiles `true` to stream the content of the files, + * `false` to accumulate it. + * @param {String} comment the comment to use. + * @param {String} platform the platform to use, "UNIX" or "DOS". + * @param {Function} encodeFileName the function to encode file names and comments. + */ +function ZipFileWorker(streamFiles, comment, platform, encodeFileName) { + GenericWorker.call(this, "ZipFileWorker"); + // The number of bytes written so far. This doesn't count accumulated chunks. + this.bytesWritten = 0; + // The comment of the zip file + this.zipComment = comment; + // The platform "generating" the zip file. + this.zipPlatform = platform; + // the function to encode file names and comments. + this.encodeFileName = encodeFileName; + // Should we stream the content of the files ? + this.streamFiles = streamFiles; + // If `streamFiles` is false, we will need to accumulate the content of the + // files to calculate sizes / crc32 (and write them *before* the content). + // This boolean indicates if we are accumulating chunks (it will change a lot + // during the lifetime of this worker). + this.accumulate = false; + // The buffer receiving chunks when accumulating content. + this.contentBuffer = []; + // The list of generated directory records. + this.dirRecords = []; + // The offset (in bytes) from the beginning of the zip file for the current source. + this.currentSourceOffset = 0; + // The total number of entries in this zip file. + this.entriesCount = 0; + // the name of the file currently being added, null when handling the end of the zip file. + // Used for the emited metadata. + this.currentFile = null; + + + + this._sources = []; +} +utils.inherits(ZipFileWorker, GenericWorker); + +/** + * @see GenericWorker.push + */ +ZipFileWorker.prototype.push = function (chunk) { + + var currentFilePercent = chunk.meta.percent || 0; + var entriesCount = this.entriesCount; + var remainingFiles = this._sources.length; + + if(this.accumulate) { + this.contentBuffer.push(chunk); + } else { + this.bytesWritten += chunk.data.length; + + GenericWorker.prototype.push.call(this, { + data : chunk.data, + meta : { + currentFile : this.currentFile, + percent : entriesCount ? (currentFilePercent + 100 * (entriesCount - remainingFiles - 1)) / entriesCount : 100 + } + }); + } +}; + +/** + * The worker started a new source (an other worker). + * @param {Object} streamInfo the streamInfo object from the new source. + */ +ZipFileWorker.prototype.openedSource = function (streamInfo) { + this.currentSourceOffset = this.bytesWritten; + this.currentFile = streamInfo['file'].name; + + var streamedContent = this.streamFiles && !streamInfo['file'].dir; + + // don't stream folders (because they don't have any content) + if(streamedContent) { + var record = generateZipParts(streamInfo, streamedContent, false, this.currentSourceOffset, this.zipPlatform, this.encodeFileName); + this.push({ + data : record.fileRecord, + meta : {percent:0} + }); + } else { + // we need to wait for the whole file before pushing anything + this.accumulate = true; + } +}; + +/** + * The worker finished a source (an other worker). + * @param {Object} streamInfo the streamInfo object from the finished source. + */ +ZipFileWorker.prototype.closedSource = function (streamInfo) { + this.accumulate = false; + var streamedContent = this.streamFiles && !streamInfo['file'].dir; + var record = generateZipParts(streamInfo, streamedContent, true, this.currentSourceOffset, this.zipPlatform, this.encodeFileName); + + this.dirRecords.push(record.dirRecord); + if(streamedContent) { + // after the streamed file, we put data descriptors + this.push({ + data : generateDataDescriptors(streamInfo), + meta : {percent:100} + }); + } else { + // the content wasn't streamed, we need to push everything now + // first the file record, then the content + this.push({ + data : record.fileRecord, + meta : {percent:0} + }); + while(this.contentBuffer.length) { + this.push(this.contentBuffer.shift()); + } + } + this.currentFile = null; +}; + +/** + * @see GenericWorker.flush + */ +ZipFileWorker.prototype.flush = function () { + + var localDirLength = this.bytesWritten; + for(var i = 0; i < this.dirRecords.length; i++) { + this.push({ + data : this.dirRecords[i], + meta : {percent:100} + }); + } + var centralDirLength = this.bytesWritten - localDirLength; + + var dirEnd = generateCentralDirectoryEnd(this.dirRecords.length, centralDirLength, localDirLength, this.zipComment, this.encodeFileName); + + this.push({ + data : dirEnd, + meta : {percent:100} + }); +}; + +/** + * Prepare the next source to be read. + */ +ZipFileWorker.prototype.prepareNextSource = function () { + this.previous = this._sources.shift(); + this.openedSource(this.previous.streamInfo); + if (this.isPaused) { + this.previous.pause(); + } else { + this.previous.resume(); + } +}; + +/** + * @see GenericWorker.registerPrevious + */ +ZipFileWorker.prototype.registerPrevious = function (previous) { + this._sources.push(previous); + var self = this; + + previous.on('data', function (chunk) { + self.processChunk(chunk); + }); + previous.on('end', function () { + self.closedSource(self.previous.streamInfo); + if(self._sources.length) { + self.prepareNextSource(); + } else { + self.end(); + } + }); + previous.on('error', function (e) { + self.error(e); + }); + return this; +}; + +/** + * @see GenericWorker.resume + */ +ZipFileWorker.prototype.resume = function () { + if(!GenericWorker.prototype.resume.call(this)) { + return false; + } + + if (!this.previous && this._sources.length) { + this.prepareNextSource(); + return true; + } + if (!this.previous && !this._sources.length && !this.generatedError) { + this.end(); + return true; + } +}; + +/** + * @see GenericWorker.error + */ +ZipFileWorker.prototype.error = function (e) { + var sources = this._sources; + if(!GenericWorker.prototype.error.call(this, e)) { + return false; + } + for(var i = 0; i < sources.length; i++) { + try { + sources[i].error(e); + } catch(e) { + // the `error` exploded, nothing to do + } + } + return true; +}; + +/** + * @see GenericWorker.lock + */ +ZipFileWorker.prototype.lock = function () { + GenericWorker.prototype.lock.call(this); + var sources = this._sources; + for(var i = 0; i < sources.length; i++) { + sources[i].lock(); + } +}; + +module.exports = ZipFileWorker; + +},{"../crc32":4,"../signature":23,"../stream/GenericWorker":28,"../utf8":31,"../utils":32}],9:[function(require,module,exports){ +'use strict'; + +var compressions = require('../compressions'); +var ZipFileWorker = require('./ZipFileWorker'); + +/** + * Find the compression to use. + * @param {String} fileCompression the compression defined at the file level, if any. + * @param {String} zipCompression the compression defined at the load() level. + * @return {Object} the compression object to use. + */ +var getCompression = function (fileCompression, zipCompression) { + + var compressionName = fileCompression || zipCompression; + var compression = compressions[compressionName]; + if (!compression) { + throw new Error(compressionName + " is not a valid compression method !"); + } + return compression; +}; + +/** + * Create a worker to generate a zip file. + * @param {JSZip} zip the JSZip instance at the right root level. + * @param {Object} options to generate the zip file. + * @param {String} comment the comment to use. + */ +exports.generateWorker = function (zip, options, comment) { + + var zipFileWorker = new ZipFileWorker(options.streamFiles, comment, options.platform, options.encodeFileName); + var entriesCount = 0; + try { + + zip.forEach(function (relativePath, file) { + entriesCount++; + var compression = getCompression(file.options.compression, options.compression); + var compressionOptions = file.options.compressionOptions || options.compressionOptions || {}; + var dir = file.dir, date = file.date; + + file._compressWorker(compression, compressionOptions) + .withStreamInfo("file", { + name : relativePath, + dir : dir, + date : date, + comment : file.comment || "", + unixPermissions : file.unixPermissions, + dosPermissions : file.dosPermissions + }) + .pipe(zipFileWorker); + }); + zipFileWorker.entriesCount = entriesCount; + } catch (e) { + zipFileWorker.error(e); + } + + return zipFileWorker; +}; + +},{"../compressions":3,"./ZipFileWorker":8}],10:[function(require,module,exports){ +'use strict'; + +/** + * Representation a of zip file in js + * @constructor + */ +function JSZip() { + // if this constructor is used without `new`, it adds `new` before itself: + if(!(this instanceof JSZip)) { + return new JSZip(); + } + + if(arguments.length) { + throw new Error("The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide."); + } + + // object containing the files : + // { + // "folder/" : {...}, + // "folder/data.txt" : {...} + // } + this.files = {}; + + this.comment = null; + + // Where we are in the hierarchy + this.root = ""; + this.clone = function() { + var newObj = new JSZip(); + for (var i in this) { + if (typeof this[i] !== "function") { + newObj[i] = this[i]; + } + } + return newObj; + }; +} +JSZip.prototype = require('./object'); +JSZip.prototype.loadAsync = require('./load'); +JSZip.support = require('./support'); +JSZip.defaults = require('./defaults'); + +// TODO find a better way to handle this version, +// a require('package.json').version doesn't work with webpack, see #327 +JSZip.version = "3.1.3"; + +JSZip.loadAsync = function (content, options) { + return new JSZip().loadAsync(content, options); +}; + +JSZip.external = require("./external"); +module.exports = JSZip; + +},{"./defaults":5,"./external":6,"./load":11,"./object":15,"./support":30}],11:[function(require,module,exports){ +'use strict'; +var utils = require('./utils'); +var external = require("./external"); +var utf8 = require('./utf8'); +var utils = require('./utils'); +var ZipEntries = require('./zipEntries'); +var Crc32Probe = require('./stream/Crc32Probe'); +var nodejsUtils = require("./nodejsUtils"); + +/** + * Check the CRC32 of an entry. + * @param {ZipEntry} zipEntry the zip entry to check. + * @return {Promise} the result. + */ +function checkEntryCRC32(zipEntry) { + return new external.Promise(function (resolve, reject) { + var worker = zipEntry.decompressed.getContentWorker().pipe(new Crc32Probe()); + worker.on("error", function (e) { + reject(e); + }) + .on("end", function () { + if (worker.streamInfo.crc32 !== zipEntry.decompressed.crc32) { + reject(new Error("Corrupted zip : CRC32 mismatch")); + } else { + resolve(); + } + }) + .resume(); + }); +} + +module.exports = function(data, options) { + var zip = this; + options = utils.extend(options || {}, { + base64: false, + checkCRC32: false, + optimizedBinaryString: false, + createFolders: false, + decodeFileName: utf8.utf8decode + }); + + if (nodejsUtils.isNode && nodejsUtils.isStream(data)) { + return external.Promise.reject(new Error("JSZip can't accept a stream when loading a zip file.")); + } + + return utils.prepareContent("the loaded zip file", data, true, options.optimizedBinaryString, options.base64) + .then(function(data) { + var zipEntries = new ZipEntries(options); + zipEntries.load(data); + return zipEntries; + }).then(function checkCRC32(zipEntries) { + var promises = [external.Promise.resolve(zipEntries)]; + var files = zipEntries.files; + if (options.checkCRC32) { + for (var i = 0; i < files.length; i++) { + promises.push(checkEntryCRC32(files[i])); + } + } + return external.Promise.all(promises); + }).then(function addFiles(results) { + var zipEntries = results.shift(); + var files = zipEntries.files; + for (var i = 0; i < files.length; i++) { + var input = files[i]; + zip.file(input.fileNameStr, input.decompressed, { + binary: true, + optimizedBinaryString: true, + date: input.date, + dir: input.dir, + comment : input.fileCommentStr.length ? input.fileCommentStr : null, + unixPermissions : input.unixPermissions, + dosPermissions : input.dosPermissions, + createFolders: options.createFolders + }); + } + if (zipEntries.zipComment.length) { + zip.comment = zipEntries.zipComment; + } + + return zip; + }); +}; + +},{"./external":6,"./nodejsUtils":14,"./stream/Crc32Probe":25,"./utf8":31,"./utils":32,"./zipEntries":33}],12:[function(require,module,exports){ +"use strict"; + +var utils = require('../utils'); +var GenericWorker = require('../stream/GenericWorker'); + +/** + * A worker that use a nodejs stream as source. + * @constructor + * @param {String} filename the name of the file entry for this stream. + * @param {Readable} stream the nodejs stream. + */ +function NodejsStreamInputAdapter(filename, stream) { + GenericWorker.call(this, "Nodejs stream input adapter for " + filename); + this._upstreamEnded = false; + this._bindStream(stream); +} + +utils.inherits(NodejsStreamInputAdapter, GenericWorker); + +/** + * Prepare the stream and bind the callbacks on it. + * Do this ASAP on node 0.10 ! A lazy binding doesn't always work. + * @param {Stream} stream the nodejs stream to use. + */ +NodejsStreamInputAdapter.prototype._bindStream = function (stream) { + var self = this; + this._stream = stream; + stream.pause(); + stream + .on("data", function (chunk) { + self.push({ + data: chunk, + meta : { + percent : 0 + } + }); + }) + .on("error", function (e) { + if(self.isPaused) { + this.generatedError = e; + } else { + self.error(e); + } + }) + .on("end", function () { + if(self.isPaused) { + self._upstreamEnded = true; + } else { + self.end(); + } + }); +}; +NodejsStreamInputAdapter.prototype.pause = function () { + if(!GenericWorker.prototype.pause.call(this)) { + return false; + } + this._stream.pause(); + return true; +}; +NodejsStreamInputAdapter.prototype.resume = function () { + if(!GenericWorker.prototype.resume.call(this)) { + return false; + } + + if(this._upstreamEnded) { + this.end(); + } else { + this._stream.resume(); + } + + return true; +}; + +module.exports = NodejsStreamInputAdapter; + +},{"../stream/GenericWorker":28,"../utils":32}],13:[function(require,module,exports){ +'use strict'; + +var Readable = require('readable-stream').Readable; + +var util = require('util'); +util.inherits(NodejsStreamOutputAdapter, Readable); + +/** +* A nodejs stream using a worker as source. +* @see the SourceWrapper in http://nodejs.org/api/stream.html +* @constructor +* @param {StreamHelper} helper the helper wrapping the worker +* @param {Object} options the nodejs stream options +* @param {Function} updateCb the update callback. +*/ +function NodejsStreamOutputAdapter(helper, options, updateCb) { + Readable.call(this, options); + this._helper = helper; + + var self = this; + helper.on("data", function (data, meta) { + if (!self.push(data)) { + self._helper.pause(); + } + if(updateCb) { + updateCb(meta); + } + }) + .on("error", function(e) { + self.emit('error', e); + }) + .on("end", function () { + self.push(null); + }); +} + + +NodejsStreamOutputAdapter.prototype._read = function() { + this._helper.resume(); +}; + +module.exports = NodejsStreamOutputAdapter; + +},{"readable-stream":16,"util":undefined}],14:[function(require,module,exports){ +'use strict'; + +module.exports = { + /** + * True if this is running in Nodejs, will be undefined in a browser. + * In a browser, browserify won't include this file and the whole module + * will be resolved an empty object. + */ + isNode : typeof Buffer !== "undefined", + /** + * Create a new nodejs Buffer. + * @param {Object} data the data to pass to the constructor. + * @param {String} encoding the encoding to use. + * @return {Buffer} a new Buffer. + */ + newBuffer : function(data, encoding){ + return new Buffer(data, encoding); + }, + /** + * Find out if an object is a Buffer. + * @param {Object} b the object to test. + * @return {Boolean} true if the object is a Buffer, false otherwise. + */ + isBuffer : function(b){ + return Buffer.isBuffer(b); + }, + + isStream : function (obj) { + return obj && + typeof obj.on === "function" && + typeof obj.pause === "function" && + typeof obj.resume === "function"; + } +}; + +},{}],15:[function(require,module,exports){ +'use strict'; +var utf8 = require('./utf8'); +var utils = require('./utils'); +var GenericWorker = require('./stream/GenericWorker'); +var StreamHelper = require('./stream/StreamHelper'); +var defaults = require('./defaults'); +var CompressedObject = require('./compressedObject'); +var ZipObject = require('./zipObject'); +var generate = require("./generate"); +var nodejsUtils = require("./nodejsUtils"); +var NodejsStreamInputAdapter = require("./nodejs/NodejsStreamInputAdapter"); + + +/** + * Add a file in the current folder. + * @private + * @param {string} name the name of the file + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file + * @param {Object} originalOptions the options of the file + * @return {Object} the new file. + */ +var fileAdd = function(name, data, originalOptions) { + // be sure sub folders exist + var dataType = utils.getTypeOf(data), + parent; + + + /* + * Correct options. + */ + + var o = utils.extend(originalOptions || {}, defaults); + o.date = o.date || new Date(); + if (o.compression !== null) { + o.compression = o.compression.toUpperCase(); + } + + if (typeof o.unixPermissions === "string") { + o.unixPermissions = parseInt(o.unixPermissions, 8); + } + + // UNX_IFDIR 0040000 see zipinfo.c + if (o.unixPermissions && (o.unixPermissions & 0x4000)) { + o.dir = true; + } + // Bit 4 Directory + if (o.dosPermissions && (o.dosPermissions & 0x0010)) { + o.dir = true; + } + + if (o.dir) { + name = forceTrailingSlash(name); + } + if (o.createFolders && (parent = parentFolder(name))) { + folderAdd.call(this, parent, true); + } + + var isUnicodeString = dataType === "string" && o.binary === false && o.base64 === false; + if (!originalOptions || typeof originalOptions.binary === "undefined") { + o.binary = !isUnicodeString; + } + + + var isCompressedEmpty = (data instanceof CompressedObject) && data.uncompressedSize === 0; + + if (isCompressedEmpty || o.dir || !data || data.length === 0) { + o.base64 = false; + o.binary = true; + data = ""; + o.compression = "STORE"; + dataType = "string"; + } + + /* + * Convert content to fit. + */ + + var zipObjectContent = null; + if (data instanceof CompressedObject || data instanceof GenericWorker) { + zipObjectContent = data; + } else if (nodejsUtils.isNode && nodejsUtils.isStream(data)) { + zipObjectContent = new NodejsStreamInputAdapter(name, data); + } else { + zipObjectContent = utils.prepareContent(name, data, o.binary, o.optimizedBinaryString, o.base64); + } + + var object = new ZipObject(name, zipObjectContent, o); + this.files[name] = object; + /* + TODO: we can't throw an exception because we have async promises + (we can have a promise of a Date() for example) but returning a + promise is useless because file(name, data) returns the JSZip + object for chaining. Should we break that to allow the user + to catch the error ? + + return external.Promise.resolve(zipObjectContent) + .then(function () { + return object; + }); + */ +}; + +/** + * Find the parent folder of the path. + * @private + * @param {string} path the path to use + * @return {string} the parent folder, or "" + */ +var parentFolder = function (path) { + if (path.slice(-1) === '/') { + path = path.substring(0, path.length - 1); + } + var lastSlash = path.lastIndexOf('/'); + return (lastSlash > 0) ? path.substring(0, lastSlash) : ""; +}; + +/** + * Returns the path with a slash at the end. + * @private + * @param {String} path the path to check. + * @return {String} the path with a trailing slash. + */ +var forceTrailingSlash = function(path) { + // Check the name ends with a / + if (path.slice(-1) !== "/") { + path += "/"; // IE doesn't like substr(-1) + } + return path; +}; + +/** + * Add a (sub) folder in the current folder. + * @private + * @param {string} name the folder's name + * @param {boolean=} [createFolders] If true, automatically create sub + * folders. Defaults to false. + * @return {Object} the new folder. + */ +var folderAdd = function(name, createFolders) { + createFolders = (typeof createFolders !== 'undefined') ? createFolders : defaults.createFolders; + + name = forceTrailingSlash(name); + + // Does this folder already exist? + if (!this.files[name]) { + fileAdd.call(this, name, null, { + dir: true, + createFolders: createFolders + }); + } + return this.files[name]; +}; + +/** +* Cross-window, cross-Node-context regular expression detection +* @param {Object} object Anything +* @return {Boolean} true if the object is a regular expression, +* false otherwise +*/ +function isRegExp(object) { + return Object.prototype.toString.call(object) === "[object RegExp]"; +} + +// return the actual prototype of JSZip +var out = { + /** + * @see loadAsync + */ + load: function() { + throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); + }, + + + /** + * Call a callback function for each entry at this folder level. + * @param {Function} cb the callback function: + * function (relativePath, file) {...} + * It takes 2 arguments : the relative path and the file. + */ + forEach: function(cb) { + var filename, relativePath, file; + for (filename in this.files) { + if (!this.files.hasOwnProperty(filename)) { + continue; + } + file = this.files[filename]; + relativePath = filename.slice(this.root.length, filename.length); + if (relativePath && filename.slice(0, this.root.length) === this.root) { // the file is in the current root + cb(relativePath, file); // TODO reverse the parameters ? need to be clean AND consistent with the filter search fn... + } + } + }, + + /** + * Filter nested files/folders with the specified function. + * @param {Function} search the predicate to use : + * function (relativePath, file) {...} + * It takes 2 arguments : the relative path and the file. + * @return {Array} An array of matching elements. + */ + filter: function(search) { + var result = []; + this.forEach(function (relativePath, entry) { + if (search(relativePath, entry)) { // the file matches the function + result.push(entry); + } + + }); + return result; + }, + + /** + * Add a file to the zip file, or search a file. + * @param {string|RegExp} name The name of the file to add (if data is defined), + * the name of the file to find (if no data) or a regex to match files. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded + * @param {Object} o File options + * @return {JSZip|Object|Array} this JSZip object (when adding a file), + * a file (when searching by string) or an array of files (when searching by regex). + */ + file: function(name, data, o) { + if (arguments.length === 1) { + if (isRegExp(name)) { + var regexp = name; + return this.filter(function(relativePath, file) { + return !file.dir && regexp.test(relativePath); + }); + } + else { // text + var obj = this.files[this.root + name]; + if (obj && !obj.dir) { + return obj; + } else { + return null; + } + } + } + else { // more than one argument : we have data ! + name = this.root + name; + fileAdd.call(this, name, data, o); + } + return this; + }, + + /** + * Add a directory to the zip file, or search. + * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders. + * @return {JSZip} an object with the new directory as the root, or an array containing matching folders. + */ + folder: function(arg) { + if (!arg) { + return this; + } + + if (isRegExp(arg)) { + return this.filter(function(relativePath, file) { + return file.dir && arg.test(relativePath); + }); + } + + // else, name is a new folder + var name = this.root + arg; + var newFolder = folderAdd.call(this, name); + + // Allow chaining by returning a new object with this folder as the root + var ret = this.clone(); + ret.root = newFolder.name; + return ret; + }, + + /** + * Delete a file, or a directory and all sub-files, from the zip + * @param {string} name the name of the file to delete + * @return {JSZip} this JSZip object + */ + remove: function(name) { + name = this.root + name; + var file = this.files[name]; + if (!file) { + // Look for any folders + if (name.slice(-1) !== "/") { + name += "/"; + } + file = this.files[name]; + } + + if (file && !file.dir) { + // file + delete this.files[name]; + } else { + // maybe a folder, delete recursively + var kids = this.filter(function(relativePath, file) { + return file.name.slice(0, name.length) === name; + }); + for (var i = 0; i < kids.length; i++) { + delete this.files[kids[i].name]; + } + } + + return this; + }, + + /** + * Generate the complete zip file + * @param {Object} options the options to generate the zip file : + * - compression, "STORE" by default. + * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob. + * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file + */ + generate: function(options) { + throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); + }, + + /** + * Generate the complete zip file as an internal stream. + * @param {Object} options the options to generate the zip file : + * - compression, "STORE" by default. + * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob. + * @return {StreamHelper} the streamed zip file. + */ + generateInternalStream: function(options) { + var worker, opts = {}; + try { + opts = utils.extend(options || {}, { + streamFiles: false, + compression: "STORE", + compressionOptions : null, + type: "", + platform: "DOS", + comment: null, + mimeType: 'application/zip', + encodeFileName: utf8.utf8encode + }); + + opts.type = opts.type.toLowerCase(); + opts.compression = opts.compression.toUpperCase(); + + // "binarystring" is prefered but the internals use "string". + if(opts.type === "binarystring") { + opts.type = "string"; + } + + if (!opts.type) { + throw new Error("No output type specified."); + } + + utils.checkSupport(opts.type); + + // accept nodejs `process.platform` + if( + opts.platform === 'darwin' || + opts.platform === 'freebsd' || + opts.platform === 'linux' || + opts.platform === 'sunos' + ) { + opts.platform = "UNIX"; + } + if (opts.platform === 'win32') { + opts.platform = "DOS"; + } + + var comment = opts.comment || this.comment || ""; + worker = generate.generateWorker(this, opts, comment); + } catch (e) { + worker = new GenericWorker("error"); + worker.error(e); + } + return new StreamHelper(worker, opts.type || "string", opts.mimeType); + }, + /** + * Generate the complete zip file asynchronously. + * @see generateInternalStream + */ + generateAsync: function(options, onUpdate) { + return this.generateInternalStream(options).accumulate(onUpdate); + }, + /** + * Generate the complete zip file asynchronously. + * @see generateInternalStream + */ + generateNodeStream: function(options, onUpdate) { + options = options || {}; + if (!options.type) { + options.type = "nodebuffer"; + } + return this.generateInternalStream(options).toNodejsStream(onUpdate); + } +}; +module.exports = out; + +},{"./compressedObject":2,"./defaults":5,"./generate":9,"./nodejs/NodejsStreamInputAdapter":12,"./nodejsUtils":14,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31,"./utils":32,"./zipObject":35}],16:[function(require,module,exports){ +/* + * This file is used by module bundlers (browserify/webpack/etc) when + * including a stream implementation. We use "readable-stream" to get a + * consistent behavior between nodejs versions but bundlers often have a shim + * for "stream". Using this shim greatly improve the compatibility and greatly + * reduce the final size of the bundle (only one stream implementation, not + * two). + */ +module.exports = require("stream"); + +},{"stream":undefined}],17:[function(require,module,exports){ +'use strict'; +var DataReader = require('./DataReader'); +var utils = require('../utils'); + +function ArrayReader(data) { + DataReader.call(this, data); + for(var i = 0; i < this.data.length; i++) { + data[i] = data[i] & 0xFF; + } +} +utils.inherits(ArrayReader, DataReader); +/** + * @see DataReader.byteAt + */ +ArrayReader.prototype.byteAt = function(i) { + return this.data[this.zero + i]; +}; +/** + * @see DataReader.lastIndexOfSignature + */ +ArrayReader.prototype.lastIndexOfSignature = function(sig) { + var sig0 = sig.charCodeAt(0), + sig1 = sig.charCodeAt(1), + sig2 = sig.charCodeAt(2), + sig3 = sig.charCodeAt(3); + for (var i = this.length - 4; i >= 0; --i) { + if (this.data[i] === sig0 && this.data[i + 1] === sig1 && this.data[i + 2] === sig2 && this.data[i + 3] === sig3) { + return i - this.zero; + } + } + + return -1; +}; +/** + * @see DataReader.readAndCheckSignature + */ +ArrayReader.prototype.readAndCheckSignature = function (sig) { + var sig0 = sig.charCodeAt(0), + sig1 = sig.charCodeAt(1), + sig2 = sig.charCodeAt(2), + sig3 = sig.charCodeAt(3), + data = this.readData(4); + return sig0 === data[0] && sig1 === data[1] && sig2 === data[2] && sig3 === data[3]; +}; +/** + * @see DataReader.readData + */ +ArrayReader.prototype.readData = function(size) { + this.checkOffset(size); + if(size === 0) { + return []; + } + var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); + this.index += size; + return result; +}; +module.exports = ArrayReader; + +},{"../utils":32,"./DataReader":18}],18:[function(require,module,exports){ +'use strict'; +var utils = require('../utils'); function DataReader(data) { - this.data = null; // type : see implementation - this.length = 0; + this.data = data; // type : see implementation + this.length = data.length; this.index = 0; + this.zero = 0; } DataReader.prototype = { /** @@ -254,7 +1776,7 @@ DataReader.prototype = { * @throws {Error} an Error if the index is out of bounds. */ checkIndex: function(newIndex) { - if (this.length < newIndex || newIndex < 0) { + if (this.length < this.zero + newIndex || newIndex < 0) { throw new Error("End of data reached (data length = " + this.length + ", asked index = " + (newIndex) + "). Corrupted zip ?"); } }, @@ -322,1194 +1844,149 @@ DataReader.prototype = { lastIndexOfSignature: function(sig) { // see implementations }, + /** + * Read the signature (4 bytes) at the current position and compare it with sig. + * @param {string} sig the expected signature + * @return {boolean} true if the signature matches, false otherwise. + */ + readAndCheckSignature: function(sig) { + // see implementations + }, /** * Get the next date. * @return {Date} the date. */ readDate: function() { var dostime = this.readInt(4); - return new Date( + return new Date(Date.UTC( ((dostime >> 25) & 0x7f) + 1980, // year ((dostime >> 21) & 0x0f) - 1, // month (dostime >> 16) & 0x1f, // day (dostime >> 11) & 0x1f, // hour (dostime >> 5) & 0x3f, // minute - (dostime & 0x1f) << 1); // second + (dostime & 0x1f) << 1)); // second } }; module.exports = DataReader; -},{"./utils":21}],6:[function(_dereq_,module,exports){ +},{"../utils":32}],19:[function(require,module,exports){ 'use strict'; -exports.base64 = false; -exports.binary = false; -exports.dir = false; -exports.createFolders = false; -exports.date = null; -exports.compression = null; -exports.compressionOptions = null; -exports.comment = null; -exports.unixPermissions = null; -exports.dosPermissions = null; - -},{}],7:[function(_dereq_,module,exports){ -'use strict'; -var utils = _dereq_('./utils'); - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.string2binary = function(str) { - return utils.string2binary(str); -}; - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.string2Uint8Array = function(str) { - return utils.transformTo("uint8array", str); -}; - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.uint8Array2String = function(array) { - return utils.transformTo("string", array); -}; - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.string2Blob = function(str) { - var buffer = utils.transformTo("arraybuffer", str); - return utils.arrayBuffer2Blob(buffer); -}; - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.arrayBuffer2Blob = function(buffer) { - return utils.arrayBuffer2Blob(buffer); -}; - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.transformTo = function(outputType, input) { - return utils.transformTo(outputType, input); -}; - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.getTypeOf = function(input) { - return utils.getTypeOf(input); -}; - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.checkSupport = function(type) { - return utils.checkSupport(type); -}; - -/** - * @deprecated - * This value will be removed in a future version without replacement. - */ -exports.MAX_VALUE_16BITS = utils.MAX_VALUE_16BITS; - -/** - * @deprecated - * This value will be removed in a future version without replacement. - */ -exports.MAX_VALUE_32BITS = utils.MAX_VALUE_32BITS; - - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.pretty = function(str) { - return utils.pretty(str); -}; - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.findCompression = function(compressionMethod) { - return utils.findCompression(compressionMethod); -}; - -/** - * @deprecated - * This function will be removed in a future version without replacement. - */ -exports.isRegExp = function (object) { - return utils.isRegExp(object); -}; - - -},{"./utils":21}],8:[function(_dereq_,module,exports){ -'use strict'; -var USE_TYPEDARRAY = (typeof Uint8Array !== 'undefined') && (typeof Uint16Array !== 'undefined') && (typeof Uint32Array !== 'undefined'); - -var pako = _dereq_("pako"); -exports.uncompressInputType = USE_TYPEDARRAY ? "uint8array" : "array"; -exports.compressInputType = USE_TYPEDARRAY ? "uint8array" : "array"; - -exports.magic = "\x08\x00"; -exports.compress = function(input, compressionOptions) { - return pako.deflateRaw(input, { - level : compressionOptions.level || -1 // default compression - }); -}; -exports.uncompress = function(input) { - return pako.inflateRaw(input); -}; - -},{"pako":24}],9:[function(_dereq_,module,exports){ -'use strict'; - -var base64 = _dereq_('./base64'); - -/** -Usage: - zip = new JSZip(); - zip.file("hello.txt", "Hello, World!").file("tempfile", "nothing"); - zip.folder("images").file("smile.gif", base64Data, {base64: true}); - zip.file("Xmas.txt", "Ho ho ho !", {date : new Date("December 25, 2007 00:00:01")}); - zip.remove("tempfile"); - - base64zip = zip.generate(); - -**/ - -/** - * Representation a of zip file in js - * @constructor - * @param {String=|ArrayBuffer=|Uint8Array=} data the data to load, if any (optional). - * @param {Object=} options the options for creating this objects (optional). - */ -function JSZip(data, options) { - // if this constructor is used without `new`, it adds `new` before itself: - if(!(this instanceof JSZip)) return new JSZip(data, options); - - // object containing the files : - // { - // "folder/" : {...}, - // "folder/data.txt" : {...} - // } - this.files = {}; - - this.comment = null; - - // Where we are in the hierarchy - this.root = ""; - if (data) { - this.load(data, options); - } - this.clone = function() { - var newObj = new JSZip(); - for (var i in this) { - if (typeof this[i] !== "function") { - newObj[i] = this[i]; - } - } - return newObj; - }; -} -JSZip.prototype = _dereq_('./object'); -JSZip.prototype.load = _dereq_('./load'); -JSZip.support = _dereq_('./support'); -JSZip.defaults = _dereq_('./defaults'); - -/** - * @deprecated - * This namespace will be removed in a future version without replacement. - */ -JSZip.utils = _dereq_('./deprecatedPublicUtils'); - -JSZip.base64 = { - /** - * @deprecated - * This method will be removed in a future version without replacement. - */ - encode : function(input) { - return base64.encode(input); - }, - /** - * @deprecated - * This method will be removed in a future version without replacement. - */ - decode : function(input) { - return base64.decode(input); - } -}; -JSZip.compressions = _dereq_('./compressions'); -module.exports = JSZip; - -},{"./base64":1,"./compressions":3,"./defaults":6,"./deprecatedPublicUtils":7,"./load":10,"./object":13,"./support":17}],10:[function(_dereq_,module,exports){ -'use strict'; -var base64 = _dereq_('./base64'); -var ZipEntries = _dereq_('./zipEntries'); -module.exports = function(data, options) { - var files, zipEntries, i, input; - options = options || {}; - if (options.base64) { - data = base64.decode(data); - } - - zipEntries = new ZipEntries(data, options); - files = zipEntries.files; - for (i = 0; i < files.length; i++) { - input = files[i]; - this.file(input.fileName, input.decompressed, { - binary: true, - optimizedBinaryString: true, - date: input.date, - dir: input.dir, - comment : input.fileComment.length ? input.fileComment : null, - unixPermissions : input.unixPermissions, - dosPermissions : input.dosPermissions, - createFolders: options.createFolders - }); - } - if (zipEntries.zipComment.length) { - this.comment = zipEntries.zipComment; - } - - return this; -}; - -},{"./base64":1,"./zipEntries":22}],11:[function(_dereq_,module,exports){ -(function (Buffer){ -'use strict'; -module.exports = function(data, encoding){ - return new Buffer(data, encoding); -}; -module.exports.test = function(b){ - return Buffer.isBuffer(b); -}; - -}).call(this,(typeof Buffer !== "undefined" ? Buffer : undefined)) -},{}],12:[function(_dereq_,module,exports){ -'use strict'; -var Uint8ArrayReader = _dereq_('./uint8ArrayReader'); +var Uint8ArrayReader = require('./Uint8ArrayReader'); +var utils = require('../utils'); function NodeBufferReader(data) { - this.data = data; - this.length = this.data.length; - this.index = 0; + Uint8ArrayReader.call(this, data); } -NodeBufferReader.prototype = new Uint8ArrayReader(); +utils.inherits(NodeBufferReader, Uint8ArrayReader); /** * @see DataReader.readData */ NodeBufferReader.prototype.readData = function(size) { this.checkOffset(size); - var result = this.data.slice(this.index, this.index + size); + var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); this.index += size; return result; }; module.exports = NodeBufferReader; -},{"./uint8ArrayReader":18}],13:[function(_dereq_,module,exports){ +},{"../utils":32,"./Uint8ArrayReader":21}],20:[function(require,module,exports){ 'use strict'; -var support = _dereq_('./support'); -var utils = _dereq_('./utils'); -var crc32 = _dereq_('./crc32'); -var signature = _dereq_('./signature'); -var defaults = _dereq_('./defaults'); -var base64 = _dereq_('./base64'); -var compressions = _dereq_('./compressions'); -var CompressedObject = _dereq_('./compressedObject'); -var nodeBuffer = _dereq_('./nodeBuffer'); -var utf8 = _dereq_('./utf8'); -var StringWriter = _dereq_('./stringWriter'); -var Uint8ArrayWriter = _dereq_('./uint8ArrayWriter'); +var DataReader = require('./DataReader'); +var utils = require('../utils'); +function StringReader(data) { + DataReader.call(this, data); +} +utils.inherits(StringReader, DataReader); /** - * Returns the raw data of a ZipObject, decompress the content if necessary. - * @param {ZipObject} file the file to use. - * @return {String|ArrayBuffer|Uint8Array|Buffer} the data. + * @see DataReader.byteAt */ -var getRawData = function(file) { - if (file._data instanceof CompressedObject) { - file._data = file._data.getContent(); - file.options.binary = true; - file.options.base64 = false; - - if (utils.getTypeOf(file._data) === "uint8array") { - var copy = file._data; - // when reading an arraybuffer, the CompressedObject mechanism will keep it and subarray() a Uint8Array. - // if we request a file in the same format, we might get the same Uint8Array or its ArrayBuffer (the original zip file). - file._data = new Uint8Array(copy.length); - // with an empty Uint8Array, Opera fails with a "Offset larger than array size" - if (copy.length !== 0) { - file._data.set(copy, 0); - } - } - } - return file._data; +StringReader.prototype.byteAt = function(i) { + return this.data.charCodeAt(this.zero + i); }; - /** - * Returns the data of a ZipObject in a binary form. If the content is an unicode string, encode it. - * @param {ZipObject} file the file to use. - * @return {String|ArrayBuffer|Uint8Array|Buffer} the data. + * @see DataReader.lastIndexOfSignature */ -var getBinaryData = function(file) { - var result = getRawData(file), - type = utils.getTypeOf(result); - if (type === "string") { - if (!file.options.binary) { - // unicode text ! - // unicode string => binary string is a painful process, check if we can avoid it. - if (support.nodebuffer) { - return nodeBuffer(result, "utf-8"); - } - } - return file.asBinary(); - } +StringReader.prototype.lastIndexOfSignature = function(sig) { + return this.data.lastIndexOf(sig) - this.zero; +}; +/** + * @see DataReader.readAndCheckSignature + */ +StringReader.prototype.readAndCheckSignature = function (sig) { + var data = this.readData(4); + return sig === data; +}; +/** + * @see DataReader.readData + */ +StringReader.prototype.readData = function(size) { + this.checkOffset(size); + // this will work because the constructor applied the "& 0xff" mask. + var result = this.data.slice(this.zero + this.index, this.zero + this.index + size); + this.index += size; return result; }; +module.exports = StringReader; +},{"../utils":32,"./DataReader":18}],21:[function(require,module,exports){ +'use strict'; +var ArrayReader = require('./ArrayReader'); +var utils = require('../utils'); + +function Uint8ArrayReader(data) { + ArrayReader.call(this, data); +} +utils.inherits(Uint8ArrayReader, ArrayReader); /** - * Transform this._data into a string. - * @param {function} filter a function String -> String, applied if not null on the result. - * @return {String} the string representing this._data. + * @see DataReader.readData */ -var dataToString = function(asUTF8) { - var result = getRawData(this); - if (result === null || typeof result === "undefined") { - return ""; - } - // if the data is a base64 string, we decode it before checking the encoding ! - if (this.options.base64) { - result = base64.decode(result); - } - if (asUTF8 && this.options.binary) { - // JSZip.prototype.utf8decode supports arrays as input - // skip to array => string step, utf8decode will do it. - result = out.utf8decode(result); - } - else { - // no utf8 transformation, do the array => string step. - result = utils.transformTo("string", result); - } - - if (!asUTF8 && !this.options.binary) { - result = utils.transformTo("string", out.utf8encode(result)); +Uint8ArrayReader.prototype.readData = function(size) { + this.checkOffset(size); + if(size === 0) { + // in IE10, when using subarray(idx, idx), we get the array [0x00] instead of []. + return new Uint8Array(0); } + var result = this.data.subarray(this.zero + this.index, this.zero + this.index + size); + this.index += size; return result; }; -/** - * A simple object representing a file in the zip file. - * @constructor - * @param {string} name the name of the file - * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data - * @param {Object} options the options of the file - */ -var ZipObject = function(name, data, options) { - this.name = name; - this.dir = options.dir; - this.date = options.date; - this.comment = options.comment; - this.unixPermissions = options.unixPermissions; - this.dosPermissions = options.dosPermissions; +module.exports = Uint8ArrayReader; - this._data = data; - this.options = options; +},{"../utils":32,"./ArrayReader":17}],22:[function(require,module,exports){ +'use strict'; - /* - * This object contains initial values for dir and date. - * With them, we can check if the user changed the deprecated metadata in - * `ZipObject#options` or not. - */ - this._initialMetadata = { - dir : options.dir, - date : options.date - }; -}; - -ZipObject.prototype = { - /** - * Return the content as UTF8 string. - * @return {string} the UTF8 string. - */ - asText: function() { - return dataToString.call(this, true); - }, - /** - * Returns the binary content. - * @return {string} the content as binary. - */ - asBinary: function() { - return dataToString.call(this, false); - }, - /** - * Returns the content as a nodejs Buffer. - * @return {Buffer} the content as a Buffer. - */ - asNodeBuffer: function() { - var result = getBinaryData(this); - return utils.transformTo("nodebuffer", result); - }, - /** - * Returns the content as an Uint8Array. - * @return {Uint8Array} the content as an Uint8Array. - */ - asUint8Array: function() { - var result = getBinaryData(this); - return utils.transformTo("uint8array", result); - }, - /** - * Returns the content as an ArrayBuffer. - * @return {ArrayBuffer} the content as an ArrayBufer. - */ - asArrayBuffer: function() { - return this.asUint8Array().buffer; - } -}; +var utils = require('../utils'); +var support = require('../support'); +var ArrayReader = require('./ArrayReader'); +var StringReader = require('./StringReader'); +var NodeBufferReader = require('./NodeBufferReader'); +var Uint8ArrayReader = require('./Uint8ArrayReader'); /** - * Transform an integer into a string in hexadecimal. - * @private - * @param {number} dec the number to convert. - * @param {number} bytes the number of bytes to generate. - * @returns {string} the result. + * Create a reader adapted to the data. + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data to read. + * @return {DataReader} the data reader. */ -var decToHex = function(dec, bytes) { - var hex = "", - i; - for (i = 0; i < bytes; i++) { - hex += String.fromCharCode(dec & 0xff); - dec = dec >>> 8; +module.exports = function (data) { + var type = utils.getTypeOf(data); + utils.checkSupport(type); + if (type === "string" && !support.uint8array) { + return new StringReader(data); } - return hex; + if (type === "nodebuffer") { + return new NodeBufferReader(data); + } + if (support.uint8array) { + return new Uint8ArrayReader(utils.transformTo("uint8array", data)); + } + return new ArrayReader(utils.transformTo("array", data)); }; -/** - * Merge the objects passed as parameters into a new one. - * @private - * @param {...Object} var_args All objects to merge. - * @return {Object} a new object with the data of the others. - */ -var extend = function() { - var result = {}, i, attr; - for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers - for (attr in arguments[i]) { - if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") { - result[attr] = arguments[i][attr]; - } - } - } - return result; -}; +// vim: set shiftwidth=4 softtabstop=4: -/** - * Transforms the (incomplete) options from the user into the complete - * set of options to create a file. - * @private - * @param {Object} o the options from the user. - * @return {Object} the complete set of options. - */ -var prepareFileAttrs = function(o) { - o = o || {}; - if (o.base64 === true && (o.binary === null || o.binary === undefined)) { - o.binary = true; - } - o = extend(o, defaults); - o.date = o.date || new Date(); - if (o.compression !== null) o.compression = o.compression.toUpperCase(); - - return o; -}; - -/** - * Add a file in the current folder. - * @private - * @param {string} name the name of the file - * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data of the file - * @param {Object} o the options of the file - * @return {Object} the new file. - */ -var fileAdd = function(name, data, o) { - // be sure sub folders exist - var dataType = utils.getTypeOf(data), - parent; - - o = prepareFileAttrs(o); - - if (typeof o.unixPermissions === "string") { - o.unixPermissions = parseInt(o.unixPermissions, 8); - } - - // UNX_IFDIR 0040000 see zipinfo.c - if (o.unixPermissions && (o.unixPermissions & 0x4000)) { - o.dir = true; - } - // Bit 4 Directory - if (o.dosPermissions && (o.dosPermissions & 0x0010)) { - o.dir = true; - } - - if (o.dir) { - name = forceTrailingSlash(name); - } - - if (o.createFolders && (parent = parentFolder(name))) { - folderAdd.call(this, parent, true); - } - - if (o.dir || data === null || typeof data === "undefined") { - o.base64 = false; - o.binary = false; - data = null; - dataType = null; - } - else if (dataType === "string") { - if (o.binary && !o.base64) { - // optimizedBinaryString == true means that the file has already been filtered with a 0xFF mask - if (o.optimizedBinaryString !== true) { - // this is a string, not in a base64 format. - // Be sure that this is a correct "binary string" - data = utils.string2binary(data); - } - } - } - else { // arraybuffer, uint8array, ... - o.base64 = false; - o.binary = true; - - if (!dataType && !(data instanceof CompressedObject)) { - throw new Error("The data of '" + name + "' is in an unsupported format !"); - } - - // special case : it's way easier to work with Uint8Array than with ArrayBuffer - if (dataType === "arraybuffer") { - data = utils.transformTo("uint8array", data); - } - } - - var object = new ZipObject(name, data, o); - this.files[name] = object; - return object; -}; - -/** - * Find the parent folder of the path. - * @private - * @param {string} path the path to use - * @return {string} the parent folder, or "" - */ -var parentFolder = function (path) { - if (path.slice(-1) == '/') { - path = path.substring(0, path.length - 1); - } - var lastSlash = path.lastIndexOf('/'); - return (lastSlash > 0) ? path.substring(0, lastSlash) : ""; -}; - - -/** - * Returns the path with a slash at the end. - * @private - * @param {String} path the path to check. - * @return {String} the path with a trailing slash. - */ -var forceTrailingSlash = function(path) { - // Check the name ends with a / - if (path.slice(-1) != "/") { - path += "/"; // IE doesn't like substr(-1) - } - return path; -}; -/** - * Add a (sub) folder in the current folder. - * @private - * @param {string} name the folder's name - * @param {boolean=} [createFolders] If true, automatically create sub - * folders. Defaults to false. - * @return {Object} the new folder. - */ -var folderAdd = function(name, createFolders) { - createFolders = (typeof createFolders !== 'undefined') ? createFolders : false; - - name = forceTrailingSlash(name); - - // Does this folder already exist? - if (!this.files[name]) { - fileAdd.call(this, name, null, { - dir: true, - createFolders: createFolders - }); - } - return this.files[name]; -}; - -/** - * Generate a JSZip.CompressedObject for a given zipOject. - * @param {ZipObject} file the object to read. - * @param {JSZip.compression} compression the compression to use. - * @param {Object} compressionOptions the options to use when compressing. - * @return {JSZip.CompressedObject} the compressed result. - */ -var generateCompressedObjectFrom = function(file, compression, compressionOptions) { - var result = new CompressedObject(), - content; - - // the data has not been decompressed, we might reuse things ! - if (file._data instanceof CompressedObject) { - result.uncompressedSize = file._data.uncompressedSize; - result.crc32 = file._data.crc32; - - if (result.uncompressedSize === 0 || file.dir) { - compression = compressions['STORE']; - result.compressedContent = ""; - result.crc32 = 0; - } - else if (file._data.compressionMethod === compression.magic) { - result.compressedContent = file._data.getCompressedContent(); - } - else { - content = file._data.getContent(); - // need to decompress / recompress - result.compressedContent = compression.compress(utils.transformTo(compression.compressInputType, content), compressionOptions); - } - } - else { - // have uncompressed data - content = getBinaryData(file); - if (!content || content.length === 0 || file.dir) { - compression = compressions['STORE']; - content = ""; - } - result.uncompressedSize = content.length; - result.crc32 = crc32(content); - result.compressedContent = compression.compress(utils.transformTo(compression.compressInputType, content), compressionOptions); - } - - result.compressedSize = result.compressedContent.length; - result.compressionMethod = compression.magic; - - return result; -}; - - - - -/** - * Generate the UNIX part of the external file attributes. - * @param {Object} unixPermissions the unix permissions or null. - * @param {Boolean} isDir true if the entry is a directory, false otherwise. - * @return {Number} a 32 bit integer. - * - * adapted from http://unix.stackexchange.com/questions/14705/the-zip-formats-external-file-attribute : - * - * TTTTsstrwxrwxrwx0000000000ADVSHR - * ^^^^____________________________ file type, see zipinfo.c (UNX_*) - * ^^^_________________________ setuid, setgid, sticky - * ^^^^^^^^^________________ permissions - * ^^^^^^^^^^______ not used ? - * ^^^^^^ DOS attribute bits : Archive, Directory, Volume label, System file, Hidden, Read only - */ -var generateUnixExternalFileAttr = function (unixPermissions, isDir) { - - var result = unixPermissions; - if (!unixPermissions) { - // I can't use octal values in strict mode, hence the hexa. - // 040775 => 0x41fd - // 0100664 => 0x81b4 - result = isDir ? 0x41fd : 0x81b4; - } - - return (result & 0xFFFF) << 16; -}; - -/** - * Generate the DOS part of the external file attributes. - * @param {Object} dosPermissions the dos permissions or null. - * @param {Boolean} isDir true if the entry is a directory, false otherwise. - * @return {Number} a 32 bit integer. - * - * Bit 0 Read-Only - * Bit 1 Hidden - * Bit 2 System - * Bit 3 Volume Label - * Bit 4 Directory - * Bit 5 Archive - */ -var generateDosExternalFileAttr = function (dosPermissions, isDir) { - - // the dir flag is already set for compatibility - - return (dosPermissions || 0) & 0x3F; -}; - -/** - * Generate the various parts used in the construction of the final zip file. - * @param {string} name the file name. - * @param {ZipObject} file the file content. - * @param {JSZip.CompressedObject} compressedObject the compressed object. - * @param {number} offset the current offset from the start of the zip file. - * @param {String} platform let's pretend we are this platform (change platform dependents fields) - * @return {object} the zip parts. - */ -var generateZipParts = function(name, file, compressedObject, offset, platform) { - var data = compressedObject.compressedContent, - utfEncodedFileName = utils.transformTo("string", utf8.utf8encode(file.name)), - comment = file.comment || "", - utfEncodedComment = utils.transformTo("string", utf8.utf8encode(comment)), - useUTF8ForFileName = utfEncodedFileName.length !== file.name.length, - useUTF8ForComment = utfEncodedComment.length !== comment.length, - o = file.options, - dosTime, - dosDate, - extraFields = "", - unicodePathExtraField = "", - unicodeCommentExtraField = "", - dir, date; - - - // handle the deprecated options.dir - if (file._initialMetadata.dir !== file.dir) { - dir = file.dir; - } else { - dir = o.dir; - } - - // handle the deprecated options.date - if(file._initialMetadata.date !== file.date) { - date = file.date; - } else { - date = o.date; - } - - var extFileAttr = 0; - var versionMadeBy = 0; - if (dir) { - // dos or unix, we set the dos dir flag - extFileAttr |= 0x00010; - } - if(platform === "UNIX") { - versionMadeBy = 0x031E; // UNIX, version 3.0 - extFileAttr |= generateUnixExternalFileAttr(file.unixPermissions, dir); - } else { // DOS or other, fallback to DOS - versionMadeBy = 0x0014; // DOS, version 2.0 - extFileAttr |= generateDosExternalFileAttr(file.dosPermissions, dir); - } - - // date - // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html - // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html - // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html - - dosTime = date.getHours(); - dosTime = dosTime << 6; - dosTime = dosTime | date.getMinutes(); - dosTime = dosTime << 5; - dosTime = dosTime | date.getSeconds() / 2; - - dosDate = date.getFullYear() - 1980; - dosDate = dosDate << 4; - dosDate = dosDate | (date.getMonth() + 1); - dosDate = dosDate << 5; - dosDate = dosDate | date.getDate(); - - if (useUTF8ForFileName) { - // set the unicode path extra field. unzip needs at least one extra - // field to correctly handle unicode path, so using the path is as good - // as any other information. This could improve the situation with - // other archive managers too. - // This field is usually used without the utf8 flag, with a non - // unicode path in the header (winrar, winzip). This helps (a bit) - // with the messy Windows' default compressed folders feature but - // breaks on p7zip which doesn't seek the unicode path extra field. - // So for now, UTF-8 everywhere ! - unicodePathExtraField = - // Version - decToHex(1, 1) + - // NameCRC32 - decToHex(crc32(utfEncodedFileName), 4) + - // UnicodeName - utfEncodedFileName; - - extraFields += - // Info-ZIP Unicode Path Extra Field - "\x75\x70" + - // size - decToHex(unicodePathExtraField.length, 2) + - // content - unicodePathExtraField; - } - - if(useUTF8ForComment) { - - unicodeCommentExtraField = - // Version - decToHex(1, 1) + - // CommentCRC32 - decToHex(this.crc32(utfEncodedComment), 4) + - // UnicodeName - utfEncodedComment; - - extraFields += - // Info-ZIP Unicode Path Extra Field - "\x75\x63" + - // size - decToHex(unicodeCommentExtraField.length, 2) + - // content - unicodeCommentExtraField; - } - - var header = ""; - - // version needed to extract - header += "\x0A\x00"; - // general purpose bit flag - // set bit 11 if utf8 - header += (useUTF8ForFileName || useUTF8ForComment) ? "\x00\x08" : "\x00\x00"; - // compression method - header += compressedObject.compressionMethod; - // last mod file time - header += decToHex(dosTime, 2); - // last mod file date - header += decToHex(dosDate, 2); - // crc-32 - header += decToHex(compressedObject.crc32, 4); - // compressed size - header += decToHex(compressedObject.compressedSize, 4); - // uncompressed size - header += decToHex(compressedObject.uncompressedSize, 4); - // file name length - header += decToHex(utfEncodedFileName.length, 2); - // extra field length - header += decToHex(extraFields.length, 2); - - - var fileRecord = signature.LOCAL_FILE_HEADER + header + utfEncodedFileName + extraFields; - - var dirRecord = signature.CENTRAL_FILE_HEADER + - // version made by (00: DOS) - decToHex(versionMadeBy, 2) + - // file header (common to file and central directory) - header + - // file comment length - decToHex(utfEncodedComment.length, 2) + - // disk number start - "\x00\x00" + - // internal file attributes TODO - "\x00\x00" + - // external file attributes - decToHex(extFileAttr, 4) + - // relative offset of local header - decToHex(offset, 4) + - // file name - utfEncodedFileName + - // extra field - extraFields + - // file comment - utfEncodedComment; - - return { - fileRecord: fileRecord, - dirRecord: dirRecord, - compressedObject: compressedObject - }; -}; - - -// return the actual prototype of JSZip -var out = { - /** - * Read an existing zip and merge the data in the current JSZip object. - * The implementation is in jszip-load.js, don't forget to include it. - * @param {String|ArrayBuffer|Uint8Array|Buffer} stream The stream to load - * @param {Object} options Options for loading the stream. - * options.base64 : is the stream in base64 ? default : false - * @return {JSZip} the current JSZip object - */ - load: function(stream, options) { - throw new Error("Load method is not defined. Is the file jszip-load.js included ?"); - }, - - /** - * Filter nested files/folders with the specified function. - * @param {Function} search the predicate to use : - * function (relativePath, file) {...} - * It takes 2 arguments : the relative path and the file. - * @return {Array} An array of matching elements. - */ - filter: function(search) { - var result = [], - filename, relativePath, file, fileClone; - for (filename in this.files) { - if (!this.files.hasOwnProperty(filename)) { - continue; - } - file = this.files[filename]; - // return a new object, don't let the user mess with our internal objects :) - fileClone = new ZipObject(file.name, file._data, extend(file.options)); - relativePath = filename.slice(this.root.length, filename.length); - if (filename.slice(0, this.root.length) === this.root && // the file is in the current root - search(relativePath, fileClone)) { // and the file matches the function - result.push(fileClone); - } - } - return result; - }, - - /** - * Add a file to the zip file, or search a file. - * @param {string|RegExp} name The name of the file to add (if data is defined), - * the name of the file to find (if no data) or a regex to match files. - * @param {String|ArrayBuffer|Uint8Array|Buffer} data The file data, either raw or base64 encoded - * @param {Object} o File options - * @return {JSZip|Object|Array} this JSZip object (when adding a file), - * a file (when searching by string) or an array of files (when searching by regex). - */ - file: function(name, data, o) { - if (arguments.length === 1) { - if (utils.isRegExp(name)) { - var regexp = name; - return this.filter(function(relativePath, file) { - return !file.dir && regexp.test(relativePath); - }); - } - else { // text - return this.filter(function(relativePath, file) { - return !file.dir && relativePath === name; - })[0] || null; - } - } - else { // more than one argument : we have data ! - name = this.root + name; - fileAdd.call(this, name, data, o); - } - return this; - }, - - /** - * Add a directory to the zip file, or search. - * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders. - * @return {JSZip} an object with the new directory as the root, or an array containing matching folders. - */ - folder: function(arg) { - if (!arg) { - return this; - } - - if (utils.isRegExp(arg)) { - return this.filter(function(relativePath, file) { - return file.dir && arg.test(relativePath); - }); - } - - // else, name is a new folder - var name = this.root + arg; - var newFolder = folderAdd.call(this, name); - - // Allow chaining by returning a new object with this folder as the root - var ret = this.clone(); - ret.root = newFolder.name; - return ret; - }, - - /** - * Delete a file, or a directory and all sub-files, from the zip - * @param {string} name the name of the file to delete - * @return {JSZip} this JSZip object - */ - remove: function(name) { - name = this.root + name; - var file = this.files[name]; - if (!file) { - // Look for any folders - if (name.slice(-1) != "/") { - name += "/"; - } - file = this.files[name]; - } - - if (file && !file.dir) { - // file - delete this.files[name]; - } else { - // maybe a folder, delete recursively - var kids = this.filter(function(relativePath, file) { - return file.name.slice(0, name.length) === name; - }); - for (var i = 0; i < kids.length; i++) { - delete this.files[kids[i].name]; - } - } - - return this; - }, - - /** - * Generate the complete zip file - * @param {Object} options the options to generate the zip file : - * - base64, (deprecated, use type instead) true to generate base64. - * - compression, "STORE" by default. - * - type, "base64" by default. Values are : string, base64, uint8array, arraybuffer, blob. - * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the zip file - */ - generate: function(options) { - options = extend(options || {}, { - base64: true, - compression: "STORE", - compressionOptions : null, - type: "base64", - platform: "DOS", - comment: null, - mimeType: 'application/zip' - }); - - utils.checkSupport(options.type); - - // accept nodejs `process.platform` - if( - options.platform === 'darwin' || - options.platform === 'freebsd' || - options.platform === 'linux' || - options.platform === 'sunos' - ) { - options.platform = "UNIX"; - } - if (options.platform === 'win32') { - options.platform = "DOS"; - } - - var zipData = [], - localDirLength = 0, - centralDirLength = 0, - writer, i, - utfEncodedComment = utils.transformTo("string", this.utf8encode(options.comment || this.comment || "")); - - // first, generate all the zip parts. - for (var name in this.files) { - if (!this.files.hasOwnProperty(name)) { - continue; - } - var file = this.files[name]; - - var compressionName = file.options.compression || options.compression.toUpperCase(); - var compression = compressions[compressionName]; - if (!compression) { - throw new Error(compressionName + " is not a valid compression method !"); - } - var compressionOptions = file.options.compressionOptions || options.compressionOptions || {}; - - var compressedObject = generateCompressedObjectFrom.call(this, file, compression, compressionOptions); - - var zipPart = generateZipParts.call(this, name, file, compressedObject, localDirLength, options.platform); - localDirLength += zipPart.fileRecord.length + compressedObject.compressedSize; - centralDirLength += zipPart.dirRecord.length; - zipData.push(zipPart); - } - - var dirEnd = ""; - - // end of central dir signature - dirEnd = signature.CENTRAL_DIRECTORY_END + - // number of this disk - "\x00\x00" + - // number of the disk with the start of the central directory - "\x00\x00" + - // total number of entries in the central directory on this disk - decToHex(zipData.length, 2) + - // total number of entries in the central directory - decToHex(zipData.length, 2) + - // size of the central directory 4 bytes - decToHex(centralDirLength, 4) + - // offset of start of central directory with respect to the starting disk number - decToHex(localDirLength, 4) + - // .ZIP file comment length - decToHex(utfEncodedComment.length, 2) + - // .ZIP file comment - utfEncodedComment; - - - // we have all the parts (and the total length) - // time to create a writer ! - var typeName = options.type.toLowerCase(); - if(typeName==="uint8array"||typeName==="arraybuffer"||typeName==="blob"||typeName==="nodebuffer") { - writer = new Uint8ArrayWriter(localDirLength + centralDirLength + dirEnd.length); - }else{ - writer = new StringWriter(localDirLength + centralDirLength + dirEnd.length); - } - - for (i = 0; i < zipData.length; i++) { - writer.append(zipData[i].fileRecord); - writer.append(zipData[i].compressedObject.compressedContent); - } - for (i = 0; i < zipData.length; i++) { - writer.append(zipData[i].dirRecord); - } - - writer.append(dirEnd); - - var zip = writer.finalize(); - - - - switch(options.type.toLowerCase()) { - // case "zip is an Uint8Array" - case "uint8array" : - case "arraybuffer" : - case "nodebuffer" : - return utils.transformTo(options.type.toLowerCase(), zip); - case "blob" : - return utils.arrayBuffer2Blob(utils.transformTo("arraybuffer", zip), options.mimeType); - // case "zip is a string" - case "base64" : - return (options.base64) ? base64.encode(zip) : zip; - default : // case "string" : - return zip; - } - - }, - - /** - * @deprecated - * This method will be removed in a future version without replacement. - */ - crc32: function (input, crc) { - return crc32(input, crc); - }, - - /** - * @deprecated - * This method will be removed in a future version without replacement. - */ - utf8encode: function (string) { - return utils.transformTo("string", utf8.utf8encode(string)); - }, - - /** - * @deprecated - * This method will be removed in a future version without replacement. - */ - utf8decode: function (input) { - return utf8.utf8decode(input); - } -}; -module.exports = out; - -},{"./base64":1,"./compressedObject":2,"./compressions":3,"./crc32":4,"./defaults":6,"./nodeBuffer":11,"./signature":14,"./stringWriter":16,"./support":17,"./uint8ArrayWriter":19,"./utf8":20,"./utils":21}],14:[function(_dereq_,module,exports){ +},{"../support":30,"../utils":32,"./ArrayReader":17,"./NodeBufferReader":19,"./StringReader":20,"./Uint8ArrayReader":21}],23:[function(require,module,exports){ 'use strict'; exports.LOCAL_FILE_HEADER = "PK\x03\x04"; exports.CENTRAL_FILE_HEADER = "PK\x01\x02"; @@ -1518,86 +1995,702 @@ exports.ZIP64_CENTRAL_DIRECTORY_LOCATOR = "PK\x06\x07"; exports.ZIP64_CENTRAL_DIRECTORY_END = "PK\x06\x06"; exports.DATA_DESCRIPTOR = "PK\x07\x08"; -},{}],15:[function(_dereq_,module,exports){ +},{}],24:[function(require,module,exports){ 'use strict'; -var DataReader = _dereq_('./dataReader'); -var utils = _dereq_('./utils'); -function StringReader(data, optimizedBinaryString) { - this.data = data; - if (!optimizedBinaryString) { - this.data = utils.string2binary(this.data); - } - this.length = this.data.length; - this.index = 0; +var GenericWorker = require('./GenericWorker'); +var utils = require('../utils'); + +/** + * A worker which convert chunks to a specified type. + * @constructor + * @param {String} destType the destination type. + */ +function ConvertWorker(destType) { + GenericWorker.call(this, "ConvertWorker to " + destType); + this.destType = destType; } -StringReader.prototype = new DataReader(); -/** - * @see DataReader.byteAt - */ -StringReader.prototype.byteAt = function(i) { - return this.data.charCodeAt(i); -}; -/** - * @see DataReader.lastIndexOfSignature - */ -StringReader.prototype.lastIndexOfSignature = function(sig) { - return this.data.lastIndexOf(sig); -}; -/** - * @see DataReader.readData - */ -StringReader.prototype.readData = function(size) { - this.checkOffset(size); - // this will work because the constructor applied the "& 0xff" mask. - var result = this.data.slice(this.index, this.index + size); - this.index += size; - return result; -}; -module.exports = StringReader; +utils.inherits(ConvertWorker, GenericWorker); -},{"./dataReader":5,"./utils":21}],16:[function(_dereq_,module,exports){ +/** + * @see GenericWorker.processChunk + */ +ConvertWorker.prototype.processChunk = function (chunk) { + this.push({ + data : utils.transformTo(this.destType, chunk.data), + meta : chunk.meta + }); +}; +module.exports = ConvertWorker; + +},{"../utils":32,"./GenericWorker":28}],25:[function(require,module,exports){ 'use strict'; -var utils = _dereq_('./utils'); +var GenericWorker = require('./GenericWorker'); +var crc32 = require('../crc32'); +var utils = require('../utils'); /** - * An object to write any content to a string. + * A worker which calculate the crc32 of the data flowing through. * @constructor */ -var StringWriter = function() { - this.data = []; +function Crc32Probe() { + GenericWorker.call(this, "Crc32Probe"); + this.withStreamInfo("crc32", 0); +} +utils.inherits(Crc32Probe, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +Crc32Probe.prototype.processChunk = function (chunk) { + this.streamInfo.crc32 = crc32(chunk.data, this.streamInfo.crc32 || 0); + this.push(chunk); }; -StringWriter.prototype = { - /** - * Append any content to the current string. - * @param {Object} input the content to add. - */ - append: function(input) { - input = utils.transformTo("string", input); - this.data.push(input); - }, - /** - * Finalize the construction an return the result. - * @return {string} the generated string. - */ - finalize: function() { - return this.data.join(""); +module.exports = Crc32Probe; + +},{"../crc32":4,"../utils":32,"./GenericWorker":28}],26:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var GenericWorker = require('./GenericWorker'); + +/** + * A worker which calculate the total length of the data flowing through. + * @constructor + * @param {String} propName the name used to expose the length + */ +function DataLengthProbe(propName) { + GenericWorker.call(this, "DataLengthProbe for " + propName); + this.propName = propName; + this.withStreamInfo(propName, 0); +} +utils.inherits(DataLengthProbe, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +DataLengthProbe.prototype.processChunk = function (chunk) { + if(chunk) { + var length = this.streamInfo[this.propName] || 0; + this.streamInfo[this.propName] = length + chunk.data.length; + } + GenericWorker.prototype.processChunk.call(this, chunk); +}; +module.exports = DataLengthProbe; + + +},{"../utils":32,"./GenericWorker":28}],27:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var GenericWorker = require('./GenericWorker'); + +// the size of the generated chunks +// TODO expose this as a public variable +var DEFAULT_BLOCK_SIZE = 16 * 1024; + +/** + * A worker that reads a content and emits chunks. + * @constructor + * @param {Promise} dataP the promise of the data to split + */ +function DataWorker(dataP) { + GenericWorker.call(this, "DataWorker"); + var self = this; + this.dataIsReady = false; + this.index = 0; + this.max = 0; + this.data = null; + this.type = ""; + + this._tickScheduled = false; + + dataP.then(function (data) { + self.dataIsReady = true; + self.data = data; + self.max = data && data.length || 0; + self.type = utils.getTypeOf(data); + if(!self.isPaused) { + self._tickAndRepeat(); + } + }, function (e) { + self.error(e); + }); +} + +utils.inherits(DataWorker, GenericWorker); + +/** + * @see GenericWorker.cleanUp + */ +DataWorker.prototype.cleanUp = function () { + GenericWorker.prototype.cleanUp.call(this); + this.data = null; +}; + +/** + * @see GenericWorker.resume + */ +DataWorker.prototype.resume = function () { + if(!GenericWorker.prototype.resume.call(this)) { + return false; + } + + if (!this._tickScheduled && this.dataIsReady) { + this._tickScheduled = true; + utils.delay(this._tickAndRepeat, [], this); + } + return true; +}; + +/** + * Trigger a tick a schedule an other call to this function. + */ +DataWorker.prototype._tickAndRepeat = function() { + this._tickScheduled = false; + if(this.isPaused || this.isFinished) { + return; + } + this._tick(); + if(!this.isFinished) { + utils.delay(this._tickAndRepeat, [], this); + this._tickScheduled = true; } }; -module.exports = StringWriter; +/** + * Read and push a chunk. + */ +DataWorker.prototype._tick = function() { -},{"./utils":21}],17:[function(_dereq_,module,exports){ -(function (Buffer){ + if(this.isPaused || this.isFinished) { + return false; + } + + var size = DEFAULT_BLOCK_SIZE; + var data = null, nextIndex = Math.min(this.max, this.index + size); + if (this.index >= this.max) { + // EOF + return this.end(); + } else { + switch(this.type) { + case "string": + data = this.data.substring(this.index, nextIndex); + break; + case "uint8array": + data = this.data.subarray(this.index, nextIndex); + break; + case "array": + case "nodebuffer": + data = this.data.slice(this.index, nextIndex); + break; + } + this.index = nextIndex; + return this.push({ + data : data, + meta : { + percent : this.max ? this.index / this.max * 100 : 0 + } + }); + } +}; + +module.exports = DataWorker; + +},{"../utils":32,"./GenericWorker":28}],28:[function(require,module,exports){ 'use strict'; + +/** + * A worker that does nothing but passing chunks to the next one. This is like + * a nodejs stream but with some differences. On the good side : + * - it works on IE 6-9 without any issue / polyfill + * - it weights less than the full dependencies bundled with browserify + * - it forwards errors (no need to declare an error handler EVERYWHERE) + * + * A chunk is an object with 2 attributes : `meta` and `data`. The former is an + * object containing anything (`percent` for example), see each worker for more + * details. The latter is the real data (String, Uint8Array, etc). + * + * @constructor + * @param {String} name the name of the stream (mainly used for debugging purposes) + */ +function GenericWorker(name) { + // the name of the worker + this.name = name || "default"; + // an object containing metadata about the workers chain + this.streamInfo = {}; + // an error which happened when the worker was paused + this.generatedError = null; + // an object containing metadata to be merged by this worker into the general metadata + this.extraStreamInfo = {}; + // true if the stream is paused (and should not do anything), false otherwise + this.isPaused = true; + // true if the stream is finished (and should not do anything), false otherwise + this.isFinished = false; + // true if the stream is locked to prevent further structure updates (pipe), false otherwise + this.isLocked = false; + // the event listeners + this._listeners = { + 'data':[], + 'end':[], + 'error':[] + }; + // the previous worker, if any + this.previous = null; +} + +GenericWorker.prototype = { + /** + * Push a chunk to the next workers. + * @param {Object} chunk the chunk to push + */ + push : function (chunk) { + this.emit("data", chunk); + }, + /** + * End the stream. + * @return {Boolean} true if this call ended the worker, false otherwise. + */ + end : function () { + if (this.isFinished) { + return false; + } + + this.flush(); + try { + this.emit("end"); + this.cleanUp(); + this.isFinished = true; + } catch (e) { + this.emit("error", e); + } + return true; + }, + /** + * End the stream with an error. + * @param {Error} e the error which caused the premature end. + * @return {Boolean} true if this call ended the worker with an error, false otherwise. + */ + error : function (e) { + if (this.isFinished) { + return false; + } + + if(this.isPaused) { + this.generatedError = e; + } else { + this.isFinished = true; + + this.emit("error", e); + + // in the workers chain exploded in the middle of the chain, + // the error event will go downward but we also need to notify + // workers upward that there has been an error. + if(this.previous) { + this.previous.error(e); + } + + this.cleanUp(); + } + return true; + }, + /** + * Add a callback on an event. + * @param {String} name the name of the event (data, end, error) + * @param {Function} listener the function to call when the event is triggered + * @return {GenericWorker} the current object for chainability + */ + on : function (name, listener) { + this._listeners[name].push(listener); + return this; + }, + /** + * Clean any references when a worker is ending. + */ + cleanUp : function () { + this.streamInfo = this.generatedError = this.extraStreamInfo = null; + this._listeners = []; + }, + /** + * Trigger an event. This will call registered callback with the provided arg. + * @param {String} name the name of the event (data, end, error) + * @param {Object} arg the argument to call the callback with. + */ + emit : function (name, arg) { + if (this._listeners[name]) { + for(var i = 0; i < this._listeners[name].length; i++) { + this._listeners[name][i].call(this, arg); + } + } + }, + /** + * Chain a worker with an other. + * @param {Worker} next the worker receiving events from the current one. + * @return {worker} the next worker for chainability + */ + pipe : function (next) { + return next.registerPrevious(this); + }, + /** + * Same as `pipe` in the other direction. + * Using an API with `pipe(next)` is very easy. + * Implementing the API with the point of view of the next one registering + * a source is easier, see the ZipFileWorker. + * @param {Worker} previous the previous worker, sending events to this one + * @return {Worker} the current worker for chainability + */ + registerPrevious : function (previous) { + if (this.isLocked) { + throw new Error("The stream '" + this + "' has already been used."); + } + + // sharing the streamInfo... + this.streamInfo = previous.streamInfo; + // ... and adding our own bits + this.mergeStreamInfo(); + this.previous = previous; + var self = this; + previous.on('data', function (chunk) { + self.processChunk(chunk); + }); + previous.on('end', function () { + self.end(); + }); + previous.on('error', function (e) { + self.error(e); + }); + return this; + }, + /** + * Pause the stream so it doesn't send events anymore. + * @return {Boolean} true if this call paused the worker, false otherwise. + */ + pause : function () { + if(this.isPaused || this.isFinished) { + return false; + } + this.isPaused = true; + + if(this.previous) { + this.previous.pause(); + } + return true; + }, + /** + * Resume a paused stream. + * @return {Boolean} true if this call resumed the worker, false otherwise. + */ + resume : function () { + if(!this.isPaused || this.isFinished) { + return false; + } + this.isPaused = false; + + // if true, the worker tried to resume but failed + var withError = false; + if(this.generatedError) { + this.error(this.generatedError); + withError = true; + } + if(this.previous) { + this.previous.resume(); + } + + return !withError; + }, + /** + * Flush any remaining bytes as the stream is ending. + */ + flush : function () {}, + /** + * Process a chunk. This is usually the method overridden. + * @param {Object} chunk the chunk to process. + */ + processChunk : function(chunk) { + this.push(chunk); + }, + /** + * Add a key/value to be added in the workers chain streamInfo once activated. + * @param {String} key the key to use + * @param {Object} value the associated value + * @return {Worker} the current worker for chainability + */ + withStreamInfo : function (key, value) { + this.extraStreamInfo[key] = value; + this.mergeStreamInfo(); + return this; + }, + /** + * Merge this worker's streamInfo into the chain's streamInfo. + */ + mergeStreamInfo : function () { + for(var key in this.extraStreamInfo) { + if (!this.extraStreamInfo.hasOwnProperty(key)) { + continue; + } + this.streamInfo[key] = this.extraStreamInfo[key]; + } + }, + + /** + * Lock the stream to prevent further updates on the workers chain. + * After calling this method, all calls to pipe will fail. + */ + lock: function () { + if (this.isLocked) { + throw new Error("The stream '" + this + "' has already been used."); + } + this.isLocked = true; + if (this.previous) { + this.previous.lock(); + } + }, + + /** + * + * Pretty print the workers chain. + */ + toString : function () { + var me = "Worker " + this.name; + if (this.previous) { + return this.previous + " -> " + me; + } else { + return me; + } + } +}; + +module.exports = GenericWorker; + +},{}],29:[function(require,module,exports){ +'use strict'; + +var utils = require('../utils'); +var ConvertWorker = require('./ConvertWorker'); +var GenericWorker = require('./GenericWorker'); +var base64 = require('../base64'); +var support = require("../support"); +var external = require("../external"); + +var NodejsStreamOutputAdapter = null; +if (support.nodestream) { + try { + NodejsStreamOutputAdapter = require('../nodejs/NodejsStreamOutputAdapter'); + } catch(e) {} +} + +/** + * Apply the final transformation of the data. If the user wants a Blob for + * example, it's easier to work with an U8intArray and finally do the + * ArrayBuffer/Blob conversion. + * @param {String} resultType the name of the final type + * @param {String} chunkType the type of the data in the given array. + * @param {Array} dataArray the array containing the data chunks to concatenate + * @param {String|Uint8Array|Buffer} content the content to transform + * @param {String} mimeType the mime type of the content, if applicable. + * @return {String|Uint8Array|ArrayBuffer|Buffer|Blob} the content in the right format. + */ +function transformZipOutput(resultType, chunkType, dataArray, mimeType) { + var content = null; + switch(resultType) { + case "blob" : + return utils.newBlob(dataArray, mimeType); + case "base64" : + content = concat(chunkType, dataArray); + return base64.encode(content); + default : + content = concat(chunkType, dataArray); + return utils.transformTo(resultType, content); + } +} + +/** + * Concatenate an array of data of the given type. + * @param {String} type the type of the data in the given array. + * @param {Array} dataArray the array containing the data chunks to concatenate + * @return {String|Uint8Array|Buffer} the concatenated data + * @throws Error if the asked type is unsupported + */ +function concat (type, dataArray) { + var i, index = 0, res = null, totalLength = 0; + for(i = 0; i < dataArray.length; i++) { + totalLength += dataArray[i].length; + } + switch(type) { + case "string": + return dataArray.join(""); + case "array": + return Array.prototype.concat.apply([], dataArray); + case "uint8array": + res = new Uint8Array(totalLength); + for(i = 0; i < dataArray.length; i++) { + res.set(dataArray[i], index); + index += dataArray[i].length; + } + return res; + case "nodebuffer": + return Buffer.concat(dataArray); + default: + throw new Error("concat : unsupported type '" + type + "'"); + } +} + +/** + * Listen a StreamHelper, accumulate its content and concatenate it into a + * complete block. + * @param {StreamHelper} helper the helper to use. + * @param {Function} updateCallback a callback called on each update. Called + * with one arg : + * - the metadata linked to the update received. + * @return Promise the promise for the accumulation. + */ +function accumulate(helper, updateCallback) { + return new external.Promise(function (resolve, reject){ + var dataArray = []; + var chunkType = helper._internalType, + resultType = helper._outputType, + mimeType = helper._mimeType; + helper + .on('data', function (data, meta) { + dataArray.push(data); + if(updateCallback) { + updateCallback(meta); + } + }) + .on('error', function(err) { + dataArray = []; + reject(err); + }) + .on('end', function (){ + try { + var result = transformZipOutput(resultType, chunkType, dataArray, mimeType); + resolve(result); + } catch (e) { + reject(e); + } + dataArray = []; + }) + .resume(); + }); +} + +/** + * An helper to easily use workers outside of JSZip. + * @constructor + * @param {Worker} worker the worker to wrap + * @param {String} outputType the type of data expected by the use + * @param {String} mimeType the mime type of the content, if applicable. + */ +function StreamHelper(worker, outputType, mimeType) { + var internalType = outputType; + switch(outputType) { + case "blob": + internalType = "arraybuffer"; + break; + case "arraybuffer": + internalType = "uint8array"; + break; + case "base64": + internalType = "string"; + break; + } + + try { + // the type used internally + this._internalType = internalType; + // the type used to output results + this._outputType = outputType; + // the mime type + this._mimeType = mimeType; + utils.checkSupport(internalType); + this._worker = worker.pipe(new ConvertWorker(internalType)); + // the last workers can be rewired without issues but we need to + // prevent any updates on previous workers. + worker.lock(); + } catch(e) { + this._worker = new GenericWorker("error"); + this._worker.error(e); + } +} + +StreamHelper.prototype = { + /** + * Listen a StreamHelper, accumulate its content and concatenate it into a + * complete block. + * @param {Function} updateCb the update callback. + * @return Promise the promise for the accumulation. + */ + accumulate : function (updateCb) { + return accumulate(this, updateCb); + }, + /** + * Add a listener on an event triggered on a stream. + * @param {String} evt the name of the event + * @param {Function} fn the listener + * @return {StreamHelper} the current helper. + */ + on : function (evt, fn) { + var self = this; + + if(evt === "data") { + this._worker.on(evt, function (chunk) { + fn.call(self, chunk.data, chunk.meta); + }); + } else { + this._worker.on(evt, function () { + utils.delay(fn, arguments, self); + }); + } + return this; + }, + /** + * Resume the flow of chunks. + * @return {StreamHelper} the current helper. + */ + resume : function () { + utils.delay(this._worker.resume, [], this._worker); + return this; + }, + /** + * Pause the flow of chunks. + * @return {StreamHelper} the current helper. + */ + pause : function () { + this._worker.pause(); + return this; + }, + /** + * Return a nodejs stream for this helper. + * @param {Function} updateCb the update callback. + * @return {NodejsStreamOutputAdapter} the nodejs stream. + */ + toNodejsStream : function (updateCb) { + utils.checkSupport("nodestream"); + if (this._outputType !== "nodebuffer") { + // an object stream containing blob/arraybuffer/uint8array/string + // is strange and I don't know if it would be useful. + // I you find this comment and have a good usecase, please open a + // bug report ! + throw new Error(this._outputType + " is not supported by this method"); + } + + return new NodejsStreamOutputAdapter(this, { + objectMode : this._outputType !== "nodebuffer" + }, updateCb); + } +}; + + +module.exports = StreamHelper; + +},{"../base64":1,"../external":6,"../nodejs/NodejsStreamOutputAdapter":13,"../support":30,"../utils":32,"./ConvertWorker":24,"./GenericWorker":28}],30:[function(require,module,exports){ +'use strict'; + exports.base64 = true; exports.array = true; exports.string = true; exports.arraybuffer = typeof ArrayBuffer !== "undefined" && typeof Uint8Array !== "undefined"; -// contains true if JSZip can read/generate nodejs Buffer, false otherwise. -// Browserify will provide a Buffer implementation for browsers, which is -// an augmented Uint8Array (i.e., can be used as either Buffer or U8). exports.nodebuffer = typeof Buffer !== "undefined"; // contains true if JSZip can read/generate Uint8Array, false otherwise. exports.uint8array = typeof Uint8Array !== "undefined"; @@ -1625,100 +2718,19 @@ else { } } -}).call(this,(typeof Buffer !== "undefined" ? Buffer : undefined)) -},{}],18:[function(_dereq_,module,exports){ -'use strict'; -var DataReader = _dereq_('./dataReader'); - -function Uint8ArrayReader(data) { - if (data) { - this.data = data; - this.length = this.data.length; - this.index = 0; - } +try { + exports.nodestream = !!require('readable-stream').Readable; +} catch(e) { + exports.nodestream = false; } -Uint8ArrayReader.prototype = new DataReader(); -/** - * @see DataReader.byteAt - */ -Uint8ArrayReader.prototype.byteAt = function(i) { - return this.data[i]; -}; -/** - * @see DataReader.lastIndexOfSignature - */ -Uint8ArrayReader.prototype.lastIndexOfSignature = function(sig) { - var sig0 = sig.charCodeAt(0), - sig1 = sig.charCodeAt(1), - sig2 = sig.charCodeAt(2), - sig3 = sig.charCodeAt(3); - for (var i = this.length - 4; i >= 0; --i) { - if (this.data[i] === sig0 && this.data[i + 1] === sig1 && this.data[i + 2] === sig2 && this.data[i + 3] === sig3) { - return i; - } - } - return -1; -}; -/** - * @see DataReader.readData - */ -Uint8ArrayReader.prototype.readData = function(size) { - this.checkOffset(size); - if(size === 0) { - // in IE10, when using subarray(idx, idx), we get the array [0x00] instead of []. - return new Uint8Array(0); - } - var result = this.data.subarray(this.index, this.index + size); - this.index += size; - return result; -}; -module.exports = Uint8ArrayReader; - -},{"./dataReader":5}],19:[function(_dereq_,module,exports){ +},{"readable-stream":16}],31:[function(require,module,exports){ 'use strict'; -var utils = _dereq_('./utils'); - -/** - * An object to write any content to an Uint8Array. - * @constructor - * @param {number} length The length of the array. - */ -var Uint8ArrayWriter = function(length) { - this.data = new Uint8Array(length); - this.index = 0; -}; -Uint8ArrayWriter.prototype = { - /** - * Append any content to the current array. - * @param {Object} input the content to add. - */ - append: function(input) { - if (input.length !== 0) { - // with an empty Uint8Array, Opera fails with a "Offset larger than array size" - input = utils.transformTo("uint8array", input); - this.data.set(input, this.index); - this.index += input.length; - } - }, - /** - * Finalize the construction an return the result. - * @return {Uint8Array} the generated array. - */ - finalize: function() { - return this.data; - } -}; - -module.exports = Uint8ArrayWriter; - -},{"./utils":21}],20:[function(_dereq_,module,exports){ -'use strict'; - -var utils = _dereq_('./utils'); -var support = _dereq_('./support'); -var nodeBuffer = _dereq_('./nodeBuffer'); +var utils = require('./utils'); +var support = require('./support'); +var nodejsUtils = require('./nodejsUtils'); +var GenericWorker = require('./stream/GenericWorker'); /** * The following functions come from pako, from pako/lib/utils/strings @@ -1883,7 +2895,7 @@ var buf2string = function (buf) { */ exports.utf8encode = function utf8encode(str) { if (support.nodebuffer) { - return nodeBuffer(str, "utf-8"); + return nodejsUtils.newBuffer(str, "utf-8"); } return string2buf(str); @@ -1903,50 +2915,134 @@ exports.utf8decode = function utf8decode(buf) { buf = utils.transformTo(support.uint8array ? "uint8array" : "array", buf); - // return buf2string(buf); - // Chrome prefers to work with "small" chunks of data - // for the method buf2string. - // Firefox and Chrome has their own shortcut, IE doesn't seem to really care. - var result = [], k = 0, len = buf.length, chunk = 65536; - while (k < len) { - var nextBoundary = utf8border(buf, Math.min(k + chunk, len)); - if (support.uint8array) { - result.push(buf2string(buf.subarray(k, nextBoundary))); - } else { - result.push(buf2string(buf.slice(k, nextBoundary))); - } - k = nextBoundary; - } - return result.join(""); - + return buf2string(buf); }; -// vim: set shiftwidth=4 softtabstop=4: -},{"./nodeBuffer":11,"./support":17,"./utils":21}],21:[function(_dereq_,module,exports){ -'use strict'; -var support = _dereq_('./support'); -var compressions = _dereq_('./compressions'); -var nodeBuffer = _dereq_('./nodeBuffer'); /** - * Convert a string to a "binary string" : a string containing only char codes between 0 and 255. - * @param {string} str the string to transform. - * @return {String} the binary string. + * A worker to decode utf8 encoded binary chunks into string chunks. + * @constructor */ -exports.string2binary = function(str) { - var result = ""; - for (var i = 0; i < str.length; i++) { - result += String.fromCharCode(str.charCodeAt(i) & 0xff); +function Utf8DecodeWorker() { + GenericWorker.call(this, "utf-8 decode"); + // the last bytes if a chunk didn't end with a complete codepoint. + this.leftOver = null; +} +utils.inherits(Utf8DecodeWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +Utf8DecodeWorker.prototype.processChunk = function (chunk) { + + var data = utils.transformTo(support.uint8array ? "uint8array" : "array", chunk.data); + + // 1st step, re-use what's left of the previous chunk + if (this.leftOver && this.leftOver.length) { + if(support.uint8array) { + var previousData = data; + data = new Uint8Array(previousData.length + this.leftOver.length); + data.set(this.leftOver, 0); + data.set(previousData, this.leftOver.length); + } else { + data = this.leftOver.concat(data); + } + this.leftOver = null; } - return result; + + var nextBoundary = utf8border(data); + var usableData = data; + if (nextBoundary !== data.length) { + if (support.uint8array) { + usableData = data.subarray(0, nextBoundary); + this.leftOver = data.subarray(nextBoundary, data.length); + } else { + usableData = data.slice(0, nextBoundary); + this.leftOver = data.slice(nextBoundary, data.length); + } + } + + this.push({ + data : exports.utf8decode(usableData), + meta : chunk.meta + }); }; -exports.arrayBuffer2Blob = function(buffer, mimeType) { + +/** + * @see GenericWorker.flush + */ +Utf8DecodeWorker.prototype.flush = function () { + if(this.leftOver && this.leftOver.length) { + this.push({ + data : exports.utf8decode(this.leftOver), + meta : {} + }); + this.leftOver = null; + } +}; +exports.Utf8DecodeWorker = Utf8DecodeWorker; + +/** + * A worker to endcode string chunks into utf8 encoded binary chunks. + * @constructor + */ +function Utf8EncodeWorker() { + GenericWorker.call(this, "utf-8 encode"); +} +utils.inherits(Utf8EncodeWorker, GenericWorker); + +/** + * @see GenericWorker.processChunk + */ +Utf8EncodeWorker.prototype.processChunk = function (chunk) { + this.push({ + data : exports.utf8encode(chunk.data), + meta : chunk.meta + }); +}; +exports.Utf8EncodeWorker = Utf8EncodeWorker; + +},{"./nodejsUtils":14,"./stream/GenericWorker":28,"./support":30,"./utils":32}],32:[function(require,module,exports){ +'use strict'; + +var support = require('./support'); +var base64 = require('./base64'); +var nodejsUtils = require('./nodejsUtils'); +var setImmediate = require('core-js/library/fn/set-immediate'); +var external = require("./external"); + + +/** + * Convert a string that pass as a "binary string": it should represent a byte + * array but may have > 255 char codes. Be sure to take only the first byte + * and returns the byte array. + * @param {String} str the string to transform. + * @return {Array|Uint8Array} the string in a binary format. + */ +function string2binary(str) { + var result = null; + if (support.uint8array) { + result = new Uint8Array(str.length); + } else { + result = new Array(str.length); + } + return stringToArrayLike(str, result); +} + +/** + * Create a new blob with the given content and the given type. + * @param {Array[String|ArrayBuffer]} parts the content to put in the blob. DO NOT use + * an Uint8Array because the stock browser of android 4 won't accept it (it + * will be silently converted to a string, "[object Uint8Array]"). + * @param {String} type the mime type of the blob. + * @return {Blob} the created blob. + */ +exports.newBlob = function(parts, type) { exports.checkSupport("blob"); - mimeType = mimeType || 'application/zip'; try { // Blob constructor - return new Blob([buffer], { - type: mimeType + return new Blob(parts, { + type: type }); } catch (e) { @@ -1955,8 +3051,10 @@ exports.arrayBuffer2Blob = function(buffer, mimeType) { // deprecated, browser only, old way var Builder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; var builder = new Builder(); - builder.append(buffer); - return builder.getBlob(mimeType); + for (var i = 0; i < parts.length; i++) { + builder.append(parts[i]); + } + return builder.getBlob(type); } catch (e) { @@ -1989,6 +3087,76 @@ function stringToArrayLike(str, array) { return array; } +/** + * An helper for the function arrayLikeToString. + * This contains static informations and functions that + * can be optimized by the browser JIT compiler. + */ +var arrayToStringHelper = { + /** + * Transform an array of int into a string, chunk by chunk. + * See the performances notes on arrayLikeToString. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. + * @param {String} type the type of the array. + * @param {Integer} chunk the chunk size. + * @return {String} the resulting string. + * @throws Error if the chunk is too big for the stack. + */ + stringifyByChunk: function(array, type, chunk) { + var result = [], k = 0, len = array.length; + // shortcut + if (len <= chunk) { + return String.fromCharCode.apply(null, array); + } + while (k < len) { + if (type === "array" || type === "nodebuffer") { + result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len)))); + } + else { + result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len)))); + } + k += chunk; + } + return result.join(""); + }, + /** + * Call String.fromCharCode on every item in the array. + * This is the naive implementation, which generate A LOT of intermediate string. + * This should be used when everything else fail. + * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. + * @return {String} the result. + */ + stringifyByChar: function(array){ + var resultStr = ""; + for(var i = 0; i < array.length; i++) { + resultStr += String.fromCharCode(array[i]); + } + return resultStr; + }, + applyCanBeUsed : { + /** + * true if the browser accepts to use String.fromCharCode on Uint8Array + */ + uint8array : (function () { + try { + return support.uint8array && String.fromCharCode.apply(null, new Uint8Array(1)).length === 1; + } catch (e) { + return false; + } + })(), + /** + * true if the browser accepts to use String.fromCharCode on nodejs Buffer. + */ + nodebuffer : (function () { + try { + return support.nodebuffer && String.fromCharCode.apply(null, nodejsUtils.newBuffer(1)).length === 1; + } catch (e) { + return false; + } + })() + } +}; + /** * Transform an array-like object to a string. * @param {Array|ArrayBuffer|Uint8Array|Buffer} array the array to transform. @@ -2004,49 +3172,29 @@ function arrayLikeToString(array) { // result += String.fromCharCode(array[i]); generate too many strings ! // // This code is inspired by http://jsperf.com/arraybuffer-to-string-apply-performance/2 - var chunk = 65536; - var result = [], - len = array.length, + // TODO : we now have workers that split the work. Do we still need that ? + var chunk = 65536, type = exports.getTypeOf(array), - k = 0, canUseApply = true; - try { - switch(type) { - case "uint8array": - String.fromCharCode.apply(null, new Uint8Array(0)); - break; - case "nodebuffer": - String.fromCharCode.apply(null, nodeBuffer(0)); - break; - } - } catch(e) { - canUseApply = false; - } + if (type === "uint8array") { + canUseApply = arrayToStringHelper.applyCanBeUsed.uint8array; + } else if (type === "nodebuffer") { + canUseApply = arrayToStringHelper.applyCanBeUsed.nodebuffer; + } - // no apply : slow and painful algorithm - // default browser on android 4.* - if (!canUseApply) { - var resultStr = ""; - for(var i = 0; i < array.length;i++) { - resultStr += String.fromCharCode(array[i]); - } - return resultStr; - } - while (k < len && chunk > 1) { - try { - if (type === "array" || type === "nodebuffer") { - result.push(String.fromCharCode.apply(null, array.slice(k, Math.min(k + chunk, len)))); + if (canUseApply) { + while (chunk > 1) { + try { + return arrayToStringHelper.stringifyByChunk(array, type, chunk); + } catch (e) { + chunk = Math.floor(chunk / 2); } - else { - result.push(String.fromCharCode.apply(null, array.subarray(k, Math.min(k + chunk, len)))); - } - k += chunk; - } - catch (e) { - chunk = Math.floor(chunk / 2); } } - return result.join(""); + + // no apply or chunk error : slow and painful algorithm + // default browser on android 4.* + return arrayToStringHelper.stringifyByChar(array); } exports.applyFromCharCode = arrayLikeToString; @@ -2081,7 +3229,7 @@ transform["string"] = { return stringToArrayLike(input, new Uint8Array(input.length)); }, "nodebuffer": function(input) { - return stringToArrayLike(input, nodeBuffer(input.length)); + return stringToArrayLike(input, nodejsUtils.newBuffer(input.length)); } }; @@ -2096,7 +3244,7 @@ transform["array"] = { return new Uint8Array(input); }, "nodebuffer": function(input) { - return nodeBuffer(input); + return nodejsUtils.newBuffer(input); } }; @@ -2113,7 +3261,7 @@ transform["arraybuffer"] = { return new Uint8Array(input); }, "nodebuffer": function(input) { - return nodeBuffer(new Uint8Array(input)); + return nodejsUtils.newBuffer(new Uint8Array(input)); } }; @@ -2124,11 +3272,17 @@ transform["uint8array"] = { return arrayLikeToArrayLike(input, new Array(input.length)); }, "arraybuffer": function(input) { - return input.buffer; + // copy the uint8array: DO NOT propagate the original ArrayBuffer, it + // can be way larger (the whole zip file for example). + var copy = new Uint8Array(input.length); + if (input.length) { + copy.set(input, 0); + } + return copy.buffer; }, "uint8array": identity, "nodebuffer": function(input) { - return nodeBuffer(input); + return nodejsUtils.newBuffer(input); } }; @@ -2183,7 +3337,7 @@ exports.getTypeOf = function(input) { if (Object.prototype.toString.call(input) === "[object Array]") { return "array"; } - if (support.nodebuffer && nodeBuffer.test(input)) { + if (support.nodebuffer && nodejsUtils.isBuffer(input)) { return "nodebuffer"; } if (support.uint8array && input instanceof Uint8Array) { @@ -2202,9 +3356,10 @@ exports.getTypeOf = function(input) { exports.checkSupport = function(type) { var supported = support[type.toLowerCase()]; if (!supported) { - throw new Error(type + " is not supported by this browser"); + throw new Error(type + " is not supported by this platform"); } }; + exports.MAX_VALUE_16BITS = 65535; exports.MAX_VALUE_32BITS = -1; // well, "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" is parsed as -1 @@ -2224,55 +3379,125 @@ exports.pretty = function(str) { }; /** - * Find a compression registered in JSZip. - * @param {string} compressionMethod the method magic to find. - * @return {Object|null} the JSZip compression object, null if none found. + * Defer the call of a function. + * @param {Function} callback the function to call asynchronously. + * @param {Array} args the arguments to give to the callback. */ -exports.findCompression = function(compressionMethod) { - for (var method in compressions) { - if (!compressions.hasOwnProperty(method)) { - continue; - } - if (compressions[method].magic === compressionMethod) { - return compressions[method]; +exports.delay = function(callback, args, self) { + setImmediate(function () { + callback.apply(self || null, args || []); + }); +}; + +/** + * Extends a prototype with an other, without calling a constructor with + * side effects. Inspired by nodejs' `utils.inherits` + * @param {Function} ctor the constructor to augment + * @param {Function} superCtor the parent constructor to use + */ +exports.inherits = function (ctor, superCtor) { + var Obj = function() {}; + Obj.prototype = superCtor.prototype; + ctor.prototype = new Obj(); +}; + +/** + * Merge the objects passed as parameters into a new one. + * @private + * @param {...Object} var_args All objects to merge. + * @return {Object} a new object with the data of the others. + */ +exports.extend = function() { + var result = {}, i, attr; + for (i = 0; i < arguments.length; i++) { // arguments is not enumerable in some browsers + for (attr in arguments[i]) { + if (arguments[i].hasOwnProperty(attr) && typeof result[attr] === "undefined") { + result[attr] = arguments[i][attr]; + } } } - return null; + return result; }; + /** -* Cross-window, cross-Node-context regular expression detection -* @param {Object} object Anything -* @return {Boolean} true if the object is a regular expression, -* false otherwise -*/ -exports.isRegExp = function (object) { - return Object.prototype.toString.call(object) === "[object RegExp]"; + * Transform arbitrary content into a Promise. + * @param {String} name a name for the content being processed. + * @param {Object} inputData the content to process. + * @param {Boolean} isBinary true if the content is not an unicode string + * @param {Boolean} isOptimizedBinaryString true if the string content only has one byte per character. + * @param {Boolean} isBase64 true if the string content is encoded with base64. + * @return {Promise} a promise in a format usable by JSZip. + */ +exports.prepareContent = function(name, inputData, isBinary, isOptimizedBinaryString, isBase64) { + + // if inputData is already a promise, this flatten it. + var promise = external.Promise.resolve(inputData).then(function(data) { + + + var isBlob = support.blob && (data instanceof Blob || ['[object File]', '[object Blob]'].indexOf(Object.prototype.toString.call(data)) !== -1); + + if (isBlob && typeof FileReader !== "undefined") { + return new external.Promise(function (resolve, reject) { + var reader = new FileReader(); + + reader.onload = function(e) { + resolve(e.target.result); + }; + reader.onerror = function(e) { + reject(e.target.error); + }; + reader.readAsArrayBuffer(data); + }); + } else { + return data; + } + }); + + return promise.then(function(data) { + var dataType = exports.getTypeOf(data); + + if (!dataType) { + return external.Promise.reject( + new Error("The data of '" + name + "' is in an unsupported format !") + ); + } + // special case : it's way easier to work with Uint8Array than with ArrayBuffer + if (dataType === "arraybuffer") { + data = exports.transformTo("uint8array", data); + } else if (dataType === "string") { + if (isBase64) { + data = base64.decode(data); + } + else if (isBinary) { + // optimizedBinaryString === true means that the file has already been filtered with a 0xFF mask + if (isOptimizedBinaryString !== true) { + // this is a string, not in a base64 format. + // Be sure that this is a correct "binary string" + data = string2binary(data); + } + } + } + return data; + }); }; - -},{"./compressions":3,"./nodeBuffer":11,"./support":17}],22:[function(_dereq_,module,exports){ +},{"./base64":1,"./external":6,"./nodejsUtils":14,"./support":30,"core-js/library/fn/set-immediate":36}],33:[function(require,module,exports){ 'use strict'; -var StringReader = _dereq_('./stringReader'); -var NodeBufferReader = _dereq_('./nodeBufferReader'); -var Uint8ArrayReader = _dereq_('./uint8ArrayReader'); -var utils = _dereq_('./utils'); -var sig = _dereq_('./signature'); -var ZipEntry = _dereq_('./zipEntry'); -var support = _dereq_('./support'); -var jszipProto = _dereq_('./object'); +var readerFor = require('./reader/readerFor'); +var utils = require('./utils'); +var sig = require('./signature'); +var ZipEntry = require('./zipEntry'); +var utf8 = require('./utf8'); +var support = require('./support'); // class ZipEntries {{{ /** * All the entries in the zip file. * @constructor - * @param {String|ArrayBuffer|Uint8Array} data the binary stream to load. * @param {Object} loadOptions Options for loading the stream. */ -function ZipEntries(data, loadOptions) { +function ZipEntries(loadOptions) { this.files = []; this.loadOptions = loadOptions; - if (data) { - this.load(data); - } } ZipEntries.prototype = { /** @@ -2281,11 +3506,26 @@ ZipEntries.prototype = { * @throws {Error} if it is an other signature. */ checkSignature: function(expectedSignature) { - var signature = this.reader.readString(4); - if (signature !== expectedSignature) { + if (!this.reader.readAndCheckSignature(expectedSignature)) { + this.reader.index -= 4; + var signature = this.reader.readString(4); throw new Error("Corrupted zip or bug : unexpected signature " + "(" + utils.pretty(signature) + ", expected " + utils.pretty(expectedSignature) + ")"); } }, + /** + * Check if the given signature is at the given index. + * @param {number} askedIndex the index to check. + * @param {string} expectedSignature the signature to expect. + * @return {boolean} true if the signature is here, false otherwise. + */ + isSignature: function(askedIndex, expectedSignature) { + var currentIndex = this.reader.index; + this.reader.setIndex(askedIndex); + var signature = this.reader.readString(4); + var result = signature === expectedSignature; + this.reader.setIndex(currentIndex); + return result; + }, /** * Read the end of the central directory. */ @@ -2301,10 +3541,12 @@ ZipEntries.prototype = { // warning : the encoding depends of the system locale // On a linux machine with LANG=en_US.utf8, this field is utf8 encoded. // On a windows machine, this field is encoded with the localized windows code page. - this.zipComment = this.reader.readString(this.zipCommentLength); + var zipComment = this.reader.readData(this.zipCommentLength); + var decodeParamType = support.uint8array ? "uint8array" : "array"; // To get consistent behavior with the generation part, we will assume that - // this is utf8 encoded. - this.zipComment = jszipProto.utf8decode(this.zipComment); + // this is utf8 encoded unless specified otherwise. + var decodeContent = utils.transformTo(decodeParamType, zipComment); + this.zipComment = this.loadOptions.decodeFileName(decodeContent); }, /** * Read the end of the Zip 64 central directory. @@ -2314,8 +3556,9 @@ ZipEntries.prototype = { */ readBlockZip64EndOfCentral: function() { this.zip64EndOfCentralSize = this.reader.readInt(8); - this.versionMadeBy = this.reader.readString(2); - this.versionNeeded = this.reader.readInt(2); + this.reader.skip(4); + // this.versionMadeBy = this.reader.readString(2); + // this.versionNeeded = this.reader.readInt(2); this.diskNumber = this.reader.readInt(4); this.diskWithCentralDirStart = this.reader.readInt(4); this.centralDirRecordsOnThisDisk = this.reader.readInt(8); @@ -2332,7 +3575,7 @@ ZipEntries.prototype = { while (index < extraDataSize) { extraFieldId = this.reader.readInt(2); extraFieldLength = this.reader.readInt(4); - extraFieldValue = this.reader.readString(extraFieldLength); + extraFieldValue = this.reader.readData(extraFieldLength); this.zip64ExtensibleData[extraFieldId] = { id: extraFieldId, length: extraFieldLength, @@ -2372,31 +3615,38 @@ ZipEntries.prototype = { var file; this.reader.setIndex(this.centralDirOffset); - while (this.reader.readString(4) === sig.CENTRAL_FILE_HEADER) { + while (this.reader.readAndCheckSignature(sig.CENTRAL_FILE_HEADER)) { file = new ZipEntry({ zip64: this.zip64 }, this.loadOptions); file.readCentralPart(this.reader); this.files.push(file); } + + if (this.centralDirRecords !== this.files.length) { + if (this.centralDirRecords !== 0 && this.files.length === 0) { + // We expected some records but couldn't find ANY. + // This is really suspicious, as if something went wrong. + throw new Error("Corrupted zip or bug: expected " + this.centralDirRecords + " records in central dir, got " + this.files.length); + } else { + // We found some records but not all. + // Something is wrong but we got something for the user: no error here. + // console.warn("expected", this.centralDirRecords, "records in central dir, got", this.files.length); + } + } }, /** * Read the end of central directory. */ readEndOfCentral: function() { var offset = this.reader.lastIndexOfSignature(sig.CENTRAL_DIRECTORY_END); - if (offset === -1) { + if (offset < 0) { // Check if the content is a truncated zip or complete garbage. // A "LOCAL_FILE_HEADER" is not required at the beginning (auto // extractible zip for example) but it can give a good hint. // If an ajax request was used without responseType, we will also // get unreadable data. - var isGarbage = true; - try { - this.reader.setIndex(0); - this.checkSignature(sig.LOCAL_FILE_HEADER); - isGarbage = false; - } catch (e) {} + var isGarbage = !this.isSignature(0, sig.LOCAL_FILE_HEADER); if (isGarbage) { throw new Error("Can't find end of central directory : is this a zip file ? " + @@ -2404,8 +3654,10 @@ ZipEntries.prototype = { } else { throw new Error("Corrupted zip : can't find end of central directory"); } + } this.reader.setIndex(offset); + var endOfCentralDirOffset = offset; this.checkSignature(sig.CENTRAL_DIRECTORY_END); this.readBlockEndOfCentral(); @@ -2434,7 +3686,7 @@ ZipEntries.prototype = { // should look for a zip64 EOCD locator offset = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_LOCATOR); - if (offset === -1) { + if (offset < 0) { throw new Error("Corrupted zip : can't find the ZIP64 end of central directory locator"); } this.reader.setIndex(offset); @@ -2442,22 +3694,42 @@ ZipEntries.prototype = { this.readBlockZip64EndOfCentralLocator(); // now the zip64 EOCD record + if (!this.isSignature(this.relativeOffsetEndOfZip64CentralDir, sig.ZIP64_CENTRAL_DIRECTORY_END)) { + // console.warn("ZIP64 end of central directory not where expected."); + this.relativeOffsetEndOfZip64CentralDir = this.reader.lastIndexOfSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); + if (this.relativeOffsetEndOfZip64CentralDir < 0) { + throw new Error("Corrupted zip : can't find the ZIP64 end of central directory"); + } + } this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir); this.checkSignature(sig.ZIP64_CENTRAL_DIRECTORY_END); this.readBlockZip64EndOfCentral(); } + + var expectedEndOfCentralDirOffset = this.centralDirOffset + this.centralDirSize; + if (this.zip64) { + expectedEndOfCentralDirOffset += 20; // end of central dir 64 locator + expectedEndOfCentralDirOffset += 12 /* should not include the leading 12 bytes */ + this.zip64EndOfCentralSize; + } + + var extraBytes = endOfCentralDirOffset - expectedEndOfCentralDirOffset; + + if (extraBytes > 0) { + // console.warn(extraBytes, "extra bytes at beginning or within zipfile"); + if (this.isSignature(endOfCentralDirOffset, sig.CENTRAL_FILE_HEADER)) { + // The offsets seem wrong, but we have something at the specified offset. + // So… we keep it. + } else { + // the offset is wrong, update the "zero" of the reader + // this happens if data has been prepended (crx files for example) + this.reader.zero = extraBytes; + } + } else if (extraBytes < 0) { + throw new Error("Corrupted zip: missing " + Math.abs(extraBytes) + " bytes."); + } }, prepareReader: function(data) { - var type = utils.getTypeOf(data); - if (type === "string" && !support.uint8array) { - this.reader = new StringReader(data, this.loadOptions.optimizedBinaryString); - } - else if (type === "nodebuffer") { - this.reader = new NodeBufferReader(data); - } - else { - this.reader = new Uint8ArrayReader(utils.transformTo("uint8array", data)); - } + this.reader = readerFor(data); }, /** * Read a zip file and create ZipEntries. @@ -2473,16 +3745,36 @@ ZipEntries.prototype = { // }}} end of ZipEntries module.exports = ZipEntries; -},{"./nodeBufferReader":12,"./object":13,"./signature":14,"./stringReader":15,"./support":17,"./uint8ArrayReader":18,"./utils":21,"./zipEntry":23}],23:[function(_dereq_,module,exports){ +},{"./reader/readerFor":22,"./signature":23,"./support":30,"./utf8":31,"./utils":32,"./zipEntry":34}],34:[function(require,module,exports){ 'use strict'; -var StringReader = _dereq_('./stringReader'); -var utils = _dereq_('./utils'); -var CompressedObject = _dereq_('./compressedObject'); -var jszipProto = _dereq_('./object'); +var readerFor = require('./reader/readerFor'); +var utils = require('./utils'); +var CompressedObject = require('./compressedObject'); +var crc32fn = require('./crc32'); +var utf8 = require('./utf8'); +var compressions = require('./compressions'); +var support = require('./support'); var MADE_BY_DOS = 0x00; var MADE_BY_UNIX = 0x03; +/** + * Find a compression registered in JSZip. + * @param {string} compressionMethod the method magic to find. + * @return {Object|null} the JSZip compression object, null if none found. + */ +var findCompression = function(compressionMethod) { + for (var method in compressions) { + if (!compressions.hasOwnProperty(method)) { + continue; + } + if (compressions[method].magic === compressionMethod) { + return compressions[method]; + } + } + return null; +}; + // class ZipEntry {{{ /** * An entry in the zip file. @@ -2511,45 +3803,6 @@ ZipEntry.prototype = { // bit 11 is set return (this.bitFlag & 0x0800) === 0x0800; }, - /** - * Prepare the function used to generate the compressed content from this ZipFile. - * @param {DataReader} reader the reader to use. - * @param {number} from the offset from where we should read the data. - * @param {number} length the length of the data to read. - * @return {Function} the callback to get the compressed content (the type depends of the DataReader class). - */ - prepareCompressedContent: function(reader, from, length) { - return function() { - var previousIndex = reader.index; - reader.setIndex(from); - var compressedFileData = reader.readData(length); - reader.setIndex(previousIndex); - - return compressedFileData; - }; - }, - /** - * Prepare the function used to generate the uncompressed content from this ZipFile. - * @param {DataReader} reader the reader to use. - * @param {number} from the offset from where we should read the data. - * @param {number} length the length of the data to read. - * @param {JSZip.compression} compression the compression used on this file. - * @param {number} uncompressedSize the uncompressed size to expect. - * @return {Function} the callback to get the uncompressed content (the type depends of the DataReader class). - */ - prepareContent: function(reader, from, length, compression, uncompressedSize) { - return function() { - - var compressedFileData = utils.transformTo(compression.uncompressInputType, this.getCompressedContent()); - var uncompressedFileData = compression.uncompress(compressedFileData); - - if (uncompressedFileData.length !== uncompressedSize) { - throw new Error("Bug : uncompressed data size mismatch"); - } - - return uncompressedFileData; - }; - }, /** * Read the local part of a zip file and add the info in this object. * @param {DataReader} reader the reader to use. @@ -2576,32 +3829,19 @@ ZipEntry.prototype = { // Unfortunately, this lead also to some issues : http://seclists.org/fulldisclosure/2009/Sep/394 this.fileNameLength = reader.readInt(2); localExtraFieldsLength = reader.readInt(2); // can't be sure this will be the same as the central dir - this.fileName = reader.readString(this.fileNameLength); + // the fileName is stored as binary data, the handleUTF8 method will take care of the encoding. + this.fileName = reader.readData(this.fileNameLength); reader.skip(localExtraFieldsLength); - if (this.compressedSize == -1 || this.uncompressedSize == -1) { - throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory " + "(compressedSize == -1 || uncompressedSize == -1)"); + if (this.compressedSize === -1 || this.uncompressedSize === -1) { + throw new Error("Bug or corrupted zip : didn't get enough informations from the central directory " + "(compressedSize === -1 || uncompressedSize === -1)"); } - compression = utils.findCompression(this.compressionMethod); + compression = findCompression(this.compressionMethod); if (compression === null) { // no compression found - throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + this.fileName + ")"); - } - this.decompressed = new CompressedObject(); - this.decompressed.compressedSize = this.compressedSize; - this.decompressed.uncompressedSize = this.uncompressedSize; - this.decompressed.crc32 = this.crc32; - this.decompressed.compressionMethod = this.compressionMethod; - this.decompressed.getCompressedContent = this.prepareCompressedContent(reader, reader.index, this.compressedSize, compression); - this.decompressed.getContent = this.prepareContent(reader, reader.index, this.compressedSize, compression, this.uncompressedSize); - - // we need to compute the crc32... - if (this.loadOptions.checkCRC32) { - this.decompressed = utils.transformTo("string", this.decompressed.getContent()); - if (jszipProto.crc32(this.decompressed) !== this.crc32) { - throw new Error("Corrupted zip : CRC32 mismatch"); - } + throw new Error("Corrupted zip : compression " + utils.pretty(this.compressionMethod) + " unknown (inner file : " + utils.transformTo("string", this.fileName) + ")"); } + this.decompressed = new CompressedObject(this.compressedSize, this.uncompressedSize, this.crc32, compression, reader.readData(this.compressedSize)); }, /** @@ -2610,14 +3850,15 @@ ZipEntry.prototype = { */ readCentralPart: function(reader) { this.versionMadeBy = reader.readInt(2); - this.versionNeeded = reader.readInt(2); + reader.skip(2); + // this.versionNeeded = reader.readInt(2); this.bitFlag = reader.readInt(2); this.compressionMethod = reader.readString(2); this.date = reader.readDate(); this.crc32 = reader.readInt(4); this.compressedSize = reader.readInt(4); this.uncompressedSize = reader.readInt(4); - this.fileNameLength = reader.readInt(2); + var fileNameLength = reader.readInt(2); this.extraFieldsLength = reader.readInt(2); this.fileCommentLength = reader.readInt(2); this.diskNumberStart = reader.readInt(2); @@ -2629,10 +3870,11 @@ ZipEntry.prototype = { throw new Error("Encrypted zip are not supported"); } - this.fileName = reader.readString(this.fileNameLength); + // will be read in the local part, see the comments there + reader.skip(fileNameLength); this.readExtraFields(reader); this.parseZIP64ExtraField(reader); - this.fileComment = reader.readString(this.fileCommentLength); + this.fileComment = reader.readData(this.fileCommentLength); }, /** @@ -2659,7 +3901,7 @@ ZipEntry.prototype = { } // fail safe : if the name ends with a / it probably means a folder - if (!this.dir && this.fileName.slice(-1) === '/') { + if (!this.dir && this.fileNameStr.slice(-1) === '/') { this.dir = true; } }, @@ -2675,7 +3917,7 @@ ZipEntry.prototype = { } // should be something, preparing the extra reader - var extraReader = new StringReader(this.extraFields[0x0001].value); + var extraReader = readerFor(this.extraFields[0x0001].value); // I really hope that these 64bits integer can fit in 32 bits integer, because js // won't let us have more. @@ -2697,17 +3939,19 @@ ZipEntry.prototype = { * @param {DataReader} reader the reader to use. */ readExtraFields: function(reader) { - var start = reader.index, + var end = reader.index + this.extraFieldsLength, extraFieldId, extraFieldLength, extraFieldValue; - this.extraFields = this.extraFields || {}; + if (!this.extraFields) { + this.extraFields = {}; + } - while (reader.index < start + this.extraFieldsLength) { + while (reader.index < end) { extraFieldId = reader.readInt(2); extraFieldLength = reader.readInt(2); - extraFieldValue = reader.readString(extraFieldLength); + extraFieldValue = reader.readData(extraFieldLength); this.extraFields[extraFieldId] = { id: extraFieldId, @@ -2720,17 +3964,27 @@ ZipEntry.prototype = { * Apply an UTF8 transformation if needed. */ handleUTF8: function() { + var decodeParamType = support.uint8array ? "uint8array" : "array"; if (this.useUTF8()) { - this.fileName = jszipProto.utf8decode(this.fileName); - this.fileComment = jszipProto.utf8decode(this.fileComment); + this.fileNameStr = utf8.utf8decode(this.fileName); + this.fileCommentStr = utf8.utf8decode(this.fileComment); } else { var upath = this.findExtraFieldUnicodePath(); if (upath !== null) { - this.fileName = upath; + this.fileNameStr = upath; + } else { + // ASCII text or unsupported code page + var fileNameByteArray = utils.transformTo(decodeParamType, this.fileName); + this.fileNameStr = this.loadOptions.decodeFileName(fileNameByteArray); } + var ucomment = this.findExtraFieldUnicodeComment(); if (ucomment !== null) { - this.fileComment = ucomment; + this.fileCommentStr = ucomment; + } else { + // ASCII text or unsupported code page + var commentByteArray = utils.transformTo(decodeParamType, this.fileComment); + this.fileCommentStr = this.loadOptions.decodeFileName(commentByteArray); } } }, @@ -2742,7 +3996,7 @@ ZipEntry.prototype = { findExtraFieldUnicodePath: function() { var upathField = this.extraFields[0x7075]; if (upathField) { - var extraReader = new StringReader(upathField.value); + var extraReader = readerFor(upathField.value); // wrong version if (extraReader.readInt(1) !== 1) { @@ -2750,11 +4004,11 @@ ZipEntry.prototype = { } // the crc of the filename changed, this field is out of date. - if (jszipProto.crc32(this.fileName) !== extraReader.readInt(4)) { + if (crc32fn(this.fileName) !== extraReader.readInt(4)) { return null; } - return jszipProto.utf8decode(extraReader.readString(upathField.length - 5)); + return utf8.utf8decode(extraReader.readData(upathField.length - 5)); } return null; }, @@ -2766,7 +4020,7 @@ ZipEntry.prototype = { findExtraFieldUnicodeComment: function() { var ucommentField = this.extraFields[0x6375]; if (ucommentField) { - var extraReader = new StringReader(ucommentField.value); + var extraReader = readerFor(ucommentField.value); // wrong version if (extraReader.readInt(1) !== 1) { @@ -2774,42 +4028,788 @@ ZipEntry.prototype = { } // the crc of the comment changed, this field is out of date. - if (jszipProto.crc32(this.fileComment) !== extraReader.readInt(4)) { + if (crc32fn(this.fileComment) !== extraReader.readInt(4)) { return null; } - return jszipProto.utf8decode(extraReader.readString(ucommentField.length - 5)); + return utf8.utf8decode(extraReader.readData(ucommentField.length - 5)); } return null; } }; module.exports = ZipEntry; -},{"./compressedObject":2,"./object":13,"./stringReader":15,"./utils":21}],24:[function(_dereq_,module,exports){ +},{"./compressedObject":2,"./compressions":3,"./crc32":4,"./reader/readerFor":22,"./support":30,"./utf8":31,"./utils":32}],35:[function(require,module,exports){ +'use strict'; + +var StreamHelper = require('./stream/StreamHelper'); +var DataWorker = require('./stream/DataWorker'); +var utf8 = require('./utf8'); +var CompressedObject = require('./compressedObject'); +var GenericWorker = require('./stream/GenericWorker'); + +/** + * A simple object representing a file in the zip file. + * @constructor + * @param {string} name the name of the file + * @param {String|ArrayBuffer|Uint8Array|Buffer} data the data + * @param {Object} options the options of the file + */ +var ZipObject = function(name, data, options) { + this.name = name; + this.dir = options.dir; + this.date = options.date; + this.comment = options.comment; + this.unixPermissions = options.unixPermissions; + this.dosPermissions = options.dosPermissions; + + this._data = data; + this._dataBinary = options.binary; + // keep only the compression + this.options = { + compression : options.compression, + compressionOptions : options.compressionOptions + }; +}; + +ZipObject.prototype = { + /** + * Create an internal stream for the content of this object. + * @param {String} type the type of each chunk. + * @return StreamHelper the stream. + */ + internalStream: function (type) { + var outputType = type.toLowerCase(); + var askUnicodeString = outputType === "string" || outputType === "text"; + if (outputType === "binarystring" || outputType === "text") { + outputType = "string"; + } + var result = this._decompressWorker(); + + var isUnicodeString = !this._dataBinary; + + if (isUnicodeString && !askUnicodeString) { + result = result.pipe(new utf8.Utf8EncodeWorker()); + } + if (!isUnicodeString && askUnicodeString) { + result = result.pipe(new utf8.Utf8DecodeWorker()); + } + + return new StreamHelper(result, outputType, ""); + }, + + /** + * Prepare the content in the asked type. + * @param {String} type the type of the result. + * @param {Function} onUpdate a function to call on each internal update. + * @return Promise the promise of the result. + */ + async: function (type, onUpdate) { + return this.internalStream(type).accumulate(onUpdate); + }, + + /** + * Prepare the content as a nodejs stream. + * @param {String} type the type of each chunk. + * @param {Function} onUpdate a function to call on each internal update. + * @return Stream the stream. + */ + nodeStream: function (type, onUpdate) { + return this.internalStream(type || "nodebuffer").toNodejsStream(onUpdate); + }, + + /** + * Return a worker for the compressed content. + * @private + * @param {Object} compression the compression object to use. + * @param {Object} compressionOptions the options to use when compressing. + * @return Worker the worker. + */ + _compressWorker: function (compression, compressionOptions) { + if ( + this._data instanceof CompressedObject && + this._data.compression.magic === compression.magic + ) { + return this._data.getCompressedWorker(); + } else { + var result = this._decompressWorker(); + if(!this._dataBinary) { + result = result.pipe(new utf8.Utf8EncodeWorker()); + } + return CompressedObject.createWorkerFrom(result, compression, compressionOptions); + } + }, + /** + * Return a worker for the decompressed content. + * @private + * @return Worker the worker. + */ + _decompressWorker : function () { + if (this._data instanceof CompressedObject) { + return this._data.getContentWorker(); + } else if (this._data instanceof GenericWorker) { + return this._data; + } else { + return new DataWorker(this._data); + } + } +}; + +var removedMethods = ["asText", "asBinary", "asNodeBuffer", "asUint8Array", "asArrayBuffer"]; +var removedFn = function () { + throw new Error("This method has been removed in JSZip 3.0, please check the upgrade guide."); +}; + +for(var i = 0; i < removedMethods.length; i++) { + ZipObject.prototype[removedMethods[i]] = removedFn; +} +module.exports = ZipObject; + +},{"./compressedObject":2,"./stream/DataWorker":27,"./stream/GenericWorker":28,"./stream/StreamHelper":29,"./utf8":31}],36:[function(require,module,exports){ +require('../modules/web.immediate'); +module.exports = require('../modules/_core').setImmediate; +},{"../modules/_core":40,"../modules/web.immediate":56}],37:[function(require,module,exports){ +module.exports = function(it){ + if(typeof it != 'function')throw TypeError(it + ' is not a function!'); + return it; +}; +},{}],38:[function(require,module,exports){ +var isObject = require('./_is-object'); +module.exports = function(it){ + if(!isObject(it))throw TypeError(it + ' is not an object!'); + return it; +}; +},{"./_is-object":51}],39:[function(require,module,exports){ +var toString = {}.toString; + +module.exports = function(it){ + return toString.call(it).slice(8, -1); +}; +},{}],40:[function(require,module,exports){ +var core = module.exports = {version: '2.3.0'}; +if(typeof __e == 'number')__e = core; // eslint-disable-line no-undef +},{}],41:[function(require,module,exports){ +// optional / simple context binding +var aFunction = require('./_a-function'); +module.exports = function(fn, that, length){ + aFunction(fn); + if(that === undefined)return fn; + switch(length){ + case 1: return function(a){ + return fn.call(that, a); + }; + case 2: return function(a, b){ + return fn.call(that, a, b); + }; + case 3: return function(a, b, c){ + return fn.call(that, a, b, c); + }; + } + return function(/* ...args */){ + return fn.apply(that, arguments); + }; +}; +},{"./_a-function":37}],42:[function(require,module,exports){ +// Thank's IE8 for his funny defineProperty +module.exports = !require('./_fails')(function(){ + return Object.defineProperty({}, 'a', {get: function(){ return 7; }}).a != 7; +}); +},{"./_fails":45}],43:[function(require,module,exports){ +var isObject = require('./_is-object') + , document = require('./_global').document + // in old IE typeof document.createElement is 'object' + , is = isObject(document) && isObject(document.createElement); +module.exports = function(it){ + return is ? document.createElement(it) : {}; +}; +},{"./_global":46,"./_is-object":51}],44:[function(require,module,exports){ +var global = require('./_global') + , core = require('./_core') + , ctx = require('./_ctx') + , hide = require('./_hide') + , PROTOTYPE = 'prototype'; + +var $export = function(type, name, source){ + var IS_FORCED = type & $export.F + , IS_GLOBAL = type & $export.G + , IS_STATIC = type & $export.S + , IS_PROTO = type & $export.P + , IS_BIND = type & $export.B + , IS_WRAP = type & $export.W + , exports = IS_GLOBAL ? core : core[name] || (core[name] = {}) + , expProto = exports[PROTOTYPE] + , target = IS_GLOBAL ? global : IS_STATIC ? global[name] : (global[name] || {})[PROTOTYPE] + , key, own, out; + if(IS_GLOBAL)source = name; + for(key in source){ + // contains in native + own = !IS_FORCED && target && target[key] !== undefined; + if(own && key in exports)continue; + // export native or passed + out = own ? target[key] : source[key]; + // prevent global pollution for namespaces + exports[key] = IS_GLOBAL && typeof target[key] != 'function' ? source[key] + // bind timers to global for call from export context + : IS_BIND && own ? ctx(out, global) + // wrap global constructors for prevent change them in library + : IS_WRAP && target[key] == out ? (function(C){ + var F = function(a, b, c){ + if(this instanceof C){ + switch(arguments.length){ + case 0: return new C; + case 1: return new C(a); + case 2: return new C(a, b); + } return new C(a, b, c); + } return C.apply(this, arguments); + }; + F[PROTOTYPE] = C[PROTOTYPE]; + return F; + // make static versions for prototype methods + })(out) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out; + // export proto methods to core.%CONSTRUCTOR%.methods.%NAME% + if(IS_PROTO){ + (exports.virtual || (exports.virtual = {}))[key] = out; + // export proto methods to core.%CONSTRUCTOR%.prototype.%NAME% + if(type & $export.R && expProto && !expProto[key])hide(expProto, key, out); + } + } +}; +// type bitmap +$export.F = 1; // forced +$export.G = 2; // global +$export.S = 4; // static +$export.P = 8; // proto +$export.B = 16; // bind +$export.W = 32; // wrap +$export.U = 64; // safe +$export.R = 128; // real proto method for `library` +module.exports = $export; +},{"./_core":40,"./_ctx":41,"./_global":46,"./_hide":47}],45:[function(require,module,exports){ +module.exports = function(exec){ + try { + return !!exec(); + } catch(e){ + return true; + } +}; +},{}],46:[function(require,module,exports){ +// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 +var global = module.exports = typeof window != 'undefined' && window.Math == Math + ? window : typeof self != 'undefined' && self.Math == Math ? self : Function('return this')(); +if(typeof __g == 'number')__g = global; // eslint-disable-line no-undef +},{}],47:[function(require,module,exports){ +var dP = require('./_object-dp') + , createDesc = require('./_property-desc'); +module.exports = require('./_descriptors') ? function(object, key, value){ + return dP.f(object, key, createDesc(1, value)); +} : function(object, key, value){ + object[key] = value; + return object; +}; +},{"./_descriptors":42,"./_object-dp":52,"./_property-desc":53}],48:[function(require,module,exports){ +module.exports = require('./_global').document && document.documentElement; +},{"./_global":46}],49:[function(require,module,exports){ +module.exports = !require('./_descriptors') && !require('./_fails')(function(){ + return Object.defineProperty(require('./_dom-create')('div'), 'a', {get: function(){ return 7; }}).a != 7; +}); +},{"./_descriptors":42,"./_dom-create":43,"./_fails":45}],50:[function(require,module,exports){ +// fast apply, http://jsperf.lnkit.com/fast-apply/5 +module.exports = function(fn, args, that){ + var un = that === undefined; + switch(args.length){ + case 0: return un ? fn() + : fn.call(that); + case 1: return un ? fn(args[0]) + : fn.call(that, args[0]); + case 2: return un ? fn(args[0], args[1]) + : fn.call(that, args[0], args[1]); + case 3: return un ? fn(args[0], args[1], args[2]) + : fn.call(that, args[0], args[1], args[2]); + case 4: return un ? fn(args[0], args[1], args[2], args[3]) + : fn.call(that, args[0], args[1], args[2], args[3]); + } return fn.apply(that, args); +}; +},{}],51:[function(require,module,exports){ +module.exports = function(it){ + return typeof it === 'object' ? it !== null : typeof it === 'function'; +}; +},{}],52:[function(require,module,exports){ +var anObject = require('./_an-object') + , IE8_DOM_DEFINE = require('./_ie8-dom-define') + , toPrimitive = require('./_to-primitive') + , dP = Object.defineProperty; + +exports.f = require('./_descriptors') ? Object.defineProperty : function defineProperty(O, P, Attributes){ + anObject(O); + P = toPrimitive(P, true); + anObject(Attributes); + if(IE8_DOM_DEFINE)try { + return dP(O, P, Attributes); + } catch(e){ /* empty */ } + if('get' in Attributes || 'set' in Attributes)throw TypeError('Accessors not supported!'); + if('value' in Attributes)O[P] = Attributes.value; + return O; +}; +},{"./_an-object":38,"./_descriptors":42,"./_ie8-dom-define":49,"./_to-primitive":55}],53:[function(require,module,exports){ +module.exports = function(bitmap, value){ + return { + enumerable : !(bitmap & 1), + configurable: !(bitmap & 2), + writable : !(bitmap & 4), + value : value + }; +}; +},{}],54:[function(require,module,exports){ +var ctx = require('./_ctx') + , invoke = require('./_invoke') + , html = require('./_html') + , cel = require('./_dom-create') + , global = require('./_global') + , process = global.process + , setTask = global.setImmediate + , clearTask = global.clearImmediate + , MessageChannel = global.MessageChannel + , counter = 0 + , queue = {} + , ONREADYSTATECHANGE = 'onreadystatechange' + , defer, channel, port; +var run = function(){ + var id = +this; + if(queue.hasOwnProperty(id)){ + var fn = queue[id]; + delete queue[id]; + fn(); + } +}; +var listener = function(event){ + run.call(event.data); +}; +// Node.js 0.9+ & IE10+ has setImmediate, otherwise: +if(!setTask || !clearTask){ + setTask = function setImmediate(fn){ + var args = [], i = 1; + while(arguments.length > i)args.push(arguments[i++]); + queue[++counter] = function(){ + invoke(typeof fn == 'function' ? fn : Function(fn), args); + }; + defer(counter); + return counter; + }; + clearTask = function clearImmediate(id){ + delete queue[id]; + }; + // Node.js 0.8- + if(require('./_cof')(process) == 'process'){ + defer = function(id){ + process.nextTick(ctx(run, id, 1)); + }; + // Browsers with MessageChannel, includes WebWorkers + } else if(MessageChannel){ + channel = new MessageChannel; + port = channel.port2; + channel.port1.onmessage = listener; + defer = ctx(port.postMessage, port, 1); + // Browsers with postMessage, skip WebWorkers + // IE8 has postMessage, but it's sync & typeof its postMessage is 'object' + } else if(global.addEventListener && typeof postMessage == 'function' && !global.importScripts){ + defer = function(id){ + global.postMessage(id + '', '*'); + }; + global.addEventListener('message', listener, false); + // IE8- + } else if(ONREADYSTATECHANGE in cel('script')){ + defer = function(id){ + html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function(){ + html.removeChild(this); + run.call(id); + }; + }; + // Rest old browsers + } else { + defer = function(id){ + setTimeout(ctx(run, id, 1), 0); + }; + } +} +module.exports = { + set: setTask, + clear: clearTask +}; +},{"./_cof":39,"./_ctx":41,"./_dom-create":43,"./_global":46,"./_html":48,"./_invoke":50}],55:[function(require,module,exports){ +// 7.1.1 ToPrimitive(input [, PreferredType]) +var isObject = require('./_is-object'); +// instead of the ES6 spec version, we didn't implement @@toPrimitive case +// and the second argument - flag - preferred type is a string +module.exports = function(it, S){ + if(!isObject(it))return it; + var fn, val; + if(S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val; + if(typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it)))return val; + if(!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it)))return val; + throw TypeError("Can't convert object to primitive value"); +}; +},{"./_is-object":51}],56:[function(require,module,exports){ +var $export = require('./_export') + , $task = require('./_task'); +$export($export.G + $export.B, { + setImmediate: $task.set, + clearImmediate: $task.clear +}); +},{"./_export":44,"./_task":54}],57:[function(require,module,exports){ +(function (global){ +'use strict'; +var Mutation = global.MutationObserver || global.WebKitMutationObserver; + +var scheduleDrain; + +{ + if (Mutation) { + var called = 0; + var observer = new Mutation(nextTick); + var element = global.document.createTextNode(''); + observer.observe(element, { + characterData: true + }); + scheduleDrain = function () { + element.data = (called = ++called % 2); + }; + } else if (!global.setImmediate && typeof global.MessageChannel !== 'undefined') { + var channel = new global.MessageChannel(); + channel.port1.onmessage = nextTick; + scheduleDrain = function () { + channel.port2.postMessage(0); + }; + } else if ('document' in global && 'onreadystatechange' in global.document.createElement('script')) { + scheduleDrain = function () { + + // Create a + diff --git a/vendor/underscore/karma.conf-sauce.js b/vendor/underscore/karma.conf-sauce.js new file mode 100644 index 000000000..7cf946ecd --- /dev/null +++ b/vendor/underscore/karma.conf-sauce.js @@ -0,0 +1,93 @@ +var _ = require('./'); + +// Browsers to run on Sauce Labs platforms +var sauceBrowsers = _.reduce([ + ['firefox', '35'], + ['firefox', '30'], + ['firefox', '21'], + ['firefox', '11'], + ['firefox', '4'], + + ['chrome', '40'], + ['chrome', '39'], + ['chrome', '31'], + ['chrome', '26'], + + ['microsoftedge', '20.10240', 'Windows 10'], + ['internet explorer', '11', 'Windows 10'], + ['internet explorer', '10', 'Windows 8'], + ['internet explorer', '9', 'Windows 7'], + // Currently disabled due to karma-sauce issues + // ['internet explorer', '8'], + // ['internet explorer', '7'], + // ['internet explorer', '6'], + + ['opera', '12'], + ['opera', '11'], + + ['android', '5'], + ['android', '4.4'], + ['android', '4.3'], + ['android', '4.0'], + + ['safari', '8.0', 'OS X 10.10'], + ['safari', '7'], + ['safari', '6'], + ['safari', '5'] +], function(memo, platform) { + // internet explorer -> ie + var label = platform[0].split(' '); + if (label.length > 1) { + label = _.invoke(label, 'charAt', 0) + } + label = (label.join("") + '_v' + platform[1]).replace(' ', '_').toUpperCase(); + memo[label] = _.pick({ + 'base': 'SauceLabs', + 'browserName': platform[0], + 'version': platform[1], + 'platform': platform[2] + }, Boolean); + return memo; +}, {}); + +module.exports = function(config) { + if ( !process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY ) { + console.log('Sauce environments not set --- Skipping'); + return process.exit(0); + } + + config.set({ + basePath: '', + frameworks: ['qunit'], + singleRun: true, + + // list of files / patterns to load in the browser + files: [ + 'test/vendor/qunit-extras.js', + 'test/qunit-setup.js', + 'underscore.js', + 'test/*.js' + ], + + // Number of sauce tests to start in parallel + concurrency: 9, + + // test results reporter to use + reporters: ['dots', 'saucelabs'], + port: 9876, + colors: true, + logLevel: config.LOG_INFO, + sauceLabs: { + build: 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')', + startConnect: true, + tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER + }, + + captureTimeout: 120000, + customLaunchers: sauceBrowsers, + + // Browsers to launch, commented out to prevent karma from starting + // too many concurrent browsers and timing sauce out. + browsers: _.keys(sauceBrowsers) + }); +}; diff --git a/vendor/underscore/karma.conf.js b/vendor/underscore/karma.conf.js new file mode 100644 index 000000000..d01f24ee0 --- /dev/null +++ b/vendor/underscore/karma.conf.js @@ -0,0 +1,35 @@ +// Note some browser launchers should be installed before using karma start. + +// For example: +// $ npm install karma-firefox-launcher +// $ karma start --browser=Firefox + +// See http://karma-runner.github.io/0.8/config/configuration-file.html +module.exports = function(config) { + config.set({ + basePath: '', + frameworks: ['qunit'], + logLevel: config.LOG_INFO, + port: 9876, + + // list of files / patterns to load in the browser + files: [ + 'test/vendor/qunit-extras.js', + 'test/qunit-setup.js', + 'underscore.js', + 'test/*.js' + ], + + // Test results reporter to use + // https://npmjs.org/browse/keyword/karma-reporter + reporters: ['progress'], + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: ['PhantomJS'], + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: true + }); +}; \ No newline at end of file diff --git a/vendor/underscore/package.json b/vendor/underscore/package.json index baf11b2eb..eff9fca51 100644 --- a/vendor/underscore/package.json +++ b/vendor/underscore/package.json @@ -1,23 +1,50 @@ { - "name" : "underscore", - "description" : "JavaScript's functional programming helper library.", - "homepage" : "http://underscorejs.org", - "keywords" : ["util", "functional", "server", "client", "browser"], - "author" : "Jeremy Ashkenas ", - "repository" : {"type": "git", "url": "git://github.com/jashkenas/underscore.git"}, - "main" : "underscore.js", - "version" : "1.5.2", + "name": "underscore", + "description": "JavaScript's functional programming helper library.", + "homepage": "http://underscorejs.org", + "keywords": [ + "util", + "functional", + "server", + "client", + "browser" + ], + "author": "Jeremy Ashkenas ", + "repository": { + "type": "git", + "url": "git://github.com/jashkenas/underscore.git" + }, + "main": "underscore.js", + "version": "1.8.3", "devDependencies": { - "phantomjs": "1.9.0-1" + "coveralls": "^2.11.2", + "docco": "*", + "eslint": "1.10.x", + "gzip-size-cli": "^1.0.0", + "karma": "^0.13.13", + "karma-qunit": "~0.1.4", + "nyc": "^2.1.3", + "pretty-bytes-cli": "^1.0.0", + "qunit-cli": "~0.2.0", + "qunitjs": "^1.18.0", + "uglify-js": "2.4.x" }, "scripts": { - "test": "phantomjs test/vendor/runner.js test/index.html?noglobals=true" + "test": "npm run test-node && npm run lint", + "coverage": "nyc npm run test-node && nyc report", + "coveralls": "nyc npm run test-node && nyc report --reporter=text-lcov | coveralls", + "lint": "eslint underscore.js test/*.js", + "test-node": "qunit-cli test/*.js", + "test-browser": "npm i karma-phantomjs-launcher && karma start", + "minify": "uglifyjs underscore.js -c \"evaluate=false\" --comments \"/ .*/\" -m", + "build": "npm run minify -- --source-map underscore-min.map --source-map-url \" \" -o underscore-min.js", + "doc": "docco underscore.js", + "weight": "npm run minify | gzip-size | pretty-bytes" }, - "licenses": [ - { - "type": "MIT", - "url": "https://raw.github.com/jashkenas/underscore/master/LICENSE" - } - ], - "files" : ["underscore.js", "underscore-min.js", "LICENSE"] + "license": "MIT", + "files": [ + "underscore.js", + "underscore-min.js", + "underscore-min.map" + ] } diff --git a/vendor/underscore/test/.eslintrc b/vendor/underscore/test/.eslintrc new file mode 100644 index 000000000..fe75e810a --- /dev/null +++ b/vendor/underscore/test/.eslintrc @@ -0,0 +1,13 @@ +{ + "env": { + "browser": true + }, + "globals": { + "QUnit": false + }, + "rules": { + "brace-style": 0, + "no-new-wrappers": 0, + "no-extend-native": 0 + } +} diff --git a/vendor/underscore/test/arrays.js b/vendor/underscore/test/arrays.js index f9ed13294..4e359d1f7 100644 --- a/vendor/underscore/test/arrays.js +++ b/vendor/underscore/test/arrays.js @@ -1,224 +1,563 @@ -$(document).ready(function() { +(function() { + var _ = typeof require == 'function' ? require('..') : window._; - module("Arrays"); + QUnit.module('Arrays'); - test("first", function() { - equal(_.first([1,2,3]), 1, 'can pull out the first element of an array'); - equal(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); - equal(_.first([1,2,3], 0).join(', '), "", 'can pass an index to first'); - equal(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first'); - equal(_.first([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to first'); - var result = (function(){ return _.first(arguments); })(4, 3, 2, 1); - equal(result, 4, 'works on an arguments object.'); - result = _.map([[1,2,3],[1,2,3]], _.first); - equal(result.join(','), '1,1', 'works well with _.map'); - result = (function() { return _.take([1,2,3], 2); })(); - equal(result.join(','), '1,2', 'aliased as take'); + QUnit.test('first', function(assert) { + assert.strictEqual(_.first([1, 2, 3]), 1, 'can pull out the first element of an array'); + assert.strictEqual(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); + assert.deepEqual(_.first([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)'); + assert.deepEqual(_.first([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)'); + assert.deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can fetch the first n elements'); + assert.deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length'); + var result = (function(){ return _.first(arguments); }(4, 3, 2, 1)); + assert.strictEqual(result, 4, 'works on an arguments object'); + result = _.map([[1, 2, 3], [1, 2, 3]], _.first); + assert.deepEqual(result, [1, 1], 'works well with _.map'); + assert.strictEqual(_.first(null), void 0, 'returns undefined when called on null'); - equal(_.first(null), undefined, 'handles nulls'); + Array.prototype[0] = 'boo'; + assert.strictEqual(_.first([]), void 0, 'return undefined when called on a empty array'); + delete Array.prototype[0]; }); - test("rest", function() { + QUnit.test('head', function(assert) { + assert.strictEqual(_.head, _.first, 'is an alias for first'); + }); + + QUnit.test('take', function(assert) { + assert.strictEqual(_.take, _.first, 'is an alias for first'); + }); + + QUnit.test('rest', function(assert) { var numbers = [1, 2, 3, 4]; - equal(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()'); - equal(_.rest(numbers, 0).join(", "), "1, 2, 3, 4", 'working rest(0)'); - equal(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index'); - var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4); - equal(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); - result = _.map([[1,2,3],[1,2,3]], _.rest); - equal(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map'); - result = (function(){ return _(arguments).drop(); })(1, 2, 3, 4); - equal(result.join(', '), '2, 3, 4', 'aliased as drop and works on arguments object'); + assert.deepEqual(_.rest(numbers), [2, 3, 4], 'fetches all but the first element'); + assert.deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'returns the whole array when index is 0'); + assert.deepEqual(_.rest(numbers, 2), [3, 4], 'returns elements starting at the given index'); + var result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4)); + assert.deepEqual(result, [2, 3, 4], 'works on an arguments object'); + result = _.map([[1, 2, 3], [1, 2, 3]], _.rest); + assert.deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map'); }); - test("initial", function() { - equal(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()'); - equal(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index'); - var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4); - equal(result.join(", "), "1, 2, 3", 'initial works on arguments object'); - result = _.map([[1,2,3],[1,2,3]], _.initial); - equal(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map'); + QUnit.test('tail', function(assert) { + assert.strictEqual(_.tail, _.rest, 'is an alias for rest'); }); - test("last", function() { - equal(_.last([1,2,3]), 3, 'can pull out the last element of an array'); - equal(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last'); - equal(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last'); - equal(_.last([1,2,3], 5).join(', '), '1, 2, 3', 'can pass an index to last'); - var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4); - equal(result, 4, 'works on an arguments object'); - result = _.map([[1,2,3],[1,2,3]], _.last); - equal(result.join(','), '3,3', 'works well with _.map'); - - equal(_.last(null), undefined, 'handles nulls'); + QUnit.test('drop', function(assert) { + assert.strictEqual(_.drop, _.rest, 'is an alias for rest'); }); - test("compact", function() { - equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); - var result = (function(){ return _.compact(arguments).length; })(0, 1, false, 2, false, 3); - equal(result, 3, 'works on an arguments object'); + QUnit.test('initial', function(assert) { + assert.deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'returns all but the last element'); + assert.deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'returns all but the last n elements'); + assert.deepEqual(_.initial([1, 2, 3, 4], 6), [], 'returns an empty array when n > length'); + var result = (function(){ return _(arguments).initial(); }(1, 2, 3, 4)); + assert.deepEqual(result, [1, 2, 3], 'works on an arguments object'); + result = _.map([[1, 2, 3], [1, 2, 3]], _.initial); + assert.deepEqual(_.flatten(result), [1, 2, 1, 2], 'works well with _.map'); }); - test("flatten", function() { + QUnit.test('last', function(assert) { + assert.strictEqual(_.last([1, 2, 3]), 3, 'can pull out the last element of an array'); + assert.strictEqual(_([1, 2, 3]).last(), 3, 'can perform OO-style "last()"'); + assert.deepEqual(_.last([1, 2, 3], 0), [], 'returns an empty array when n <= 0 (0 case)'); + assert.deepEqual(_.last([1, 2, 3], -1), [], 'returns an empty array when n <= 0 (negative case)'); + assert.deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can fetch the last n elements'); + assert.deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'returns the whole array if n > length'); + var result = (function(){ return _(arguments).last(); }(1, 2, 3, 4)); + assert.strictEqual(result, 4, 'works on an arguments object'); + result = _.map([[1, 2, 3], [1, 2, 3]], _.last); + assert.deepEqual(result, [3, 3], 'works well with _.map'); + assert.strictEqual(_.last(null), void 0, 'returns undefined when called on null'); + + var arr = []; + arr[-1] = 'boo'; + assert.strictEqual(_.last(arr), void 0, 'return undefined when called on a empty array'); + }); + + QUnit.test('compact', function(assert) { + assert.deepEqual(_.compact([1, false, null, 0, '', void 0, NaN, 2]), [1, 2], 'removes all falsy values'); + var result = (function(){ return _.compact(arguments); }(0, 1, false, 2, false, 3)); + assert.deepEqual(result, [1, 2, 3], 'works on an arguments object'); + result = _.map([[1, false, false], [false, false, 3]], _.compact); + assert.deepEqual(result, [[1], [3]], 'works well with _.map'); + }); + + QUnit.test('flatten', function(assert) { + assert.deepEqual(_.flatten(null), [], 'supports null'); + assert.deepEqual(_.flatten(void 0), [], 'supports undefined'); + + assert.deepEqual(_.flatten([[], [[]], []]), [], 'supports empty arrays'); + assert.deepEqual(_.flatten([[], [[]], []], true), [[]], 'can shallowly flatten empty arrays'); + var list = [1, [2], [3, [[[4]]]]]; - deepEqual(_.flatten(list), [1,2,3,4], 'can flatten nested arrays'); - deepEqual(_.flatten(list, true), [1,2,3,[[[4]]]], 'can shallowly flatten nested arrays'); - var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]); - deepEqual(result, [1,2,3,4], 'works on an arguments object'); + assert.deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays'); + assert.deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays'); + var result = (function(){ return _.flatten(arguments); }(1, [2], [3, [[[4]]]])); + assert.deepEqual(result, [1, 2, 3, 4], 'works on an arguments object'); list = [[1], [2], [3], [[4]]]; - deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays'); + assert.deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays'); + + assert.strictEqual(_.flatten([_.range(10), _.range(10), 5, 1, 3], true).length, 23, 'can flatten medium length arrays'); + assert.strictEqual(_.flatten([_.range(10), _.range(10), 5, 1, 3]).length, 23, 'can shallowly flatten medium length arrays'); + assert.strictEqual(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3]).length, 1056003, 'can handle massive arrays'); + assert.strictEqual(_.flatten([new Array(1000000), _.range(56000), 5, 1, 3], true).length, 1056003, 'can handle massive arrays in shallow mode'); + + var x = _.range(100000); + for (var i = 0; i < 1000; i++) x = [x]; + assert.deepEqual(_.flatten(x), _.range(100000), 'can handle very deep arrays'); + assert.deepEqual(_.flatten(x, true), x[0], 'can handle very deep arrays in shallow mode'); }); - test("without", function() { + QUnit.test('without', function(assert) { var list = [1, 2, 1, 0, 3, 1, 4]; - equal(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object'); - var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4); - equal(result.join(', '), '2, 3, 4', 'works on an arguments object'); + assert.deepEqual(_.without(list, 0, 1), [2, 3, 4], 'removes all instances of the given values'); + var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4)); + assert.deepEqual(result, [2, 3, 4], 'works on an arguments object'); - list = [{one : 1}, {two : 2}]; - ok(_.without(list, {one : 1}).length == 2, 'uses real object identity for comparisons.'); - ok(_.without(list, list[0]).length == 1, 'ditto.'); + list = [{one: 1}, {two: 2}]; + assert.deepEqual(_.without(list, {one: 1}), list, 'compares objects by reference (value case)'); + assert.deepEqual(_.without(list, list[0]), [{two: 2}], 'compares objects by reference (reference case)'); }); - test("uniq", function() { + QUnit.test('sortedIndex', function(assert) { + var numbers = [10, 20, 30, 40, 50]; + var indexFor35 = _.sortedIndex(numbers, 35); + assert.strictEqual(indexFor35, 3, 'finds the index at which a value should be inserted to retain order'); + var indexFor30 = _.sortedIndex(numbers, 30); + assert.strictEqual(indexFor30, 2, 'finds the smallest index at which a value could be inserted to retain order'); + + var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}]; + var iterator = function(obj){ return obj.x; }; + assert.strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2, 'uses the result of `iterator` for order comparisons'); + assert.strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3, 'when `iterator` is a string, uses that key for order comparisons'); + + var context = {1: 2, 2: 3, 3: 4}; + iterator = function(obj){ return this[obj]; }; + assert.strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1, 'can execute its iterator in the given context'); + + var values = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535, 131071, 262143, 524287, + 1048575, 2097151, 4194303, 8388607, 16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647]; + var largeArray = Array(Math.pow(2, 32) - 1); + var length = values.length; + // Sparsely populate `array` + while (length--) { + largeArray[values[length]] = values[length]; + } + assert.strictEqual(_.sortedIndex(largeArray, 2147483648), 2147483648, 'works with large indexes'); + }); + + QUnit.test('uniq', function(assert) { var list = [1, 2, 1, 3, 1, 4]; - equal(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array'); - + assert.deepEqual(_.uniq(list), [1, 2, 3, 4], 'can find the unique values of an unsorted array'); list = [1, 1, 1, 2, 2, 3]; - equal(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster'); + assert.deepEqual(_.uniq(list, true), [1, 2, 3], 'can find the unique values of a sorted array faster'); - list = [{name:'moe'}, {name:'curly'}, {name:'larry'}, {name:'curly'}]; - var iterator = function(value) { return value.name; }; - equal(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator'); + list = [{name: 'Moe'}, {name: 'Curly'}, {name: 'Larry'}, {name: 'Curly'}]; + var expected = [{name: 'Moe'}, {name: 'Curly'}, {name: 'Larry'}]; + var iterator = function(stooge) { return stooge.name; }; + assert.deepEqual(_.uniq(list, false, iterator), expected, 'uses the result of `iterator` for uniqueness comparisons (unsorted case)'); + assert.deepEqual(_.uniq(list, iterator), expected, '`sorted` argument defaults to false when omitted'); + assert.deepEqual(_.uniq(list, 'name'), expected, 'when `iterator` is a string, uses that key for comparisons (unsorted case)'); - equal(_.map(_.uniq(list, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator without specifying whether array is sorted'); + list = [{score: 8}, {score: 10}, {score: 10}]; + expected = [{score: 8}, {score: 10}]; + iterator = function(item) { return item.score; }; + assert.deepEqual(_.uniq(list, true, iterator), expected, 'uses the result of `iterator` for uniqueness comparisons (sorted case)'); + assert.deepEqual(_.uniq(list, true, 'score'), expected, 'when `iterator` is a string, uses that key for comparisons (sorted case)'); - iterator = function(value) { return value +1; }; - list = [1, 2, 2, 3, 4, 4]; - equal(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array'); + assert.deepEqual(_.uniq([{0: 1}, {0: 1}, {0: 1}, {0: 2}], 0), [{0: 1}, {0: 2}], 'can use falsy pluck like iterator'); + + var result = (function(){ return _.uniq(arguments); }(1, 2, 1, 3, 1, 4)); + assert.deepEqual(result, [1, 2, 3, 4], 'works on an arguments object'); + + var a = {}, b = {}, c = {}; + assert.deepEqual(_.uniq([a, b, a, b, c]), [a, b, c], 'works on values that can be tested for equivalency but not ordered'); + + assert.deepEqual(_.uniq(null), [], 'returns an empty array when `array` is not iterable'); + + var context = {}; + list = [3]; + _.uniq(list, function(value, index, array) { + assert.strictEqual(this, context, 'executes its iterator in the given context'); + assert.strictEqual(value, 3, 'passes its iterator the value'); + assert.strictEqual(index, 0, 'passes its iterator the index'); + assert.strictEqual(array, list, 'passes its iterator the entire array'); + }, context); - var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4); - equal(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); }); - test("intersection", function() { + QUnit.test('unique', function(assert) { + assert.strictEqual(_.unique, _.uniq, 'is an alias for uniq'); + }); + + QUnit.test('intersection', function(assert) { var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; - equal(_.intersection(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); - equal(_(stooges).intersection(leaders).join(''), 'moe', 'can perform an OO-style intersection'); - var result = (function(){ return _.intersection(arguments, leaders); })('moe', 'curly', 'larry'); - equal(result.join(''), 'moe', 'works on an arguments object'); + assert.deepEqual(_.intersection(stooges, leaders), ['moe'], 'can find the set intersection of two arrays'); + assert.deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection'); + var result = (function(){ return _.intersection(arguments, leaders); }('moe', 'curly', 'larry')); + assert.deepEqual(result, ['moe'], 'works on an arguments object'); var theSixStooges = ['moe', 'moe', 'curly', 'curly', 'larry', 'larry']; - equal(_.intersection(theSixStooges, leaders).join(''), 'moe', 'returns a duplicate-free array'); + assert.deepEqual(_.intersection(theSixStooges, leaders), ['moe'], 'returns a duplicate-free array'); + result = _.intersection([2, 4, 3, 1], [1, 2, 3]); + assert.deepEqual(result, [2, 3, 1], 'preserves the order of the first array'); + result = _.intersection(null, [1, 2, 3]); + assert.deepEqual(result, [], 'returns an empty array when passed null as the first argument'); + result = _.intersection([1, 2, 3], null); + assert.deepEqual(result, [], 'returns an empty array when passed null as an argument beyond the first'); }); - test("union", function() { + QUnit.test('union', function(assert) { var result = _.union([1, 2, 3], [2, 30, 1], [1, 40]); - equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays'); + assert.deepEqual(result, [1, 2, 3, 30, 40], 'can find the union of a list of arrays'); + + result = _([1, 2, 3]).union([2, 30, 1], [1, 40]); + assert.deepEqual(result, [1, 2, 3, 30, 40], 'can perform an OO-style union'); result = _.union([1, 2, 3], [2, 30, 1], [1, 40, [1]]); - equal(result.join(' '), '1 2 3 30 40 1', 'takes the union of a list of nested arrays'); + assert.deepEqual(result, [1, 2, 3, 30, 40, [1]], 'can find the union of a list of nested arrays'); - var args = null; - (function(){ args = arguments; })(1, 2, 3); - result = _.union(args, [2, 30, 1], [1, 40]); - equal(result.join(' '), '1 2 3 30 40', 'takes the union of a list of arrays'); + result = _.union([10, 20], [1, 30, 10], [0, 40]); + assert.deepEqual(result, [10, 20, 1, 30, 0, 40], 'orders values by their first encounter'); - result = _.union(null, [1, 2, 3]); - deepEqual(result, [null, 1, 2, 3]); + result = (function(){ return _.union(arguments, [2, 30, 1], [1, 40]); }(1, 2, 3)); + assert.deepEqual(result, [1, 2, 3, 30, 40], 'works on an arguments object'); + + assert.deepEqual(_.union([1, 2, 3], 4), [1, 2, 3], 'restricts the union to arrays only'); }); - test("difference", function() { + QUnit.test('difference', function(assert) { var result = _.difference([1, 2, 3], [2, 30, 40]); - equal(result.join(' '), '1 3', 'takes the difference of two arrays'); + assert.deepEqual(result, [1, 3], 'can find the difference of two arrays'); + + result = _([1, 2, 3]).difference([2, 30, 40]); + assert.deepEqual(result, [1, 3], 'can perform an OO-style difference'); result = _.difference([1, 2, 3, 4], [2, 30, 40], [1, 11, 111]); - equal(result.join(' '), '3 4', 'takes the difference of three arrays'); + assert.deepEqual(result, [3, 4], 'can find the difference of three arrays'); + + result = _.difference([8, 9, 3, 1], [3, 8]); + assert.deepEqual(result, [9, 1], 'preserves the order of the first array'); + + result = (function(){ return _.difference(arguments, [2, 30, 40]); }(1, 2, 3)); + assert.deepEqual(result, [1, 3], 'works on an arguments object'); + + result = _.difference([1, 2, 3], 1); + assert.deepEqual(result, [1, 2, 3], 'restrict the difference to arrays only'); }); - test('zip', function() { + QUnit.test('zip', function(assert) { var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; - var stooges = _.zip(names, ages, leaders); - equal(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); + assert.deepEqual(_.zip(names, ages, leaders), [ + ['moe', 30, true], + ['larry', 40, void 0], + ['curly', 50, void 0] + ], 'zipped together arrays of different lengths'); - stooges = _.zip(['moe',30, 'stooge 1'],['larry',40, 'stooge 2'],['curly',50, 'stooge 3']); - deepEqual(stooges, [['moe','larry','curly'],[30,40,50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs'); + var stooges = _.zip(['moe', 30, 'stooge 1'], ['larry', 40, 'stooge 2'], ['curly', 50, 'stooge 3']); + assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], ['stooge 1', 'stooge 2', 'stooge 3']], 'zipped pairs'); - // In the case of difference lengths of the tuples undefineds + // In the case of different lengths of the tuples, undefined values // should be used as placeholder - stooges = _.zip(['moe',30],['larry',40],['curly',50, 'extra data']); - deepEqual(stooges, [['moe','larry','curly'],[30,40,50], [undefined, undefined, 'extra data']], 'zipped pairs with empties'); + stooges = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']); + assert.deepEqual(stooges, [['moe', 'larry', 'curly'], [30, 40, 50], [void 0, void 0, 'extra data']], 'zipped pairs with empties'); var empty = _.zip([]); - deepEqual(empty, [], 'unzipped empty'); + assert.deepEqual(empty, [], 'unzipped empty'); + + assert.deepEqual(_.zip(null), [], 'handles null'); + assert.deepEqual(_.zip(), [], '_.zip() returns []'); }); - test('object', function() { + QUnit.test('unzip', function(assert) { + assert.deepEqual(_.unzip(null), [], 'handles null'); + + assert.deepEqual(_.unzip([['a', 'b'], [1, 2]]), [['a', 1], ['b', 2]]); + + // complements zip + var zipped = _.zip(['fred', 'barney'], [30, 40], [true, false]); + assert.deepEqual(_.unzip(zipped), [['fred', 'barney'], [30, 40], [true, false]]); + + zipped = _.zip(['moe', 30], ['larry', 40], ['curly', 50, 'extra data']); + assert.deepEqual(_.unzip(zipped), [['moe', 30, void 0], ['larry', 40, void 0], ['curly', 50, 'extra data']], 'Uses length of largest array'); + }); + + QUnit.test('object', function(assert) { var result = _.object(['moe', 'larry', 'curly'], [30, 40, 50]); var shouldBe = {moe: 30, larry: 40, curly: 50}; - ok(_.isEqual(result, shouldBe), 'two arrays zipped together into an object'); + assert.deepEqual(result, shouldBe, 'two arrays zipped together into an object'); result = _.object([['one', 1], ['two', 2], ['three', 3]]); shouldBe = {one: 1, two: 2, three: 3}; - ok(_.isEqual(result, shouldBe), 'an array of pairs zipped together into an object'); + assert.deepEqual(result, shouldBe, 'an array of pairs zipped together into an object'); var stooges = {moe: 30, larry: 40, curly: 50}; - ok(_.isEqual(_.object(_.pairs(stooges)), stooges), 'an object converted to pairs and back to an object'); + assert.deepEqual(_.object(_.pairs(stooges)), stooges, 'an object converted to pairs and back to an object'); - ok(_.isEqual(_.object(null), {}), 'handles nulls'); + assert.deepEqual(_.object(null), {}, 'handles nulls'); }); - test("indexOf", function() { + QUnit.test('indexOf', function(assert) { var numbers = [1, 2, 3]; - numbers.indexOf = null; - equal(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function'); - var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3); - equal(result, 1, 'works on an arguments object'); - equal(_.indexOf(null, 2), -1, 'handles nulls properly'); + assert.strictEqual(_.indexOf(numbers, 2), 1, 'can compute indexOf'); + var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3)); + assert.strictEqual(result, 1, 'works on an arguments object'); + + _.each([null, void 0, [], false], function(val) { + var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val); + assert.strictEqual(_.indexOf(val, 2), -1, msg); + assert.strictEqual(_.indexOf(val, 2, -1), -1, msg); + assert.strictEqual(_.indexOf(val, 2, -20), -1, msg); + assert.strictEqual(_.indexOf(val, 2, 15), -1, msg); + }); var num = 35; numbers = [10, 20, 30, 40, 50]; var index = _.indexOf(numbers, num, true); - equal(index, -1, '35 is not in the list'); + assert.strictEqual(index, -1, '35 is not in the list'); numbers = [10, 20, 30, 40, 50]; num = 40; index = _.indexOf(numbers, num, true); - equal(index, 3, '40 is in the list'); + assert.strictEqual(index, 3, '40 is in the list'); numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40; - index = _.indexOf(numbers, num, true); - equal(index, 1, '40 is in the list'); + assert.strictEqual(_.indexOf(numbers, num, true), 1, '40 is in the list'); + assert.strictEqual(_.indexOf(numbers, 6, true), -1, '6 isnt in the list'); + assert.strictEqual(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t use binary search'); + assert.ok(_.every(['1', [], {}, null], function() { + return _.indexOf(numbers, num, {}) === 1; + }), 'non-nums as fromIndex make indexOf assume sorted'); numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; index = _.indexOf(numbers, 2, 5); - equal(index, 7, 'supports the fromIndex argument'); + assert.strictEqual(index, 7, 'supports the fromIndex argument'); + + index = _.indexOf([,,, 0], void 0); + assert.strictEqual(index, 0, 'treats sparse arrays as if they were dense'); + + var array = [1, 2, 3, 1, 2, 3]; + assert.strictEqual(_.indexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index'); + assert.strictEqual(_.indexOf(array, 1, -2), -1, 'neg `fromIndex` starts at the right index'); + assert.strictEqual(_.indexOf(array, 2, -3), 4); + _.each([-6, -8, -Infinity], function(fromIndex) { + assert.strictEqual(_.indexOf(array, 1, fromIndex), 0); + }); + assert.strictEqual(_.indexOf([1, 2, 3], 1, true), 0); + + index = _.indexOf([], void 0, true); + assert.strictEqual(index, -1, 'empty array with truthy `isSorted` returns -1'); }); - test("lastIndexOf", function() { + QUnit.test('indexOf with NaN', function(assert) { + assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN), 2, 'Expected [1, 2, NaN] to contain NaN'); + assert.strictEqual(_.indexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN'); + + assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, 1), 2, 'startIndex does not affect result'); + assert.strictEqual(_.indexOf([1, 2, NaN, NaN], NaN, -2), 2, 'startIndex does not affect result'); + + (function() { + assert.strictEqual(_.indexOf(arguments, NaN), 2, 'Expected arguments [1, 2, NaN] to contain NaN'); + }(1, 2, NaN, NaN)); + }); + + QUnit.test('indexOf with +- 0', function(assert) { + _.each([-0, +0], function(val) { + assert.strictEqual(_.indexOf([1, 2, val, val], val), 2); + assert.strictEqual(_.indexOf([1, 2, val, val], -val), 2); + }); + }); + + QUnit.test('lastIndexOf', function(assert) { var numbers = [1, 0, 1]; - equal(_.lastIndexOf(numbers, 1), 2); + var falsy = [void 0, '', 0, false, NaN, null, void 0]; + assert.strictEqual(_.lastIndexOf(numbers, 1), 2); numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; numbers.lastIndexOf = null; - equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); - equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); - var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0); - equal(result, 5, 'works on an arguments object'); - equal(_.lastIndexOf(null, 2), -1, 'handles nulls properly'); + assert.strictEqual(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); + assert.strictEqual(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); + var result = (function(){ return _.lastIndexOf(arguments, 1); }(1, 0, 1, 0, 0, 1, 0, 0, 0)); + assert.strictEqual(result, 5, 'works on an arguments object'); + + _.each([null, void 0, [], false], function(val) { + var msg = 'Handles: ' + (_.isArray(val) ? '[]' : val); + assert.strictEqual(_.lastIndexOf(val, 2), -1, msg); + assert.strictEqual(_.lastIndexOf(val, 2, -1), -1, msg); + assert.strictEqual(_.lastIndexOf(val, 2, -20), -1, msg); + assert.strictEqual(_.lastIndexOf(val, 2, 15), -1, msg); + }); numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; var index = _.lastIndexOf(numbers, 2, 2); - equal(index, 1, 'supports the fromIndex argument'); + assert.strictEqual(index, 1, 'supports the fromIndex argument'); + + var array = [1, 2, 3, 1, 2, 3]; + + assert.strictEqual(_.lastIndexOf(array, 1, 0), 0, 'starts at the correct from idx'); + assert.strictEqual(_.lastIndexOf(array, 3), 5, 'should return the index of the last matched value'); + assert.strictEqual(_.lastIndexOf(array, 4), -1, 'should return `-1` for an unmatched value'); + + assert.strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`'); + + _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) { + assert.strictEqual(_.lastIndexOf(array, void 0, fromIndex), -1); + assert.strictEqual(_.lastIndexOf(array, 1, fromIndex), 3); + assert.strictEqual(_.lastIndexOf(array, '', fromIndex), -1); + }); + + var expected = _.map(falsy, function(value) { + return typeof value == 'number' ? -1 : 5; + }); + + var actual = _.map(falsy, function(fromIndex) { + return _.lastIndexOf(array, 3, fromIndex); + }); + + assert.deepEqual(actual, expected, 'should treat falsy `fromIndex` values, except `0` and `NaN`, as `array.length`'); + assert.strictEqual(_.lastIndexOf(array, 3, '1'), 5, 'should treat non-number `fromIndex` values as `array.length`'); + assert.strictEqual(_.lastIndexOf(array, 3, true), 5, 'should treat non-number `fromIndex` values as `array.length`'); + + assert.strictEqual(_.lastIndexOf(array, 2, -3), 1, 'should work with a negative `fromIndex`'); + assert.strictEqual(_.lastIndexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index'); + + assert.deepEqual(_.map([-6, -8, -Infinity], function(fromIndex) { + return _.lastIndexOf(array, 1, fromIndex); + }), [0, -1, -1]); }); - test("range", function() { - equal(_.range(0).join(''), '', 'range with 0 as a first argument generates an empty array'); - equal(_.range(4).join(' '), '0 1 2 3', 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); - equal(_.range(5, 8).join(' '), '5 6 7', 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); - equal(_.range(8, 5).join(''), '', 'range with two arguments a & b, b<a generates an empty array'); - equal(_.range(3, 10, 3).join(' '), '3 6 9', 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); - equal(_.range(3, 10, 15).join(''), '3', 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); - equal(_.range(12, 7, -2).join(' '), '12 10 8', 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); - equal(_.range(0, -10, -1).join(' '), '0 -1 -2 -3 -4 -5 -6 -7 -8 -9', 'final example in the Python docs'); + QUnit.test('lastIndexOf with NaN', function(assert) { + assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN), 3, 'Expected [1, 2, NaN] to contain NaN'); + assert.strictEqual(_.lastIndexOf([1, 2, Infinity], NaN), -1, 'Expected [1, 2, NaN] to contain NaN'); + + assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, 2), 2, 'fromIndex does not affect result'); + assert.strictEqual(_.lastIndexOf([1, 2, NaN, NaN], NaN, -2), 2, 'fromIndex does not affect result'); + + (function() { + assert.strictEqual(_.lastIndexOf(arguments, NaN), 3, 'Expected arguments [1, 2, NaN] to contain NaN'); + }(1, 2, NaN, NaN)); }); -}); + QUnit.test('lastIndexOf with +- 0', function(assert) { + _.each([-0, +0], function(val) { + assert.strictEqual(_.lastIndexOf([1, 2, val, val], val), 3); + assert.strictEqual(_.lastIndexOf([1, 2, val, val], -val), 3); + assert.strictEqual(_.lastIndexOf([-1, 1, 2], -val), -1); + }); + }); + + QUnit.test('findIndex', function(assert) { + var objects = [ + {a: 0, b: 0}, + {a: 1, b: 1}, + {a: 2, b: 2}, + {a: 0, b: 0} + ]; + + assert.strictEqual(_.findIndex(objects, function(obj) { + return obj.a === 0; + }), 0); + + assert.strictEqual(_.findIndex(objects, function(obj) { + return obj.b * obj.a === 4; + }), 2); + + assert.strictEqual(_.findIndex(objects, 'a'), 1, 'Uses lookupIterator'); + + assert.strictEqual(_.findIndex(objects, function(obj) { + return obj.b * obj.a === 5; + }), -1); + + assert.strictEqual(_.findIndex(null, _.noop), -1); + assert.strictEqual(_.findIndex(objects, function(a) { + return a.foo === null; + }), -1); + _.findIndex([{a: 1}], function(a, key, obj) { + assert.strictEqual(key, 0); + assert.deepEqual(obj, [{a: 1}]); + assert.strictEqual(this, objects, 'called with context'); + }, objects); + + var sparse = []; + sparse[20] = {a: 2, b: 2}; + assert.strictEqual(_.findIndex(sparse, function(obj) { + return obj && obj.b * obj.a === 4; + }), 20, 'Works with sparse arrays'); + + var array = [1, 2, 3, 4]; + array.match = 55; + assert.strictEqual(_.findIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys'); + }); + + QUnit.test('findLastIndex', function(assert) { + var objects = [ + {a: 0, b: 0}, + {a: 1, b: 1}, + {a: 2, b: 2}, + {a: 0, b: 0} + ]; + + assert.strictEqual(_.findLastIndex(objects, function(obj) { + return obj.a === 0; + }), 3); + + assert.strictEqual(_.findLastIndex(objects, function(obj) { + return obj.b * obj.a === 4; + }), 2); + + assert.strictEqual(_.findLastIndex(objects, 'a'), 2, 'Uses lookupIterator'); + + assert.strictEqual(_.findLastIndex(objects, function(obj) { + return obj.b * obj.a === 5; + }), -1); + + assert.strictEqual(_.findLastIndex(null, _.noop), -1); + assert.strictEqual(_.findLastIndex(objects, function(a) { + return a.foo === null; + }), -1); + _.findLastIndex([{a: 1}], function(a, key, obj) { + assert.strictEqual(key, 0); + assert.deepEqual(obj, [{a: 1}]); + assert.strictEqual(this, objects, 'called with context'); + }, objects); + + var sparse = []; + sparse[20] = {a: 2, b: 2}; + assert.strictEqual(_.findLastIndex(sparse, function(obj) { + return obj && obj.b * obj.a === 4; + }), 20, 'Works with sparse arrays'); + + var array = [1, 2, 3, 4]; + array.match = 55; + assert.strictEqual(_.findLastIndex(array, function(x) { return x === 55; }), -1, 'doesn\'t match array-likes keys'); + }); + + QUnit.test('range', function(assert) { + assert.deepEqual(_.range(0), [], 'range with 0 as a first argument generates an empty array'); + assert.deepEqual(_.range(4), [0, 1, 2, 3], 'range with a single positive argument generates an array of elements 0,1,2,...,n-1'); + assert.deepEqual(_.range(5, 8), [5, 6, 7], 'range with two arguments a & b, a<b generates an array of elements a,a+1,a+2,...,b-2,b-1'); + assert.deepEqual(_.range(3, 10, 3), [3, 6, 9], 'range with three arguments a & b & c, c < b-a, a < b generates an array of elements a,a+c,a+2c,...,b - (multiplier of a) < c'); + assert.deepEqual(_.range(3, 10, 15), [3], 'range with three arguments a & b & c, c > b-a, a < b generates an array with a single element, equal to a'); + assert.deepEqual(_.range(12, 7, -2), [12, 10, 8], 'range with three arguments a & b & c, a > b, c < 0 generates an array of elements a,a-c,a-2c and ends with the number not less than b'); + assert.deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs'); + assert.strictEqual(1 / _.range(-0, 1)[0], -Infinity, 'should preserve -0'); + assert.deepEqual(_.range(8, 5), [8, 7, 6], 'negative range generates descending array'); + assert.deepEqual(_.range(-3), [0, -1, -2], 'negative range generates descending array'); + }); + + QUnit.test('chunk', function(assert) { + assert.deepEqual(_.chunk([], 2), [], 'chunk for empty array returns an empty array'); + + assert.deepEqual(_.chunk([1, 2, 3], 0), [], 'chunk into parts of 0 elements returns empty array'); + assert.deepEqual(_.chunk([1, 2, 3], -1), [], 'chunk into parts of negative amount of elements returns an empty array'); + assert.deepEqual(_.chunk([1, 2, 3]), [], 'defaults to empty array (chunk size 0)'); + + assert.deepEqual(_.chunk([1, 2, 3], 1), [[1], [2], [3]], 'chunk into parts of 1 elements returns original array'); + + assert.deepEqual(_.chunk([1, 2, 3], 3), [[1, 2, 3]], 'chunk into parts of current array length elements returns the original array'); + assert.deepEqual(_.chunk([1, 2, 3], 5), [[1, 2, 3]], 'chunk into parts of more then current array length elements returns the original array'); + + assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 2), [[10, 20], [30, 40], [50, 60], [70]], 'chunk into parts of less then current array length elements'); + assert.deepEqual(_.chunk([10, 20, 30, 40, 50, 60, 70], 3), [[10, 20, 30], [40, 50, 60], [70]], 'chunk into parts of less then current array length elements'); + }); +}()); diff --git a/vendor/underscore/test/chaining.js b/vendor/underscore/test/chaining.js index 6eeef0f87..5d7595cdc 100644 --- a/vendor/underscore/test/chaining.js +++ b/vendor/underscore/test/chaining.js @@ -1,13 +1,14 @@ -$(document).ready(function() { +(function() { + var _ = typeof require == 'function' ? require('..') : window._; - module("Chaining"); + QUnit.module('Chaining'); - test("map/flatten/reduce", function() { + QUnit.test('map/flatten/reduce', function(assert) { var lyrics = [ - "I'm a lumberjack and I'm okay", - "I sleep all night and I work all day", - "He's a lumberjack and he's okay", - "He sleeps all night and he works all day" + 'I\'m a lumberjack and I\'m okay', + 'I sleep all night and I work all day', + 'He\'s a lumberjack and he\'s okay', + 'He sleeps all night and he works all day' ]; var counts = _(lyrics).chain() .map(function(line) { return line.split(''); }) @@ -16,12 +17,14 @@ $(document).ready(function() { hash[l] = hash[l] || 0; hash[l]++; return hash; - }, {}).value(); - ok(counts.a == 16 && counts.e == 10, 'counted all the letters in the song'); + }, {}) + .value(); + assert.strictEqual(counts.a, 16, 'counted all the letters in the song'); + assert.strictEqual(counts.e, 10, 'counted all the letters in the song'); }); - test("select/reject/sortBy", function() { - var numbers = [1,2,3,4,5,6,7,8,9,10]; + QUnit.test('select/reject/sortBy', function(assert) { + var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; numbers = _(numbers).chain().select(function(n) { return n % 2 === 0; }).reject(function(n) { @@ -29,11 +32,11 @@ $(document).ready(function() { }).sortBy(function(n) { return -n; }).value(); - equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); + assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers'); }); - test("select/reject/sortBy in functional style", function() { - var numbers = [1,2,3,4,5,6,7,8,9,10]; + QUnit.test('select/reject/sortBy in functional style', function(assert) { + var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; numbers = _.chain(numbers).select(function(n) { return n % 2 === 0; }).reject(function(n) { @@ -41,11 +44,11 @@ $(document).ready(function() { }).sortBy(function(n) { return -n; }).value(); - equal(numbers.join(', '), "10, 6, 2", "filtered and reversed the numbers"); + assert.deepEqual(numbers, [10, 6, 2], 'filtered and reversed the numbers'); }); - test("reverse/concat/unshift/pop/map", function() { - var numbers = [1,2,3,4,5]; + QUnit.test('reverse/concat/unshift/pop/map', function(assert) { + var numbers = [1, 2, 3, 4, 5]; numbers = _(numbers).chain() .reverse() .concat([5, 5, 5]) @@ -53,13 +56,44 @@ $(document).ready(function() { .pop() .map(function(n){ return n * 2; }) .value(); - equal(numbers.join(', '), "34, 10, 8, 6, 4, 2, 10, 10", 'can chain together array functions.'); + assert.deepEqual(numbers, [34, 10, 8, 6, 4, 2, 10, 10], 'can chain together array functions.'); }); - test("chaining works in small stages", function() { + QUnit.test('splice', function(assert) { + var instance = _([1, 2, 3, 4, 5]).chain(); + assert.deepEqual(instance.splice(1, 3).value(), [1, 5]); + assert.deepEqual(instance.splice(1, 0).value(), [1, 5]); + assert.deepEqual(instance.splice(1, 1).value(), [1]); + assert.deepEqual(instance.splice(0, 1).value(), [], '#397 Can create empty array'); + }); + + QUnit.test('shift', function(assert) { + var instance = _([1, 2, 3]).chain(); + assert.deepEqual(instance.shift().value(), [2, 3]); + assert.deepEqual(instance.shift().value(), [3]); + assert.deepEqual(instance.shift().value(), [], '#397 Can create empty array'); + }); + + QUnit.test('pop', function(assert) { + var instance = _([1, 2, 3]).chain(); + assert.deepEqual(instance.pop().value(), [1, 2]); + assert.deepEqual(instance.pop().value(), [1]); + assert.deepEqual(instance.pop().value(), [], '#397 Can create empty array'); + }); + + QUnit.test('chaining works in small stages', function(assert) { var o = _([1, 2, 3, 4]).chain(); - deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]); - deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]); + assert.deepEqual(o.filter(function(i) { return i < 3; }).value(), [1, 2]); + assert.deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]); }); -}); + QUnit.test('#1562: Engine proxies for chained functions', function(assert) { + var wrapped = _(512); + assert.strictEqual(wrapped.toJSON(), 512); + assert.strictEqual(wrapped.valueOf(), 512); + assert.strictEqual(+wrapped, 512); + assert.strictEqual(wrapped.toString(), '512'); + assert.strictEqual('' + wrapped, '512'); + }); + +}()); diff --git a/vendor/underscore/test/collections.js b/vendor/underscore/test/collections.js index 82176ad01..ce780f873 100644 --- a/vendor/underscore/test/collections.js +++ b/vendor/underscore/test/collections.js @@ -1,508 +1,939 @@ -$(document).ready(function() { +(function() { + var _ = typeof require == 'function' ? require('..') : window._; - module("Collections"); + QUnit.module('Collections'); - test("each", function() { + QUnit.test('each', function(assert) { _.each([1, 2, 3], function(num, i) { - equal(num, i + 1, 'each iterators provide value and iteration count'); + assert.strictEqual(num, i + 1, 'each iterators provide value and iteration count'); }); var answers = []; - _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier);}, {multiplier : 5}); - equal(answers.join(', '), '5, 10, 15', 'context object property accessed'); + _.each([1, 2, 3], function(num){ answers.push(num * this.multiplier); }, {multiplier: 5}); + assert.deepEqual(answers, [5, 10, 15], 'context object property accessed'); answers = []; - _.forEach([1, 2, 3], function(num){ answers.push(num); }); - equal(answers.join(', '), '1, 2, 3', 'aliased as "forEach"'); + _.each([1, 2, 3], function(num){ answers.push(num); }); + assert.deepEqual(answers, [1, 2, 3], 'can iterate a simple array'); answers = []; - var obj = {one : 1, two : 2, three : 3}; + var obj = {one: 1, two: 2, three: 3}; obj.constructor.prototype.four = 4; _.each(obj, function(value, key){ answers.push(key); }); - equal(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.'); + assert.deepEqual(answers, ['one', 'two', 'three'], 'iterating over objects works, and ignores the object prototype.'); delete obj.constructor.prototype.four; + // ensure the each function is JITed + _(1000).times(function() { _.each([], function(){}); }); + var count = 0; + obj = {1: 'foo', 2: 'bar', 3: 'baz'}; + _.each(obj, function(){ count++; }); + assert.strictEqual(count, 3, 'the fun should be called only 3 times'); + var answer = null; _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; }); - ok(answer, 'can reference the original collection from inside the iterator'); + assert.ok(answer, 'can reference the original collection from inside the iterator'); answers = 0; _.each(null, function(){ ++answers; }); - equal(answers, 0, 'handles a null properly'); + assert.strictEqual(answers, 0, 'handles a null properly'); + + _.each(false, function(){}); + + var a = [1, 2, 3]; + assert.strictEqual(_.each(a, function(){}), a); + assert.strictEqual(_.each(null, function(){}), null); }); - test('map', function() { + QUnit.test('forEach', function(assert) { + assert.strictEqual(_.forEach, _.each, 'is an alias for each'); + }); + + QUnit.test('lookupIterator with contexts', function(assert) { + _.each([true, false, 'yes', '', 0, 1, {}], function(context) { + _.each([1], function() { + assert.strictEqual(typeof this, 'object', 'context is a wrapped primitive'); + assert.strictEqual(this.valueOf(), context, 'the unwrapped context is the specified primitive'); + assert.equal(this, context, 'context can be coerced to the specified primitive'); + }, context); + }); + }); + + QUnit.test('Iterating objects with sketchy length properties', function(assert) { + var functions = [ + 'each', 'map', 'filter', 'find', + 'some', 'every', 'max', 'min', + 'groupBy', 'countBy', 'partition', 'indexBy' + ]; + var reducers = ['reduce', 'reduceRight']; + + var tricks = [ + {length: '5'}, + {length: {valueOf: _.constant(5)}}, + {length: Math.pow(2, 53) + 1}, + {length: Math.pow(2, 53)}, + {length: null}, + {length: -2}, + {length: new Number(15)} + ]; + + assert.expect(tricks.length * (functions.length + reducers.length + 4)); + + _.each(tricks, function(trick) { + var length = trick.length; + assert.strictEqual(_.size(trick), 1, 'size on obj with length: ' + length); + assert.deepEqual(_.toArray(trick), [length], 'toArray on obj with length: ' + length); + assert.deepEqual(_.shuffle(trick), [length], 'shuffle on obj with length: ' + length); + assert.deepEqual(_.sample(trick), length, 'sample on obj with length: ' + length); + + + _.each(functions, function(method) { + _[method](trick, function(val, key) { + assert.strictEqual(key, 'length', method + ': ran with length = ' + val); + }); + }); + + _.each(reducers, function(method) { + assert.strictEqual(_[method](trick), trick.length, method); + }); + }); + }); + + QUnit.test('Resistant to collection length and properties changing while iterating', function(assert) { + + var collection = [ + 'each', 'map', 'filter', 'find', + 'some', 'every', 'max', 'min', 'reject', + 'groupBy', 'countBy', 'partition', 'indexBy', + 'reduce', 'reduceRight' + ]; + var array = [ + 'findIndex', 'findLastIndex' + ]; + var object = [ + 'mapObject', 'findKey', 'pick', 'omit' + ]; + + _.each(collection.concat(array), function(method) { + var sparseArray = [1, 2, 3]; + sparseArray.length = 100; + var answers = 0; + _[method](sparseArray, function(){ + ++answers; + return method === 'every' ? true : null; + }, {}); + assert.strictEqual(answers, 100, method + ' enumerates [0, length)'); + + var growingCollection = [1, 2, 3], count = 0; + _[method](growingCollection, function() { + if (count < 10) growingCollection.push(count++); + return method === 'every' ? true : null; + }, {}); + assert.strictEqual(count, 3, method + ' is resistant to length changes'); + }); + + _.each(collection.concat(object), function(method) { + var changingObject = {0: 0, 1: 1}, count = 0; + _[method](changingObject, function(val) { + if (count < 10) changingObject[++count] = val + 1; + return method === 'every' ? true : null; + }, {}); + + assert.strictEqual(count, 2, method + ' is resistant to property changes'); + }); + }); + + QUnit.test('map', function(assert) { var doubled = _.map([1, 2, 3], function(num){ return num * 2; }); - equal(doubled.join(', '), '2, 4, 6', 'doubled numbers'); + assert.deepEqual(doubled, [2, 4, 6], 'doubled numbers'); - doubled = _.collect([1, 2, 3], function(num){ return num * 2; }); - equal(doubled.join(', '), '2, 4, 6', 'aliased as "collect"'); + var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier: 3}); + assert.deepEqual(tripled, [3, 6, 9], 'tripled numbers with context'); - var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3}); - equal(tripled.join(', '), '3, 6, 9', 'tripled numbers with context'); + doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); + assert.deepEqual(doubled, [2, 4, 6], 'OO-style doubled numbers'); - var doubled = _([1, 2, 3]).map(function(num){ return num * 2; }); - equal(doubled.join(', '), '2, 4, 6', 'OO-style doubled numbers'); + var ids = _.map({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){ + return n.id; + }); + assert.deepEqual(ids, ['1', '2'], 'Can use collection methods on Array-likes.'); - if (document.querySelectorAll) { - var ids = _.map(document.querySelectorAll('#map-test *'), function(n){ return n.id; }); - deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on NodeLists.'); - } + assert.deepEqual(_.map(null, _.noop), [], 'handles a null properly'); - var ids = _.map($('#map-test').children(), function(n){ return n.id; }); - deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on jQuery Array-likes.'); + assert.deepEqual(_.map([1], function() { + return this.length; + }, [5]), [1], 'called with context'); - var ids = _.map(document.images, function(n){ return n.id; }); - ok(ids[0] == 'chart_image', 'can use collection methods on HTMLCollections'); - - var ifnull = _.map(null, function(){}); - ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly'); + // Passing a property name like _.pluck. + var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}]; + assert.deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties'); }); - test('reduce', function() { - var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0); - equal(sum, 6, 'can sum up an array'); - - var context = {multiplier : 3}; - sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num * this.multiplier; }, 0, context); - equal(sum, 18, 'can reduce with a context object'); - - sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0); - equal(sum, 6, 'aliased as "inject"'); - - sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0); - equal(sum, 6, 'OO-style reduce'); - - var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }); - equal(sum, 6, 'default initial value'); - - var prod = _.reduce([1, 2, 3, 4], function(prod, num){ return prod * num; }); - equal(prod, 24, 'can reduce via multiplication'); - - var ifnull; - try { - _.reduce(null, function(){}); - } catch (ex) { - ifnull = ex; - } - ok(ifnull instanceof TypeError, 'handles a null (without initial value) properly'); - - ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); - equal(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); - raises(function() { _.reduce([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); + QUnit.test('collect', function(assert) { + assert.strictEqual(_.collect, _.map, 'is an alias for map'); }); - test('reduceRight', function() { - var list = _.reduceRight(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); - equal(list, 'bazbarfoo', 'can perform right folds'); + QUnit.test('reduce', function(assert) { + var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0); + assert.strictEqual(sum, 6, 'can sum up an array'); - var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }, ''); - equal(list, 'bazbarfoo', 'aliased as "foldr"'); + var context = {multiplier: 3}; + sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num * this.multiplier; }, 0, context); + assert.strictEqual(sum, 18, 'can reduce with a context object'); - var list = _.foldr(["foo", "bar", "baz"], function(memo, str){ return memo + str; }); - equal(list, 'bazbarfoo', 'default initial value'); + sum = _([1, 2, 3]).reduce(function(memo, num){ return memo + num; }, 0); + assert.strictEqual(sum, 6, 'OO-style reduce'); - var ifnull; - try { - _.reduceRight(null, function(){}); - } catch (ex) { - ifnull = ex; - } - ok(ifnull instanceof TypeError, 'handles a null (without initial value) properly'); + sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }); + assert.strictEqual(sum, 6, 'default initial value'); - var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(sum, num){ return sum + num; }); - equal(sum, 6, 'default initial value on object'); + var prod = _.reduce([1, 2, 3, 4], function(memo, num){ return memo * num; }); + assert.strictEqual(prod, 24, 'can reduce via multiplication'); - ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly'); + assert.strictEqual(_.reduce(null, _.noop, 138), 138, 'handles a null (with initial value) properly'); + assert.strictEqual(_.reduce([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); + assert.strictEqual(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item'); + assert.strictEqual(_.reduce([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); + }); - equal(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case'); - raises(function() { _.reduceRight([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value'); + QUnit.test('foldl', function(assert) { + assert.strictEqual(_.foldl, _.reduce, 'is an alias for reduce'); + }); + + QUnit.test('inject', function(assert) { + assert.strictEqual(_.inject, _.reduce, 'is an alias for reduce'); + }); + + QUnit.test('reduceRight', function(assert) { + var list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }, ''); + assert.strictEqual(list, 'bazbarfoo', 'can perform right folds'); + + list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }); + assert.strictEqual(list, 'bazbarfoo', 'default initial value'); + + var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(memo, num){ return memo + num; }); + assert.strictEqual(sum, 6, 'default initial value on object'); + + assert.strictEqual(_.reduceRight(null, _.noop, 138), 138, 'handles a null (with initial value) properly'); + assert.strictEqual(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item'); + + assert.strictEqual(_.reduceRight([], _.noop, void 0), void 0, 'undefined can be passed as a special case'); + assert.strictEqual(_.reduceRight([], _.noop), void 0, 'returns undefined when collection is empty and no initial value'); // Assert that the correct arguments are being passed. var args, - memo = {}, + init = {}, object = {a: 1, b: 2}, lastKey = _.keys(object).pop(); - var expected = lastKey == 'a' - ? [memo, 1, 'a', object] - : [memo, 2, 'b', object]; + var expected = lastKey === 'a' + ? [init, 1, 'a', object] + : [init, 2, 'b', object]; _.reduceRight(object, function() { - args || (args = _.toArray(arguments)); - }, memo); + if (!args) args = _.toArray(arguments); + }, init); - deepEqual(args, expected); + assert.deepEqual(args, expected); // And again, with numeric keys. - object = {'2': 'a', '1': 'b'}; + object = {2: 'a', 1: 'b'}; lastKey = _.keys(object).pop(); args = null; - expected = lastKey == '2' - ? [memo, 'a', '2', object] - : [memo, 'b', '1', object]; + expected = lastKey === '2' + ? [init, 'a', '2', object] + : [init, 'b', '1', object]; _.reduceRight(object, function() { - args || (args = _.toArray(arguments)); - }, memo); + if (!args) args = _.toArray(arguments); + }, init); - deepEqual(args, expected); + assert.deepEqual(args, expected); }); - test('find', function() { + QUnit.test('foldr', function(assert) { + assert.strictEqual(_.foldr, _.reduceRight, 'is an alias for reduceRight'); + }); + + QUnit.test('find', function(assert) { var array = [1, 2, 3, 4]; - strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`'); - strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found'); + assert.strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`'); + assert.strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found'); + + array.dontmatch = 55; + assert.strictEqual(_.find(array, function(x) { return x === 55; }), void 0, 'iterates array-likes correctly'); + + // Matching an object like _.findWhere. + var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}]; + assert.deepEqual(_.find(list, {a: 1}), {a: 1, b: 2}, 'can be used as findWhere'); + assert.deepEqual(_.find(list, {b: 4}), {a: 1, b: 4}); + assert.notOk(_.find(list, {c: 1}), 'undefined when not found'); + assert.notOk(_.find([], {c: 1}), 'undefined when searching empty list'); + + var result = _.find([1, 2, 3], function(num){ return num * 2 === 4; }); + assert.strictEqual(result, 2, 'found the first "2" and broke the loop'); + + var obj = { + a: {x: 1, z: 3}, + b: {x: 2, z: 2}, + c: {x: 3, z: 4}, + d: {x: 4, z: 1} + }; + + assert.deepEqual(_.find(obj, {x: 2}), {x: 2, z: 2}, 'works on objects'); + assert.deepEqual(_.find(obj, {x: 2, z: 1}), void 0); + assert.deepEqual(_.find(obj, function(x) { + return x.x === 4; + }), {x: 4, z: 1}); + + _.findIndex([{a: 1}], function(a, key, o) { + assert.strictEqual(key, 0); + assert.deepEqual(o, [{a: 1}]); + assert.strictEqual(this, _, 'called with context'); + }, _); }); - test('detect', function() { - var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; }); - equal(result, 2, 'found the first "2" and broke the loop'); + QUnit.test('detect', function(assert) { + assert.strictEqual(_.detect, _.find, 'is an alias for find'); }); - test('select', function() { - var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); - equal(evens.join(', '), '2, 4, 6', 'selected each even number'); + QUnit.test('filter', function(assert) { + var evenArray = [1, 2, 3, 4, 5, 6]; + var evenObject = {one: 1, two: 2, three: 3}; + var isEven = function(num){ return num % 2 === 0; }; - evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); - equal(evens.join(', '), '2, 4, 6', 'aliased as "filter"'); + assert.deepEqual(_.filter(evenArray, isEven), [2, 4, 6]); + assert.deepEqual(_.filter(evenObject, isEven), [2], 'can filter objects'); + assert.deepEqual(_.filter([{}, evenObject, []], 'two'), [evenObject], 'predicate string map to object properties'); + + _.filter([1], function() { + assert.strictEqual(this, evenObject, 'given context'); + }, evenObject); + + // Can be used like _.where. + var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; + assert.deepEqual(_.filter(list, {a: 1}), [{a: 1, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]); + assert.deepEqual(_.filter(list, {b: 2}), [{a: 1, b: 2}, {a: 2, b: 2}]); + assert.deepEqual(_.filter(list, {}), list, 'Empty object accepts all items'); + assert.deepEqual(_(list).filter({}), list, 'OO-filter'); }); - test('reject', function() { - var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; }); - equal(odds.join(', '), '1, 3, 5', 'rejected each even number'); + QUnit.test('select', function(assert) { + assert.strictEqual(_.select, _.filter, 'is an alias for filter'); + }); - var context = "obj"; + QUnit.test('reject', function(assert) { + var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 === 0; }); + assert.deepEqual(odds, [1, 3, 5], 'rejected each even number'); + + var context = 'obj'; var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){ - equal(context, "obj"); - return num % 2 != 0; + assert.strictEqual(context, 'obj'); + return num % 2 !== 0; }, context); - equal(evens.join(', '), '2, 4, 6', 'rejected each odd number'); + assert.deepEqual(evens, [2, 4, 6], 'rejected each odd number'); + + assert.deepEqual(_.reject([odds, {one: 1, two: 2, three: 3}], 'two'), [odds], 'predicate string map to object properties'); + + // Can be used like _.where. + var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; + assert.deepEqual(_.reject(list, {a: 1}), [{a: 2, b: 2}]); + assert.deepEqual(_.reject(list, {b: 2}), [{a: 1, b: 3}, {a: 1, b: 4}]); + assert.deepEqual(_.reject(list, {}), [], 'Returns empty list given empty object'); }); - test('all', function() { - ok(_.all([], _.identity), 'the empty set'); - ok(_.all([true, true, true], _.identity), 'all true values'); - ok(!_.all([true, false, true], _.identity), 'one false value'); - ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers'); - ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number'); - ok(_.all([1], _.identity) === true, 'cast to boolean - true'); - ok(_.all([0], _.identity) === false, 'cast to boolean - false'); - ok(_.every([true, true, true], _.identity), 'aliased as "every"'); - ok(!_.all([undefined, undefined, undefined], _.identity), 'works with arrays of undefined'); + QUnit.test('every', function(assert) { + assert.ok(_.every([], _.identity), 'the empty set'); + assert.ok(_.every([true, true, true], _.identity), 'every true values'); + assert.notOk(_.every([true, false, true], _.identity), 'one false value'); + assert.ok(_.every([0, 10, 28], function(num){ return num % 2 === 0; }), 'even numbers'); + assert.notOk(_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number'); + assert.strictEqual(_.every([1], _.identity), true, 'cast to boolean - true'); + assert.strictEqual(_.every([0], _.identity), false, 'cast to boolean - false'); + assert.notOk(_.every([void 0, void 0, void 0], _.identity), 'works with arrays of undefined'); + + var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; + assert.notOk(_.every(list, {a: 1, b: 2}), 'Can be called with object'); + assert.ok(_.every(list, 'a'), 'String mapped to object property'); + + list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}]; + assert.ok(_.every(list, {b: 2}), 'Can be called with object'); + assert.notOk(_.every(list, 'c'), 'String mapped to object property'); + + assert.ok(_.every({a: 1, b: 2, c: 3, d: 4}, _.isNumber), 'takes objects'); + assert.notOk(_.every({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); + assert.ok(_.every(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); + assert.notOk(_.every(['a', 'b', 'c', 'd', 'f'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); }); - test('any', function() { - var nativeSome = Array.prototype.some; - Array.prototype.some = null; - ok(!_.any([]), 'the empty set'); - ok(!_.any([false, false, false]), 'all false values'); - ok(_.any([false, false, true]), 'one true value'); - ok(_.any([null, 0, 'yes', false]), 'a string'); - ok(!_.any([null, 0, '', false]), 'falsy values'); - ok(!_.any([1, 11, 29], function(num){ return num % 2 == 0; }), 'all odd numbers'); - ok(_.any([1, 10, 29], function(num){ return num % 2 == 0; }), 'an even number'); - ok(_.any([1], _.identity) === true, 'cast to boolean - true'); - ok(_.any([0], _.identity) === false, 'cast to boolean - false'); - ok(_.some([false, false, true]), 'aliased as "some"'); - Array.prototype.some = nativeSome; + QUnit.test('all', function(assert) { + assert.strictEqual(_.all, _.every, 'is an alias for every'); }); - test('include', function() { - ok(_.include([1,2,3], 2), 'two is in the array'); - ok(!_.include([1,3,9], 2), 'two is not in the array'); - ok(_.contains({moe:1, larry:3, curly:9}, 3) === true, '_.include on objects checks their values'); - ok(_([1,2,3]).include(2), 'OO-style include'); + QUnit.test('some', function(assert) { + assert.notOk(_.some([]), 'the empty set'); + assert.notOk(_.some([false, false, false]), 'all false values'); + assert.ok(_.some([false, false, true]), 'one true value'); + assert.ok(_.some([null, 0, 'yes', false]), 'a string'); + assert.notOk(_.some([null, 0, '', false]), 'falsy values'); + assert.notOk(_.some([1, 11, 29], function(num){ return num % 2 === 0; }), 'all odd numbers'); + assert.ok(_.some([1, 10, 29], function(num){ return num % 2 === 0; }), 'an even number'); + assert.strictEqual(_.some([1], _.identity), true, 'cast to boolean - true'); + assert.strictEqual(_.some([0], _.identity), false, 'cast to boolean - false'); + assert.ok(_.some([false, false, true])); + + var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; + assert.notOk(_.some(list, {a: 5, b: 2}), 'Can be called with object'); + assert.ok(_.some(list, 'a'), 'String mapped to object property'); + + list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}]; + assert.ok(_.some(list, {b: 2}), 'Can be called with object'); + assert.notOk(_.some(list, 'd'), 'String mapped to object property'); + + assert.ok(_.some({a: '1', b: '2', c: '3', d: '4', e: 6}, _.isNumber), 'takes objects'); + assert.notOk(_.some({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects'); + assert.ok(_.some(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); + assert.notOk(_.some(['x', 'y', 'z'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works'); }); - test('invoke', function() { + QUnit.test('any', function(assert) { + assert.strictEqual(_.any, _.some, 'is an alias for some'); + }); + + QUnit.test('includes', function(assert) { + _.each([null, void 0, 0, 1, NaN, {}, []], function(val) { + assert.strictEqual(_.includes(val, 'hasOwnProperty'), false); + }); + assert.strictEqual(_.includes([1, 2, 3], 2), true, 'two is in the array'); + assert.notOk(_.includes([1, 3, 9], 2), 'two is not in the array'); + + assert.strictEqual(_.includes([5, 4, 3, 2, 1], 5, true), true, 'doesn\'t delegate to binary search'); + + assert.strictEqual(_.includes({moe: 1, larry: 3, curly: 9}, 3), true, '_.includes on objects checks their values'); + assert.ok(_([1, 2, 3]).includes(2), 'OO-style includes'); + + var numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; + assert.strictEqual(_.includes(numbers, 1, 1), true, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, -1), false, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, -2), false, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, -3), true, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, 6), true, 'takes a fromIndex'); + assert.strictEqual(_.includes(numbers, 1, 7), false, 'takes a fromIndex'); + + assert.ok(_.every([1, 2, 3], _.partial(_.includes, numbers)), 'fromIndex is guarded'); + }); + + QUnit.test('include', function(assert) { + assert.strictEqual(_.include, _.includes, 'is an alias for includes'); + }); + + QUnit.test('contains', function(assert) { + assert.strictEqual(_.contains, _.includes, 'is an alias for includes'); + + }); + + QUnit.test('includes with NaN', function(assert) { + assert.strictEqual(_.includes([1, 2, NaN, NaN], NaN), true, 'Expected [1, 2, NaN] to contain NaN'); + assert.strictEqual(_.includes([1, 2, Infinity], NaN), false, 'Expected [1, 2, NaN] to contain NaN'); + }); + + QUnit.test('includes with +- 0', function(assert) { + _.each([-0, +0], function(val) { + assert.strictEqual(_.includes([1, 2, val, val], val), true); + assert.strictEqual(_.includes([1, 2, val, val], -val), true); + assert.strictEqual(_.includes([-1, 1, 2], -val), false); + }); + }); + + + QUnit.test('invoke', function(assert) { + assert.expect(13); var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, 'sort'); - equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); - equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); + assert.deepEqual(result[0], [1, 5, 7], 'first array sorted'); + assert.deepEqual(result[1], [1, 2, 3], 'second array sorted'); + + _.invoke([{ + method: function() { + assert.deepEqual(_.toArray(arguments), [1, 2, 3], 'called with arguments'); + } + }], 'method', 1, 2, 3); + + assert.deepEqual(_.invoke([{a: null}, {}, {a: _.constant(1)}], 'a'), [null, void 0, 1], 'handles null & undefined'); + + assert.raises(function() { + _.invoke([{a: 1}], 'a'); + }, TypeError, 'throws for non-functions'); + + var getFoo = _.constant('foo'); + var getThis = function() { return this; }; + var item = { + a: { + b: getFoo, + c: getThis, + d: null + }, + e: getFoo, + f: getThis, + g: function() { + return { + h: getFoo + }; + } + }; + var arr = [item]; + assert.deepEqual(_.invoke(arr, ['a', 'b']), ['foo'], 'supports deep method access via an array syntax'); + assert.deepEqual(_.invoke(arr, ['a', 'c']), [item.a], 'executes deep methods on their direct parent'); + assert.deepEqual(_.invoke(arr, ['a', 'd', 'z']), [void 0], 'does not try to access attributes of non-objects'); + assert.deepEqual(_.invoke(arr, ['a', 'd']), [null], 'handles deep null values'); + assert.deepEqual(_.invoke(arr, ['e']), ['foo'], 'handles path arrays of length one'); + assert.deepEqual(_.invoke(arr, ['f']), [item], 'correct uses parent context with shallow array syntax'); + assert.deepEqual(_.invoke(arr, ['g', 'h']), [void 0], 'does not execute intermediate functions'); + + arr = [{ + a: function() { return 'foo'; } + }, { + a: function() { return 'bar'; } + }]; + assert.deepEqual(_.invoke(arr, 'a'), ['foo', 'bar'], 'can handle different methods on subsequent objects'); }); - test('invoke w/ function reference', function() { + QUnit.test('invoke w/ function reference', function(assert) { var list = [[5, 1, 7], [3, 2, 1]]; var result = _.invoke(list, Array.prototype.sort); - equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); - equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); + assert.deepEqual(result[0], [1, 5, 7], 'first array sorted'); + assert.deepEqual(result[1], [1, 2, 3], 'second array sorted'); + + assert.deepEqual(_.invoke([1, 2, 3], function(a) { + return a + this; + }, 5), [6, 7, 8], 'receives params from invoke'); }); // Relevant when using ClojureScript - test('invoke when strings have a call method', function() { + QUnit.test('invoke when strings have a call method', function(assert) { String.prototype.call = function() { return 42; }; var list = [[5, 1, 7], [3, 2, 1]]; - var s = "foo"; - equal(s.call(), 42, "call function exists"); + var s = 'foo'; + assert.strictEqual(s.call(), 42, 'call function exists'); var result = _.invoke(list, 'sort'); - equal(result[0].join(', '), '1, 5, 7', 'first array sorted'); - equal(result[1].join(', '), '1, 2, 3', 'second array sorted'); + assert.deepEqual(result[0], [1, 5, 7], 'first array sorted'); + assert.deepEqual(result[1], [1, 2, 3], 'second array sorted'); delete String.prototype.call; - equal(s.call, undefined, "call function removed"); + assert.strictEqual(s.call, void 0, 'call function removed'); }); - test('pluck', function() { - var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}]; - equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'pulls names out of objects'); + QUnit.test('pluck', function(assert) { + var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}]; + assert.deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects'); + assert.deepEqual(_.pluck(people, 'address'), [void 0, void 0], 'missing properties are returned as undefined'); + //compat: most flexible handling of edge cases + assert.deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]); }); - test('where', function() { + QUnit.test('where', function(assert) { var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]; var result = _.where(list, {a: 1}); - equal(result.length, 3); - equal(result[result.length - 1].b, 4); + assert.strictEqual(result.length, 3); + assert.strictEqual(result[result.length - 1].b, 4); result = _.where(list, {b: 2}); - equal(result.length, 2); - equal(result[0].a, 1); + assert.strictEqual(result.length, 2); + assert.strictEqual(result[0].a, 1); + result = _.where(list, {}); + assert.strictEqual(result.length, list.length); - result = _.where(list, {a: 1}, true); - equal(result.b, 2, "Only get the first object matched.") - result = _.where(list, {a: 1}, false); - equal(result.length, 3); + function test() {} + test.map = _.map; + assert.deepEqual(_.where([_, {a: 1, b: 2}, _], test), [_, _], 'checks properties given function'); }); - test('findWhere', function() { + QUnit.test('findWhere', function(assert) { var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}]; var result = _.findWhere(list, {a: 1}); - deepEqual(result, {a: 1, b: 2}); + assert.deepEqual(result, {a: 1, b: 2}); result = _.findWhere(list, {b: 4}); - deepEqual(result, {a: 1, b: 4}); + assert.deepEqual(result, {a: 1, b: 4}); - result = _.findWhere(list, {c:1}) - ok(_.isUndefined(result), "undefined when not found"); + result = _.findWhere(list, {c: 1}); + assert.ok(_.isUndefined(result), 'undefined when not found'); - result = _.findWhere([], {c:1}); - ok(_.isUndefined(result), "undefined when searching empty list"); + result = _.findWhere([], {c: 1}); + assert.ok(_.isUndefined(result), 'undefined when searching empty list'); + + function test() {} + test.map = _.map; + assert.strictEqual(_.findWhere([_, {a: 1, b: 2}, _], test), _, 'checks properties given function'); + + function TestClass() { + this.y = 5; + this.x = 'foo'; + } + var expect = {c: 1, x: 'foo', y: 5}; + assert.deepEqual(_.findWhere([{y: 5, b: 6}, expect], new TestClass()), expect, 'uses class instance properties'); }); - test('max', function() { - equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max'); + QUnit.test('max', function(assert) { + assert.strictEqual(-Infinity, _.max(null), 'can handle null/undefined'); + assert.strictEqual(-Infinity, _.max(void 0), 'can handle null/undefined'); + assert.strictEqual(-Infinity, _.max(null, _.identity), 'can handle null/undefined'); + + assert.strictEqual(_.max([1, 2, 3]), 3, 'can perform a regular Math.max'); var neg = _.max([1, 2, 3], function(num){ return -num; }); - equal(neg, 1, 'can perform a computation-based max'); + assert.strictEqual(neg, 1, 'can perform a computation-based max'); - equal(-Infinity, _.max({}), 'Maximum value of an empty object'); - equal(-Infinity, _.max([]), 'Maximum value of an empty array'); - equal(_.max({'a': 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); + assert.strictEqual(-Infinity, _.max({}), 'Maximum value of an empty object'); + assert.strictEqual(-Infinity, _.max([]), 'Maximum value of an empty array'); + assert.strictEqual(_.max({a: 'a'}), -Infinity, 'Maximum value of a non-numeric collection'); - equal(299999, _.max(_.range(1,300000)), "Maximum value of a too-big array"); + assert.strictEqual(_.max(_.range(1, 300000)), 299999, 'Maximum value of a too-big array'); + + assert.strictEqual(_.max([1, 2, 3, 'test']), 3, 'Finds correct max in array starting with num and containing a NaN'); + assert.strictEqual(_.max(['test', 1, 2, 3]), 3, 'Finds correct max in array starting with NaN'); + + assert.strictEqual(_.max([1, 2, 3, null]), 3, 'Finds correct max in array starting with num and containing a `null`'); + assert.strictEqual(_.max([null, 1, 2, 3]), 3, 'Finds correct max in array starting with a `null`'); + + assert.strictEqual(_.max([1, 2, 3, '']), 3, 'Finds correct max in array starting with num and containing an empty string'); + assert.strictEqual(_.max(['', 1, 2, 3]), 3, 'Finds correct max in array starting with an empty string'); + + assert.strictEqual(_.max([1, 2, 3, false]), 3, 'Finds correct max in array starting with num and containing a false'); + assert.strictEqual(_.max([false, 1, 2, 3]), 3, 'Finds correct max in array starting with a false'); + + assert.strictEqual(_.max([0, 1, 2, 3, 4]), 4, 'Finds correct max in array containing a zero'); + assert.strictEqual(_.max([-3, -2, -1, 0]), 0, 'Finds correct max in array containing negative numbers'); + + assert.deepEqual(_.map([[1, 2, 3], [4, 5, 6]], _.max), [3, 6], 'Finds correct max in array when mapping through multiple arrays'); + + var a = {x: -Infinity}; + var b = {x: -Infinity}; + var iterator = function(o){ return o.x; }; + assert.strictEqual(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity'); + + assert.deepEqual(_.max([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 4}, 'String keys use property iterator'); + + assert.deepEqual(_.max([0, 2], function(c){ return c * this.x; }, {x: 1}), 2, 'Iterator context'); + assert.deepEqual(_.max([[1], [2, 3], [-1, 4], [5]], 0), [5], 'Lookup falsy iterator'); + assert.deepEqual(_.max([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: 2}, 'Lookup falsy iterator'); }); - test('min', function() { - equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min'); + QUnit.test('min', function(assert) { + assert.strictEqual(_.min(null), Infinity, 'can handle null/undefined'); + assert.strictEqual(_.min(void 0), Infinity, 'can handle null/undefined'); + assert.strictEqual(_.min(null, _.identity), Infinity, 'can handle null/undefined'); + + assert.strictEqual(_.min([1, 2, 3]), 1, 'can perform a regular Math.min'); var neg = _.min([1, 2, 3], function(num){ return -num; }); - equal(neg, 3, 'can perform a computation-based min'); + assert.strictEqual(neg, 3, 'can perform a computation-based min'); - equal(Infinity, _.min({}), 'Minimum value of an empty object'); - equal(Infinity, _.min([]), 'Minimum value of an empty array'); - equal(_.min({'a': 'a'}), Infinity, 'Minimum value of a non-numeric collection'); + assert.strictEqual(_.min({}), Infinity, 'Minimum value of an empty object'); + assert.strictEqual(_.min([]), Infinity, 'Minimum value of an empty array'); + assert.strictEqual(_.min({a: 'a'}), Infinity, 'Minimum value of a non-numeric collection'); + + assert.deepEqual(_.map([[1, 2, 3], [4, 5, 6]], _.min), [1, 4], 'Finds correct min in array when mapping through multiple arrays'); var now = new Date(9999999999); var then = new Date(0); - equal(_.min([now, then]), then); + assert.strictEqual(_.min([now, then]), then); - equal(1, _.min(_.range(1,300000)), "Minimum value of a too-big array"); + assert.strictEqual(_.min(_.range(1, 300000)), 1, 'Minimum value of a too-big array'); + + assert.strictEqual(_.min([1, 2, 3, 'test']), 1, 'Finds correct min in array starting with num and containing a NaN'); + assert.strictEqual(_.min(['test', 1, 2, 3]), 1, 'Finds correct min in array starting with NaN'); + + assert.strictEqual(_.min([1, 2, 3, null]), 1, 'Finds correct min in array starting with num and containing a `null`'); + assert.strictEqual(_.min([null, 1, 2, 3]), 1, 'Finds correct min in array starting with a `null`'); + + assert.strictEqual(_.min([0, 1, 2, 3, 4]), 0, 'Finds correct min in array containing a zero'); + assert.strictEqual(_.min([-3, -2, -1, 0]), -3, 'Finds correct min in array containing negative numbers'); + + var a = {x: Infinity}; + var b = {x: Infinity}; + var iterator = function(o){ return o.x; }; + assert.strictEqual(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity'); + + assert.deepEqual(_.min([{a: 1}, {a: 0, b: 3}, {a: 4}, {a: 2}], 'a'), {a: 0, b: 3}, 'String keys use property iterator'); + + assert.deepEqual(_.min([0, 2], function(c){ return c * this.x; }, {x: -1}), 2, 'Iterator context'); + assert.deepEqual(_.min([[1], [2, 3], [-1, 4], [5]], 0), [-1, 4], 'Lookup falsy iterator'); + assert.deepEqual(_.min([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: -1}, 'Lookup falsy iterator'); }); - test('sortBy', function() { - var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}]; + QUnit.test('sortBy', function(assert) { + var people = [{name: 'curly', age: 50}, {name: 'moe', age: 30}]; people = _.sortBy(people, function(person){ return person.age; }); - equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age'); + assert.deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'stooges sorted by age'); - var list = [undefined, 4, 1, undefined, 3, 2]; - equal(_.sortBy(list, _.identity).join(','), '1,2,3,4,,', 'sortBy with undefined values'); + var list = [void 0, 4, 1, void 0, 3, 2]; + assert.deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, void 0, void 0], 'sortBy with undefined values'); - var list = ["one", "two", "three", "four", "five"]; + list = ['one', 'two', 'three', 'four', 'five']; var sorted = _.sortBy(list, 'length'); - equal(sorted.join(' '), 'one two four five three', 'sorted by length'); + assert.deepEqual(sorted, ['one', 'two', 'four', 'five', 'three'], 'sorted by length'); function Pair(x, y) { this.x = x; this.y = y; } - var collection = [ + var stableArray = [ new Pair(1, 1), new Pair(1, 2), new Pair(1, 3), new Pair(1, 4), new Pair(1, 5), new Pair(1, 6), new Pair(2, 1), new Pair(2, 2), new Pair(2, 3), new Pair(2, 4), new Pair(2, 5), new Pair(2, 6), - new Pair(undefined, 1), new Pair(undefined, 2), - new Pair(undefined, 3), new Pair(undefined, 4), - new Pair(undefined, 5), new Pair(undefined, 6) + new Pair(void 0, 1), new Pair(void 0, 2), + new Pair(void 0, 3), new Pair(void 0, 4), + new Pair(void 0, 5), new Pair(void 0, 6) ]; - var actual = _.sortBy(collection, function(pair) { + var stableObject = _.object('abcdefghijklmnopqr'.split(''), stableArray); + + var actual = _.sortBy(stableArray, function(pair) { return pair.x; }); - deepEqual(actual, collection, 'sortBy should be stable'); + assert.deepEqual(actual, stableArray, 'sortBy should be stable for arrays'); + assert.deepEqual(_.sortBy(stableArray, 'x'), stableArray, 'sortBy accepts property string'); + + actual = _.sortBy(stableObject, function(pair) { + return pair.x; + }); + + assert.deepEqual(actual, stableArray, 'sortBy should be stable for objects'); + + list = ['q', 'w', 'e', 'r', 't', 'y']; + assert.deepEqual(_.sortBy(list), ['e', 'q', 'r', 't', 'w', 'y'], 'uses _.identity if iterator is not specified'); }); - test('groupBy', function() { + QUnit.test('groupBy', function(assert) { var parity = _.groupBy([1, 2, 3, 4, 5, 6], function(num){ return num % 2; }); - ok('0' in parity && '1' in parity, 'created a group for each value'); - equal(parity[0].join(', '), '2, 4, 6', 'put each even number in the right group'); + assert.ok('0' in parity && '1' in parity, 'created a group for each value'); + assert.deepEqual(parity[0], [2, 4, 6], 'put each even number in the right group'); - var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; + var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']; var grouped = _.groupBy(list, 'length'); - equal(grouped['3'].join(' '), 'one two six ten'); - equal(grouped['4'].join(' '), 'four five nine'); - equal(grouped['5'].join(' '), 'three seven eight'); + assert.deepEqual(grouped['3'], ['one', 'two', 'six', 'ten']); + assert.deepEqual(grouped['4'], ['four', 'five', 'nine']); + assert.deepEqual(grouped['5'], ['three', 'seven', 'eight']); var context = {}; - _.groupBy([{}], function(){ ok(this === context); }, context); + _.groupBy([{}], function(){ assert.strictEqual(this, context); }, context); grouped = _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; }); - equal(grouped.constructor.length, 1); - equal(grouped.hasOwnProperty.length, 2); + assert.strictEqual(grouped.constructor.length, 1); + assert.strictEqual(grouped.hasOwnProperty.length, 2); var array = [{}]; - _.groupBy(array, function(value, index, obj){ ok(obj === array); }); + _.groupBy(array, function(value, index, obj){ assert.strictEqual(obj, array); }); - var array = [1, 2, 1, 2, 3]; - var grouped = _.groupBy(array); - equal(grouped['1'].length, 2); - equal(grouped['3'].length, 1); + array = [1, 2, 1, 2, 3]; + grouped = _.groupBy(array); + assert.strictEqual(grouped['1'].length, 2); + assert.strictEqual(grouped['3'].length, 1); var matrix = [ - [1,2], - [1,3], - [2,3] + [1, 2], + [1, 3], + [2, 3] ]; - deepEqual(_.groupBy(matrix, 0), {1: [[1,2], [1,3]], 2: [[2,3]]}) - deepEqual(_.groupBy(matrix, 1), {2: [[1,2]], 3: [[1,3], [2,3]]}) + assert.deepEqual(_.groupBy(matrix, 0), {1: [[1, 2], [1, 3]], 2: [[2, 3]]}); + assert.deepEqual(_.groupBy(matrix, 1), {2: [[1, 2]], 3: [[1, 3], [2, 3]]}); + + var liz = {name: 'Liz', stats: {power: 10}}; + var chelsea = {name: 'Chelsea', stats: {power: 10}}; + var jordan = {name: 'Jordan', stats: {power: 6}}; + var collection = [liz, chelsea, jordan]; + var expected = { + 10: [liz, chelsea], + 6: [jordan] + }; + assert.deepEqual(_.groupBy(collection, ['stats', 'power']), expected, 'can group by deep properties'); }); - test('indexBy', function() { - var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; }); - equal(parity['true'], 4); - equal(parity['false'], 5); + QUnit.test('indexBy', function(assert) { + var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; }); + assert.strictEqual(parity['true'], 4); + assert.strictEqual(parity['false'], 5); - var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; + var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']; var grouped = _.indexBy(list, 'length'); - equal(grouped['3'], 'ten'); - equal(grouped['4'], 'nine'); - equal(grouped['5'], 'eight'); + assert.strictEqual(grouped['3'], 'ten'); + assert.strictEqual(grouped['4'], 'nine'); + assert.strictEqual(grouped['5'], 'eight'); var array = [1, 2, 1, 2, 3]; - var grouped = _.indexBy(array); - equal(grouped['1'], 1); - equal(grouped['2'], 2); - equal(grouped['3'], 3); + grouped = _.indexBy(array); + assert.strictEqual(grouped['1'], 1); + assert.strictEqual(grouped['2'], 2); + assert.strictEqual(grouped['3'], 3); }); - test('countBy', function() { - var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; }); - equal(parity['true'], 2); - equal(parity['false'], 3); + QUnit.test('countBy', function(assert) { + var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; }); + assert.strictEqual(parity['true'], 2); + assert.strictEqual(parity['false'], 3); - var list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; + var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten']; var grouped = _.countBy(list, 'length'); - equal(grouped['3'], 4); - equal(grouped['4'], 3); - equal(grouped['5'], 3); + assert.strictEqual(grouped['3'], 4); + assert.strictEqual(grouped['4'], 3); + assert.strictEqual(grouped['5'], 3); var context = {}; - _.countBy([{}], function(){ ok(this === context); }, context); + _.countBy([{}], function(){ assert.strictEqual(this, context); }, context); grouped = _.countBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num) > 4 ? 'hasOwnProperty' : 'constructor'; }); - equal(grouped.constructor, 1); - equal(grouped.hasOwnProperty, 2); + assert.strictEqual(grouped.constructor, 1); + assert.strictEqual(grouped.hasOwnProperty, 2); var array = [{}]; - _.countBy(array, function(value, index, obj){ ok(obj === array); }); + _.countBy(array, function(value, index, obj){ assert.strictEqual(obj, array); }); - var array = [1, 2, 1, 2, 3]; - var grouped = _.countBy(array); - equal(grouped['1'], 2); - equal(grouped['3'], 1); + array = [1, 2, 1, 2, 3]; + grouped = _.countBy(array); + assert.strictEqual(grouped['1'], 2); + assert.strictEqual(grouped['3'], 1); }); - test('sortedIndex', function() { - var numbers = [10, 20, 30, 40, 50], num = 35; - var indexForNum = _.sortedIndex(numbers, num); - equal(indexForNum, 3, '35 should be inserted at index 3'); + QUnit.test('shuffle', function(assert) { + assert.deepEqual(_.shuffle([1]), [1], 'behaves correctly on size 1 arrays'); + var numbers = _.range(20); + var shuffled = _.shuffle(numbers); + assert.notDeepEqual(numbers, shuffled, 'does change the order'); // Chance of false negative: 1 in ~2.4*10^18 + assert.notStrictEqual(numbers, shuffled, 'original object is unmodified'); + assert.deepEqual(numbers, _.sortBy(shuffled), 'contains the same members before and after shuffle'); - var indexFor30 = _.sortedIndex(numbers, 30); - equal(indexFor30, 2, '30 should be inserted at index 2'); - - var objects = [{x: 10}, {x: 20}, {x: 30}, {x: 40}]; - var iterator = function(obj){ return obj.x; }; - strictEqual(_.sortedIndex(objects, {x: 25}, iterator), 2); - strictEqual(_.sortedIndex(objects, {x: 35}, 'x'), 3); - - var context = {1: 2, 2: 3, 3: 4}; - iterator = function(obj){ return this[obj]; }; - strictEqual(_.sortedIndex([1, 3], 2, iterator, context), 1); + shuffled = _.shuffle({a: 1, b: 2, c: 3, d: 4}); + assert.strictEqual(shuffled.length, 4); + assert.deepEqual(shuffled.sort(), [1, 2, 3, 4], 'works on objects'); }); - test('shuffle', function() { + QUnit.test('sample', function(assert) { + assert.strictEqual(_.sample([1]), 1, 'behaves correctly when no second parameter is given'); + assert.deepEqual(_.sample([1, 2, 3], -2), [], 'behaves correctly on negative n'); var numbers = _.range(10); - var shuffled = _.shuffle(numbers).sort(); - notStrictEqual(numbers, shuffled, 'original object is unmodified'); - equal(shuffled.join(','), numbers.join(','), 'contains the same members before and after shuffle'); + var allSampled = _.sample(numbers, 10).sort(); + assert.deepEqual(allSampled, numbers, 'contains the same members before and after sample'); + allSampled = _.sample(numbers, 20).sort(); + assert.deepEqual(allSampled, numbers, 'also works when sampling more objects than are present'); + assert.ok(_.contains(numbers, _.sample(numbers)), 'sampling a single element returns something from the array'); + assert.strictEqual(_.sample([]), void 0, 'sampling empty array with no number returns undefined'); + assert.notStrictEqual(_.sample([], 5), [], 'sampling empty array with a number returns an empty array'); + assert.notStrictEqual(_.sample([1, 2, 3], 0), [], 'sampling an array with 0 picks returns an empty array'); + assert.deepEqual(_.sample([1, 2], -1), [], 'sampling a negative number of picks returns an empty array'); + assert.ok(_.contains([1, 2, 3], _.sample({a: 1, b: 2, c: 3})), 'sample one value from an object'); + var partialSample = _.sample(_.range(1000), 10); + var partialSampleSorted = partialSample.sort(); + assert.notDeepEqual(partialSampleSorted, _.range(10), 'samples from the whole array, not just the beginning'); }); - test('sample', function() { - var numbers = _.range(10); - var all_sampled = _.sample(numbers, 10).sort(); - equal(all_sampled.join(','), numbers.join(','), 'contains the same members before and after sample'); - all_sampled = _.sample(numbers, 20).sort(); - equal(all_sampled.join(','), numbers.join(','), 'also works when sampling more objects than are present'); - ok(_.contains(numbers, _.sample(numbers)), 'sampling a single element returns something from the array'); - strictEqual(_.sample([]), undefined, 'sampling empty array with no number returns undefined'); - notStrictEqual(_.sample([], 5), [], 'sampling empty array with a number returns an empty array'); - notStrictEqual(_.sample([1, 2, 3], 0), [], 'sampling an array with 0 picks returns an empty array'); - deepEqual(_.sample([1, 2], -1), [], 'sampling a negative number of picks returns an empty array'); + QUnit.test('toArray', function(assert) { + assert.notOk(_.isArray(arguments), 'arguments object is not an array'); + assert.ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); + var a = [1, 2, 3]; + assert.notStrictEqual(_.toArray(a), a, 'array is cloned'); + assert.deepEqual(_.toArray(a), [1, 2, 3], 'cloned array contains same elements'); + + var numbers = _.toArray({one: 1, two: 2, three: 3}); + assert.deepEqual(numbers, [1, 2, 3], 'object flattened into array'); + + var hearts = '\uD83D\uDC95'; + var pair = hearts.split(''); + var expected = [pair[0], hearts, '&', hearts, pair[1]]; + assert.deepEqual(_.toArray(expected.join('')), expected, 'maintains astral characters'); + assert.deepEqual(_.toArray(''), [], 'empty string into empty array'); + + if (typeof document != 'undefined') { + // test in IE < 9 + var actual; + try { + actual = _.toArray(document.childNodes); + } catch (e) { /* ignored */ } + assert.deepEqual(actual, _.map(document.childNodes, _.identity), 'works on NodeList'); + } }); - test('toArray', function() { - ok(!_.isArray(arguments), 'arguments object is not an array'); - ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array'); - var a = [1,2,3]; - ok(_.toArray(a) !== a, 'array is cloned'); - equal(_.toArray(a).join(', '), '1, 2, 3', 'cloned array contains same elements'); - - var numbers = _.toArray({one : 1, two : 2, three : 3}); - equal(numbers.join(', '), '1, 2, 3', 'object flattened into array'); - - // test in IE < 9 - try { - var actual = _.toArray(document.childNodes); - } catch(ex) { } - - ok(_.isArray(actual), 'should not throw converting a node list'); - }); - - test('size', function() { - equal(_.size({one : 1, two : 2, three : 3}), 3, 'can compute the size of an object'); - equal(_.size([1, 2, 3]), 3, 'can compute the size of an array'); - equal(_.size($('
    ').add('').add('')), 3, 'can compute the size of jQuery objects'); + QUnit.test('size', function(assert) { + assert.strictEqual(_.size({one: 1, two: 2, three: 3}), 3, 'can compute the size of an object'); + assert.strictEqual(_.size([1, 2, 3]), 3, 'can compute the size of an array'); + assert.strictEqual(_.size({length: 3, 0: 0, 1: 0, 2: 0}), 3, 'can compute the size of Array-likes'); var func = function() { return _.size(arguments); }; - equal(func(1, 2, 3, 4), 4, 'can test the size of the arguments object'); + assert.strictEqual(func(1, 2, 3, 4), 4, 'can test the size of the arguments object'); - equal(_.size('hello'), 5, 'can compute the size of a string literal'); - equal(_.size(new String('hello')), 5, 'can compute the size of string object'); + assert.strictEqual(_.size('hello'), 5, 'can compute the size of a string literal'); + assert.strictEqual(_.size(new String('hello')), 5, 'can compute the size of string object'); - equal(_.size(null), 0, 'handles nulls'); + assert.strictEqual(_.size(null), 0, 'handles nulls'); + assert.strictEqual(_.size(0), 0, 'handles numbers'); }); -}); + QUnit.test('partition', function(assert) { + var list = [0, 1, 2, 3, 4, 5]; + assert.deepEqual(_.partition(list, function(x) { return x < 4; }), [[0, 1, 2, 3], [4, 5]], 'handles bool return values'); + assert.deepEqual(_.partition(list, function(x) { return x & 1; }), [[1, 3, 5], [0, 2, 4]], 'handles 0 and 1 return values'); + assert.deepEqual(_.partition(list, function(x) { return x - 3; }), [[0, 1, 2, 4, 5], [3]], 'handles other numeric return values'); + assert.deepEqual(_.partition(list, function(x) { return x > 1 ? null : true; }), [[0, 1], [2, 3, 4, 5]], 'handles null return values'); + assert.deepEqual(_.partition(list, function(x) { if (x < 2) return true; }), [[0, 1], [2, 3, 4, 5]], 'handles undefined return values'); + assert.deepEqual(_.partition({a: 1, b: 2, c: 3}, function(x) { return x > 1; }), [[2, 3], [1]], 'handles objects'); + + assert.deepEqual(_.partition(list, function(x, index) { return index % 2; }), [[1, 3, 5], [0, 2, 4]], 'can reference the array index'); + assert.deepEqual(_.partition(list, function(x, index, arr) { return x === arr.length - 1; }), [[5], [0, 1, 2, 3, 4]], 'can reference the collection'); + + // Default iterator + assert.deepEqual(_.partition([1, false, true, '']), [[1, true], [false, '']], 'Default iterator'); + assert.deepEqual(_.partition([{x: 1}, {x: 0}, {x: 1}], 'x'), [[{x: 1}, {x: 1}], [{x: 0}]], 'Takes a string'); + + // Context + var predicate = function(x){ return x === this.x; }; + assert.deepEqual(_.partition([1, 2, 3], predicate, {x: 2}), [[2], [1, 3]], 'partition takes a context argument'); + + assert.deepEqual(_.partition([{a: 1}, {b: 2}, {a: 1, b: 2}], {a: 1}), [[{a: 1}, {a: 1, b: 2}], [{b: 2}]], 'predicate can be object'); + + var object = {a: 1}; + _.partition(object, function(val, key, obj) { + assert.strictEqual(val, 1); + assert.strictEqual(key, 'a'); + assert.strictEqual(obj, object); + assert.strictEqual(this, predicate); + }, predicate); + }); + + if (typeof document != 'undefined') { + QUnit.test('Can use various collection methods on NodeLists', function(assert) { + var parent = document.createElement('div'); + parent.innerHTML = 'textnode'; + + var elementChildren = _.filter(parent.childNodes, _.isElement); + assert.strictEqual(elementChildren.length, 2); + + assert.deepEqual(_.map(elementChildren, 'id'), ['id1', 'id2']); + assert.deepEqual(_.map(parent.childNodes, 'nodeType'), [1, 3, 1]); + + assert.notOk(_.every(parent.childNodes, _.isElement)); + assert.ok(_.some(parent.childNodes, _.isElement)); + + function compareNode(node) { + return _.isElement(node) ? node.id.charAt(2) : void 0; + } + assert.strictEqual(_.max(parent.childNodes, compareNode), _.last(parent.childNodes)); + assert.strictEqual(_.min(parent.childNodes, compareNode), _.first(parent.childNodes)); + }); + } + +}()); diff --git a/vendor/underscore/test/cross-document.js b/vendor/underscore/test/cross-document.js new file mode 100644 index 000000000..89642760b --- /dev/null +++ b/vendor/underscore/test/cross-document.js @@ -0,0 +1,142 @@ +(function() { + if (typeof document == 'undefined') return; + + var _ = typeof require == 'function' ? require('..') : window._; + + QUnit.module('Cross Document'); + /* global iObject, iElement, iArguments, iFunction, iArray, iError, iString, iNumber, iBoolean, iDate, iRegExp, iNaN, iNull, iUndefined, ActiveXObject */ + + // Setup remote variables for iFrame tests. + var iframe = document.createElement('iframe'); + iframe.frameBorder = iframe.height = iframe.width = 0; + document.body.appendChild(iframe); + var iframeContent = iframe.contentDocument || iframe.contentWindow; + var iDoc = iframeContent.document || iframeContent; + iDoc.write( + [ + '' + ].join('\n') + ); + iDoc.close(); + + QUnit.test('isEqual', function(assert) { + + assert.notOk(_.isEqual(iNumber, 101)); + assert.ok(_.isEqual(iNumber, 100)); + + // Objects from another frame. + assert.ok(_.isEqual({}, iObject), 'Objects with equivalent members created in different documents are equal'); + + // Array from another frame. + assert.ok(_.isEqual([1, 2, 3], iArray), 'Arrays with equivalent elements created in different documents are equal'); + }); + + QUnit.test('isEmpty', function(assert) { + assert.notOk(_([iNumber]).isEmpty(), '[1] is not empty'); + assert.notOk(_.isEmpty(iArray), '[] is empty'); + assert.ok(_.isEmpty(iObject), '{} is empty'); + }); + + QUnit.test('isElement', function(assert) { + assert.notOk(_.isElement('div'), 'strings are not dom elements'); + assert.ok(_.isElement(document.body), 'the body tag is a DOM element'); + assert.ok(_.isElement(iElement), 'even from another frame'); + }); + + QUnit.test('isArguments', function(assert) { + assert.ok(_.isArguments(iArguments), 'even from another frame'); + }); + + QUnit.test('isObject', function(assert) { + assert.ok(_.isObject(iElement), 'even from another frame'); + assert.ok(_.isObject(iFunction), 'even from another frame'); + }); + + QUnit.test('isArray', function(assert) { + assert.ok(_.isArray(iArray), 'even from another frame'); + }); + + QUnit.test('isString', function(assert) { + assert.ok(_.isString(iString), 'even from another frame'); + }); + + QUnit.test('isNumber', function(assert) { + assert.ok(_.isNumber(iNumber), 'even from another frame'); + }); + + QUnit.test('isBoolean', function(assert) { + assert.ok(_.isBoolean(iBoolean), 'even from another frame'); + }); + + QUnit.test('isFunction', function(assert) { + assert.ok(_.isFunction(iFunction), 'even from another frame'); + }); + + QUnit.test('isDate', function(assert) { + assert.ok(_.isDate(iDate), 'even from another frame'); + }); + + QUnit.test('isRegExp', function(assert) { + assert.ok(_.isRegExp(iRegExp), 'even from another frame'); + }); + + QUnit.test('isNaN', function(assert) { + assert.ok(_.isNaN(iNaN), 'even from another frame'); + }); + + QUnit.test('isNull', function(assert) { + assert.ok(_.isNull(iNull), 'even from another frame'); + }); + + QUnit.test('isUndefined', function(assert) { + assert.ok(_.isUndefined(iUndefined), 'even from another frame'); + }); + + QUnit.test('isError', function(assert) { + assert.ok(_.isError(iError), 'even from another frame'); + }); + + if (typeof ActiveXObject != 'undefined') { + QUnit.test('IE host objects', function(assert) { + var xml = new ActiveXObject('Msxml2.DOMDocument.3.0'); + assert.notOk(_.isNumber(xml)); + assert.notOk(_.isBoolean(xml)); + assert.notOk(_.isNaN(xml)); + assert.notOk(_.isFunction(xml)); + assert.notOk(_.isNull(xml)); + assert.notOk(_.isUndefined(xml)); + }); + + QUnit.test('#1621 IE 11 compat mode DOM elements are not functions', function(assert) { + var fn = function() {}; + var xml = new ActiveXObject('Msxml2.DOMDocument.3.0'); + var div = document.createElement('div'); + + // JIT the function + var count = 200; + while (count--) { + _.isFunction(fn); + } + + assert.strictEqual(_.isFunction(xml), false); + assert.strictEqual(_.isFunction(div), false); + assert.strictEqual(_.isFunction(fn), true); + }); + } + +}()); diff --git a/vendor/underscore/test/functions.js b/vendor/underscore/test/functions.js index 7a773f32e..522a99e48 100644 --- a/vendor/underscore/test/functions.js +++ b/vendor/underscore/test/functions.js @@ -1,162 +1,265 @@ -$(document).ready(function() { +(function() { + var _ = typeof require == 'function' ? require('..') : window._; - module("Functions"); + QUnit.module('Functions'); + QUnit.config.asyncRetries = 3; - test("bind", function() { - var context = {name : 'moe'}; - var func = function(arg) { return "name: " + (this.name || arg); }; + QUnit.test('bind', function(assert) { + var context = {name: 'moe'}; + var func = function(arg) { return 'name: ' + (this.name || arg); }; var bound = _.bind(func, context); - equal(bound(), 'name: moe', 'can bind a function to a context'); + assert.strictEqual(bound(), 'name: moe', 'can bind a function to a context'); bound = _(func).bind(context); - equal(bound(), 'name: moe', 'can do OO-style binding'); + assert.strictEqual(bound(), 'name: moe', 'can do OO-style binding'); bound = _.bind(func, null, 'curly'); - equal(bound(), 'name: curly', 'can bind without specifying a context'); + var result = bound(); + // Work around a PhantomJS bug when applying a function with null|undefined. + assert.ok(result === 'name: curly' || result === 'name: ' + window.name, 'can bind without specifying a context'); func = function(salutation, name) { return salutation + ': ' + name; }; func = _.bind(func, this, 'hello'); - equal(func('moe'), 'hello: moe', 'the function was partially applied in advance'); + assert.strictEqual(func('moe'), 'hello: moe', 'the function was partially applied in advance'); func = _.bind(func, this, 'curly'); - equal(func(), 'hello: curly', 'the function was completely applied in advance'); + assert.strictEqual(func(), 'hello: curly', 'the function was completely applied in advance'); func = function(salutation, firstname, lastname) { return salutation + ': ' + firstname + ' ' + lastname; }; func = _.bind(func, this, 'hello', 'moe', 'curly'); - equal(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); + assert.strictEqual(func(), 'hello: moe curly', 'the function was partially applied in advance and can accept multiple arguments'); - func = function(context, message) { equal(this, context, message); }; - _.bind(func, 0, 0, 'can bind a function to `0`')(); - _.bind(func, '', '', 'can bind a function to an empty string')(); - _.bind(func, false, false, 'can bind a function to `false`')(); + func = function() { return this; }; + assert.strictEqual(typeof _.bind(func, 0)(), 'object', 'binding a primitive to `this` returns a wrapped primitive'); + + assert.strictEqual(_.bind(func, 0)().valueOf(), 0, 'can bind a function to `0`'); + assert.strictEqual(_.bind(func, '')().valueOf(), '', 'can bind a function to an empty string'); + assert.strictEqual(_.bind(func, false)().valueOf(), false, 'can bind a function to `false`'); // These tests are only meaningful when using a browser without a native bind function // To test this with a modern browser, set underscore's nativeBind to undefined - var F = function () { return this; }; - var Boundf = _.bind(F, {hello: "moe curly"}); + var F = function() { return this; }; + var boundf = _.bind(F, {hello: 'moe curly'}); + var Boundf = boundf; // make eslint happy. var newBoundf = new Boundf(); - equal(newBoundf.hello, undefined, "function should not be bound to the context, to comply with ECMAScript 5"); - equal(Boundf().hello, "moe curly", "When called without the new operator, it's OK to be bound to the context"); - ok(newBoundf instanceof F, "a bound instance is an instance of the original function"); + assert.strictEqual(newBoundf.hello, void 0, 'function should not be bound to the context, to comply with ECMAScript 5'); + assert.strictEqual(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context"); + assert.ok(newBoundf instanceof F, 'a bound instance is an instance of the original function'); + + assert.raises(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function'); }); - test("partial", function() { + QUnit.test('partial', function(assert) { var obj = {name: 'moe'}; var func = function() { return this.name + ' ' + _.toArray(arguments).join(' '); }; obj.func = _.partial(func, 'a', 'b'); - equal(obj.func('c', 'd'), 'moe a b c d', 'can partially apply'); + assert.strictEqual(obj.func('c', 'd'), 'moe a b c d', 'can partially apply'); + + obj.func = _.partial(func, _, 'b', _, 'd'); + assert.strictEqual(obj.func('a', 'c'), 'moe a b c d', 'can partially apply with placeholders'); + + func = _.partial(function() { return arguments.length; }, _, 'b', _, 'd'); + assert.strictEqual(func('a', 'c', 'e'), 5, 'accepts more arguments than the number of placeholders'); + assert.strictEqual(func('a'), 4, 'accepts fewer arguments than the number of placeholders'); + + func = _.partial(function() { return typeof arguments[2]; }, _, 'b', _, 'd'); + assert.strictEqual(func('a'), 'undefined', 'unfilled placeholders are undefined'); + + // passes context + function MyWidget(name, options) { + this.name = name; + this.options = options; + } + MyWidget.prototype.get = function() { + return this.name; + }; + var MyWidgetWithCoolOpts = _.partial(MyWidget, _, {a: 1}); + var widget = new MyWidgetWithCoolOpts('foo'); + assert.ok(widget instanceof MyWidget, 'Can partially bind a constructor'); + assert.strictEqual(widget.get(), 'foo', 'keeps prototype'); + assert.deepEqual(widget.options, {a: 1}); + + _.partial.placeholder = obj; + func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd'); + assert.strictEqual(func('a'), 4, 'allows the placeholder to be swapped out'); + + _.partial.placeholder = {}; + func = _.partial(function() { return arguments.length; }, obj, 'b', obj, 'd'); + assert.strictEqual(func('a'), 5, 'swapping the placeholder preserves previously bound arguments'); + + _.partial.placeholder = _; }); - test("bindAll", function() { - var curly = {name : 'curly'}, moe = { - name : 'moe', - getName : function() { return 'name: ' + this.name; }, - sayHi : function() { return 'hi: ' + this.name; } + QUnit.test('bindAll', function(assert) { + var curly = {name: 'curly'}; + var moe = { + name: 'moe', + getName: function() { return 'name: ' + this.name; }, + sayHi: function() { return 'hi: ' + this.name; } }; curly.getName = moe.getName; _.bindAll(moe, 'getName', 'sayHi'); curly.sayHi = moe.sayHi; - equal(curly.getName(), 'name: curly', 'unbound function is bound to current object'); - equal(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); + assert.strictEqual(curly.getName(), 'name: curly', 'unbound function is bound to current object'); + assert.strictEqual(curly.sayHi(), 'hi: moe', 'bound function is still bound to original object'); - curly = {name : 'curly'}; + curly = {name: 'curly'}; moe = { - name : 'moe', - getName : function() { return 'name: ' + this.name; }, - sayHi : function() { return 'hi: ' + this.name; } + name: 'moe', + getName: function() { return 'name: ' + this.name; }, + sayHi: function() { return 'hi: ' + this.name; }, + sayLast: function() { return this.sayHi(_.last(arguments)); } }; - raises(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named'); + assert.raises(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named'); + assert.raises(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined'); + assert.raises(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function'); - _.bindAll(moe, 'sayHi'); + _.bindAll(moe, 'sayHi', 'sayLast'); curly.sayHi = moe.sayHi; - equal(curly.sayHi(), 'hi: moe'); + assert.strictEqual(curly.sayHi(), 'hi: moe'); + + var sayLast = moe.sayLast; + assert.strictEqual(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments'); + + _.bindAll(moe, ['getName']); + var getName = moe.getName; + assert.strictEqual(getName(), 'name: moe', 'flattens arguments into a single list'); }); - test("memoize", function() { + QUnit.test('memoize', function(assert) { var fib = function(n) { return n < 2 ? n : fib(n - 1) + fib(n - 2); }; - equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); + assert.strictEqual(fib(10), 55, 'a memoized version of fibonacci produces identical results'); fib = _.memoize(fib); // Redefine `fib` for memoization - equal(fib(10), 55, 'a memoized version of fibonacci produces identical results'); + assert.strictEqual(fib(10), 55, 'a memoized version of fibonacci produces identical results'); var o = function(str) { return str; }; var fastO = _.memoize(o); - equal(o('toString'), 'toString', 'checks hasOwnProperty'); - equal(fastO('toString'), 'toString', 'checks hasOwnProperty'); + assert.strictEqual(o('toString'), 'toString', 'checks hasOwnProperty'); + assert.strictEqual(fastO('toString'), 'toString', 'checks hasOwnProperty'); + + // Expose the cache. + var upper = _.memoize(function(s) { + return s.toUpperCase(); + }); + assert.strictEqual(upper('foo'), 'FOO'); + assert.strictEqual(upper('bar'), 'BAR'); + assert.deepEqual(upper.cache, {foo: 'FOO', bar: 'BAR'}); + upper.cache = {foo: 'BAR', bar: 'FOO'}; + assert.strictEqual(upper('foo'), 'BAR'); + assert.strictEqual(upper('bar'), 'FOO'); + + var hashed = _.memoize(function(key) { + //https://github.com/jashkenas/underscore/pull/1679#discussion_r13736209 + assert.ok(/[a-z]+/.test(key), 'hasher doesn\'t change keys'); + return key; + }, function(key) { + return key.toUpperCase(); + }); + hashed('yep'); + assert.deepEqual(hashed.cache, {YEP: 'yep'}, 'takes a hasher'); + + // Test that the hash function can be used to swizzle the key. + var objCacher = _.memoize(function(value, key) { + return {key: key, value: value}; + }, function(value, key) { + return key; + }); + var myObj = objCacher('a', 'alpha'); + var myObjAlias = objCacher('b', 'alpha'); + assert.notStrictEqual(myObj, void 0, 'object is created if second argument used as key'); + assert.strictEqual(myObj, myObjAlias, 'object is cached if second argument used as key'); + assert.strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key'); }); - asyncTest("delay", 2, function() { + QUnit.test('delay', function(assert) { + assert.expect(2); + var done = assert.async(); var delayed = false; _.delay(function(){ delayed = true; }, 100); - setTimeout(function(){ ok(!delayed, "didn't delay the function quite yet"); }, 50); - setTimeout(function(){ ok(delayed, 'delayed the function'); start(); }, 150); + setTimeout(function(){ assert.notOk(delayed, "didn't delay the function quite yet"); }, 50); + setTimeout(function(){ assert.ok(delayed, 'delayed the function'); done(); }, 150); }); - asyncTest("defer", 1, function() { + QUnit.test('defer', function(assert) { + assert.expect(1); + var done = assert.async(); var deferred = false; _.defer(function(bool){ deferred = bool; }, true); - _.delay(function(){ ok(deferred, "deferred the function"); start(); }, 50); + _.delay(function(){ assert.ok(deferred, 'deferred the function'); done(); }, 50); }); - asyncTest("throttle", 2, function() { + QUnit.test('throttle', function(assert) { + assert.expect(2); + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32); throttledIncr(); throttledIncr(); - equal(counter, 1, "incr was called immediately"); - _.delay(function(){ equal(counter, 2, "incr was throttled"); start(); }, 64); + assert.strictEqual(counter, 1, 'incr was called immediately'); + _.delay(function(){ assert.strictEqual(counter, 2, 'incr was throttled'); done(); }, 64); }); - asyncTest("throttle arguments", 2, function() { + QUnit.test('throttle arguments', function(assert) { + assert.expect(2); + var done = assert.async(); var value = 0; var update = function(val){ value = val; }; var throttledUpdate = _.throttle(update, 32); throttledUpdate(1); throttledUpdate(2); _.delay(function(){ throttledUpdate(3); }, 64); - equal(value, 1, "updated to latest value"); - _.delay(function(){ equal(value, 3, "updated to latest value"); start(); }, 96); + assert.strictEqual(value, 1, 'updated to latest value'); + _.delay(function(){ assert.strictEqual(value, 3, 'updated to latest value'); done(); }, 96); }); - asyncTest("throttle once", 2, function() { + QUnit.test('throttle once', function(assert) { + assert.expect(2); + var done = assert.async(); var counter = 0; var incr = function(){ return ++counter; }; var throttledIncr = _.throttle(incr, 32); var result = throttledIncr(); _.delay(function(){ - equal(result, 1, "throttled functions return their value"); - equal(counter, 1, "incr was called once"); start(); + assert.strictEqual(result, 1, 'throttled functions return their value'); + assert.strictEqual(counter, 1, 'incr was called once'); done(); }, 64); }); - asyncTest("throttle twice", 1, function() { + QUnit.test('throttle twice', function(assert) { + assert.expect(1); + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32); throttledIncr(); throttledIncr(); - _.delay(function(){ equal(counter, 2, "incr was called twice"); start(); }, 64); + _.delay(function(){ assert.strictEqual(counter, 2, 'incr was called twice'); done(); }, 64); }); - asyncTest("more throttling", 3, function() { + QUnit.test('more throttling', function(assert) { + assert.expect(3); + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 30); throttledIncr(); throttledIncr(); - ok(counter == 1); + assert.strictEqual(counter, 1); _.delay(function(){ - ok(counter == 2); + assert.strictEqual(counter, 2); throttledIncr(); - ok(counter == 3); - start(); + assert.strictEqual(counter, 3); + done(); }, 85); }); - asyncTest("throttle repeatedly with results", 6, function() { + QUnit.test('throttle repeatedly with results', function(assert) { + assert.expect(6); + var done = assert.async(); var counter = 0; var incr = function(){ return ++counter; }; var throttledIncr = _.throttle(incr, 100); @@ -168,50 +271,56 @@ $(document).ready(function() { _.delay(saveResult, 160); _.delay(saveResult, 230); _.delay(function() { - equal(results[0], 1, "incr was called once"); - equal(results[1], 1, "incr was throttled"); - equal(results[2], 1, "incr was throttled"); - equal(results[3], 2, "incr was called twice"); - equal(results[4], 2, "incr was throttled"); - equal(results[5], 3, "incr was called trailing"); - start(); + assert.strictEqual(results[0], 1, 'incr was called once'); + assert.strictEqual(results[1], 1, 'incr was throttled'); + assert.strictEqual(results[2], 1, 'incr was throttled'); + assert.strictEqual(results[3], 2, 'incr was called twice'); + assert.strictEqual(results[4], 2, 'incr was throttled'); + assert.strictEqual(results[5], 3, 'incr was called trailing'); + done(); }, 300); }); - asyncTest("throttle triggers trailing call when invoked repeatedly", 2, function() { + QUnit.test('throttle triggers trailing call when invoked repeatedly', function(assert) { + assert.expect(2); + var done = assert.async(); var counter = 0; var limit = 48; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 32); var stamp = new Date; - while ((new Date - stamp) < limit) { + while (new Date - stamp < limit) { throttledIncr(); } var lastCount = counter; - ok(counter > 1); + assert.ok(counter > 1); _.delay(function() { - ok(counter > lastCount); - start(); + assert.ok(counter > lastCount); + done(); }, 96); }); - asyncTest("throttle does not trigger leading call when leading is set to false", 2, function() { + QUnit.test('throttle does not trigger leading call when leading is set to false', function(assert) { + assert.expect(2); + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 60, {leading: false}); throttledIncr(); throttledIncr(); - ok(counter === 0); + assert.strictEqual(counter, 0); _.delay(function() { - ok(counter == 1); - start(); + assert.strictEqual(counter, 1); + done(); }, 96); }); - asyncTest("more throttle does not trigger leading call when leading is set to false", 3, function() { + QUnit.test('more throttle does not trigger leading call when leading is set to false', function(assert) { + assert.expect(3); + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 100, {leading: false}); @@ -220,133 +329,325 @@ $(document).ready(function() { _.delay(throttledIncr, 50); _.delay(throttledIncr, 60); _.delay(throttledIncr, 200); - ok(counter === 0); + assert.strictEqual(counter, 0); _.delay(function() { - ok(counter == 1); + assert.strictEqual(counter, 1); }, 250); _.delay(function() { - ok(counter == 2); - start(); + assert.strictEqual(counter, 2); + done(); }, 350); }); - asyncTest("one more throttle with leading: false test", 2, function() { + QUnit.test('one more throttle with leading: false test', function(assert) { + assert.expect(2); + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 100, {leading: false}); var time = new Date; while (new Date - time < 350) throttledIncr(); - ok(counter <= 3); + assert.ok(counter <= 3); _.delay(function() { - ok(counter <= 4); - start(); + assert.ok(counter <= 4); + done(); }, 200); }); - asyncTest("throttle does not trigger trailing call when trailing is set to false", 4, function() { + QUnit.test('throttle does not trigger trailing call when trailing is set to false', function(assert) { + assert.expect(4); + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var throttledIncr = _.throttle(incr, 60, {trailing: false}); throttledIncr(); throttledIncr(); throttledIncr(); - ok(counter === 1); + assert.strictEqual(counter, 1); _.delay(function() { - ok(counter == 1); + assert.strictEqual(counter, 1); throttledIncr(); throttledIncr(); - ok(counter == 2); + assert.strictEqual(counter, 2); _.delay(function() { - ok(counter == 2); - start(); + assert.strictEqual(counter, 2); + done(); }, 96); }, 96); }); - asyncTest("debounce", 1, function() { + QUnit.test('throttle continues to function after system time is set backwards', function(assert) { + assert.expect(2); + var done = assert.async(); + var counter = 0; + var incr = function(){ counter++; }; + var throttledIncr = _.throttle(incr, 100); + var origNowFunc = _.now; + + throttledIncr(); + assert.strictEqual(counter, 1); + _.now = function() { + return new Date(2013, 0, 1, 1, 1, 1); + }; + + _.delay(function() { + throttledIncr(); + assert.strictEqual(counter, 2); + done(); + _.now = origNowFunc; + }, 200); + }); + + QUnit.test('throttle re-entrant', function(assert) { + assert.expect(2); + var done = assert.async(); + var sequence = [ + ['b1', 'b2'], + ['c1', 'c2'] + ]; + var value = ''; + var throttledAppend; + var append = function(arg){ + value += this + arg; + var args = sequence.pop(); + if (args) { + throttledAppend.call(args[0], args[1]); + } + }; + throttledAppend = _.throttle(append, 32); + throttledAppend.call('a1', 'a2'); + assert.strictEqual(value, 'a1a2'); + _.delay(function(){ + assert.strictEqual(value, 'a1a2c1c2b1b2', 'append was throttled successfully'); + done(); + }, 100); + }); + + QUnit.test('throttle cancel', function(assert) { + var done = assert.async(); + var counter = 0; + var incr = function(){ counter++; }; + var throttledIncr = _.throttle(incr, 32); + throttledIncr(); + throttledIncr.cancel(); + throttledIncr(); + throttledIncr(); + + assert.strictEqual(counter, 2, 'incr was called immediately'); + _.delay(function(){ assert.strictEqual(counter, 3, 'incr was throttled'); done(); }, 64); + }); + + QUnit.test('throttle cancel with leading: false', function(assert) { + var done = assert.async(); + var counter = 0; + var incr = function(){ counter++; }; + var throttledIncr = _.throttle(incr, 32, {leading: false}); + throttledIncr(); + throttledIncr.cancel(); + + assert.strictEqual(counter, 0, 'incr was throttled'); + _.delay(function(){ assert.strictEqual(counter, 0, 'incr was throttled'); done(); }, 64); + }); + + QUnit.test('debounce', function(assert) { + assert.expect(1); + var done = assert.async(); var counter = 0; var incr = function(){ counter++; }; var debouncedIncr = _.debounce(incr, 32); debouncedIncr(); debouncedIncr(); _.delay(debouncedIncr, 16); - _.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 96); + _.delay(function(){ assert.strictEqual(counter, 1, 'incr was debounced'); done(); }, 96); }); - asyncTest("debounce asap", 4, function() { - var a, b; + QUnit.test('debounce cancel', function(assert) { + assert.expect(1); + var done = assert.async(); + var counter = 0; + var incr = function(){ counter++; }; + var debouncedIncr = _.debounce(incr, 32); + debouncedIncr(); + debouncedIncr.cancel(); + _.delay(function(){ assert.strictEqual(counter, 0, 'incr was not called'); done(); }, 96); + }); + + QUnit.test('debounce asap', function(assert) { + assert.expect(6); + var done = assert.async(); + var a, b, c; var counter = 0; var incr = function(){ return ++counter; }; var debouncedIncr = _.debounce(incr, 64, true); a = debouncedIncr(); b = debouncedIncr(); - equal(a, 1); - equal(b, 1); - equal(counter, 1, 'incr was called immediately'); + assert.strictEqual(a, 1); + assert.strictEqual(b, 1); + assert.strictEqual(counter, 1, 'incr was called immediately'); _.delay(debouncedIncr, 16); _.delay(debouncedIncr, 32); _.delay(debouncedIncr, 48); - _.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 128); + _.delay(function(){ + assert.strictEqual(counter, 1, 'incr was debounced'); + c = debouncedIncr(); + assert.strictEqual(c, 2); + assert.strictEqual(counter, 2, 'incr was called again'); + done(); + }, 128); }); - asyncTest("debounce asap recursively", 2, function() { + QUnit.test('debounce asap cancel', function(assert) { + assert.expect(4); + var done = assert.async(); + var a, b; + var counter = 0; + var incr = function(){ return ++counter; }; + var debouncedIncr = _.debounce(incr, 64, true); + a = debouncedIncr(); + debouncedIncr.cancel(); + b = debouncedIncr(); + assert.strictEqual(a, 1); + assert.strictEqual(b, 2); + assert.strictEqual(counter, 2, 'incr was called immediately'); + _.delay(debouncedIncr, 16); + _.delay(debouncedIncr, 32); + _.delay(debouncedIncr, 48); + _.delay(function(){ assert.strictEqual(counter, 2, 'incr was debounced'); done(); }, 128); + }); + + QUnit.test('debounce asap recursively', function(assert) { + assert.expect(2); + var done = assert.async(); var counter = 0; var debouncedIncr = _.debounce(function(){ counter++; if (counter < 10) debouncedIncr(); }, 32, true); debouncedIncr(); - equal(counter, 1, "incr was called immediately"); - _.delay(function(){ equal(counter, 1, "incr was debounced"); start(); }, 96); + assert.strictEqual(counter, 1, 'incr was called immediately'); + _.delay(function(){ assert.strictEqual(counter, 1, 'incr was debounced'); done(); }, 96); }); - test("once", function() { + QUnit.test('debounce after system time is set backwards', function(assert) { + assert.expect(2); + var done = assert.async(); + var counter = 0; + var origNowFunc = _.now; + var debouncedIncr = _.debounce(function(){ + counter++; + }, 100, true); + + debouncedIncr(); + assert.strictEqual(counter, 1, 'incr was called immediately'); + + _.now = function() { + return new Date(2013, 0, 1, 1, 1, 1); + }; + + _.delay(function() { + debouncedIncr(); + assert.strictEqual(counter, 2, 'incr was debounced successfully'); + done(); + _.now = origNowFunc; + }, 200); + }); + + QUnit.test('debounce re-entrant', function(assert) { + assert.expect(2); + var done = assert.async(); + var sequence = [ + ['b1', 'b2'] + ]; + var value = ''; + var debouncedAppend; + var append = function(arg){ + value += this + arg; + var args = sequence.pop(); + if (args) { + debouncedAppend.call(args[0], args[1]); + } + }; + debouncedAppend = _.debounce(append, 32); + debouncedAppend.call('a1', 'a2'); + assert.strictEqual(value, ''); + _.delay(function(){ + assert.strictEqual(value, 'a1a2b1b2', 'append was debounced successfully'); + done(); + }, 100); + }); + + QUnit.test('once', function(assert) { var num = 0; - var increment = _.once(function(){ num++; }); + var increment = _.once(function(){ return ++num; }); increment(); increment(); - equal(num, 1); + assert.strictEqual(num, 1); + + assert.strictEqual(increment(), 1, 'stores a memo to the last value'); }); - test("Recursive onced function.", 1, function() { + QUnit.test('Recursive onced function.', function(assert) { + assert.expect(1); var f = _.once(function(){ - ok(true); + assert.ok(true); f(); }); f(); }); - test("wrap", function() { - var greet = function(name){ return "hi: " + name; }; + QUnit.test('wrap', function(assert) { + var greet = function(name){ return 'hi: ' + name; }; var backwards = _.wrap(greet, function(func, name){ return func(name) + ' ' + name.split('').reverse().join(''); }); - equal(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function'); + assert.strictEqual(backwards('moe'), 'hi: moe eom', 'wrapped the salutation function'); - var inner = function(){ return "Hello "; }; - var obj = {name : "Moe"}; - obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); - equal(obj.hi(), "Hello Moe"); + var inner = function(){ return 'Hello '; }; + var obj = {name: 'Moe'}; + obj.hi = _.wrap(inner, function(fn){ return fn() + this.name; }); + assert.strictEqual(obj.hi(), 'Hello Moe'); - var noop = function(){}; - var wrapped = _.wrap(noop, function(fn){ return Array.prototype.slice.call(arguments, 0); }); - var ret = wrapped(['whats', 'your'], 'vector', 'victor'); - deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); + var noop = function(){}; + var wrapped = _.wrap(noop, function(){ return Array.prototype.slice.call(arguments, 0); }); + var ret = wrapped(['whats', 'your'], 'vector', 'victor'); + assert.deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']); }); - test("compose", function() { - var greet = function(name){ return "hi: " + name; }; + QUnit.test('negate', function(assert) { + var isOdd = function(n){ return n & 1; }; + assert.strictEqual(_.negate(isOdd)(2), true, 'should return the complement of the given function'); + assert.strictEqual(_.negate(isOdd)(3), false, 'should return the complement of the given function'); + }); + + QUnit.test('compose', function(assert) { + var greet = function(name){ return 'hi: ' + name; }; var exclaim = function(sentence){ return sentence + '!'; }; var composed = _.compose(exclaim, greet); - equal(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); + assert.strictEqual(composed('moe'), 'hi: moe!', 'can compose a function that takes another'); composed = _.compose(greet, exclaim); - equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); + assert.strictEqual(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative'); + + // f(g(h(x, y, z))) + function h(x, y, z) { + assert.strictEqual(arguments.length, 3, 'First function called with multiple args'); + return z * y; + } + function g(x) { + assert.strictEqual(arguments.length, 1, 'Composed function is called with 1 argument'); + return x; + } + function f(x) { + assert.strictEqual(arguments.length, 1, 'Composed function is called with 1 argument'); + return x * 2; + } + composed = _.compose(f, g, h); + assert.strictEqual(composed(1, 2, 3), 12); }); - test("after", function() { + QUnit.test('after', function(assert) { var testAfter = function(afterAmount, timesCalled) { var afterCalled = 0; var after = _.after(afterAmount, function() { @@ -356,10 +657,114 @@ $(document).ready(function() { return afterCalled; }; - equal(testAfter(5, 5), 1, "after(N) should fire after being called N times"); - equal(testAfter(5, 4), 0, "after(N) should not fire unless called N times"); - equal(testAfter(0, 0), 0, "after(0) should not fire immediately"); - equal(testAfter(0, 1), 1, "after(0) should fire when first invoked"); + assert.strictEqual(testAfter(5, 5), 1, 'after(N) should fire after being called N times'); + assert.strictEqual(testAfter(5, 4), 0, 'after(N) should not fire unless called N times'); + assert.strictEqual(testAfter(0, 0), 0, 'after(0) should not fire immediately'); + assert.strictEqual(testAfter(0, 1), 1, 'after(0) should fire when first invoked'); }); -}); + QUnit.test('before', function(assert) { + var testBefore = function(beforeAmount, timesCalled) { + var beforeCalled = 0; + var before = _.before(beforeAmount, function() { beforeCalled++; }); + while (timesCalled--) before(); + return beforeCalled; + }; + + assert.strictEqual(testBefore(5, 5), 4, 'before(N) should not fire after being called N times'); + assert.strictEqual(testBefore(5, 4), 4, 'before(N) should fire before being called N times'); + assert.strictEqual(testBefore(0, 0), 0, 'before(0) should not fire immediately'); + assert.strictEqual(testBefore(0, 1), 0, 'before(0) should not fire when first invoked'); + + var context = {num: 0}; + var increment = _.before(3, function(){ return ++this.num; }); + _.times(10, increment, context); + assert.strictEqual(increment(), 2, 'stores a memo to the last value'); + assert.strictEqual(context.num, 2, 'provides context'); + }); + + QUnit.test('iteratee', function(assert) { + var identity = _.iteratee(); + assert.strictEqual(identity, _.identity, '_.iteratee is exposed as an external function.'); + + function fn() { + return arguments; + } + _.each([_.iteratee(fn), _.iteratee(fn, {})], function(cb) { + assert.strictEqual(cb().length, 0); + assert.deepEqual(_.toArray(cb(1, 2, 3)), _.range(1, 4)); + assert.deepEqual(_.toArray(cb(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)), _.range(1, 11)); + }); + + var deepProperty = _.iteratee(['a', 'b']); + assert.strictEqual(deepProperty({a: {b: 2}}), 2, 'treats an array as a deep property accessor'); + + // Test custom iteratee + var builtinIteratee = _.iteratee; + _.iteratee = function(value) { + // RegEx values return a function that returns the number of matches + if (_.isRegExp(value)) return function(obj) { + return (obj.match(value) || []).length; + }; + return value; + }; + + var collection = ['foo', 'bar', 'bbiz']; + + // Test all methods that claim to be transformed through `_.iteratee` + assert.deepEqual(_.countBy(collection, /b/g), {0: 1, 1: 1, 2: 1}); + assert.strictEqual(_.every(collection, /b/g), false); + assert.deepEqual(_.filter(collection, /b/g), ['bar', 'bbiz']); + assert.strictEqual(_.find(collection, /b/g), 'bar'); + assert.strictEqual(_.findIndex(collection, /b/g), 1); + assert.strictEqual(_.findKey(collection, /b/g), '1'); + assert.strictEqual(_.findLastIndex(collection, /b/g), 2); + assert.deepEqual(_.groupBy(collection, /b/g), {0: ['foo'], 1: ['bar'], 2: ['bbiz']}); + assert.deepEqual(_.indexBy(collection, /b/g), {0: 'foo', 1: 'bar', 2: 'bbiz'}); + assert.deepEqual(_.map(collection, /b/g), [0, 1, 2]); + assert.strictEqual(_.max(collection, /b/g), 'bbiz'); + assert.strictEqual(_.min(collection, /b/g), 'foo'); + assert.deepEqual(_.partition(collection, /b/g), [['bar', 'bbiz'], ['foo']]); + assert.deepEqual(_.reject(collection, /b/g), ['foo']); + assert.strictEqual(_.some(collection, /b/g), true); + assert.deepEqual(_.sortBy(collection, /b/g), ['foo', 'bar', 'bbiz']); + assert.strictEqual(_.sortedIndex(collection, 'blah', /b/g), 1); + assert.deepEqual(_.uniq(collection, /b/g), ['foo', 'bar', 'bbiz']); + + var objCollection = {a: 'foo', b: 'bar', c: 'bbiz'}; + assert.deepEqual(_.mapObject(objCollection, /b/g), {a: 0, b: 1, c: 2}); + + // Restore the builtin iteratee + _.iteratee = builtinIteratee; + }); + + QUnit.test('restArgs', function(assert) { + assert.expect(10); + _.restArgs(function(a, args) { + assert.strictEqual(a, 1); + assert.deepEqual(args, [2, 3], 'collects rest arguments into an array'); + })(1, 2, 3); + + _.restArgs(function(a, args) { + assert.strictEqual(a, void 0); + assert.deepEqual(args, [], 'passes empty array if there are not enough arguments'); + })(); + + _.restArgs(function(a, b, c, args) { + assert.strictEqual(arguments.length, 4); + assert.deepEqual(args, [4, 5], 'works on functions with many named parameters'); + })(1, 2, 3, 4, 5); + + var obj = {}; + _.restArgs(function() { + assert.strictEqual(this, obj, 'invokes function with this context'); + }).call(obj); + + _.restArgs(function(array, iteratee, context) { + assert.deepEqual(array, [1, 2, 3, 4], 'startIndex can be used manually specify index of rest parameter'); + assert.strictEqual(iteratee, void 0); + assert.strictEqual(context, void 0); + }, 0)(1, 2, 3, 4); + }); + +}()); diff --git a/vendor/underscore/test/index.html b/vendor/underscore/test/index.html index ea7a13603..ab523afe4 100644 --- a/vendor/underscore/test/index.html +++ b/vendor/underscore/test/index.html @@ -3,42 +3,21 @@ Underscore Test Suite - + + + +
    + - + - + + - - - -
    -
    -
    -
    -
    -
    -
    -
    -

    Underscore Speed Suite

    -

    - A representative sample of the functions are benchmarked here, to provide - a sense of how fast they might run in different browsers. - Each iteration runs on an array of 1000 elements.

    - For example, the 'intersection' test measures the number of times you can - find the intersection of two thousand-element arrays in one second. -

    -
    - diff --git a/vendor/underscore/test/objects.js b/vendor/underscore/test/objects.js index 492171e06..1abdce21f 100644 --- a/vendor/underscore/test/objects.js +++ b/vendor/underscore/test/objects.js @@ -1,135 +1,318 @@ -$(document).ready(function() { +(function() { + var _ = typeof require == 'function' ? require('..') : window._; - module("Objects"); + QUnit.module('Objects'); - test("keys", function() { - equal(_.keys({one : 1, two : 2}).join(', '), 'one, two', 'can extract the keys from an object'); + var testElement = typeof document === 'object' ? document.createElement('div') : void 0; + + QUnit.test('keys', function(assert) { + assert.deepEqual(_.keys({one: 1, two: 2}), ['one', 'two'], 'can extract the keys from an object'); // the test above is not safe because it relies on for-in enumeration order var a = []; a[1] = 0; - equal(_.keys(a).join(', '), '1', 'is not fooled by sparse arrays; see issue #95'); - raises(function() { _.keys(null); }, TypeError, 'throws an error for `null` values'); - raises(function() { _.keys(void 0); }, TypeError, 'throws an error for `undefined` values'); - raises(function() { _.keys(1); }, TypeError, 'throws an error for number primitives'); - raises(function() { _.keys('a'); }, TypeError, 'throws an error for string primitives'); - raises(function() { _.keys(true); }, TypeError, 'throws an error for boolean primitives'); + assert.deepEqual(_.keys(a), ['1'], 'is not fooled by sparse arrays; see issue #95'); + assert.deepEqual(_.keys(null), []); + assert.deepEqual(_.keys(void 0), []); + assert.deepEqual(_.keys(1), []); + assert.deepEqual(_.keys('a'), []); + assert.deepEqual(_.keys(true), []); + + // keys that may be missed if the implementation isn't careful + var trouble = { + constructor: Object, + valueOf: _.noop, + hasOwnProperty: null, + toString: 5, + toLocaleString: void 0, + propertyIsEnumerable: /a/, + isPrototypeOf: this, + __defineGetter__: Boolean, + __defineSetter__: {}, + __lookupSetter__: false, + __lookupGetter__: [] + }; + var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable', + 'isPrototypeOf', '__defineGetter__', '__defineSetter__', '__lookupSetter__', '__lookupGetter__'].sort(); + assert.deepEqual(_.keys(trouble).sort(), troubleKeys, 'matches non-enumerable properties'); }); - test("values", function() { - equal(_.values({one: 1, two: 2}).join(', '), '1, 2', 'can extract the values from an object'); - equal(_.values({one: 1, two: 2, length: 3}).join(', '), '1, 2, 3', '... even when one of them is "length"'); + QUnit.test('allKeys', function(assert) { + assert.deepEqual(_.allKeys({one: 1, two: 2}), ['one', 'two'], 'can extract the allKeys from an object'); + // the test above is not safe because it relies on for-in enumeration order + var a = []; a[1] = 0; + assert.deepEqual(_.allKeys(a), ['1'], 'is not fooled by sparse arrays; see issue #95'); + + a.a = a; + assert.deepEqual(_.allKeys(a), ['1', 'a'], 'is not fooled by sparse arrays with additional properties'); + + _.each([null, void 0, 1, 'a', true, NaN, {}, [], new Number(5), new Date(0)], function(val) { + assert.deepEqual(_.allKeys(val), []); + }); + + // allKeys that may be missed if the implementation isn't careful + var trouble = { + constructor: Object, + valueOf: _.noop, + hasOwnProperty: null, + toString: 5, + toLocaleString: void 0, + propertyIsEnumerable: /a/, + isPrototypeOf: this + }; + var troubleKeys = ['constructor', 'valueOf', 'hasOwnProperty', 'toString', 'toLocaleString', 'propertyIsEnumerable', + 'isPrototypeOf'].sort(); + assert.deepEqual(_.allKeys(trouble).sort(), troubleKeys, 'matches non-enumerable properties'); + + function A() {} + A.prototype.foo = 'foo'; + var b = new A(); + b.bar = 'bar'; + assert.deepEqual(_.allKeys(b).sort(), ['bar', 'foo'], 'should include inherited keys'); + + function y() {} + y.x = 'z'; + assert.deepEqual(_.allKeys(y), ['x'], 'should get keys from constructor'); }); - test("pairs", function() { - deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs'); - deepEqual(_.pairs({one: 1, two: 2, length: 3}), [['one', 1], ['two', 2], ['length', 3]], '... even when one of them is "length"'); + QUnit.test('values', function(assert) { + assert.deepEqual(_.values({one: 1, two: 2}), [1, 2], 'can extract the values from an object'); + assert.deepEqual(_.values({one: 1, two: 2, length: 3}), [1, 2, 3], '... even when one of them is "length"'); }); - test("invert", function() { + QUnit.test('pairs', function(assert) { + assert.deepEqual(_.pairs({one: 1, two: 2}), [['one', 1], ['two', 2]], 'can convert an object into pairs'); + assert.deepEqual(_.pairs({one: 1, two: 2, length: 3}), [['one', 1], ['two', 2], ['length', 3]], '... even when one of them is "length"'); + }); + + QUnit.test('invert', function(assert) { var obj = {first: 'Moe', second: 'Larry', third: 'Curly'}; - equal(_.keys(_.invert(obj)).join(' '), 'Moe Larry Curly', 'can invert an object'); - ok(_.isEqual(_.invert(_.invert(obj)), obj), 'two inverts gets you back where you started'); + assert.deepEqual(_.keys(_.invert(obj)), ['Moe', 'Larry', 'Curly'], 'can invert an object'); + assert.deepEqual(_.invert(_.invert(obj)), obj, 'two inverts gets you back where you started'); - var obj = {length: 3}; - ok(_.invert(obj)['3'] == 'length', 'can invert an object with "length"') + obj = {length: 3}; + assert.strictEqual(_.invert(obj)['3'], 'length', 'can invert an object with "length"'); }); - test("functions", function() { - var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce}; - ok(_.isEqual(['b', 'd'], _.functions(obj)), 'can grab the function names of any passed-in object'); + QUnit.test('functions', function(assert) { + var obj = {a: 'dash', b: _.map, c: /yo/, d: _.reduce}; + assert.deepEqual(['b', 'd'], _.functions(obj), 'can grab the function names of any passed-in object'); var Animal = function(){}; Animal.prototype.run = function(){}; - equal(_.functions(new Animal).join(''), 'run', 'also looks up functions on the prototype'); + assert.deepEqual(_.functions(new Animal), ['run'], 'also looks up functions on the prototype'); }); - test("extend", function() { + QUnit.test('methods', function(assert) { + assert.strictEqual(_.methods, _.functions, 'is an alias for functions'); + }); + + QUnit.test('extend', function(assert) { var result; - equal(_.extend({}, {a:'b'}).a, 'b', 'can extend an object with the attributes of another'); - equal(_.extend({a:'x'}, {a:'b'}).a, 'b', 'properties in source override destination'); - equal(_.extend({x:'x'}, {a:'b'}).x, 'x', "properties not in source don't get overriden"); - result = _.extend({x:'x'}, {a:'a'}, {b:'b'}); - ok(_.isEqual(result, {x:'x', a:'a', b:'b'}), 'can extend from multiple source objects'); - result = _.extend({x:'x'}, {a:'a', x:2}, {a:'b'}); - ok(_.isEqual(result, {x:2, a:'b'}), 'extending from multiple source objects last property trumps'); + assert.strictEqual(_.extend({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another'); + assert.strictEqual(_.extend({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination'); + assert.strictEqual(_.extend({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overridden"); + result = _.extend({x: 'x'}, {a: 'a'}, {b: 'b'}); + assert.deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can extend from multiple source objects'); + result = _.extend({x: 'x'}, {a: 'a', x: 2}, {a: 'b'}); + assert.deepEqual(result, {x: 2, a: 'b'}, 'extending from multiple source objects last property trumps'); result = _.extend({}, {a: void 0, b: null}); - equal(_.keys(result).join(''), 'ab', 'extend copies undefined values'); + assert.deepEqual(_.keys(result), ['a', 'b'], 'extend copies undefined values'); + + var F = function() {}; + F.prototype = {a: 'b'}; + var subObj = new F(); + subObj.c = 'd'; + assert.deepEqual(_.extend({}, subObj), {a: 'b', c: 'd'}, 'extend copies all properties from source'); + _.extend(subObj, {}); + assert.notOk(subObj.hasOwnProperty('a'), "extend does not convert destination object's 'in' properties to 'own' properties"); try { result = {}; - _.extend(result, null, undefined, {a:1}); - } catch(ex) {} + _.extend(result, null, void 0, {a: 1}); + } catch (e) { /* ignored */ } - equal(result.a, 1, 'should not error on `null` or `undefined` sources'); + assert.strictEqual(result.a, 1, 'should not error on `null` or `undefined` sources'); + + assert.strictEqual(_.extend(null, {a: 1}), null, 'extending null results in null'); + assert.strictEqual(_.extend(void 0, {a: 1}), void 0, 'extending undefined results in undefined'); }); - test("pick", function() { + QUnit.test('extendOwn', function(assert) { var result; - result = _.pick({a:1, b:2, c:3}, 'a', 'c'); - ok(_.isEqual(result, {a:1, c:3}), 'can restrict properties to those named'); - result = _.pick({a:1, b:2, c:3}, ['b', 'c']); - ok(_.isEqual(result, {b:2, c:3}), 'can restrict properties to those named in an array'); - result = _.pick({a:1, b:2, c:3}, ['a'], 'b'); - ok(_.isEqual(result, {a:1, b:2}), 'can restrict properties to those named in mixed args'); + assert.strictEqual(_.extendOwn({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another'); + assert.strictEqual(_.extendOwn({a: 'x'}, {a: 'b'}).a, 'b', 'properties in source override destination'); + assert.strictEqual(_.extendOwn({x: 'x'}, {a: 'b'}).x, 'x', "properties not in source don't get overridden"); + result = _.extendOwn({x: 'x'}, {a: 'a'}, {b: 'b'}); + assert.deepEqual(result, {x: 'x', a: 'a', b: 'b'}, 'can extend from multiple source objects'); + result = _.extendOwn({x: 'x'}, {a: 'a', x: 2}, {a: 'b'}); + assert.deepEqual(result, {x: 2, a: 'b'}, 'extending from multiple source objects last property trumps'); + assert.deepEqual(_.extendOwn({}, {a: void 0, b: null}), {a: void 0, b: null}, 'copies undefined values'); + + var F = function() {}; + F.prototype = {a: 'b'}; + var subObj = new F(); + subObj.c = 'd'; + assert.deepEqual(_.extendOwn({}, subObj), {c: 'd'}, 'copies own properties from source'); + + result = {}; + assert.deepEqual(_.extendOwn(result, null, void 0, {a: 1}), {a: 1}, 'should not error on `null` or `undefined` sources'); + + _.each(['a', 5, null, false], function(val) { + assert.strictEqual(_.extendOwn(val, {a: 1}), val, 'extending non-objects results in returning the non-object value'); + }); + + assert.strictEqual(_.extendOwn(void 0, {a: 1}), void 0, 'extending undefined results in undefined'); + + result = _.extendOwn({a: 1, 0: 2, 1: '5', length: 6}, {0: 1, 1: 2, length: 2}); + assert.deepEqual(result, {a: 1, 0: 1, 1: 2, length: 2}, 'should treat array-like objects like normal objects'); + }); + + QUnit.test('assign', function(assert) { + assert.strictEqual(_.assign, _.extendOwn, 'is an alias for extendOwn'); + }); + + QUnit.test('pick', function(assert) { + var result; + result = _.pick({a: 1, b: 2, c: 3}, 'a', 'c'); + assert.deepEqual(result, {a: 1, c: 3}, 'can restrict properties to those named'); + result = _.pick({a: 1, b: 2, c: 3}, ['b', 'c']); + assert.deepEqual(result, {b: 2, c: 3}, 'can restrict properties to those named in an array'); + result = _.pick({a: 1, b: 2, c: 3}, ['a'], 'b'); + assert.deepEqual(result, {a: 1, b: 2}, 'can restrict properties to those named in mixed args'); + result = _.pick(['a', 'b'], 1); + assert.deepEqual(result, {1: 'b'}, 'can pick numeric properties'); + + _.each([null, void 0], function(val) { + assert.deepEqual(_.pick(val, 'hasOwnProperty'), {}, 'Called with null/undefined'); + assert.deepEqual(_.pick(val, _.constant(true)), {}); + }); + assert.deepEqual(_.pick(5, 'toString', 'b'), {toString: Number.prototype.toString}, 'can iterate primitives'); + + var data = {a: 1, b: 2, c: 3}; + var callback = function(value, key, object) { + assert.strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]); + assert.strictEqual(object, data); + return value !== this.value; + }; + result = _.pick(data, callback, {value: 2}); + assert.deepEqual(result, {a: 1, c: 3}, 'can accept a predicate and context'); var Obj = function(){}; Obj.prototype = {a: 1, b: 2, c: 3}; - ok(_.isEqual(_.pick(new Obj, 'a', 'c'), {a:1, c: 3}), 'include prototype props'); + var instance = new Obj(); + assert.deepEqual(_.pick(instance, 'a', 'c'), {a: 1, c: 3}, 'include prototype props'); + + assert.deepEqual(_.pick(data, function(val, key) { + return this[key] === 3 && this === instance; + }, instance), {c: 3}, 'function is given context'); + + assert.notOk(_.has(_.pick({}, 'foo'), 'foo'), 'does not set own property if property not in object'); + _.pick(data, function(value, key, obj) { + assert.strictEqual(obj, data, 'passes same object as third parameter of iteratee'); + }); }); - test("omit", function() { + QUnit.test('omit', function(assert) { var result; - result = _.omit({a:1, b:2, c:3}, 'b'); - ok(_.isEqual(result, {a:1, c:3}), 'can omit a single named property'); - result = _.omit({a:1, b:2, c:3}, 'a', 'c'); - ok(_.isEqual(result, {b:2}), 'can omit several named properties'); - result = _.omit({a:1, b:2, c:3}, ['b', 'c']); - ok(_.isEqual(result, {a:1}), 'can omit properties named in an array'); + result = _.omit({a: 1, b: 2, c: 3}, 'b'); + assert.deepEqual(result, {a: 1, c: 3}, 'can omit a single named property'); + result = _.omit({a: 1, b: 2, c: 3}, 'a', 'c'); + assert.deepEqual(result, {b: 2}, 'can omit several named properties'); + result = _.omit({a: 1, b: 2, c: 3}, ['b', 'c']); + assert.deepEqual(result, {a: 1}, 'can omit properties named in an array'); + result = _.omit(['a', 'b'], 0); + assert.deepEqual(result, {1: 'b'}, 'can omit numeric properties'); + + assert.deepEqual(_.omit(null, 'a', 'b'), {}, 'non objects return empty object'); + assert.deepEqual(_.omit(void 0, 'toString'), {}, 'null/undefined return empty object'); + assert.deepEqual(_.omit(5, 'toString', 'b'), {}, 'returns empty object for primitives'); + + var data = {a: 1, b: 2, c: 3}; + var callback = function(value, key, object) { + assert.strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]); + assert.strictEqual(object, data); + return value !== this.value; + }; + result = _.omit(data, callback, {value: 2}); + assert.deepEqual(result, {b: 2}, 'can accept a predicate'); var Obj = function(){}; Obj.prototype = {a: 1, b: 2, c: 3}; - ok(_.isEqual(_.omit(new Obj, 'b'), {a:1, c: 3}), 'include prototype props'); + var instance = new Obj(); + assert.deepEqual(_.omit(instance, 'b'), {a: 1, c: 3}, 'include prototype props'); + + assert.deepEqual(_.omit(data, function(val, key) { + return this[key] === 3 && this === instance; + }, instance), {a: 1, b: 2}, 'function is given context'); }); - test("defaults", function() { - var result; - var options = {zero: 0, one: 1, empty: "", nan: NaN, nothing: null}; + QUnit.test('defaults', function(assert) { + var options = {zero: 0, one: 1, empty: '', nan: NaN, nothing: null}; _.defaults(options, {zero: 1, one: 10, twenty: 20, nothing: 'str'}); - equal(options.zero, 0, 'value exists'); - equal(options.one, 1, 'value exists'); - equal(options.twenty, 20, 'default applied'); - equal(options.nothing, null, "null isn't overridden"); + assert.strictEqual(options.zero, 0, 'value exists'); + assert.strictEqual(options.one, 1, 'value exists'); + assert.strictEqual(options.twenty, 20, 'default applied'); + assert.strictEqual(options.nothing, null, "null isn't overridden"); - _.defaults(options, {empty: "full"}, {nan: "nan"}, {word: "word"}, {word: "dog"}); - equal(options.empty, "", 'value exists'); - ok(_.isNaN(options.nan), "NaN isn't overridden"); - equal(options.word, "word", 'new value is added, first one wins'); + _.defaults(options, {empty: 'full'}, {nan: 'nan'}, {word: 'word'}, {word: 'dog'}); + assert.strictEqual(options.empty, '', 'value exists'); + assert.ok(_.isNaN(options.nan), "NaN isn't overridden"); + assert.strictEqual(options.word, 'word', 'new value is added, first one wins'); try { options = {}; - _.defaults(options, null, undefined, {a:1}); - } catch(ex) {} + _.defaults(options, null, void 0, {a: 1}); + } catch (e) { /* ignored */ } - equal(options.a, 1, 'should not error on `null` or `undefined` sources'); + assert.strictEqual(options.a, 1, 'should not error on `null` or `undefined` sources'); + + assert.deepEqual(_.defaults(null, {a: 1}), {a: 1}, 'defaults skips nulls'); + assert.deepEqual(_.defaults(void 0, {a: 1}), {a: 1}, 'defaults skips undefined'); }); - test("clone", function() { - var moe = {name : 'moe', lucky : [13, 27, 34]}; + QUnit.test('clone', function(assert) { + var moe = {name: 'moe', lucky: [13, 27, 34]}; var clone = _.clone(moe); - equal(clone.name, 'moe', 'the clone as the attributes of the original'); + assert.strictEqual(clone.name, 'moe', 'the clone as the attributes of the original'); clone.name = 'curly'; - ok(clone.name == 'curly' && moe.name == 'moe', 'clones can change shallow attributes without affecting the original'); + assert.ok(clone.name === 'curly' && moe.name === 'moe', 'clones can change shallow attributes without affecting the original'); clone.lucky.push(101); - equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); + assert.strictEqual(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original'); - equal(_.clone(undefined), void 0, 'non objects should not be changed by clone'); - equal(_.clone(1), 1, 'non objects should not be changed by clone'); - equal(_.clone(null), null, 'non objects should not be changed by clone'); + assert.strictEqual(_.clone(void 0), void 0, 'non objects should not be changed by clone'); + assert.strictEqual(_.clone(1), 1, 'non objects should not be changed by clone'); + assert.strictEqual(_.clone(null), null, 'non objects should not be changed by clone'); }); - test("isEqual", function() { + QUnit.test('create', function(assert) { + var Parent = function() {}; + Parent.prototype = {foo: function() {}, bar: 2}; + + _.each(['foo', null, void 0, 1], function(val) { + assert.deepEqual(_.create(val), {}, 'should return empty object when a non-object is provided'); + }); + + assert.ok(_.create([]) instanceof Array, 'should return new instance of array when array is provided'); + + var Child = function() {}; + Child.prototype = _.create(Parent.prototype); + assert.ok(new Child instanceof Parent, 'object should inherit prototype'); + + var func = function() {}; + Child.prototype = _.create(Parent.prototype, {func: func}); + assert.strictEqual(Child.prototype.func, func, 'properties should be added to object'); + + Child.prototype = _.create(Parent.prototype, {constructor: Child}); + assert.strictEqual(Child.prototype.constructor, Child); + + Child.prototype.foo = 'foo'; + var created = _.create(Child.prototype, new Child); + assert.notOk(created.hasOwnProperty('foo'), 'should only add own properties'); + }); + + QUnit.test('isEqual', function(assert) { function First() { this.value = 1; } @@ -140,133 +323,139 @@ $(document).ready(function() { Second.prototype.value = 2; // Basic equality and identity comparisons. - ok(_.isEqual(null, null), "`null` is equal to `null`"); - ok(_.isEqual(), "`undefined` is equal to `undefined`"); + assert.ok(_.isEqual(null, null), '`null` is equal to `null`'); + assert.ok(_.isEqual(), '`undefined` is equal to `undefined`'); - ok(!_.isEqual(0, -0), "`0` is not equal to `-0`"); - ok(!_.isEqual(-0, 0), "Commutative equality is implemented for `0` and `-0`"); - ok(!_.isEqual(null, undefined), "`null` is not equal to `undefined`"); - ok(!_.isEqual(undefined, null), "Commutative equality is implemented for `null` and `undefined`"); + assert.notOk(_.isEqual(0, -0), '`0` is not equal to `-0`'); + assert.notOk(_.isEqual(-0, 0), 'Commutative equality is implemented for `0` and `-0`'); + assert.notOk(_.isEqual(null, void 0), '`null` is not equal to `undefined`'); + assert.notOk(_.isEqual(void 0, null), 'Commutative equality is implemented for `null` and `undefined`'); // String object and primitive comparisons. - ok(_.isEqual("Curly", "Curly"), "Identical string primitives are equal"); - ok(_.isEqual(new String("Curly"), new String("Curly")), "String objects with identical primitive values are equal"); - ok(_.isEqual(new String("Curly"), "Curly"), "String primitives and their corresponding object wrappers are equal"); - ok(_.isEqual("Curly", new String("Curly")), "Commutative equality is implemented for string objects and primitives"); + assert.ok(_.isEqual('Curly', 'Curly'), 'Identical string primitives are equal'); + assert.ok(_.isEqual(new String('Curly'), new String('Curly')), 'String objects with identical primitive values are equal'); + assert.ok(_.isEqual(new String('Curly'), 'Curly'), 'String primitives and their corresponding object wrappers are equal'); + assert.ok(_.isEqual('Curly', new String('Curly')), 'Commutative equality is implemented for string objects and primitives'); - ok(!_.isEqual("Curly", "Larry"), "String primitives with different values are not equal"); - ok(!_.isEqual(new String("Curly"), new String("Larry")), "String objects with different primitive values are not equal"); - ok(!_.isEqual(new String("Curly"), {toString: function(){ return "Curly"; }}), "String objects and objects with a custom `toString` method are not equal"); + assert.notOk(_.isEqual('Curly', 'Larry'), 'String primitives with different values are not equal'); + assert.notOk(_.isEqual(new String('Curly'), new String('Larry')), 'String objects with different primitive values are not equal'); + assert.notOk(_.isEqual(new String('Curly'), {toString: function(){ return 'Curly'; }}), 'String objects and objects with a custom `toString` method are not equal'); // Number object and primitive comparisons. - ok(_.isEqual(75, 75), "Identical number primitives are equal"); - ok(_.isEqual(new Number(75), new Number(75)), "Number objects with identical primitive values are equal"); - ok(_.isEqual(75, new Number(75)), "Number primitives and their corresponding object wrappers are equal"); - ok(_.isEqual(new Number(75), 75), "Commutative equality is implemented for number objects and primitives"); - ok(!_.isEqual(new Number(0), -0), "`new Number(0)` and `-0` are not equal"); - ok(!_.isEqual(0, new Number(-0)), "Commutative equality is implemented for `new Number(0)` and `-0`"); + assert.ok(_.isEqual(75, 75), 'Identical number primitives are equal'); + assert.ok(_.isEqual(new Number(75), new Number(75)), 'Number objects with identical primitive values are equal'); + assert.ok(_.isEqual(75, new Number(75)), 'Number primitives and their corresponding object wrappers are equal'); + assert.ok(_.isEqual(new Number(75), 75), 'Commutative equality is implemented for number objects and primitives'); + assert.notOk(_.isEqual(new Number(0), -0), '`new Number(0)` and `-0` are not equal'); + assert.notOk(_.isEqual(0, new Number(-0)), 'Commutative equality is implemented for `new Number(0)` and `-0`'); - ok(!_.isEqual(new Number(75), new Number(63)), "Number objects with different primitive values are not equal"); - ok(!_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), "Number objects and objects with a `valueOf` method are not equal"); + assert.notOk(_.isEqual(new Number(75), new Number(63)), 'Number objects with different primitive values are not equal'); + assert.notOk(_.isEqual(new Number(63), {valueOf: function(){ return 63; }}), 'Number objects and objects with a `valueOf` method are not equal'); // Comparisons involving `NaN`. - ok(_.isEqual(NaN, NaN), "`NaN` is equal to `NaN`"); - ok(!_.isEqual(61, NaN), "A number primitive is not equal to `NaN`"); - ok(!_.isEqual(new Number(79), NaN), "A number object is not equal to `NaN`"); - ok(!_.isEqual(Infinity, NaN), "`Infinity` is not equal to `NaN`"); + assert.ok(_.isEqual(NaN, NaN), '`NaN` is equal to `NaN`'); + assert.ok(_.isEqual(new Number(NaN), NaN), 'Object(`NaN`) is equal to `NaN`'); + assert.notOk(_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`'); + assert.notOk(_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`'); + assert.notOk(_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`'); // Boolean object and primitive comparisons. - ok(_.isEqual(true, true), "Identical boolean primitives are equal"); - ok(_.isEqual(new Boolean, new Boolean), "Boolean objects with identical primitive values are equal"); - ok(_.isEqual(true, new Boolean(true)), "Boolean primitives and their corresponding object wrappers are equal"); - ok(_.isEqual(new Boolean(true), true), "Commutative equality is implemented for booleans"); - ok(!_.isEqual(new Boolean(true), new Boolean), "Boolean objects with different primitive values are not equal"); + assert.ok(_.isEqual(true, true), 'Identical boolean primitives are equal'); + assert.ok(_.isEqual(new Boolean, new Boolean), 'Boolean objects with identical primitive values are equal'); + assert.ok(_.isEqual(true, new Boolean(true)), 'Boolean primitives and their corresponding object wrappers are equal'); + assert.ok(_.isEqual(new Boolean(true), true), 'Commutative equality is implemented for booleans'); + assert.notOk(_.isEqual(new Boolean(true), new Boolean), 'Boolean objects with different primitive values are not equal'); // Common type coercions. - ok(!_.isEqual(true, new Boolean(false)), "Boolean objects are not equal to the boolean primitive `true`"); - ok(!_.isEqual("75", 75), "String and number primitives with like values are not equal"); - ok(!_.isEqual(new Number(63), new String(63)), "String and number objects with like values are not equal"); - ok(!_.isEqual(75, "75"), "Commutative equality is implemented for like string and number values"); - ok(!_.isEqual(0, ""), "Number and string primitives with like values are not equal"); - ok(!_.isEqual(1, true), "Number and boolean primitives with like values are not equal"); - ok(!_.isEqual(new Boolean(false), new Number(0)), "Boolean and number objects with like values are not equal"); - ok(!_.isEqual(false, new String("")), "Boolean primitives and string objects with like values are not equal"); - ok(!_.isEqual(12564504e5, new Date(2009, 9, 25)), "Dates and their corresponding numeric primitive values are not equal"); + assert.notOk(_.isEqual(new Boolean(false), true), '`new Boolean(false)` is not equal to `true`'); + assert.notOk(_.isEqual('75', 75), 'String and number primitives with like values are not equal'); + assert.notOk(_.isEqual(new Number(63), new String(63)), 'String and number objects with like values are not equal'); + assert.notOk(_.isEqual(75, '75'), 'Commutative equality is implemented for like string and number values'); + assert.notOk(_.isEqual(0, ''), 'Number and string primitives with like values are not equal'); + assert.notOk(_.isEqual(1, true), 'Number and boolean primitives with like values are not equal'); + assert.notOk(_.isEqual(new Boolean(false), new Number(0)), 'Boolean and number objects with like values are not equal'); + assert.notOk(_.isEqual(false, new String('')), 'Boolean primitives and string objects with like values are not equal'); + assert.notOk(_.isEqual(12564504e5, new Date(2009, 9, 25)), 'Dates and their corresponding numeric primitive values are not equal'); // Dates. - ok(_.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)), "Date objects referencing identical times are equal"); - ok(!_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), "Date objects referencing different times are not equal"); - ok(!_.isEqual(new Date(2009, 11, 13), { + assert.ok(_.isEqual(new Date(2009, 9, 25), new Date(2009, 9, 25)), 'Date objects referencing identical times are equal'); + assert.notOk(_.isEqual(new Date(2009, 9, 25), new Date(2009, 11, 13)), 'Date objects referencing different times are not equal'); + assert.notOk(_.isEqual(new Date(2009, 11, 13), { getTime: function(){ return 12606876e5; } - }), "Date objects and objects with a `getTime` method are not equal"); - ok(!_.isEqual(new Date("Curly"), new Date("Curly")), "Invalid dates are not equal"); + }), 'Date objects and objects with a `getTime` method are not equal'); + assert.notOk(_.isEqual(new Date('Curly'), new Date('Curly')), 'Invalid dates are not equal'); // Functions. - ok(!_.isEqual(First, Second), "Different functions with identical bodies and source code representations are not equal"); + assert.notOk(_.isEqual(First, Second), 'Different functions with identical bodies and source code representations are not equal'); // RegExps. - ok(_.isEqual(/(?:)/gim, /(?:)/gim), "RegExps with equivalent patterns and flags are equal"); - ok(!_.isEqual(/(?:)/g, /(?:)/gi), "RegExps with equivalent patterns and different flags are not equal"); - ok(!_.isEqual(/Moe/gim, /Curly/gim), "RegExps with different patterns and equivalent flags are not equal"); - ok(!_.isEqual(/(?:)/gi, /(?:)/g), "Commutative equality is implemented for RegExps"); - ok(!_.isEqual(/Curly/g, {source: "Larry", global: true, ignoreCase: false, multiline: false}), "RegExps and RegExp-like objects are not equal"); + assert.ok(_.isEqual(/(?:)/gim, /(?:)/gim), 'RegExps with equivalent patterns and flags are equal'); + assert.ok(_.isEqual(/(?:)/gi, /(?:)/ig), 'Flag order is not significant'); + assert.notOk(_.isEqual(/(?:)/g, /(?:)/gi), 'RegExps with equivalent patterns and different flags are not equal'); + assert.notOk(_.isEqual(/Moe/gim, /Curly/gim), 'RegExps with different patterns and equivalent flags are not equal'); + assert.notOk(_.isEqual(/(?:)/gi, /(?:)/g), 'Commutative equality is implemented for RegExps'); + assert.notOk(_.isEqual(/Curly/g, {source: 'Larry', global: true, ignoreCase: false, multiline: false}), 'RegExps and RegExp-like objects are not equal'); // Empty arrays, array-like objects, and object literals. - ok(_.isEqual({}, {}), "Empty object literals are equal"); - ok(_.isEqual([], []), "Empty array literals are equal"); - ok(_.isEqual([{}], [{}]), "Empty nested arrays and objects are equal"); - ok(!_.isEqual({length: 0}, []), "Array-like objects and arrays are not equal."); - ok(!_.isEqual([], {length: 0}), "Commutative equality is implemented for array-like objects"); + assert.ok(_.isEqual({}, {}), 'Empty object literals are equal'); + assert.ok(_.isEqual([], []), 'Empty array literals are equal'); + assert.ok(_.isEqual([{}], [{}]), 'Empty nested arrays and objects are equal'); + assert.notOk(_.isEqual({length: 0}, []), 'Array-like objects and arrays are not equal.'); + assert.notOk(_.isEqual([], {length: 0}), 'Commutative equality is implemented for array-like objects'); - ok(!_.isEqual({}, []), "Object literals and array literals are not equal"); - ok(!_.isEqual([], {}), "Commutative equality is implemented for objects and arrays"); + assert.notOk(_.isEqual({}, []), 'Object literals and array literals are not equal'); + assert.notOk(_.isEqual([], {}), 'Commutative equality is implemented for objects and arrays'); // Arrays with primitive and object values. - ok(_.isEqual([1, "Larry", true], [1, "Larry", true]), "Arrays containing identical primitives are equal"); - ok(_.isEqual([(/Moe/g), new Date(2009, 9, 25)], [(/Moe/g), new Date(2009, 9, 25)]), "Arrays containing equivalent elements are equal"); + assert.ok(_.isEqual([1, 'Larry', true], [1, 'Larry', true]), 'Arrays containing identical primitives are equal'); + assert.ok(_.isEqual([/Moe/g, new Date(2009, 9, 25)], [/Moe/g, new Date(2009, 9, 25)]), 'Arrays containing equivalent elements are equal'); // Multi-dimensional arrays. - var a = [new Number(47), false, "Larry", /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; - var b = [new Number(47), false, "Larry", /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; - ok(_.isEqual(a, b), "Arrays containing nested arrays and objects are recursively compared"); + var a = [new Number(47), false, 'Larry', /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; + var b = [new Number(47), false, 'Larry', /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}]; + assert.ok(_.isEqual(a, b), 'Arrays containing nested arrays and objects are recursively compared'); // Overwrite the methods defined in ES 5.1 section 15.4.4. a.forEach = a.map = a.filter = a.every = a.indexOf = a.lastIndexOf = a.some = a.reduce = a.reduceRight = null; b.join = b.pop = b.reverse = b.shift = b.slice = b.splice = b.concat = b.sort = b.unshift = null; // Array elements and properties. - ok(_.isEqual(a, b), "Arrays containing equivalent elements and different non-numeric properties are equal"); - a.push("White Rocks"); - ok(!_.isEqual(a, b), "Arrays of different lengths are not equal"); - a.push("East Boulder"); - b.push("Gunbarrel Ranch", "Teller Farm"); - ok(!_.isEqual(a, b), "Arrays of identical lengths containing different elements are not equal"); + assert.ok(_.isEqual(a, b), 'Arrays containing equivalent elements and different non-numeric properties are equal'); + a.push('White Rocks'); + assert.notOk(_.isEqual(a, b), 'Arrays of different lengths are not equal'); + a.push('East Boulder'); + b.push('Gunbarrel Ranch', 'Teller Farm'); + assert.notOk(_.isEqual(a, b), 'Arrays of identical lengths containing different elements are not equal'); // Sparse arrays. - ok(_.isEqual(Array(3), Array(3)), "Sparse arrays of identical lengths are equal"); - ok(!_.isEqual(Array(3), Array(6)), "Sparse arrays of different lengths are not equal when both are empty"); + assert.ok(_.isEqual(Array(3), Array(3)), 'Sparse arrays of identical lengths are equal'); + assert.notOk(_.isEqual(Array(3), Array(6)), 'Sparse arrays of different lengths are not equal when both are empty'); + + var sparse = []; + sparse[1] = 5; + assert.ok(_.isEqual(sparse, [void 0, 5]), 'Handles sparse arrays as dense'); // Simple objects. - ok(_.isEqual({a: "Curly", b: 1, c: true}, {a: "Curly", b: 1, c: true}), "Objects containing identical primitives are equal"); - ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), "Objects containing equivalent members are equal"); - ok(!_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), "Objects of identical sizes with different values are not equal"); - ok(!_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), "Objects of identical sizes with different property names are not equal"); - ok(!_.isEqual({a: 1, b: 2}, {a: 1}), "Objects of different sizes are not equal"); - ok(!_.isEqual({a: 1}, {a: 1, b: 2}), "Commutative equality is implemented for objects"); - ok(!_.isEqual({x: 1, y: undefined}, {x: 1, z: 2}), "Objects with identical keys and different values are not equivalent"); + assert.ok(_.isEqual({a: 'Curly', b: 1, c: true}, {a: 'Curly', b: 1, c: true}), 'Objects containing identical primitives are equal'); + assert.ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), 'Objects containing equivalent members are equal'); + assert.notOk(_.isEqual({a: 63, b: 75}, {a: 61, b: 55}), 'Objects of identical sizes with different values are not equal'); + assert.notOk(_.isEqual({a: 63, b: 75}, {a: 61, c: 55}), 'Objects of identical sizes with different property names are not equal'); + assert.notOk(_.isEqual({a: 1, b: 2}, {a: 1}), 'Objects of different sizes are not equal'); + assert.notOk(_.isEqual({a: 1}, {a: 1, b: 2}), 'Commutative equality is implemented for objects'); + assert.notOk(_.isEqual({x: 1, y: void 0}, {x: 1, z: 2}), 'Objects with identical keys and different values are not equivalent'); // `A` contains nested objects and arrays. a = { - name: new String("Moe Howard"), + name: new String('Moe Howard'), age: new Number(77), stooge: true, - hobbies: ["acting"], + hobbies: ['acting'], film: { - name: "Sing a Song of Six Pants", + name: 'Sing a Song of Six Pants', release: new Date(1947, 9, 30), - stars: [new String("Larry Fine"), "Shemp Howard"], + stars: [new String('Larry Fine'), 'Shemp Howard'], minutes: new Number(16), seconds: 54 } @@ -274,304 +463,692 @@ $(document).ready(function() { // `B` contains equivalent nested objects and arrays. b = { - name: new String("Moe Howard"), + name: new String('Moe Howard'), age: new Number(77), stooge: true, - hobbies: ["acting"], + hobbies: ['acting'], film: { - name: "Sing a Song of Six Pants", + name: 'Sing a Song of Six Pants', release: new Date(1947, 9, 30), - stars: [new String("Larry Fine"), "Shemp Howard"], + stars: [new String('Larry Fine'), 'Shemp Howard'], minutes: new Number(16), seconds: 54 } }; - ok(_.isEqual(a, b), "Objects with nested equivalent members are recursively compared"); + assert.ok(_.isEqual(a, b), 'Objects with nested equivalent members are recursively compared'); // Instances. - ok(_.isEqual(new First, new First), "Object instances are equal"); - ok(!_.isEqual(new First, new Second), "Objects with different constructors and identical own properties are not equal"); - ok(!_.isEqual({value: 1}, new First), "Object instances and objects sharing equivalent properties are not equal"); - ok(!_.isEqual({value: 2}, new Second), "The prototype chain of objects should not be examined"); + assert.ok(_.isEqual(new First, new First), 'Object instances are equal'); + assert.notOk(_.isEqual(new First, new Second), 'Objects with different constructors and identical own properties are not equal'); + assert.notOk(_.isEqual({value: 1}, new First), 'Object instances and objects sharing equivalent properties are not equal'); + assert.notOk(_.isEqual({value: 2}, new Second), 'The prototype chain of objects should not be examined'); // Circular Arrays. (a = []).push(a); (b = []).push(b); - ok(_.isEqual(a, b), "Arrays containing circular references are equal"); - a.push(new String("Larry")); - b.push(new String("Larry")); - ok(_.isEqual(a, b), "Arrays containing circular references and equivalent properties are equal"); - a.push("Shemp"); - b.push("Curly"); - ok(!_.isEqual(a, b), "Arrays containing circular references and different properties are not equal"); + assert.ok(_.isEqual(a, b), 'Arrays containing circular references are equal'); + a.push(new String('Larry')); + b.push(new String('Larry')); + assert.ok(_.isEqual(a, b), 'Arrays containing circular references and equivalent properties are equal'); + a.push('Shemp'); + b.push('Curly'); + assert.notOk(_.isEqual(a, b), 'Arrays containing circular references and different properties are not equal'); // More circular arrays #767. - a = ["everything is checked but", "this", "is not"]; + a = ['everything is checked but', 'this', 'is not']; a[1] = a; - b = ["everything is checked but", ["this", "array"], "is not"]; - ok(!_.isEqual(a, b), "Comparison of circular references with non-circular references are not equal"); + b = ['everything is checked but', ['this', 'array'], 'is not']; + assert.notOk(_.isEqual(a, b), 'Comparison of circular references with non-circular references are not equal'); // Circular Objects. a = {abc: null}; b = {abc: null}; a.abc = a; b.abc = b; - ok(_.isEqual(a, b), "Objects containing circular references are equal"); + assert.ok(_.isEqual(a, b), 'Objects containing circular references are equal'); a.def = 75; b.def = 75; - ok(_.isEqual(a, b), "Objects containing circular references and equivalent properties are equal"); + assert.ok(_.isEqual(a, b), 'Objects containing circular references and equivalent properties are equal'); a.def = new Number(75); b.def = new Number(63); - ok(!_.isEqual(a, b), "Objects containing circular references and different properties are not equal"); + assert.notOk(_.isEqual(a, b), 'Objects containing circular references and different properties are not equal'); // More circular objects #767. - a = {everything: "is checked", but: "this", is: "not"}; + a = {everything: 'is checked', but: 'this', is: 'not'}; a.but = a; - b = {everything: "is checked", but: {that:"object"}, is: "not"}; - ok(!_.isEqual(a, b), "Comparison of circular references with non-circular object references are not equal"); + b = {everything: 'is checked', but: {that: 'object'}, is: 'not'}; + assert.notOk(_.isEqual(a, b), 'Comparison of circular references with non-circular object references are not equal'); // Cyclic Structures. a = [{abc: null}]; b = [{abc: null}]; (a[0].abc = a).push(a); (b[0].abc = b).push(b); - ok(_.isEqual(a, b), "Cyclic structures are equal"); - a[0].def = "Larry"; - b[0].def = "Larry"; - ok(_.isEqual(a, b), "Cyclic structures containing equivalent properties are equal"); - a[0].def = new String("Larry"); - b[0].def = new String("Curly"); - ok(!_.isEqual(a, b), "Cyclic structures containing different properties are not equal"); + assert.ok(_.isEqual(a, b), 'Cyclic structures are equal'); + a[0].def = 'Larry'; + b[0].def = 'Larry'; + assert.ok(_.isEqual(a, b), 'Cyclic structures containing equivalent properties are equal'); + a[0].def = new String('Larry'); + b[0].def = new String('Curly'); + assert.notOk(_.isEqual(a, b), 'Cyclic structures containing different properties are not equal'); // Complex Circular References. a = {foo: {b: {foo: {c: {foo: null}}}}}; b = {foo: {b: {foo: {c: {foo: null}}}}}; a.foo.b.foo.c.foo = a; b.foo.b.foo.c.foo = b; - ok(_.isEqual(a, b), "Cyclic structures with nested and identically-named properties are equal"); + assert.ok(_.isEqual(a, b), 'Cyclic structures with nested and identically-named properties are equal'); // Chaining. - ok(!_.isEqual(_({x: 1, y: undefined}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); + assert.notOk(_.isEqual(_({x: 1, y: void 0}).chain(), _({x: 1, z: 2}).chain()), 'Chained objects containing different values are not equal'); a = _({x: 1, y: 2}).chain(); b = _({x: 1, y: 2}).chain(); - equal(_.isEqual(a.isEqual(b), _(true)), true, '`isEqual` can be chained'); + assert.strictEqual(_.isEqual(a.isEqual(b), _(true)), true, '`isEqual` can be chained'); - // Objects from another frame. - ok(_.isEqual({}, iObject)); + // Objects without a `constructor` property + if (Object.create) { + a = Object.create(null, {x: {value: 1, enumerable: true}}); + b = {x: 1}; + assert.ok(_.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create'); + } + + function Foo() { this.a = 1; } + Foo.prototype.constructor = null; + + var other = {a: 1}; + assert.strictEqual(_.isEqual(new Foo, other), false, 'Objects from different constructors are not equal'); + + + // Tricky object cases val comparisons + assert.strictEqual(_.isEqual([0], [-0]), false); + assert.strictEqual(_.isEqual({a: 0}, {a: -0}), false); + assert.strictEqual(_.isEqual([NaN], [NaN]), true); + assert.strictEqual(_.isEqual({a: NaN}, {a: NaN}), true); + + if (typeof Symbol !== 'undefined') { + var symbol = Symbol('x'); + assert.strictEqual(_.isEqual(symbol, symbol), true, 'A symbol is equal to itself'); + assert.strictEqual(_.isEqual(symbol, Object(symbol)), true, 'Even when wrapped in Object()'); + assert.strictEqual(_.isEqual(symbol, null), false, 'Different types are not equal'); + + var symbolY = Symbol('y'); + assert.strictEqual(_.isEqual(symbol, symbolY), false, 'Different symbols are not equal'); + + var sameStringSymbol = Symbol('x'); + assert.strictEqual(_.isEqual(symbol, sameStringSymbol), false, 'Different symbols of same string are not equal'); + } }); - test("isEmpty", function() { - ok(!_([1]).isEmpty(), '[1] is not empty'); - ok(_.isEmpty([]), '[] is empty'); - ok(!_.isEmpty({one : 1}), '{one : 1} is not empty'); - ok(_.isEmpty({}), '{} is empty'); - ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); - ok(_.isEmpty(null), 'null is empty'); - ok(_.isEmpty(), 'undefined is empty'); - ok(_.isEmpty(''), 'the empty string is empty'); - ok(!_.isEmpty('moe'), 'but other strings are not'); + QUnit.test('isEmpty', function(assert) { + assert.notOk(_([1]).isEmpty(), '[1] is not empty'); + assert.ok(_.isEmpty([]), '[] is empty'); + assert.notOk(_.isEmpty({one: 1}), '{one: 1} is not empty'); + assert.ok(_.isEmpty({}), '{} is empty'); + assert.ok(_.isEmpty(new RegExp('')), 'objects with prototype properties are empty'); + assert.ok(_.isEmpty(null), 'null is empty'); + assert.ok(_.isEmpty(), 'undefined is empty'); + assert.ok(_.isEmpty(''), 'the empty string is empty'); + assert.notOk(_.isEmpty('moe'), 'but other strings are not'); - var obj = {one : 1}; + var obj = {one: 1}; delete obj.one; - ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); + assert.ok(_.isEmpty(obj), 'deleting all the keys from an object empties it'); + + var args = function(){ return arguments; }; + assert.ok(_.isEmpty(args()), 'empty arguments object is empty'); + assert.notOk(_.isEmpty(args('')), 'non-empty arguments object is not empty'); + + // covers collecting non-enumerable properties in IE < 9 + var nonEnumProp = {toString: 5}; + assert.notOk(_.isEmpty(nonEnumProp), 'non-enumerable property is not empty'); }); - // Setup remote variables for iFrame tests. - var iframe = document.createElement('iframe'); - jQuery(iframe).appendTo(document.body); - var iDoc = iframe.contentDocument || iframe.contentWindow.document; - iDoc.write( - "" - ); - iDoc.close(); - - test("isElement", function() { - ok(!_.isElement('div'), 'strings are not dom elements'); - ok(_.isElement($('html')[0]), 'the html tag is a DOM element'); - ok(_.isElement(iElement), 'even from another frame'); - }); - - test("isArguments", function() { - var args = (function(){ return arguments; })(1, 2, 3); - ok(!_.isArguments('string'), 'a string is not an arguments object'); - ok(!_.isArguments(_.isArguments), 'a function is not an arguments object'); - ok(_.isArguments(args), 'but the arguments object is an arguments object'); - ok(!_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); - ok(!_.isArguments([1,2,3]), 'and not vanilla arrays.'); - ok(_.isArguments(iArguments), 'even from another frame'); - }); - - test("isObject", function() { - ok(_.isObject(arguments), 'the arguments object is object'); - ok(_.isObject([1, 2, 3]), 'and arrays'); - ok(_.isObject($('html')[0]), 'and DOM element'); - ok(_.isObject(iElement), 'even from another frame'); - ok(_.isObject(function () {}), 'and functions'); - ok(_.isObject(iFunction), 'even from another frame'); - ok(!_.isObject(null), 'but not null'); - ok(!_.isObject(undefined), 'and not undefined'); - ok(!_.isObject('string'), 'and not string'); - ok(!_.isObject(12), 'and not number'); - ok(!_.isObject(true), 'and not boolean'); - ok(_.isObject(new String('string')), 'but new String()'); - }); - - test("isArray", function() { - ok(!_.isArray(undefined), 'undefined vars are not arrays'); - ok(!_.isArray(arguments), 'the arguments object is not an array'); - ok(_.isArray([1, 2, 3]), 'but arrays are'); - ok(_.isArray(iArray), 'even from another frame'); - }); - - test("isString", function() { - var obj = new String("I am a string object"); - ok(!_.isString(document.body), 'the document body is not a string'); - ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); - ok(_.isString(iString), 'even from another frame'); - ok(_.isString("I am a string literal"), 'string literals are'); - ok(_.isString(obj), 'so are String objects'); - }); - - test("isNumber", function() { - ok(!_.isNumber('string'), 'a string is not a number'); - ok(!_.isNumber(arguments), 'the arguments object is not a number'); - ok(!_.isNumber(undefined), 'undefined is not a number'); - ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); - ok(_.isNumber(NaN), 'NaN *is* a number'); - ok(_.isNumber(Infinity), 'Infinity is a number'); - ok(_.isNumber(iNumber), 'even from another frame'); - ok(!_.isNumber('1'), 'numeric strings are not numbers'); - }); - - test("isBoolean", function() { - ok(!_.isBoolean(2), 'a number is not a boolean'); - ok(!_.isBoolean("string"), 'a string is not a boolean'); - ok(!_.isBoolean("false"), 'the string "false" is not a boolean'); - ok(!_.isBoolean("true"), 'the string "true" is not a boolean'); - ok(!_.isBoolean(arguments), 'the arguments object is not a boolean'); - ok(!_.isBoolean(undefined), 'undefined is not a boolean'); - ok(!_.isBoolean(NaN), 'NaN is not a boolean'); - ok(!_.isBoolean(null), 'null is not a boolean'); - ok(_.isBoolean(true), 'but true is'); - ok(_.isBoolean(false), 'and so is false'); - ok(_.isBoolean(iBoolean), 'even from another frame'); - }); - - test("isFunction", function() { - ok(!_.isFunction(undefined), 'undefined vars are not functions'); - ok(!_.isFunction([1, 2, 3]), 'arrays are not functions'); - ok(!_.isFunction('moe'), 'strings are not functions'); - ok(_.isFunction(_.isFunction), 'but functions are'); - ok(_.isFunction(iFunction), 'even from another frame'); - ok(_.isFunction(function(){}), 'even anonymous ones'); - }); - - test("isDate", function() { - ok(!_.isDate(100), 'numbers are not dates'); - ok(!_.isDate({}), 'objects are not dates'); - ok(_.isDate(new Date()), 'but dates are'); - ok(_.isDate(iDate), 'even from another frame'); - }); - - test("isRegExp", function() { - ok(!_.isRegExp(_.identity), 'functions are not RegExps'); - ok(_.isRegExp(/identity/), 'but RegExps are'); - ok(_.isRegExp(iRegExp), 'even from another frame'); - }); - - test("isFinite", function() { - ok(!_.isFinite(undefined), 'undefined is not Finite'); - ok(!_.isFinite(null), 'null is not Finite'); - ok(!_.isFinite(NaN), 'NaN is not Finite'); - ok(!_.isFinite(Infinity), 'Infinity is not Finite'); - ok(!_.isFinite(-Infinity), '-Infinity is not Finite'); - ok(_.isFinite('12'), 'Numeric strings are numbers'); - ok(!_.isFinite('1a'), 'Non numeric strings are not numbers'); - ok(!_.isFinite(''), 'Empty strings are not numbers'); - var obj = new Number(5); - ok(_.isFinite(obj), 'Number instances can be finite'); - ok(_.isFinite(0), '0 is Finite'); - ok(_.isFinite(123), 'Ints are Finite'); - ok(_.isFinite(-12.44), 'Floats are Finite'); - }); - - test("isNaN", function() { - ok(!_.isNaN(undefined), 'undefined is not NaN'); - ok(!_.isNaN(null), 'null is not NaN'); - ok(!_.isNaN(0), '0 is not NaN'); - ok(_.isNaN(NaN), 'but NaN is'); - ok(_.isNaN(iNaN), 'even from another frame'); - ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); - }); - - test("isNull", function() { - ok(!_.isNull(undefined), 'undefined is not null'); - ok(!_.isNull(NaN), 'NaN is not null'); - ok(_.isNull(null), 'but null is'); - ok(_.isNull(iNull), 'even from another frame'); - }); - - test("isUndefined", function() { - ok(!_.isUndefined(1), 'numbers are defined'); - ok(!_.isUndefined(null), 'null is defined'); - ok(!_.isUndefined(false), 'false is defined'); - ok(!_.isUndefined(NaN), 'NaN is defined'); - ok(_.isUndefined(), 'nothing is undefined'); - ok(_.isUndefined(undefined), 'undefined is undefined'); - ok(_.isUndefined(iUndefined), 'even from another frame'); - }); - - if (window.ActiveXObject) { - test("IE host objects", function() { - var xml = new ActiveXObject("Msxml2.DOMDocument.3.0"); - ok(!_.isNumber(xml)); - ok(!_.isBoolean(xml)); - ok(!_.isNaN(xml)); - ok(!_.isFunction(xml)); - ok(!_.isNull(xml)); - ok(!_.isUndefined(xml)); + if (typeof document === 'object') { + QUnit.test('isElement', function(assert) { + assert.notOk(_.isElement('div'), 'strings are not dom elements'); + assert.ok(_.isElement(testElement), 'an element is a DOM element'); }); } - test("tap", function() { + QUnit.test('isArguments', function(assert) { + var args = (function(){ return arguments; }(1, 2, 3)); + assert.notOk(_.isArguments('string'), 'a string is not an arguments object'); + assert.notOk(_.isArguments(_.isArguments), 'a function is not an arguments object'); + assert.ok(_.isArguments(args), 'but the arguments object is an arguments object'); + assert.notOk(_.isArguments(_.toArray(args)), 'but not when it\'s converted into an array'); + assert.notOk(_.isArguments([1, 2, 3]), 'and not vanilla arrays.'); + }); + + QUnit.test('isObject', function(assert) { + assert.ok(_.isObject(arguments), 'the arguments object is object'); + assert.ok(_.isObject([1, 2, 3]), 'and arrays'); + if (testElement) { + assert.ok(_.isObject(testElement), 'and DOM element'); + } + assert.ok(_.isObject(function() {}), 'and functions'); + assert.notOk(_.isObject(null), 'but not null'); + assert.notOk(_.isObject(void 0), 'and not undefined'); + assert.notOk(_.isObject('string'), 'and not string'); + assert.notOk(_.isObject(12), 'and not number'); + assert.notOk(_.isObject(true), 'and not boolean'); + assert.ok(_.isObject(new String('string')), 'but new String()'); + }); + + QUnit.test('isArray', function(assert) { + assert.notOk(_.isArray(void 0), 'undefined vars are not arrays'); + assert.notOk(_.isArray(arguments), 'the arguments object is not an array'); + assert.ok(_.isArray([1, 2, 3]), 'but arrays are'); + }); + + QUnit.test('isString', function(assert) { + var obj = new String('I am a string object'); + if (testElement) { + assert.notOk(_.isString(testElement), 'an element is not a string'); + } + assert.ok(_.isString([1, 2, 3].join(', ')), 'but strings are'); + assert.strictEqual(_.isString('I am a string literal'), true, 'string literals are'); + assert.ok(_.isString(obj), 'so are String objects'); + assert.strictEqual(_.isString(1), false); + }); + + QUnit.test('isSymbol', function(assert) { + assert.notOk(_.isSymbol(0), 'numbers are not symbols'); + assert.notOk(_.isSymbol(''), 'strings are not symbols'); + assert.notOk(_.isSymbol(_.isSymbol), 'functions are not symbols'); + if (typeof Symbol === 'function') { + assert.ok(_.isSymbol(Symbol()), 'symbols are symbols'); + assert.ok(_.isSymbol(Symbol('description')), 'described symbols are symbols'); + assert.ok(_.isSymbol(Object(Symbol())), 'boxed symbols are symbols'); + } + }); + + QUnit.test('isNumber', function(assert) { + assert.notOk(_.isNumber('string'), 'a string is not a number'); + assert.notOk(_.isNumber(arguments), 'the arguments object is not a number'); + assert.notOk(_.isNumber(void 0), 'undefined is not a number'); + assert.ok(_.isNumber(3 * 4 - 7 / 10), 'but numbers are'); + assert.ok(_.isNumber(NaN), 'NaN *is* a number'); + assert.ok(_.isNumber(Infinity), 'Infinity is a number'); + assert.notOk(_.isNumber('1'), 'numeric strings are not numbers'); + }); + + QUnit.test('isBoolean', function(assert) { + assert.notOk(_.isBoolean(2), 'a number is not a boolean'); + assert.notOk(_.isBoolean('string'), 'a string is not a boolean'); + assert.notOk(_.isBoolean('false'), 'the string "false" is not a boolean'); + assert.notOk(_.isBoolean('true'), 'the string "true" is not a boolean'); + assert.notOk(_.isBoolean(arguments), 'the arguments object is not a boolean'); + assert.notOk(_.isBoolean(void 0), 'undefined is not a boolean'); + assert.notOk(_.isBoolean(NaN), 'NaN is not a boolean'); + assert.notOk(_.isBoolean(null), 'null is not a boolean'); + assert.ok(_.isBoolean(true), 'but true is'); + assert.ok(_.isBoolean(false), 'and so is false'); + }); + + QUnit.test('isMap', function(assert) { + assert.notOk(_.isMap('string'), 'a string is not a map'); + assert.notOk(_.isMap(2), 'a number is not a map'); + assert.notOk(_.isMap({}), 'an object is not a map'); + assert.notOk(_.isMap(false), 'a boolean is not a map'); + assert.notOk(_.isMap(void 0), 'undefined is not a map'); + assert.notOk(_.isMap([1, 2, 3]), 'an array is not a map'); + if (typeof Set === 'function') { + assert.notOk(_.isMap(new Set()), 'a set is not a map'); + } + if (typeof WeakSet === 'function') { + assert.notOk(_.isMap(new WeakSet()), 'a weakset is not a map'); + } + if (typeof WeakMap === 'function') { + assert.notOk(_.isMap(new WeakMap()), 'a weakmap is not a map'); + } + if (typeof Map === 'function') { + var keyString = 'a string'; + var obj = new Map(); + obj.set(keyString, 'value'); + assert.ok(_.isMap(obj), 'but a map is'); + } + }); + + QUnit.test('isWeakMap', function(assert) { + assert.notOk(_.isWeakMap('string'), 'a string is not a weakmap'); + assert.notOk(_.isWeakMap(2), 'a number is not a weakmap'); + assert.notOk(_.isWeakMap({}), 'an object is not a weakmap'); + assert.notOk(_.isWeakMap(false), 'a boolean is not a weakmap'); + assert.notOk(_.isWeakMap(void 0), 'undefined is not a weakmap'); + assert.notOk(_.isWeakMap([1, 2, 3]), 'an array is not a weakmap'); + if (typeof Set === 'function') { + assert.notOk(_.isWeakMap(new Set()), 'a set is not a weakmap'); + } + if (typeof WeakSet === 'function') { + assert.notOk(_.isWeakMap(new WeakSet()), 'a weakset is not a weakmap'); + } + if (typeof Map === 'function') { + assert.notOk(_.isWeakMap(new Map()), 'a map is not a weakmap'); + } + if (typeof WeakMap === 'function') { + var keyObj = {}, obj = new WeakMap(); + obj.set(keyObj, 'value'); + assert.ok(_.isWeakMap(obj), 'but a weakmap is'); + } + }); + + QUnit.test('isSet', function(assert) { + assert.notOk(_.isSet('string'), 'a string is not a set'); + assert.notOk(_.isSet(2), 'a number is not a set'); + assert.notOk(_.isSet({}), 'an object is not a set'); + assert.notOk(_.isSet(false), 'a boolean is not a set'); + assert.notOk(_.isSet(void 0), 'undefined is not a set'); + assert.notOk(_.isSet([1, 2, 3]), 'an array is not a set'); + if (typeof Map === 'function') { + assert.notOk(_.isSet(new Map()), 'a map is not a set'); + } + if (typeof WeakMap === 'function') { + assert.notOk(_.isSet(new WeakMap()), 'a weakmap is not a set'); + } + if (typeof WeakSet === 'function') { + assert.notOk(_.isSet(new WeakSet()), 'a weakset is not a set'); + } + if (typeof Set === 'function') { + var obj = new Set(); + obj.add(1).add('string').add(false).add({}); + assert.ok(_.isSet(obj), 'but a set is'); + } + }); + + QUnit.test('isWeakSet', function(assert) { + + assert.notOk(_.isWeakSet('string'), 'a string is not a weakset'); + assert.notOk(_.isWeakSet(2), 'a number is not a weakset'); + assert.notOk(_.isWeakSet({}), 'an object is not a weakset'); + assert.notOk(_.isWeakSet(false), 'a boolean is not a weakset'); + assert.notOk(_.isWeakSet(void 0), 'undefined is not a weakset'); + assert.notOk(_.isWeakSet([1, 2, 3]), 'an array is not a weakset'); + if (typeof Map === 'function') { + assert.notOk(_.isWeakSet(new Map()), 'a map is not a weakset'); + } + if (typeof WeakMap === 'function') { + assert.notOk(_.isWeakSet(new WeakMap()), 'a weakmap is not a weakset'); + } + if (typeof Set === 'function') { + assert.notOk(_.isWeakSet(new Set()), 'a set is not a weakset'); + } + if (typeof WeakSet === 'function') { + var obj = new WeakSet(); + obj.add({x: 1}, {y: 'string'}).add({y: 'string'}).add({z: [1, 2, 3]}); + assert.ok(_.isWeakSet(obj), 'but a weakset is'); + } + }); + + QUnit.test('isFunction', function(assert) { + assert.notOk(_.isFunction(void 0), 'undefined vars are not functions'); + assert.notOk(_.isFunction([1, 2, 3]), 'arrays are not functions'); + assert.notOk(_.isFunction('moe'), 'strings are not functions'); + assert.ok(_.isFunction(_.isFunction), 'but functions are'); + assert.ok(_.isFunction(function(){}), 'even anonymous ones'); + + if (testElement) { + assert.notOk(_.isFunction(testElement), 'elements are not functions'); + } + + var nodelist = typeof document != 'undefined' && document.childNodes; + if (nodelist) { + assert.notOk(_.isFunction(nodelist)); + } + }); + + if (typeof Int8Array !== 'undefined') { + QUnit.test('#1929 Typed Array constructors are functions', function(assert) { + _.chain(['Float32Array', 'Float64Array', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array']) + .map(_.propertyOf(typeof GLOBAL != 'undefined' ? GLOBAL : window)) + .compact() + .each(function(TypedArray) { + // PhantomJS reports `typeof UInt8Array == 'object'` and doesn't report toString TypeArray + // as a function + assert.strictEqual(_.isFunction(TypedArray), Object.prototype.toString.call(TypedArray) === '[object Function]'); + }); + }); + } + + QUnit.test('isDate', function(assert) { + assert.notOk(_.isDate(100), 'numbers are not dates'); + assert.notOk(_.isDate({}), 'objects are not dates'); + assert.ok(_.isDate(new Date()), 'but dates are'); + }); + + QUnit.test('isRegExp', function(assert) { + assert.notOk(_.isRegExp(_.identity), 'functions are not RegExps'); + assert.ok(_.isRegExp(/identity/), 'but RegExps are'); + }); + + QUnit.test('isFinite', function(assert) { + assert.notOk(_.isFinite(void 0), 'undefined is not finite'); + assert.notOk(_.isFinite(null), 'null is not finite'); + assert.notOk(_.isFinite(NaN), 'NaN is not finite'); + assert.notOk(_.isFinite(Infinity), 'Infinity is not finite'); + assert.notOk(_.isFinite(-Infinity), '-Infinity is not finite'); + assert.ok(_.isFinite('12'), 'Numeric strings are numbers'); + assert.notOk(_.isFinite('1a'), 'Non numeric strings are not numbers'); + assert.notOk(_.isFinite(''), 'Empty strings are not numbers'); + var obj = new Number(5); + assert.ok(_.isFinite(obj), 'Number instances can be finite'); + assert.ok(_.isFinite(0), '0 is finite'); + assert.ok(_.isFinite(123), 'Ints are finite'); + assert.ok(_.isFinite(-12.44), 'Floats are finite'); + if (typeof Symbol === 'function') { + assert.notOk(_.isFinite(Symbol()), 'symbols are not numbers'); + assert.notOk(_.isFinite(Symbol('description')), 'described symbols are not numbers'); + assert.notOk(_.isFinite(Object(Symbol())), 'boxed symbols are not numbers'); + } + }); + + QUnit.test('isNaN', function(assert) { + assert.notOk(_.isNaN(void 0), 'undefined is not NaN'); + assert.notOk(_.isNaN(null), 'null is not NaN'); + assert.notOk(_.isNaN(0), '0 is not NaN'); + assert.notOk(_.isNaN(new Number(0)), 'wrapped 0 is not NaN'); + assert.ok(_.isNaN(NaN), 'but NaN is'); + assert.ok(_.isNaN(new Number(NaN)), 'wrapped NaN is still NaN'); + if (typeof Symbol !== 'undefined'){ + assert.notOk(_.isNaN(Symbol()), 'symbol is not NaN'); + } + }); + + QUnit.test('isNull', function(assert) { + assert.notOk(_.isNull(void 0), 'undefined is not null'); + assert.notOk(_.isNull(NaN), 'NaN is not null'); + assert.ok(_.isNull(null), 'but null is'); + }); + + QUnit.test('isUndefined', function(assert) { + assert.notOk(_.isUndefined(1), 'numbers are defined'); + assert.notOk(_.isUndefined(null), 'null is defined'); + assert.notOk(_.isUndefined(false), 'false is defined'); + assert.notOk(_.isUndefined(NaN), 'NaN is defined'); + assert.ok(_.isUndefined(), 'nothing is undefined'); + assert.ok(_.isUndefined(void 0), 'undefined is undefined'); + }); + + QUnit.test('isError', function(assert) { + assert.notOk(_.isError(1), 'numbers are not Errors'); + assert.notOk(_.isError(null), 'null is not an Error'); + assert.notOk(_.isError(Error), 'functions are not Errors'); + assert.ok(_.isError(new Error()), 'Errors are Errors'); + assert.ok(_.isError(new EvalError()), 'EvalErrors are Errors'); + assert.ok(_.isError(new RangeError()), 'RangeErrors are Errors'); + assert.ok(_.isError(new ReferenceError()), 'ReferenceErrors are Errors'); + assert.ok(_.isError(new SyntaxError()), 'SyntaxErrors are Errors'); + assert.ok(_.isError(new TypeError()), 'TypeErrors are Errors'); + assert.ok(_.isError(new URIError()), 'URIErrors are Errors'); + }); + + QUnit.test('tap', function(assert) { var intercepted = null; var interceptor = function(obj) { intercepted = obj; }; var returned = _.tap(1, interceptor); - equal(intercepted, 1, "passes tapped object to interceptor"); - equal(returned, 1, "returns tapped object"); + assert.strictEqual(intercepted, 1, 'passes tapped object to interceptor'); + assert.strictEqual(returned, 1, 'returns tapped object'); - returned = _([1,2,3]).chain(). + returned = _([1, 2, 3]).chain(). map(function(n){ return n * 2; }). max(). tap(interceptor). value(); - ok(returned == 6 && intercepted == 6, 'can use tapped objects in a chain'); + assert.strictEqual(returned, 6, 'can use tapped objects in a chain'); + assert.strictEqual(intercepted, returned, 'can use tapped objects in a chain'); }); - test("has", function () { - var obj = {foo: "bar", func: function () {} }; - ok (_.has(obj, "foo"), "has() checks that the object has a property."); - ok (_.has(obj, "baz") == false, "has() returns false if the object doesn't have the property."); - ok (_.has(obj, "func"), "has() works for functions too."); - obj.hasOwnProperty = null; - ok (_.has(obj, "foo"), "has() works even when the hasOwnProperty method is deleted."); - var child = {}; - child.prototype = obj; - ok (_.has(child, "foo") == false, "has() does not check the prototype chain for a property.") + QUnit.test('has', function(assert) { + var obj = {foo: 'bar', func: function(){}}; + assert.ok(_.has(obj, 'foo'), 'checks that the object has a property.'); + assert.notOk(_.has(obj, 'baz'), "returns false if the object doesn't have the property."); + assert.ok(_.has(obj, 'func'), 'works for functions too.'); + obj.hasOwnProperty = null; + assert.ok(_.has(obj, 'foo'), 'works even when the hasOwnProperty method is deleted.'); + var child = {}; + child.prototype = obj; + assert.notOk(_.has(child, 'foo'), 'does not check the prototype chain for a property.'); + assert.strictEqual(_.has(null, 'foo'), false, 'returns false for null'); + assert.strictEqual(_.has(void 0, 'foo'), false, 'returns false for undefined'); + + assert.ok(_.has({a: {b: 'foo'}}, ['a', 'b']), 'can check for nested properties.'); + assert.notOk(_.has({a: child}, ['a', 'foo']), 'does not check the prototype of nested props.'); }); -}); + + QUnit.test('property', function(assert) { + var stooge = {name: 'moe'}; + assert.strictEqual(_.property('name')(stooge), 'moe', 'should return the property with the given name'); + assert.strictEqual(_.property('name')(null), void 0, 'should return undefined for null values'); + assert.strictEqual(_.property('name')(void 0), void 0, 'should return undefined for undefined values'); + assert.strictEqual(_.property(null)('foo'), void 0, 'should return undefined for null object'); + assert.strictEqual(_.property('x')({x: null}), null, 'can fetch null values'); + assert.strictEqual(_.property('length')(null), void 0, 'does not crash on property access of non-objects'); + + // Deep property access + assert.strictEqual(_.property('a')({a: 1}), 1, 'can get a direct property'); + assert.strictEqual(_.property(['a', 'b'])({a: {b: 2}}), 2, 'can get a nested property'); + assert.strictEqual(_.property(['a'])({a: false}), false, 'can fetch falsy values'); + assert.strictEqual(_.property(['x', 'y'])({x: {y: null}}), null, 'can fetch null values deeply'); + assert.strictEqual(_.property(['x', 'y'])({x: null}), void 0, 'does not crash on property access of nested non-objects'); + assert.strictEqual(_.property([])({x: 'y'}), void 0, 'returns `undefined` for a path that is an empty array'); + }); + + QUnit.test('propertyOf', function(assert) { + var stoogeRanks = _.propertyOf({curly: 2, moe: 1, larry: 3}); + assert.strictEqual(stoogeRanks('curly'), 2, 'should return the property with the given name'); + assert.strictEqual(stoogeRanks(null), void 0, 'should return undefined for null values'); + assert.strictEqual(stoogeRanks(void 0), void 0, 'should return undefined for undefined values'); + assert.strictEqual(_.propertyOf({a: null})('a'), null, 'can fetch null values'); + + function MoreStooges() { this.shemp = 87; } + MoreStooges.prototype = {curly: 2, moe: 1, larry: 3}; + var moreStoogeRanks = _.propertyOf(new MoreStooges()); + assert.strictEqual(moreStoogeRanks('curly'), 2, 'should return properties from further up the prototype chain'); + + var nullPropertyOf = _.propertyOf(null); + assert.strictEqual(nullPropertyOf('curly'), void 0, 'should return undefined when obj is null'); + + var undefPropertyOf = _.propertyOf(void 0); + assert.strictEqual(undefPropertyOf('curly'), void 0, 'should return undefined when obj is undefined'); + + var deepPropertyOf = _.propertyOf({curly: {number: 2}, joe: {number: null}}); + assert.strictEqual(deepPropertyOf(['curly', 'number']), 2, 'can fetch nested properties of obj'); + assert.strictEqual(deepPropertyOf(['joe', 'number']), null, 'can fetch nested null properties of obj'); + }); + + QUnit.test('isMatch', function(assert) { + var moe = {name: 'Moe Howard', hair: true}; + var curly = {name: 'Curly Howard', hair: false}; + + assert.strictEqual(_.isMatch(moe, {hair: true}), true, 'Returns a boolean'); + assert.strictEqual(_.isMatch(curly, {hair: true}), false, 'Returns a boolean'); + + assert.strictEqual(_.isMatch(5, {__x__: void 0}), false, 'can match undefined props on primitives'); + assert.strictEqual(_.isMatch({__x__: void 0}, {__x__: void 0}), true, 'can match undefined props'); + + assert.strictEqual(_.isMatch(null, {}), true, 'Empty spec called with null object returns true'); + assert.strictEqual(_.isMatch(null, {a: 1}), false, 'Non-empty spec called with null object returns false'); + + _.each([null, void 0], function(item) { assert.strictEqual(_.isMatch(item, null), true, 'null matches null'); }); + _.each([null, void 0], function(item) { assert.strictEqual(_.isMatch(item, null), true, 'null matches {}'); }); + assert.strictEqual(_.isMatch({b: 1}, {a: void 0}), false, 'handles undefined values (1683)'); + + _.each([true, 5, NaN, null, void 0], function(item) { + assert.strictEqual(_.isMatch({a: 1}, item), true, 'treats primitives as empty'); + }); + + function Prototest() {} + Prototest.prototype.x = 1; + var specObj = new Prototest; + assert.strictEqual(_.isMatch({x: 2}, specObj), true, 'spec is restricted to own properties'); + + specObj.y = 5; + assert.strictEqual(_.isMatch({x: 1, y: 5}, specObj), true); + assert.strictEqual(_.isMatch({x: 1, y: 4}, specObj), false); + + assert.ok(_.isMatch(specObj, {x: 1, y: 5}), 'inherited and own properties are checked on the test object'); + + Prototest.x = 5; + assert.ok(_.isMatch({x: 5, y: 1}, Prototest), 'spec can be a function'); + + //null edge cases + var oCon = {constructor: Object}; + assert.deepEqual(_.map([null, void 0, 5, {}], _.partial(_.isMatch, _, oCon)), [false, false, false, true], 'doesnt falsy match constructor on undefined/null'); + }); + + QUnit.test('matcher', function(assert) { + var moe = {name: 'Moe Howard', hair: true}; + var curly = {name: 'Curly Howard', hair: false}; + var stooges = [moe, curly]; + + assert.strictEqual(_.matcher({hair: true})(moe), true, 'Returns a boolean'); + assert.strictEqual(_.matcher({hair: true})(curly), false, 'Returns a boolean'); + + assert.strictEqual(_.matcher({__x__: void 0})(5), false, 'can match undefined props on primitives'); + assert.strictEqual(_.matcher({__x__: void 0})({__x__: void 0}), true, 'can match undefined props'); + + assert.strictEqual(_.matcher({})(null), true, 'Empty spec called with null object returns true'); + assert.strictEqual(_.matcher({a: 1})(null), false, 'Non-empty spec called with null object returns false'); + + assert.strictEqual(_.find(stooges, _.matcher({hair: false})), curly, 'returns a predicate that can be used by finding functions.'); + assert.strictEqual(_.find(stooges, _.matcher(moe)), moe, 'can be used to locate an object exists in a collection.'); + assert.deepEqual(_.filter([null, void 0], _.matcher({a: 1})), [], 'Do not throw on null values.'); + + assert.deepEqual(_.filter([null, void 0], _.matcher(null)), [null, void 0], 'null matches null'); + assert.deepEqual(_.filter([null, void 0], _.matcher({})), [null, void 0], 'null matches {}'); + assert.deepEqual(_.filter([{b: 1}], _.matcher({a: void 0})), [], 'handles undefined values (1683)'); + + _.each([true, 5, NaN, null, void 0], function(item) { + assert.strictEqual(_.matcher(item)({a: 1}), true, 'treats primitives as empty'); + }); + + function Prototest() {} + Prototest.prototype.x = 1; + var specObj = new Prototest; + var protospec = _.matcher(specObj); + assert.strictEqual(protospec({x: 2}), true, 'spec is restricted to own properties'); + + specObj.y = 5; + protospec = _.matcher(specObj); + assert.strictEqual(protospec({x: 1, y: 5}), true); + assert.strictEqual(protospec({x: 1, y: 4}), false); + + assert.ok(_.matcher({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object'); + + Prototest.x = 5; + assert.ok(_.matcher(Prototest)({x: 5, y: 1}), 'spec can be a function'); + + // #1729 + var o = {b: 1}; + var m = _.matcher(o); + + assert.strictEqual(m({b: 1}), true); + o.b = 2; + o.a = 1; + assert.strictEqual(m({b: 1}), true, 'changing spec object doesnt change matches result'); + + + //null edge cases + var oCon = _.matcher({constructor: Object}); + assert.deepEqual(_.map([null, void 0, 5, {}], oCon), [false, false, false, true], 'doesnt falsy match constructor on undefined/null'); + }); + + QUnit.test('matches', function(assert) { + assert.strictEqual(_.matches, _.matcher, 'is an alias for matcher'); + }); + + QUnit.test('findKey', function(assert) { + var objects = { + a: {a: 0, b: 0}, + b: {a: 1, b: 1}, + c: {a: 2, b: 2} + }; + + assert.strictEqual(_.findKey(objects, function(obj) { + return obj.a === 0; + }), 'a'); + + assert.strictEqual(_.findKey(objects, function(obj) { + return obj.b * obj.a === 4; + }), 'c'); + + assert.strictEqual(_.findKey(objects, 'a'), 'b', 'Uses lookupIterator'); + + assert.strictEqual(_.findKey(objects, function(obj) { + return obj.b * obj.a === 5; + }), void 0); + + assert.strictEqual(_.findKey([1, 2, 3, 4, 5, 6], function(obj) { + return obj === 3; + }), '2', 'Keys are strings'); + + assert.strictEqual(_.findKey(objects, function(a) { + return a.foo === null; + }), void 0); + + _.findKey({a: {a: 1}}, function(a, key, obj) { + assert.strictEqual(key, 'a'); + assert.deepEqual(obj, {a: {a: 1}}); + assert.strictEqual(this, objects, 'called with context'); + }, objects); + + var array = [1, 2, 3, 4]; + array.match = 55; + assert.strictEqual(_.findKey(array, function(x) { return x === 55; }), 'match', 'matches array-likes keys'); + }); + + + QUnit.test('mapObject', function(assert) { + var obj = {a: 1, b: 2}; + var objects = { + a: {a: 0, b: 0}, + b: {a: 1, b: 1}, + c: {a: 2, b: 2} + }; + + assert.deepEqual(_.mapObject(obj, function(val) { + return val * 2; + }), {a: 2, b: 4}, 'simple objects'); + + assert.deepEqual(_.mapObject(objects, function(val) { + return _.reduce(val, function(memo, v){ + return memo + v; + }, 0); + }), {a: 0, b: 2, c: 4}, 'nested objects'); + + assert.deepEqual(_.mapObject(obj, function(val, key, o) { + return o[key] * 2; + }), {a: 2, b: 4}, 'correct keys'); + + assert.deepEqual(_.mapObject([1, 2], function(val) { + return val * 2; + }), {0: 2, 1: 4}, 'check behavior for arrays'); + + assert.deepEqual(_.mapObject(obj, function(val) { + return val * this.multiplier; + }, {multiplier: 3}), {a: 3, b: 6}, 'keep context'); + + assert.deepEqual(_.mapObject({a: 1}, function() { + return this.length; + }, [1, 2]), {a: 2}, 'called with context'); + + var ids = _.mapObject({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){ + return n.id; + }); + assert.deepEqual(ids, {length: void 0, 0: '1', 1: '2'}, 'Check with array-like objects'); + + // Passing a property name like _.pluck. + var people = {a: {name: 'moe', age: 30}, b: {name: 'curly', age: 50}}; + assert.deepEqual(_.mapObject(people, 'name'), {a: 'moe', b: 'curly'}, 'predicate string map to object properties'); + + _.each([null, void 0, 1, 'abc', [], {}, void 0], function(val){ + assert.deepEqual(_.mapObject(val, _.identity), {}, 'mapValue identity'); + }); + + var Proto = function(){ this.a = 1; }; + Proto.prototype.b = 1; + var protoObj = new Proto(); + assert.deepEqual(_.mapObject(protoObj, _.identity), {a: 1}, 'ignore inherited values from prototypes'); + + }); +}()); diff --git a/vendor/underscore/test/qunit-setup.js b/vendor/underscore/test/qunit-setup.js new file mode 100644 index 000000000..ad1e8cd8c --- /dev/null +++ b/vendor/underscore/test/qunit-setup.js @@ -0,0 +1,3 @@ +(function() { + QUnit.config.noglobals = true; +}()); diff --git a/vendor/underscore/test/utility.js b/vendor/underscore/test/utility.js index 52c5495fb..23289afd9 100644 --- a/vendor/underscore/test/utility.js +++ b/vendor/underscore/test/utility.js @@ -1,272 +1,452 @@ -$(document).ready(function() { - +(function() { + var _ = typeof require == 'function' ? require('..') : window._; var templateSettings; - module("Utility", { + QUnit.module('Utility', { - setup: function() { + beforeEach: function() { templateSettings = _.clone(_.templateSettings); }, - teardown: function() { + afterEach: function() { _.templateSettings = templateSettings; } }); - test("#750 - Return _ instance.", 2, function() { + if (typeof this == 'object') { + QUnit.test('noConflict', function(assert) { + var underscore = _.noConflict(); + assert.strictEqual(underscore.identity(1), 1); + if (typeof require != 'function') { + assert.strictEqual(this._, void 0, 'global underscore is removed'); + this._ = underscore; + } else if (typeof global !== 'undefined') { + delete global._; + } + }); + } + + if (typeof require == 'function') { + QUnit.test('noConflict (node vm)', function(assert) { + assert.expect(2); + var done = assert.async(); + var fs = require('fs'); + var vm = require('vm'); + var filename = __dirname + '/../underscore.js'; + fs.readFile(filename, function(err, content){ + var sandbox = vm.createScript( + content + 'this.underscore = this._.noConflict();', + filename + ); + var context = {_: 'oldvalue'}; + sandbox.runInNewContext(context); + assert.strictEqual(context._, 'oldvalue'); + assert.strictEqual(context.underscore.VERSION, _.VERSION); + + done(); + }); + }); + } + + QUnit.test('#750 - Return _ instance.', function(assert) { + assert.expect(2); var instance = _([]); - ok(_(instance) === instance); - ok(new _(instance) === instance); + assert.strictEqual(_(instance), instance); + assert.strictEqual(new _(instance), instance); }); - test("identity", function() { - var moe = {name : 'moe'}; - equal(_.identity(moe), moe, 'moe is the same as his identity'); + QUnit.test('identity', function(assert) { + var stooge = {name: 'moe'}; + assert.strictEqual(_.identity(stooge), stooge, 'stooge is the same as his identity'); }); - test("random", function() { + QUnit.test('constant', function(assert) { + var stooge = {name: 'moe'}; + assert.strictEqual(_.constant(stooge)(), stooge, 'should create a function that returns stooge'); + }); + + QUnit.test('noop', function(assert) { + assert.strictEqual(_.noop('curly', 'larry', 'moe'), void 0, 'should always return undefined'); + }); + + QUnit.test('random', function(assert) { var array = _.range(1000); var min = Math.pow(2, 31); var max = Math.pow(2, 62); - ok(_.every(array, function() { + assert.ok(_.every(array, function() { return _.random(min, max) >= min; - }), "should produce a random number greater than or equal to the minimum number"); + }), 'should produce a random number greater than or equal to the minimum number'); - ok(_.some(array, function() { + assert.ok(_.some(array, function() { return _.random(Number.MAX_VALUE) > 0; - }), "should produce a random number when passed `Number.MAX_VALUE`"); + }), 'should produce a random number when passed `Number.MAX_VALUE`'); }); - test("uniqueId", function() { + QUnit.test('now', function(assert) { + var diff = _.now() - new Date().getTime(); + assert.ok(diff <= 0 && diff > -5, 'Produces the correct time in milliseconds');//within 5ms + }); + + QUnit.test('uniqueId', function(assert) { var ids = [], i = 0; - while(i++ < 100) ids.push(_.uniqueId()); - equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); + while (i++ < 100) ids.push(_.uniqueId()); + assert.strictEqual(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids'); }); - test("times", function() { + QUnit.test('times', function(assert) { var vals = []; - _.times(3, function (i) { vals.push(i); }); - ok(_.isEqual(vals, [0,1,2]), "is 0 indexed"); + _.times(3, function(i) { vals.push(i); }); + assert.deepEqual(vals, [0, 1, 2], 'is 0 indexed'); // vals = []; _(3).times(function(i) { vals.push(i); }); - ok(_.isEqual(vals, [0,1,2]), "works as a wrapper"); + assert.deepEqual(vals, [0, 1, 2], 'works as a wrapper'); // collects return values - ok(_.isEqual([0, 1, 2], _.times(3, function(i) { return i; })), "collects return values"); + assert.deepEqual([0, 1, 2], _.times(3, function(i) { return i; }), 'collects return values'); - deepEqual(_.times(0, _.identity), []); - deepEqual(_.times(-1, _.identity), []); - deepEqual(_.times(parseFloat('-Infinity'), _.identity), []); + assert.deepEqual(_.times(0, _.identity), []); + assert.deepEqual(_.times(-1, _.identity), []); + assert.deepEqual(_.times(parseFloat('-Infinity'), _.identity), []); }); - test("mixin", function() { - _.mixin({ + QUnit.test('mixin', function(assert) { + var ret = _.mixin({ myReverse: function(string) { return string.split('').reverse().join(''); } }); - equal(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); - equal(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); + assert.strictEqual(ret, _, 'returns the _ object to facilitate chaining'); + assert.strictEqual(_.myReverse('panacea'), 'aecanap', 'mixed in a function to _'); + assert.strictEqual(_('champ').myReverse(), 'pmahc', 'mixed in a function to the OOP wrapper'); }); - test("_.escape", function() { - equal(_.escape("Curly & Moe"), "Curly & Moe"); - equal(_.escape('Curly & Moe\'s'), '<a href="http://moe.com">Curly & Moe's</a>'); - equal(_.escape("Curly & Moe"), "Curly &amp; Moe"); - equal(_.escape(null), ''); + QUnit.test('_.escape', function(assert) { + assert.strictEqual(_.escape(null), ''); }); - test("_.unescape", function() { - var string = "Curly & Moe"; - equal(_.unescape("Curly & Moe"), string); - equal(_.unescape('<a href="http://moe.com">Curly & Moe's</a>'), 'Curly & Moe\'s'); - equal(_.unescape("Curly &amp; Moe"), "Curly & Moe"); - equal(_.unescape(null), ''); - equal(_.unescape(_.escape(string)), string); + QUnit.test('_.unescape', function(assert) { + var string = 'Curly & Moe'; + assert.strictEqual(_.unescape(null), ''); + assert.strictEqual(_.unescape(_.escape(string)), string); + assert.strictEqual(_.unescape(string), string, 'don\'t unescape unnecessarily'); }); - test("template", function() { + // Don't care what they escape them to just that they're escaped and can be unescaped + QUnit.test('_.escape & unescape', function(assert) { + // test & (&) separately obviously + var escapeCharacters = ['<', '>', '"', '\'', '`']; + + _.each(escapeCharacters, function(escapeChar) { + var s = 'a ' + escapeChar + ' string escaped'; + var e = _.escape(s); + assert.notEqual(s, e, escapeChar + ' is escaped'); + assert.strictEqual(s, _.unescape(e), escapeChar + ' can be unescaped'); + + s = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar; + e = _.escape(s); + + assert.strictEqual(e.indexOf(escapeChar), -1, 'can escape multiple occurrences of ' + escapeChar); + assert.strictEqual(_.unescape(e), s, 'multiple occurrences of ' + escapeChar + ' can be unescaped'); + }); + + // handles multiple escape characters at once + var joiner = ' other stuff '; + var allEscaped = escapeCharacters.join(joiner); + allEscaped += allEscaped; + assert.ok(_.every(escapeCharacters, function(escapeChar) { + return allEscaped.indexOf(escapeChar) !== -1; + }), 'handles multiple characters'); + assert.ok(allEscaped.indexOf(joiner) >= 0, 'can escape multiple escape characters at the same time'); + + // test & -> & + var str = 'some string & another string & yet another'; + var escaped = _.escape(str); + + assert.notStrictEqual(escaped.indexOf('&'), -1, 'handles & aka &'); + assert.strictEqual(_.unescape(str), str, 'can unescape &'); + }); + + QUnit.test('template', function(assert) { var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); - var result = basicTemplate({thing : 'This'}); - equal(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); + var result = basicTemplate({thing: 'This'}); + assert.strictEqual(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); - var sansSemicolonTemplate = _.template("A <% this %> B"); - equal(sansSemicolonTemplate(), "A B"); + var sansSemicolonTemplate = _.template('A <% this %> B'); + assert.strictEqual(sansSemicolonTemplate(), 'A B'); - var backslashTemplate = _.template("<%= thing %> is \\ridanculous"); - equal(backslashTemplate({thing: 'This'}), "This is \\ridanculous"); + var backslashTemplate = _.template('<%= thing %> is \\ridanculous'); + assert.strictEqual(backslashTemplate({thing: 'This'}), 'This is \\ridanculous'); var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>'); - equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); + assert.strictEqual(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); - var fancyTemplate = _.template("
      <% \ - for (var key in people) { \ - %>
    • <%= people[key] %>
    • <% } %>
    "); - result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); - equal(result, "
    • Moe
    • Larry
    • Curly
    ", 'can run arbitrary javascript in templates'); + var fancyTemplate = _.template('
      <% ' + + ' for (var key in people) { ' + + '%>
    • <%= people[key] %>
    • <% } %>
    '); + result = fancyTemplate({people: {moe: 'Moe', larry: 'Larry', curly: 'Curly'}}); + assert.strictEqual(result, '
    • Moe
    • Larry
    • Curly
    ', 'can run arbitrary javascript in templates'); - var escapedCharsInJavascriptTemplate = _.template("
      <% _.each(numbers.split('\\n'), function(item) { %>
    • <%= item %>
    • <% }) %>
    "); - result = escapedCharsInJavascriptTemplate({numbers: "one\ntwo\nthree\nfour"}); - equal(result, "
    • one
    • two
    • three
    • four
    ", 'Can use escaped characters (e.g. \\n) in Javascript'); + var escapedCharsInJavaScriptTemplate = _.template('
      <% _.each(numbers.split("\\n"), function(item) { %>
    • <%= item %>
    • <% }) %>
    '); + result = escapedCharsInJavaScriptTemplate({numbers: 'one\ntwo\nthree\nfour'}); + assert.strictEqual(result, '
    • one
    • two
    • three
    • four
    ', 'Can use escaped characters (e.g. \\n) in JavaScript'); - var namespaceCollisionTemplate = _.template("<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
    \">
    <% }); %>"); + var namespaceCollisionTemplate = _.template('<%= pageCount %> <%= thumbnails[pageCount] %> <% _.each(thumbnails, function(p) { %>
    <% }); %>'); result = namespaceCollisionTemplate({ pageCount: 3, thumbnails: { - 1: "p1-thumbnail.gif", - 2: "p2-thumbnail.gif", - 3: "p3-thumbnail.gif" + 1: 'p1-thumbnail.gif', + 2: 'p2-thumbnail.gif', + 3: 'p3-thumbnail.gif' } }); - equal(result, "3 p3-thumbnail.gif
    "); + assert.strictEqual(result, '3 p3-thumbnail.gif
    '); - var noInterpolateTemplate = _.template("

    Just some text. Hey, I know this is silly but it aids consistency.

    "); + var noInterpolateTemplate = _.template('

    Just some text. Hey, I know this is silly but it aids consistency.

    '); result = noInterpolateTemplate(); - equal(result, "

    Just some text. Hey, I know this is silly but it aids consistency.

    "); + assert.strictEqual(result, '

    Just some text. Hey, I know this is silly but it aids consistency.

    '); var quoteTemplate = _.template("It's its, not it's"); - equal(quoteTemplate({}), "It's its, not it's"); + assert.strictEqual(quoteTemplate({}), "It's its, not it's"); - var quoteInStatementAndBody = _.template("<%\ - if(foo == 'bar'){ \ - %>Statement quotes and 'quotes'.<% } %>"); - equal(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); + var quoteInStatementAndBody = _.template('<% ' + + " if(foo == 'bar'){ " + + "%>Statement quotes and 'quotes'.<% } %>"); + assert.strictEqual(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'."); var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); - equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); + assert.strictEqual(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); - var template = _.template("<%- value %>"); - var result = template({value: " - - - - diff --git a/apps/documenteditor/mobile/app-dev.js b/apps/documenteditor/mobile/app-dev.js index 43c8bce3c..bc204844a 100644 --- a/apps/documenteditor/mobile/app-dev.js +++ b/apps/documenteditor/mobile/app-dev.js @@ -51,8 +51,6 @@ require.config({ text : '../vendor/requirejs-text/text', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jszip : '../vendor/jszip/jszip.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', api : 'api/documents/api', core : 'common/main/lib/core/application', extendes : 'common/mobile/utils/extendes', @@ -117,8 +115,6 @@ require([ 'analytics', 'gateway', 'locale', - 'jszip', - 'jsziputils', 'sockjs' ], function (Backbone, Framework7, Core) { Backbone.history.start(); diff --git a/apps/documenteditor/mobile/app.js b/apps/documenteditor/mobile/app.js index 0c8542859..9f6b018a8 100644 --- a/apps/documenteditor/mobile/app.js +++ b/apps/documenteditor/mobile/app.js @@ -51,8 +51,6 @@ require.config({ text : '../vendor/requirejs-text/text', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jszip : '../vendor/jszip/jszip.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/word/sdk-all-min', api : 'api/documents/api', @@ -102,9 +100,7 @@ require.config({ 'underscore', 'allfonts', 'xregexp', - 'sockjs', - 'jszip', - 'jsziputils' + 'sockjs' ] }, gateway: { diff --git a/apps/presentationeditor/embed/index.html b/apps/presentationeditor/embed/index.html index ceabd4062..b093b6577 100644 --- a/apps/presentationeditor/embed/index.html +++ b/apps/presentationeditor/embed/index.html @@ -323,8 +323,6 @@ - - - - diff --git a/apps/presentationeditor/main/app.js b/apps/presentationeditor/main/app.js index 8e30eb1c2..3a3f9a384 100644 --- a/apps/presentationeditor/main/app.js +++ b/apps/presentationeditor/main/app.js @@ -54,7 +54,6 @@ require.config({ jmousewheel : '../vendor/perfect-scrollbar/src/jquery.mousewheel', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/slide/sdk-all-min', api : 'api/documents/api', @@ -107,8 +106,7 @@ require.config({ 'underscore', 'allfonts', 'xregexp', - 'sockjs', - 'jsziputils' + 'sockjs' ] }, gateway: { diff --git a/apps/presentationeditor/main/app_dev.js b/apps/presentationeditor/main/app_dev.js index 46dc5c451..bca5796fa 100644 --- a/apps/presentationeditor/main/app_dev.js +++ b/apps/presentationeditor/main/app_dev.js @@ -54,7 +54,6 @@ require.config({ jmousewheel : '../vendor/perfect-scrollbar/src/jquery.mousewheel', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', api : 'api/documents/api', core : 'common/main/lib/core/application', notification : 'common/main/lib/core/NotificationCenter', @@ -120,7 +119,6 @@ require([ 'analytics', 'gateway', 'locale', - 'jsziputils', 'sockjs', 'xregexp', 'underscore' diff --git a/apps/presentationeditor/mobile/app-dev.js b/apps/presentationeditor/mobile/app-dev.js index 013a3cf16..022594cbb 100644 --- a/apps/presentationeditor/mobile/app-dev.js +++ b/apps/presentationeditor/mobile/app-dev.js @@ -52,8 +52,6 @@ require.config({ text : '../vendor/requirejs-text/text', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jszip : '../vendor/jszip/jszip.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', api : 'api/documents/api', core : 'common/main/lib/core/application', extendes : 'common/mobile/utils/extendes', @@ -118,8 +116,6 @@ require([ 'analytics', 'gateway', 'locale', - 'jszip', - 'jsziputils', 'sockjs' ], function (Backbone, Framework7, Core) { Backbone.history.start(); diff --git a/apps/presentationeditor/mobile/app.js b/apps/presentationeditor/mobile/app.js index 4df2708c1..b7c7d32ee 100644 --- a/apps/presentationeditor/mobile/app.js +++ b/apps/presentationeditor/mobile/app.js @@ -52,8 +52,6 @@ require.config({ text : '../vendor/requirejs-text/text', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jszip : '../vendor/jszip/jszip.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/slide/sdk-all-min', api : 'api/documents/api', @@ -103,9 +101,7 @@ require.config({ 'underscore', 'allfonts', 'xregexp', - 'sockjs', - 'jszip', - 'jsziputils' + 'sockjs' ] }, gateway: { diff --git a/apps/spreadsheeteditor/embed/index.html b/apps/spreadsheeteditor/embed/index.html index 99a46ebed..f45c3ecef 100644 --- a/apps/spreadsheeteditor/embed/index.html +++ b/apps/spreadsheeteditor/embed/index.html @@ -322,8 +322,6 @@ - - - - - diff --git a/apps/spreadsheeteditor/main/app.js b/apps/spreadsheeteditor/main/app.js index c397378b5..7ef127121 100644 --- a/apps/spreadsheeteditor/main/app.js +++ b/apps/spreadsheeteditor/main/app.js @@ -54,7 +54,6 @@ require.config({ jmousewheel : '../vendor/perfect-scrollbar/src/jquery.mousewheel', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/cell/sdk-all-min', api : 'api/documents/api', @@ -107,8 +106,7 @@ require.config({ 'underscore', 'allfonts', 'xregexp', - 'sockjs', - 'jsziputils' + 'sockjs' ] }, gateway: { diff --git a/apps/spreadsheeteditor/main/app_dev.js b/apps/spreadsheeteditor/main/app_dev.js index 2caaafbfe..1cc730a2d 100644 --- a/apps/spreadsheeteditor/main/app_dev.js +++ b/apps/spreadsheeteditor/main/app_dev.js @@ -54,7 +54,6 @@ require.config({ jmousewheel : '../vendor/perfect-scrollbar/src/jquery.mousewheel', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', api : 'api/documents/api', core : 'common/main/lib/core/application', notification : 'common/main/lib/core/NotificationCenter', @@ -120,7 +119,6 @@ require([ 'analytics', 'gateway', 'locale', - 'jsziputils', 'sockjs', 'underscore' ], function (Backbone, Bootstrap, Core) { diff --git a/apps/spreadsheeteditor/mobile/app-dev.js b/apps/spreadsheeteditor/mobile/app-dev.js index d3a031a77..ead495523 100644 --- a/apps/spreadsheeteditor/mobile/app-dev.js +++ b/apps/spreadsheeteditor/mobile/app-dev.js @@ -51,8 +51,6 @@ require.config({ text : '../vendor/requirejs-text/text', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jszip : '../vendor/jszip/jszip.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', api : 'api/documents/api', core : 'common/main/lib/core/application', extendes : 'common/mobile/utils/extendes', @@ -99,8 +97,6 @@ require([ 'analytics', 'gateway', 'locale', - 'jszip', - 'jsziputils', 'sockjs' ], function (Backbone, Framework7) { Backbone.history.start(); diff --git a/apps/spreadsheeteditor/mobile/app.js b/apps/spreadsheeteditor/mobile/app.js index f87754175..fd4cd8d79 100644 --- a/apps/spreadsheeteditor/mobile/app.js +++ b/apps/spreadsheeteditor/mobile/app.js @@ -51,8 +51,6 @@ require.config({ text : '../vendor/requirejs-text/text', xregexp : '../vendor/xregexp/xregexp-all-min', sockjs : '../vendor/sockjs/sockjs.min', - jszip : '../vendor/jszip/jszip.min', - jsziputils : '../vendor/jszip-utils/jszip-utils.min', allfonts : '../../sdkjs/common/AllFonts', sdk : '../../sdkjs/cell/sdk-all-min', api : 'api/documents/api', @@ -79,9 +77,7 @@ require.config({ 'underscore', 'allfonts', 'xregexp', - 'sockjs', - 'jszip', - 'jsziputils' + 'sockjs' ] }, backbone: { From 06e025b8034d941d56deba085be2a6966ddbb18e Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Mon, 17 Apr 2017 17:12:17 +0300 Subject: [PATCH 24/27] [SSE] Update translation. --- apps/spreadsheeteditor/main/locale/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/spreadsheeteditor/main/locale/en.json b/apps/spreadsheeteditor/main/locale/en.json index 8cb9d4962..70aaa91b8 100644 --- a/apps/spreadsheeteditor/main/locale/en.json +++ b/apps/spreadsheeteditor/main/locale/en.json @@ -1484,7 +1484,6 @@ "SSE.Views.TableSettings.textTableName": "Table Name", "SSE.Views.TableSettings.textTemplate": "Select From Template", "SSE.Views.TableSettings.textTotal": "Total", - "SSE.Views.TableSettings.textConvertRange": "Convert to range", "SSE.Views.TableSettings.textLongOperation": "Long operation", "SSE.Views.TableSettings.warnLongOperation": "The operation you are about to perform might take rather much time to complete.
    Are you sure you want to continue?", "SSE.Views.TableSettingsAdvanced.cancelButtonText": "Cancel", @@ -1565,6 +1564,7 @@ "SSE.Views.Toolbar.textDiagUpBorder": "Diagonal Up Border", "SSE.Views.Toolbar.textEntireCol": "Entire Column", "SSE.Views.Toolbar.textEntireRow": "Entire Row", + "SSE.Views.Toolbar.textFreezePanes": "Freeze Panes", "SSE.Views.Toolbar.textHideFBar": "Hide Formula Bar", "SSE.Views.Toolbar.textHideGridlines": "Hide Gridlines", "SSE.Views.Toolbar.textHideHeadings": "Hide Headings", From 1d7897095a53c651ba6831d65008ccb088c70f7b Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Tue, 18 Apr 2017 13:02:36 +0300 Subject: [PATCH 25/27] [DE] Use event asc_onSpellCheckInit instead of asc_getSpellCheckLanguages. --- apps/common/main/lib/component/Menu.js | 1 + .../main/app/controller/Main.js | 27 ++++++++++++------- .../main/app/view/DocumentHolder.js | 4 ++- .../documenteditor/main/app/view/Statusbar.js | 1 + apps/documenteditor/sdk_dev_scripts.js | 2 -- 5 files changed, 23 insertions(+), 12 deletions(-) diff --git a/apps/common/main/lib/component/Menu.js b/apps/common/main/lib/component/Menu.js index 5cbd8a087..cf6917e6a 100644 --- a/apps/common/main/lib/component/Menu.js +++ b/apps/common/main/lib/component/Menu.js @@ -405,6 +405,7 @@ define([ item.off('click').off('toggle'); item.remove(); }); + this.rendered && this.cmpEl.find('.menu-scroll').off('click').remove(); me.items = []; }, diff --git a/apps/documenteditor/main/app/controller/Main.js b/apps/documenteditor/main/app/controller/Main.js index 50867def2..fe4e7e7c3 100644 --- a/apps/documenteditor/main/app/controller/Main.js +++ b/apps/documenteditor/main/app/controller/Main.js @@ -105,7 +105,7 @@ define([ }); this._state = {isDisconnected: false, usersCount: 1, fastCoauth: true, lostEditingRights: false, licenseWarning: false}; - + this.languages = null; // Initialize viewport if (!Common.Utils.isBrowserSupported()){ @@ -141,6 +141,8 @@ define([ this.api.asc_registerCallback('asc_onDocumentName', _.bind(this.onDocumentName, this)); this.api.asc_registerCallback('asc_onPrintUrl', _.bind(this.onPrintUrl, this)); this.api.asc_registerCallback('asc_onMeta', _.bind(this.onMeta, this)); + this.api.asc_registerCallback('asc_onSpellCheckInit', _.bind(this.loadLanguages, this)); + Common.NotificationCenter.on('api:disconnect', _.bind(this.onCoAuthoringDisconnect, this)); Common.NotificationCenter.on('goback', _.bind(this.goBack, this)); @@ -868,7 +870,7 @@ define([ toolbarController.createDelayedElements(); documentHolderController.getView('DocumentHolder').createDelayedElements(); - me.loadLanguages(); + me.setLanguages(); var shapes = me.api.asc_getPropertyEditorShapes(); if (shapes) @@ -1655,15 +1657,15 @@ define([ } }, - loadLanguages: function() { - var apiLangs = this.api.asc_getSpellCheckLanguages(), - langs = [], info; + loadLanguages: function(apiLangs) { + var langs = [], info; _.each(apiLangs, function(lang, index, list){ - info = Common.util.LanguageInfo.getLocalLanguageName(lang.asc_getId()); + lang = parseInt(lang); + info = Common.util.LanguageInfo.getLocalLanguageName(lang); langs.push({ title: info[1], tip: info[0], - code: lang.asc_getId() + code: lang }); }, this); @@ -1673,8 +1675,15 @@ define([ return 0; }); - this.getApplication().getController('DocumentHolder').getView('DocumentHolder').setLanguages(langs); - this.getApplication().getController('Statusbar').setLanguages(langs); + this.languages = langs; + window.styles_loaded && this.setLanguages(); + }, + + setLanguages: function() { + if (this.languages && this.languages.length>0) { + this.getApplication().getController('DocumentHolder').getView('DocumentHolder').setLanguages(this.languages); + this.getApplication().getController('Statusbar').setLanguages(this.languages); + } }, onInsertTable: function() { diff --git a/apps/documenteditor/main/app/view/DocumentHolder.js b/apps/documenteditor/main/app/view/DocumentHolder.js index 57dc75acd..b29869844 100644 --- a/apps/documenteditor/main/app/view/DocumentHolder.js +++ b/apps/documenteditor/main/app/view/DocumentHolder.js @@ -3119,7 +3119,9 @@ define([ setLanguages: function(langs){ var me = this; - if (langs && langs.length > 0) { + if (langs && langs.length > 0 && me.langParaMenu && me.langTableMenu) { + me.langParaMenu.menu.removeAll(); + me.langTableMenu.menu.removeAll(); _.each(langs, function(lang, index){ me.langParaMenu.menu.addItem(new Common.UI.MenuItem({ caption : lang.title, diff --git a/apps/documenteditor/main/app/view/Statusbar.js b/apps/documenteditor/main/app/view/Statusbar.js index fd3fe1a48..325469076 100644 --- a/apps/documenteditor/main/app/view/Statusbar.js +++ b/apps/documenteditor/main/app/view/Statusbar.js @@ -466,6 +466,7 @@ define([ /** coauthoring end **/ reloadLanguages: function(array) { + this.langMenu.removeAll(); _.each(array, function(item) { this.langMenu.addItem({ iconCls : item['tip'], diff --git a/apps/documenteditor/sdk_dev_scripts.js b/apps/documenteditor/sdk_dev_scripts.js index b306b7857..65821e8d7 100644 --- a/apps/documenteditor/sdk_dev_scripts.js +++ b/apps/documenteditor/sdk_dev_scripts.js @@ -6,8 +6,6 @@ var sdk_dev_scrpipts = [ "../../../../sdkjs/common/docscoapicommon.js", "../../../../sdkjs/common/docscoapi.js", "../../../../sdkjs/common/spellcheckapi.js", - "../../../../sdkjs/common/spellCheckLanguage.js", - "../../../../sdkjs/common/spellCheckLanguagesAll.js", "../../../../sdkjs/common/apiCommon.js", "../../../../sdkjs/common/SerializeCommonWordExcel.js", "../../../../sdkjs/common/editorscommon.js", From f9ab4318e2f607f24a2e4f651b71894187e164b5 Mon Sep 17 00:00:00 2001 From: Julia Radzhabova Date: Tue, 18 Apr 2017 16:16:03 +0300 Subject: [PATCH 26/27] [SSE] Fix Bug 34790. --- .../main/app/controller/Toolbar.js | 20 +++++++++++++++++-- apps/spreadsheeteditor/main/locale/en.json | 3 +++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/apps/spreadsheeteditor/main/app/controller/Toolbar.js b/apps/spreadsheeteditor/main/app/controller/Toolbar.js index 2e7dc6f1b..afcb9ede3 100644 --- a/apps/spreadsheeteditor/main/app/controller/Toolbar.js +++ b/apps/spreadsheeteditor/main/app/controller/Toolbar.js @@ -810,7 +810,20 @@ define([ props = me.api.asc_getChartObject(); if (props) { props.putType(record.get('type')); - (ischartedit) ? me.api.asc_editChartDrawingObject(props) : me.api.asc_addChartDrawingObject(props); + var range = props.getRange(), + isvalid = me.api.asc_checkDataRange(Asc.c_oAscSelectionDialogType.Chart, range, true, !props.getInColumns(), props.getType()); + if (isvalid == Asc.c_oAscError.ID.No) { + (ischartedit) ? me.api.asc_editChartDrawingObject(props) : me.api.asc_addChartDrawingObject(props); + } else { + Common.UI.warning({ + msg: (isvalid == Asc.c_oAscError.ID.StockChartError) ? me.errorStockChart : ((isvalid == Asc.c_oAscError.ID.MaxDataSeriesError) ? me.errorMaxRows : me.txtInvalidRange), + callback: function() { + _.defer(function(btn) { + Common.NotificationCenter.trigger('edit:complete', me.toolbar); + }) + } + }); + } } } } @@ -3055,7 +3068,10 @@ define([ txtSorting: 'Sorting', txtSortSelected: 'Sort selected', textLongOperation: 'Long operation', - warnLongOperation: 'The operation you are about to perform might take rather much time to complete.
    Are you sure you want to continue?' + warnLongOperation: 'The operation you are about to perform might take rather much time to complete.
    Are you sure you want to continue?', + txtInvalidRange: 'ERROR! Invalid cells range', + errorMaxRows: 'ERROR! The maximum number of data series per chart is 255.', + errorStockChart: 'Incorrect row order. To build a stock chart place the data on the sheet in the following order:
    opening price, max price, min price, closing price.' }, SSE.Controllers.Toolbar || {})); }); \ No newline at end of file diff --git a/apps/spreadsheeteditor/main/locale/en.json b/apps/spreadsheeteditor/main/locale/en.json index 70aaa91b8..7f399ded7 100644 --- a/apps/spreadsheeteditor/main/locale/en.json +++ b/apps/spreadsheeteditor/main/locale/en.json @@ -365,6 +365,8 @@ "SSE.Controllers.Statusbar.warnDeleteSheet": "The worksheet might contain data. Are you sure you want to proceed?", "SSE.Controllers.Statusbar.zoomText": "Zoom {0}%", "SSE.Controllers.Toolbar.confirmAddFontName": "The font you are going to save is not available on the current device.
    The text style will be displayed using one of the system fonts, the saved font will be used when it is available.
    Do you want to continue?", + "SSE.Controllers.Toolbar.errorMaxRows": "ERROR! The maximum number of data series per chart is 255", + "SSE.Controllers.Toolbar.errorStockChart": "Incorrect row order. To build a stock chart place the data on the sheet in the following order:
    opening price, max price, min price, closing price.", "SSE.Controllers.Toolbar.textAccent": "Accents", "SSE.Controllers.Toolbar.textBracket": "Brackets", "SSE.Controllers.Toolbar.textCancel": "Cancel", @@ -372,6 +374,7 @@ "SSE.Controllers.Toolbar.textFraction": "Fractions", "SSE.Controllers.Toolbar.textFunction": "Functions", "SSE.Controllers.Toolbar.textIntegral": "Integrals", + "SSE.Controllers.Toolbar.txtInvalidRange": "ERROR! Invalid cells range", "SSE.Controllers.Toolbar.textLargeOperator": "Large Operators", "SSE.Controllers.Toolbar.textLimitAndLog": "Limits And Logarithms", "SSE.Controllers.Toolbar.textMatrix": "Matrices", From 1971c2e7cb33aafde06070ae0dae251ba2147026 Mon Sep 17 00:00:00 2001 From: SergeyLuzyanin Date: Fri, 21 Apr 2017 11:39:29 +0300 Subject: [PATCH 27/27] add spellcheckapi.js to presentation editor --- apps/presentationeditor/sdk_dev_scripts.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/presentationeditor/sdk_dev_scripts.js b/apps/presentationeditor/sdk_dev_scripts.js index b28e390be..3d2674dd3 100644 --- a/apps/presentationeditor/sdk_dev_scripts.js +++ b/apps/presentationeditor/sdk_dev_scripts.js @@ -5,6 +5,7 @@ var sdk_dev_scrpipts = [ "../../../../sdkjs/common/downloaderfiles.js", "../../../../sdkjs/common/docscoapicommon.js", "../../../../sdkjs/common/docscoapi.js", + "../../../../sdkjs/common/spellcheckapi.js", "../../../../sdkjs/common/apiCommon.js", "../../../../sdkjs/common/SerializeCommonWordExcel.js", "../../../../sdkjs/common/editorscommon.js",