| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| import ace from 'brace'; |
| import _ from 'lodash'; |
| |
| previewPanelDirective.$inject = ['$interval', '$timeout']; |
| |
| export default function previewPanelDirective($interval: ng.IIntervalService, $timeout: ng.ITimeoutService) { |
| let animation = {editor: null, stage: 0, start: 0, stop: 0}; |
| let prevContent = []; |
| |
| const Range = ace.acequire('ace/range').Range; |
| |
| const _clearSelection = (editor) => { |
| _.forEach(editor.session.getMarkers(false), (marker) => { |
| editor.session.removeMarker(marker.id); |
| }); |
| }; |
| |
| /** |
| * Switch to next stage of animation. |
| */ |
| const _animate = () => { |
| animation.stage += animation.step; |
| |
| const stage = animation.stage; |
| const editor = animation.editor; |
| |
| _clearSelection(editor); |
| |
| animation.selections.forEach((selection) => { |
| editor.session.addMarker(new Range(selection.start, 0, selection.stop, 0), |
| 'preview-highlight-' + stage, 'line', false); |
| }); |
| |
| if (stage === animation.finalStage) { |
| editor.animatePromise = null; |
| |
| if (animation.clearOnFinal) |
| _clearSelection(editor); |
| } |
| }; |
| |
| /** |
| * Selection with animation. |
| * |
| * @param editor Editor to show selection animation. |
| * @param selections Array of selection intervals. |
| * @param step Step of animation (1 or -1). |
| * @param stage Start stage of animation. |
| * @param finalStage Final stage of animation. |
| * @param clearOnFinal Boolean flat to clear selection on animation finish. |
| */ |
| const _fade = (editor, selections, step, stage, finalStage, clearOnFinal) => { |
| const promise = editor.animatePromise; |
| |
| if (promise) { |
| $interval.cancel(promise); |
| |
| _clearSelection(editor); |
| } |
| |
| animation = {editor, selections, step, stage, finalStage, clearOnFinal}; |
| |
| editor.animatePromise = $interval(_animate, 100, 10, false); |
| }; |
| |
| /** |
| * Show selections with animation. |
| * |
| * @param editor Editor to show selection. |
| * @param selections Array of selection intervals. |
| */ |
| const _fadeIn = (editor, selections) => { |
| _fade(editor, selections, 1, 0, 10, false); |
| }; |
| |
| /** |
| * Hide selections with animation. |
| * |
| * @param editor Editor to show selection. |
| * @param selections Array of selection intervals. |
| */ |
| const _fadeOut = (editor, selections) => { |
| _fade(editor, selections, -1, 10, 0, true); |
| }; |
| |
| const onChange = ([content, editor]) => { |
| const {clearPromise} = editor; |
| const {lines} = content; |
| |
| if (content.action === 'remove') |
| prevContent = lines; |
| else if (prevContent.length > 0 && lines.length > 0 && editor.attractAttention) { |
| if (clearPromise) { |
| $timeout.cancel(clearPromise); |
| |
| _clearSelection(editor); |
| } |
| |
| const selections = []; |
| |
| let newIx = 0; |
| let prevIx = 0; |
| |
| let prevLen = prevContent.length - (prevContent[prevContent.length - 1] === '' ? 1 : 0); |
| let newLen = lines.length - (lines[lines.length - 1] === '' ? 1 : 0); |
| |
| const removed = newLen < prevLen; |
| |
| let skipEnd = 0; |
| |
| let selected = false; |
| let scrollTo = -1; |
| |
| while (lines[newLen - 1] === prevContent[prevLen - 1] && newLen > 0 && prevLen > 0) { |
| prevLen -= 1; |
| newLen -= 1; |
| |
| skipEnd += 1; |
| } |
| |
| while (newIx < newLen || prevIx < prevLen) { |
| let start = -1; |
| let stop = -1; |
| |
| // Find an index of a first line with different text. |
| for (; (newIx < newLen || prevIx < prevLen) && start < 0; newIx++, prevIx++) { |
| if (newIx >= newLen || prevIx >= prevLen || lines[newIx] !== prevContent[prevIx]) { |
| start = newIx; |
| |
| break; |
| } |
| } |
| |
| if (start >= 0) { |
| // Find an index of a last line with different text by checking last string of old and new content in reverse order. |
| for (let i = start; i < newLen && stop < 0; i++) { |
| for (let j = prevIx; j < prevLen && stop < 0; j++) { |
| if (lines[i] === prevContent[j] && lines[i] !== '') { |
| stop = i; |
| |
| newIx = i; |
| prevIx = j; |
| |
| break; |
| } |
| } |
| } |
| |
| if (stop < 0) { |
| stop = newLen; |
| |
| newIx = newLen; |
| prevIx = prevLen; |
| } |
| |
| if (start === stop) { |
| if (removed) |
| start = Math.max(0, start - 1); |
| |
| stop = Math.min(newLen + skipEnd, stop + 1); |
| } |
| |
| if (start <= stop) { |
| selections.push({start, stop}); |
| |
| if (!selected) |
| scrollTo = start; |
| |
| selected = true; |
| } |
| } |
| } |
| |
| // Run clear selection one time. |
| if (selected) { |
| _fadeIn(editor, selections); |
| |
| editor.clearPromise = $timeout(() => { |
| _fadeOut(editor, selections); |
| |
| editor.clearPromise = null; |
| }, 2000); |
| |
| editor.scrollToRow(scrollTo); |
| } |
| |
| prevContent = []; |
| } |
| else |
| editor.attractAttention = true; |
| }; |
| |
| |
| const link = (scope, $element, $attrs, [igniteUiAceTabs1, igniteUiAceTabs2]) => { |
| const igniteUiAceTabs = igniteUiAceTabs1 || igniteUiAceTabs2; |
| |
| if (!igniteUiAceTabs) |
| return; |
| |
| igniteUiAceTabs.onLoad = (editor) => { |
| editor.setReadOnly(true); |
| editor.setOption('highlightActiveLine', false); |
| editor.setAutoScrollEditorIntoView(true); |
| editor.$blockScrolling = Infinity; |
| editor.attractAttention = false; |
| |
| const renderer = editor.renderer; |
| |
| renderer.setHighlightGutterLine(false); |
| renderer.setShowPrintMargin(false); |
| renderer.setOption('fontSize', '10px'); |
| renderer.setOption('maxLines', '50'); |
| |
| editor.setTheme('ace/theme/chrome'); |
| }; |
| |
| igniteUiAceTabs.onChange = onChange; |
| }; |
| |
| return { |
| restrict: 'C', |
| link, |
| require: ['?igniteUiAceTabs', '?^igniteUiAceTabs'] |
| }; |
| } |