<commit_before>// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

//Currently not in use

library dart_pad.embed_ui;

import 'dart:async';
import 'dart:html' hide Document;

import 'package:logging/logging.dart';
import 'package:route_hierarchical/client.dart';

import '../completion.dart';
import '../elements/elements.dart';
import '../dart_pad.dart';
import '../documentation.dart';
import '../context.dart';
import '../core/dependencies.dart';
import '../core/keys.dart';
import '../core/modules.dart';
import '../editing/editor.dart';
import '../modules/codemirror_module.dart';
import '../modules/dartservices_module.dart';
import '../modules/dart_pad_module.dart';
import '../polymer/base.dart';
import '../polymer/iron.dart';
import '../polymer/paper.dart';
import '../services/common.dart';
import '../services/dartservices.dart';
import '../services/_dartpadsupportservices.dart';
import '../services/execution_iframe.dart';
import '../sharing/gists.dart';
import '../src/ga.dart';
import '../src/sample.dart' as sample;

PlaygroundMobile get playground => _playground;

PlaygroundMobile _playground;
Analytics ga = new Analytics();

Logger _logger = new Logger('mobile');

void init() {
  _playground = new PlaygroundMobile();
}

enum _FileType {
  DART, CSS, HTML
}

class PlaygroundMobile {
  final String webURL = "https://dartpad.dartlang.org";
  
  PaperFab _runButton;
  PaperIconButton _exportButton;
  PaperIconButton _cancelButton;
  PaperIconButton _affirmButton;
  PaperIconButton _resetButton;
  PaperTabs _tabs;

  Map<_FileType, String> _lastRun;
  Router _router;
  
  CompileResponse _cachedCompile;
  Editor editor;
  PlaygroundContext _context;
  Future _analysisRequest;

  PaperToast _resetToast;
  PaperToast _messageToast;
  PaperToast _errorsToast;
  PaperDialog _messageDialog;
  PaperDialog _resetDialog;
  PaperDialog _exportDialog;
  PolymerElement _output;
  PaperProgress _editProgress;

  DivElement _docPanel;

  DocHandler docHandler;
  String _gistId;

  bool get _isCompletionActive => editor.completionActive;

  Future createNewGist() {
    return null;
  }

  ModuleManager modules = new ModuleManager();

  PlaygroundMobile() {
    //Asyncronous processing to load UI faster in parallel
    Timer.run(() {
      _createUi();
    });
    Timer.run(() {
      _initModules().then((_) => _initPlayground());
    });
  }

  void showHome(RouteEnterEvent event) {
    _logger.info('routed to showHome, ${window.location}, ${event.parameters}');

    Uri url = Uri.parse(window.location.toString());
    if (url.hasQuery) {
      String id = url.queryParameters['id'];
      if (isLegalGistId(id)) {
        _showGist(id);
        _storePreviousResult();
        return;
      }
    }

    _clearErrors();
    _setGistDescription(null);
    _setGistId(null);

    context.dartSource = sample.dartCode;
    context.htmlSource = sample.htmlCode;
    context.cssSource = sample.cssCode;
    _storePreviousResult();
  }

  void showGist(RouteEnterEvent event) {
    _logger.info('routed to showGist, ${window.location}, ${event.parameters}');

    String gistId = event.parameters['gist'];
    String page = event.parameters['page'];

    if (!isLegalGistId(gistId)) {
      showHome(event);
      return;
    }

    _showGist(gistId, run: page == 'run');
    _storePreviousResult();
  }

  void registerMessageToast() {
    _messageToast = new PaperToast();
    document.body.children.add(_messageToast.element);
  }

  void registerErrorToast() {
    if ($('#errorToast') != null) {
      _errorsToast = new PaperToast.from($('#errorToast'));
    } else {
      _errorsToast = new PaperToast();
    }
    _errorsToast.duration = 100000;
  }

  void registerResetToast() {
    if ($('#resetToast') != null) {
      _resetToast = new PaperToast.from($('#resetToast'));
    } else {
      _resetToast = new PaperToast();
    }
    _resetToast.duration = 3000;
  }

  void registerMessageDialog() {
    if ($("#messageDialog") != null) {
      _messageDialog = new PaperDialog.from($("#messageDialog"));
    }
  }

  void registerResetDialog() {
    if ($("#resetDialog") != null) {
      _resetDialog = new PaperDialog.from($("#resetDialog"));
    } else {
      _resetDialog = new PaperDialog();
    }
  }

  void registerExportDialog() {
    if ($("#exportDialog") != null) {
      _exportDialog = new PaperDialog.from($("#exportDialog"));
    } else {
      _exportDialog = new PaperDialog();
    }
  }

  void registerDocPanel() {
    if ($('#documentation') != null) {
      _docPanel = $('#documentation');
      _docPanel.innerHtml =
          "<div class='default-text-div layout horizontal center-center'><span class='default-text'>Documentation</span></div>";
    } else {
      _docPanel = new DivElement();
    }
  }

  void registerSelectorTabs() {
    if ($("#selector-tabs") != null) {
      _tabs = new PaperTabs.from($("#selector-tabs"));
      _tabs.ironSelect.listen((_) {
        String name = _tabs.selectedName;
        ga.sendEvent('edit', name);
        _context.switchTo(name);
      });
    }
  }

  void registerEditProgress() {
    if ($("#edit-progress") != null) {
      _editProgress = new PaperProgress.from($("#edit-progress"));
    }
  }

  void registerRunButton() {
    _runButton = new PaperFab.from($("#run-button"));
    _runButton = _runButton != null ? _runButton : new PaperFab();
    _runButton.clickAction(_handleRun);
  }

  void registerExportButton() {
    if ($('[icon="launch"]') != null) {
      _exportButton = new PaperIconButton.from($('[icon="launch"]'));
      _exportButton.clickAction(_exportDialog.toggle);
    }
  }

  void registerResetButton() {
    if ($('[icon="refresh"]') != null) {
      _resetButton = new PaperIconButton.from($('[icon="refresh"]'));
      _resetButton.clickAction(_resetDialog.toggle);
    }
  }

  void registerCancelRefreshButton() {
    if ($('#cancelButton') != null) {
      _cancelButton = new PaperIconButton.from($('#cancelButton'));
      _cancelButton.clickAction(_resetDialog.toggle);
    }
  }

  void registerAffirmRefreshButton() {
    if ($('#affirmButton') != null) {
      _affirmButton = new PaperIconButton.from($('#affirmButton'));
      _affirmButton.clickAction(() {
        _resetDialog.toggle();
        _reset();
      });
    }
  }

  void registerCancelExportButton() {
    if ($('#cancelExportButton') != null) {
      _cancelButton = new PaperIconButton.from($('#cancelExportButton'));
      _cancelButton.clickAction(_exportDialog.toggle);
    }
  }

  void registerAffirmExportButton() {
    if ($('#affirmExportButton') != null) {
      _affirmButton = new PaperIconButton.from($('#affirmExportButton'));
      _affirmButton.clickAction(() {
        _export();
        _exportDialog.toggle();
      });
    }
  }

  //Console must exist
  void registerConsole() {
    _output = new PolymerElement.from($("#console"));
  }

  void _createUi() {
    registerMessageToast();
    registerErrorToast();
    registerResetToast();
    registerMessageDialog();
    registerResetDialog();
    registerExportDialog();
    registerDocPanel();
    registerSelectorTabs();
    registerEditProgress();
    registerRunButton();
    registerExportButton();
    registerResetButton();
    registerCancelRefreshButton();
    registerAffirmRefreshButton();
    registerCancelExportButton();
    registerAffirmExportButton();
    registerConsole();

    _clearOutput();
  }

  void _export() {
    WindowBase exportWindow = window.open("", 'Export');
    PadSaveObject exportObject = new PadSaveObject()..html = context.htmlSource
        ..css = context.cssSource ..dart = context.dartSource;
    Future<UuidContainer> id = dartSupportServices.export(exportObject);
    id.then((UuidContainer container) {
      exportWindow.location.href='$webURL/index.html?export=${container.uuid}';
    });
  }

  void _reset() {
    _router = new Router();
    _router
      ..root.addRoute(name: 'home', defaultRoute: true, enter: showHome)
      ..root.addRoute(name: 'gist', path: '/:gist', enter: showGist)
      ..listen();
    _clearOutput();
  }

  void _showGist(String gistId, {bool run: false}) {
    gistLoader.loadGist(gistId).then((Gist gist) {
      _setGistDescription(gist.description);
      _setGistId(gist.id);

      GistFile dart =
          chooseGistFile(gist, ['main.dart'], (f) => f.endsWith('.dart'));
      GistFile html = chooseGistFile(gist, ['index.html', 'body.html']);
      GistFile css = chooseGistFile(gist, ['styles.css', 'style.css']);

      context.dartSource = dart == null ? '' : dart.content;
      context.htmlSource = html == null ? '' : extractHtmlBody(html.content);
      context.cssSource = css == null ? '' : css.content;

      _clearErrors();
      // Analyze and run it.
      Timer.run(() {
        _performAnalysis();
      });
    }).catchError((e) {
      print('Error loading gist ${gistId}.\n${e}');
      _showError('Error Loading Gist', '${gistId} - ${e}');
    });
  }

  Future _initModules() {
    modules.register(new DartPadModule());
    //modules.register(new MockAnalysisModule());
    //modules.register(new MockCompilerModule());
    modules.register(new DartServicesModule());
    modules.register(new DartSupportServicesModule());
    //modules.register(new AceModule());
    modules.register(new CodeMirrorModule());

    return modules.start();
  }

  void registerExecutionService() {
    if ($('#frame') != null) {
      deps[ExecutionService] = new ExecutionServiceIFrame($('#frame'));
    } else {
      deps[ExecutionService] = new ExecutionServiceIFrame(new IFrameElement());
    }
  }

  // Sync toolbar ratios to splitters.
  void _syncToolbar() {
    if ($('#toolbarLeftPanel') != null) {
      $('#toolbarLeftPanel').style.width = $('#leftPanel').style.width;
    }
  }

  // Determine if the query parameters for splitter ratios are valid (0 to 100).
  bool validFlex(String input) {
    return input != null &&
        double.parse(input) > 0.0 &&
        double.parse(input) < 100.0;
  }

  int roundFlex(double flex) => (flex * 10.0).round();

  void removeFlex(Element e) {
    e.classes.removeWhere((elementClass) => elementClass.startsWith('flex-'));
  }

  void _initPlayground() {
    // Set up the iframe execution.
    registerExecutionService();
    executionService.onStdout.listen(_showOutput);
    executionService.onStderr.listen((m) => _showOutput(m, error: true));

    // Set up the editing area.
    editor = editorFactory.createFromElement($('#editpanel'));
    //$('editpanel').children.first.attributes['flex'] = '';
    editor.resize();

    // TODO: Add a real code completer here.
    //editorFactory.registerCompleter('dart', new DartCompleter());

    // Listen for changes that would effect the documentation panel.
    editor.onMouseDown.listen((e) {
      // Delay to give codemirror time to process the mouse event.
      Timer.run(() {
        if (!_context.cursorPositionIsWhitespace()) {
          docHandler.generateDocWithText(_docPanel);
        }
      });
    });

    _context = new PlaygroundContext(editor);
    deps[Context] = _context;

    context.onModeChange
        .listen((_) => docHandler.generateDocWithText(_docPanel));

    _context.onHtmlReconcile.listen((_) {
      executionService.replaceHtml(_context.htmlSource);
    });

    _context.onCssReconcile.listen((_) {
      executionService.replaceCss(_context.cssSource);
    });

    _context.onDartReconcile.listen((_) => _performAnalysis());

    keys.bind(['alt-enter', 'ctrl-1'], () {
      editor.showCompletions(onlyShowFixes: true);
    }, "Quick fix");

    keys.bind(['ctrl-space', 'macctrl-space'], () {
      editor.showCompletions();
    }, "Completion");

    document.onKeyUp.listen((e) {
      if (editor.completionActive ||
          DocHandler.cursorKeys.contains(e.keyCode)) {
        docHandler.generateDoc(_docPanel);
      }
      _handleAutoCompletion(e);
    });

    editorFactory.registerCompleter(
        'dart', new DartCompleter(dartServices, _context._dartDoc));
    // Set up the gist loader.
    // TODO: Move to using the defaultFilters().
    deps[GistLoader] = new GistLoader();

    document.onKeyUp.listen((e) {
      if (editor.completionActive ||
          DocHandler.cursorKeys.contains(e.keyCode)) {
        docHandler.generateDocWithText(_docPanel);
      }
    });
    docHandler = new DocHandler(editor, _context);

    // Set up the splitters.
    Uri url = Uri.parse(window.location.toString());
    String v = url.queryParameters['verticalRatio'];
    String h = url.queryParameters['horizontalRatio'];
    num defaultVerticalRatio = 60;
    num defaultHorizontalRatio = 70;
    Element leftPanel = $('#leftPanel');
    Element rightPanel = $('#rightPanel');
    Element topPanel = $('#topPanel');
    Element bottomPanel = $('#bottomPanel');
    if (rightPanel != null && leftPanel != null) {
      num percent = validFlex(h) ? int.parse(h) : defaultVerticalRatio;
      leftPanel.style.width = '${leftPanel.parent.client.width * percent / 100}px';
      editor.resize();
      _syncToolbar();
    }
    if (topPanel != null && bottomPanel != null) {
      num percent = validFlex(v) ? int.parse(v) : defaultHorizontalRatio;
      topPanel.style.height = '${topPanel.parent.client.height * percent / 100}px';
    }
    var disablePointerEvents = () {
      if ($("#frame") != null) $("#frame").style.pointerEvents = "none";
    };
    var enablePointerEvents = () {
      if ($("#frame") != null) $("#frame").style.pointerEvents = "inherit";
    };
    if ($('vertical-splitter') != null) {
      _syncToolbar();
      DSplitter verticalSplitter = new DSplitter($('vertical-splitter'),
          onDragStart: disablePointerEvents, onDragEnd: enablePointerEvents);
      verticalSplitter.onPositionChanged.listen((pos) {
        editor.resize();
        _syncToolbar();
      });
    }
    if ($('horizontal-splitter') != null) {
      new DSplitter($('horizontal-splitter'),
          onDragStart: disablePointerEvents, onDragEnd: enablePointerEvents);
    }

    _finishedInit();
  }

  _finishedInit() {
    Timer.run(() {
      editor.resize();
    });

    _router = new Router()
      ..root.addRoute(name: 'home', defaultRoute: true, enter: showHome)
      ..root.addRoute(name: 'gist', path: '/:gist', enter: showGist)
      ..listen();
  }

  _handleAutoCompletion(KeyboardEvent e) {
    if (context.focusedEditor == 'dart' && editor.hasFocus) {
      if (e.keyCode == KeyCode.PERIOD) {
        editor.showCompletions(autoInvoked: true);
      }
    }

    if (!options.getValueBool('autopopup_code_completion') ||
        _isCompletionActive ||
        !editor.hasFocus) {
      return;
    }

    if (context.focusedEditor == 'dart') {
      RegExp exp = new RegExp(r"[A-Z]");
      if (exp.hasMatch(new String.fromCharCode(e.keyCode))) {
        editor.showCompletions(autoInvoked: true);
      }
    } else if (context.focusedEditor == "html") {
      // TODO: Autocompletion for attributes.
      if (printKeyEvent(e) == "shift-,") {
        editor.showCompletions(autoInvoked: true);
      }
    } else if (context.focusedEditor == "css") {
      RegExp exp = new RegExp(r"[A-Z]");
      if (exp.hasMatch(new String.fromCharCode(e.keyCode))) {
        editor.showCompletions(autoInvoked: true);
      }
    }
  }

  void _handleRun() {
    _clearOutput();
    ga.sendEvent('main', 'run');
    _runButton.disabled = true;

    if (_editProgress != null) {
      _editProgress.indeterminate = true;
      _editProgress.hidden(false);
    }

    if (_hasStoredRequest()) {
      try {
        CompileResponse response = _cachedCompile;
        if (executionService != null) {
          executionService.execute(
              _context.htmlSource, _context.cssSource, response.result);
        }
      } catch (e) {
        _showOutput('Error compiling to JavaScript:\n${e}', error: true);
        _showError('Error compiling to JavaScript', '${e}');
      }
      _runButton.disabled = false;
      if (_editProgress != null) {
        _editProgress.hidden(true);
        _editProgress.indeterminate = false;
      }
      return;
    }

    var input = new CompileRequest()..source = context.dartSource;
    _setBackup();
    _cachedCompile = null;
    dartServices
        .compile(input)
        .timeout(longServiceCallTimeout)
        .then((CompileResponse response) {
      _cachedCompile = response;
      if (executionService != null) {
        return executionService.execute(
            _context.htmlSource, _context.cssSource, response.result);
      }
    }).catchError((e) {
      _showOutput('Error compiling to JavaScript:\n${e}', error: true);
      _showError('Error compiling to JavaScript', '${e}');
    }).whenComplete(() {
      _runButton.disabled = false;
      if (_editProgress != null) {
        _editProgress.hidden(true);
        _editProgress.indeterminate = false;
      }
    });
  }

  bool _hasStoredRequest() {
    return (_lastRun != null && _lastRun[_FileType.DART] == context.dartSource && _lastRun[_FileType.CSS] == context.htmlSource 
        &&  _lastRun[_FileType.CSS] == context.cssSource && _cachedCompile != null);
  }
 
  void _storePreviousResult() {
    var input = new CompileRequest()..source = context.dartSource;
    _setBackup();
    dartServices
        .compile(input)
        .timeout(longServiceCallTimeout)
        .then((CompileResponse response) {
      _cachedCompile = response;
    });
  }
  
  void _performAnalysis() {
    var input = new SourceRequest()..source = _context.dartSource;
    Lines lines = new Lines(input.source);
    
    Future<AnalysisResults> request =
        dartServices.analyze(input).timeout(serviceCallTimeout);

    _analysisRequest = request;

    request.then((AnalysisResults result) {
      // Discard if we requested another analysis.
      if (_analysisRequest != request) return;

      // Discard if the document has been mutated since we requested analysis.
      if (input.source != _context.dartSource) return;

      _displayIssues(result.issues);

      _context.dartDocument.setAnnotations(result.issues
          .map((AnalysisIssue issue) {
        int startLine = lines.getLineForOffset(issue.charStart);
        int endLine =
            lines.getLineForOffset(issue.charStart + issue.charLength);

        Position start = new Position(
            startLine, issue.charStart - lines.offsetForLine(startLine));
        Position end = new Position(endLine, issue.charStart +
            issue.charLength -
            lines.offsetForLine(startLine));

        return new Annotation(issue.kind, issue.message, issue.line,
            start: start, end: end);
      }).toList());
    }).catchError((e) {
      _context.dartDocument.setAnnotations([]);
      _logger.severe(e);
    });
  }

  void _clearErrors() {
    _errorsToast.hide();
  }

  void _clearOutput() {
    _output.text = '';
    _output.element.innerHtml =
        "<div class='consoleTitle default-text-div layout horizontal center-center'><span class='default-text'>Console output</span></div>";
  }

  void _showOutput(String message, {bool error: false}) {
    if (message == null) return;
    Element title = $('.consoleTitle');
    if (title != null) title.hidden = true;
    message = message + '\n';
    SpanElement span = new SpanElement();
    span.classes.add(error ? 'errorOutput' : 'normal');
    span.text = message;
    _output.add(span);
    _output.element.scrollTop = _output.element.scrollHeight;
  }

  void _setGistDescription(String description) {
    if (description == null || description.isEmpty) {
      description = 'DartPad';
    }

    for (Element e in querySelectorAll('.sample-titles')) {
      e.text = description == null ? '' : description;
    }
  }
  
  void _setBackup () {
    _lastRun = new Map<_FileType, String>();
    _lastRun[_FileType.DART] = context.dartSource;
    _lastRun[_FileType.HTML] = context.htmlSource;
    _lastRun[_FileType.CSS] = context.cssSource;
  }
  
  void _setGistId(String id) {
    if (id == null || id.isEmpty) {
      _gistId = null;
    } else {
      _gistId = id;
    }
  }

  void _displayIssues(List<AnalysisIssue> issues) {
    if (issues.isEmpty) {
      _clearErrors();
    } else {
      Element errorElement = _errorsToast.element;

      errorElement.children.clear();

      issues.sort((a, b) => a.charStart - b.charStart);

      // Create an item for each issue.
      for (AnalysisIssue issue in issues) {
        DivElement error = new DivElement();
        error.classes.add('issue');
        error.classes.add('layout');<commit_msg>Getter function and renaming of backup<commit_after>// Copyright (c) 2015, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

//Currently not in use

library dart_pad.embed_ui;

import 'dart:async';
import 'dart:html' hide Document;

import 'package:logging/logging.dart';
import 'package:route_hierarchical/client.dart';

import '../completion.dart';
import '../elements/elements.dart';
import '../dart_pad.dart';
import '../documentation.dart';
import '../context.dart';
import '../core/dependencies.dart';
import '../core/keys.dart';
import '../core/modules.dart';
import '../editing/editor.dart';
import '../modules/codemirror_module.dart';
import '../modules/dartservices_module.dart';
import '../modules/dart_pad_module.dart';
import '../polymer/base.dart';
import '../polymer/iron.dart';
import '../polymer/paper.dart';
import '../services/common.dart';
import '../services/dartservices.dart';
import '../services/_dartpadsupportservices.dart';
import '../services/execution_iframe.dart';
import '../sharing/gists.dart';
import '../src/ga.dart';
import '../src/sample.dart' as sample;

PlaygroundMobile get playground => _playground;

PlaygroundMobile _playground;
Analytics ga = new Analytics();

Logger _logger = new Logger('mobile');

void init() {
  _playground = new PlaygroundMobile();
}

enum _FileType {
  DART, CSS, HTML
}

class PlaygroundMobile {
  final String webURL = "https://dartpad.dartlang.org";
  
  PaperFab _runButton;
  PaperIconButton _exportButton;
  PaperIconButton _cancelButton;
  PaperIconButton _affirmButton;
  PaperIconButton _resetButton;
  PaperTabs _tabs;

  Map<_FileType, String> _lastRun;
  Router _router;
  
  CompileResponse _cachedCompile;
  Editor editor;
  PlaygroundContext _context;
  Future _analysisRequest;

  PaperToast _resetToast;
  PaperToast _messageToast;
  PaperToast _errorsToast;
  PaperDialog _messageDialog;
  PaperDialog _resetDialog;
  PaperDialog _exportDialog;
  PolymerElement _output;
  PaperProgress _editProgress;

  DivElement _docPanel;

  DocHandler docHandler;
  String _gistId;

  bool get _isCompletionActive => editor.completionActive;

  Future createNewGist() {
    return null;
  }

  ModuleManager modules = new ModuleManager();

  PlaygroundMobile() {
    //Asyncronous processing to load UI faster in parallel
    Timer.run(() {
      _createUi();
    });
    Timer.run(() {
      _initModules().then((_) => _initPlayground());
    });
  }

  void showHome(RouteEnterEvent event) {
    _logger.info('routed to showHome, ${window.location}, ${event.parameters}');

    Uri url = Uri.parse(window.location.toString());
    if (url.hasQuery) {
      String id = url.queryParameters['id'];
      if (isLegalGistId(id)) {
        _showGist(id);
        _storePreviousResult();
        return;
      }
    }

    _clearErrors();
    _setGistDescription(null);
    _setGistId(null);

    context.dartSource = sample.dartCode;
    context.htmlSource = sample.htmlCode;
    context.cssSource = sample.cssCode;
    _storePreviousResult();
  }

  void showGist(RouteEnterEvent event) {
    _logger.info('routed to showGist, ${window.location}, ${event.parameters}');

    String gistId = event.parameters['gist'];
    String page = event.parameters['page'];

    if (!isLegalGistId(gistId)) {
      showHome(event);
      return;
    }

    _showGist(gistId, run: page == 'run');
    _storePreviousResult();
  }

  void registerMessageToast() {
    _messageToast = new PaperToast();
    document.body.children.add(_messageToast.element);
  }

  void registerErrorToast() {
    if ($('#errorToast') != null) {
      _errorsToast = new PaperToast.from($('#errorToast'));
    } else {
      _errorsToast = new PaperToast();
    }
    _errorsToast.duration = 100000;
  }

  void registerResetToast() {
    if ($('#resetToast') != null) {
      _resetToast = new PaperToast.from($('#resetToast'));
    } else {
      _resetToast = new PaperToast();
    }
    _resetToast.duration = 3000;
  }

  void registerMessageDialog() {
    if ($("#messageDialog") != null) {
      _messageDialog = new PaperDialog.from($("#messageDialog"));
    }
  }

  void registerResetDialog() {
    if ($("#resetDialog") != null) {
      _resetDialog = new PaperDialog.from($("#resetDialog"));
    } else {
      _resetDialog = new PaperDialog();
    }
  }

  void registerExportDialog() {
    if ($("#exportDialog") != null) {
      _exportDialog = new PaperDialog.from($("#exportDialog"));
    } else {
      _exportDialog = new PaperDialog();
    }
  }

  void registerDocPanel() {
    if ($('#documentation') != null) {
      _docPanel = $('#documentation');
      _docPanel.innerHtml =
          "<div class='default-text-div layout horizontal center-center'><span class='default-text'>Documentation</span></div>";
    } else {
      _docPanel = new DivElement();
    }
  }

  void registerSelectorTabs() {
    if ($("#selector-tabs") != null) {
      _tabs = new PaperTabs.from($("#selector-tabs"));
      _tabs.ironSelect.listen((_) {
        String name = _tabs.selectedName;
        ga.sendEvent('edit', name);
        _context.switchTo(name);
      });
    }
  }

  void registerEditProgress() {
    if ($("#edit-progress") != null) {
      _editProgress = new PaperProgress.from($("#edit-progress"));
    }
  }

  void registerRunButton() {
    _runButton = new PaperFab.from($("#run-button"));
    _runButton = _runButton != null ? _runButton : new PaperFab();
    _runButton.clickAction(_handleRun);
  }

  void registerExportButton() {
    if ($('[icon="launch"]') != null) {
      _exportButton = new PaperIconButton.from($('[icon="launch"]'));
      _exportButton.clickAction(_exportDialog.toggle);
    }
  }

  void registerResetButton() {
    if ($('[icon="refresh"]') != null) {
      _resetButton = new PaperIconButton.from($('[icon="refresh"]'));
      _resetButton.clickAction(_resetDialog.toggle);
    }
  }

  void registerCancelRefreshButton() {
    if ($('#cancelButton') != null) {
      _cancelButton = new PaperIconButton.from($('#cancelButton'));
      _cancelButton.clickAction(_resetDialog.toggle);
    }
  }

  void registerAffirmRefreshButton() {
    if ($('#affirmButton') != null) {
      _affirmButton = new PaperIconButton.from($('#affirmButton'));
      _affirmButton.clickAction(() {
        _resetDialog.toggle();
        _reset();
      });
    }
  }

  void registerCancelExportButton() {
    if ($('#cancelExportButton') != null) {
      _cancelButton = new PaperIconButton.from($('#cancelExportButton'));
      _cancelButton.clickAction(_exportDialog.toggle);
    }
  }

  void registerAffirmExportButton() {
    if ($('#affirmExportButton') != null) {
      _affirmButton = new PaperIconButton.from($('#affirmExportButton'));
      _affirmButton.clickAction(() {
        _export();
        _exportDialog.toggle();
      });
    }
  }

  //Console must exist
  void registerConsole() {
    _output = new PolymerElement.from($("#console"));
  }

  void _createUi() {
    registerMessageToast();
    registerErrorToast();
    registerResetToast();
    registerMessageDialog();
    registerResetDialog();
    registerExportDialog();
    registerDocPanel();
    registerSelectorTabs();
    registerEditProgress();
    registerRunButton();
    registerExportButton();
    registerResetButton();
    registerCancelRefreshButton();
    registerAffirmRefreshButton();
    registerCancelExportButton();
    registerAffirmExportButton();
    registerConsole();

    _clearOutput();
  }

  void _export() {
    WindowBase exportWindow = window.open("", 'Export');
    PadSaveObject exportObject = new PadSaveObject()..html = context.htmlSource
        ..css = context.cssSource ..dart = context.dartSource;
    Future<UuidContainer> id = dartSupportServices.export(exportObject);
    id.then((UuidContainer container) {
      exportWindow.location.href='$webURL/index.html?export=${container.uuid}';
    });
  }

  void _reset() {
    _router = new Router();
    _router
      ..root.addRoute(name: 'home', defaultRoute: true, enter: showHome)
      ..root.addRoute(name: 'gist', path: '/:gist', enter: showGist)
      ..listen();
    _clearOutput();
  }

  void _showGist(String gistId, {bool run: false}) {
    gistLoader.loadGist(gistId).then((Gist gist) {
      _setGistDescription(gist.description);
      _setGistId(gist.id);

      GistFile dart =
          chooseGistFile(gist, ['main.dart'], (f) => f.endsWith('.dart'));
      GistFile html = chooseGistFile(gist, ['index.html', 'body.html']);
      GistFile css = chooseGistFile(gist, ['styles.css', 'style.css']);

      context.dartSource = dart == null ? '' : dart.content;
      context.htmlSource = html == null ? '' : extractHtmlBody(html.content);
      context.cssSource = css == null ? '' : css.content;

      _clearErrors();
      // Analyze and run it.
      Timer.run(() {
        _performAnalysis();
      });
    }).catchError((e) {
      print('Error loading gist ${gistId}.\n${e}');
      _showError('Error Loading Gist', '${gistId} - ${e}');
    });
  }

  Future _initModules() {
    modules.register(new DartPadModule());
    //modules.register(new MockAnalysisModule());
    //modules.register(new MockCompilerModule());
    modules.register(new DartServicesModule());
    modules.register(new DartSupportServicesModule());
    //modules.register(new AceModule());
    modules.register(new CodeMirrorModule());

    return modules.start();
  }

  void registerExecutionService() {
    if ($('#frame') != null) {
      deps[ExecutionService] = new ExecutionServiceIFrame($('#frame'));
    } else {
      deps[ExecutionService] = new ExecutionServiceIFrame(new IFrameElement());
    }
  }

  // Sync toolbar ratios to splitters.
  void _syncToolbar() {
    if ($('#toolbarLeftPanel') != null) {
      $('#toolbarLeftPanel').style.width = $('#leftPanel').style.width;
    }
  }

  // Determine if the query parameters for splitter ratios are valid (0 to 100).
  bool validFlex(String input) {
    return input != null &&
        double.parse(input) > 0.0 &&
        double.parse(input) < 100.0;
  }

  int roundFlex(double flex) => (flex * 10.0).round();

  void removeFlex(Element e) {
    e.classes.removeWhere((elementClass) => elementClass.startsWith('flex-'));
  }

  void _initPlayground() {
    // Set up the iframe execution.
    registerExecutionService();
    executionService.onStdout.listen(_showOutput);
    executionService.onStderr.listen((m) => _showOutput(m, error: true));

    // Set up the editing area.
    editor = editorFactory.createFromElement($('#editpanel'));
    //$('editpanel').children.first.attributes['flex'] = '';
    editor.resize();

    // TODO: Add a real code completer here.
    //editorFactory.registerCompleter('dart', new DartCompleter());

    // Listen for changes that would effect the documentation panel.
    editor.onMouseDown.listen((e) {
      // Delay to give codemirror time to process the mouse event.
      Timer.run(() {
        if (!_context.cursorPositionIsWhitespace()) {
          docHandler.generateDocWithText(_docPanel);
        }
      });
    });

    _context = new PlaygroundContext(editor);
    deps[Context] = _context;

    context.onModeChange
        .listen((_) => docHandler.generateDocWithText(_docPanel));

    _context.onHtmlReconcile.listen((_) {
      executionService.replaceHtml(_context.htmlSource);
    });

    _context.onCssReconcile.listen((_) {
      executionService.replaceCss(_context.cssSource);
    });

    _context.onDartReconcile.listen((_) => _performAnalysis());

    keys.bind(['alt-enter', 'ctrl-1'], () {
      editor.showCompletions(onlyShowFixes: true);
    }, "Quick fix");

    keys.bind(['ctrl-space', 'macctrl-space'], () {
      editor.showCompletions();
    }, "Completion");

    document.onKeyUp.listen((e) {
      if (editor.completionActive ||
          DocHandler.cursorKeys.contains(e.keyCode)) {
        docHandler.generateDoc(_docPanel);
      }
      _handleAutoCompletion(e);
    });

    editorFactory.registerCompleter(
        'dart', new DartCompleter(dartServices, _context._dartDoc));
    // Set up the gist loader.
    // TODO: Move to using the defaultFilters().
    deps[GistLoader] = new GistLoader();

    document.onKeyUp.listen((e) {
      if (editor.completionActive ||
          DocHandler.cursorKeys.contains(e.keyCode)) {
        docHandler.generateDocWithText(_docPanel);
      }
    });
    docHandler = new DocHandler(editor, _context);

    // Set up the splitters.
    Uri url = Uri.parse(window.location.toString());
    String v = url.queryParameters['verticalRatio'];
    String h = url.queryParameters['horizontalRatio'];
    num defaultVerticalRatio = 60;
    num defaultHorizontalRatio = 70;
    Element leftPanel = $('#leftPanel');
    Element rightPanel = $('#rightPanel');
    Element topPanel = $('#topPanel');
    Element bottomPanel = $('#bottomPanel');
    if (rightPanel != null && leftPanel != null) {
      num percent = validFlex(h) ? int.parse(h) : defaultVerticalRatio;
      leftPanel.style.width = '${leftPanel.parent.client.width * percent / 100}px';
      editor.resize();
      _syncToolbar();
    }
    if (topPanel != null && bottomPanel != null) {
      num percent = validFlex(v) ? int.parse(v) : defaultHorizontalRatio;
      topPanel.style.height = '${topPanel.parent.client.height * percent / 100}px';
    }
    var disablePointerEvents = () {
      if ($("#frame") != null) $("#frame").style.pointerEvents = "none";
    };
    var enablePointerEvents = () {
      if ($("#frame") != null) $("#frame").style.pointerEvents = "inherit";
    };
    if ($('vertical-splitter') != null) {
      _syncToolbar();
      DSplitter verticalSplitter = new DSplitter($('vertical-splitter'),
          onDragStart: disablePointerEvents, onDragEnd: enablePointerEvents);
      verticalSplitter.onPositionChanged.listen((pos) {
        editor.resize();
        _syncToolbar();
      });
    }
    if ($('horizontal-splitter') != null) {
      new DSplitter($('horizontal-splitter'),
          onDragStart: disablePointerEvents, onDragEnd: enablePointerEvents);
    }

    _finishedInit();
  }

  _finishedInit() {
    Timer.run(() {
      editor.resize();
    });

    _router = new Router()
      ..root.addRoute(name: 'home', defaultRoute: true, enter: showHome)
      ..root.addRoute(name: 'gist', path: '/:gist', enter: showGist)
      ..listen();
  }

  _handleAutoCompletion(KeyboardEvent e) {
    if (context.focusedEditor == 'dart' && editor.hasFocus) {
      if (e.keyCode == KeyCode.PERIOD) {
        editor.showCompletions(autoInvoked: true);
      }
    }

    if (!options.getValueBool('autopopup_code_completion') ||
        _isCompletionActive ||
        !editor.hasFocus) {
      return;
    }

    if (context.focusedEditor == 'dart') {
      RegExp exp = new RegExp(r"[A-Z]");
      if (exp.hasMatch(new String.fromCharCode(e.keyCode))) {
        editor.showCompletions(autoInvoked: true);
      }
    } else if (context.focusedEditor == "html") {
      // TODO: Autocompletion for attributes.
      if (printKeyEvent(e) == "shift-,") {
        editor.showCompletions(autoInvoked: true);
      }
    } else if (context.focusedEditor == "css") {
      RegExp exp = new RegExp(r"[A-Z]");
      if (exp.hasMatch(new String.fromCharCode(e.keyCode))) {
        editor.showCompletions(autoInvoked: true);
      }
    }
  }

  void _handleRun() {
    _clearOutput();
    ga.sendEvent('main', 'run');
    _runButton.disabled = true;

    if (_editProgress != null) {
      _editProgress.indeterminate = true;
      _editProgress.hidden(false);
    }

    if (_hasStoredRequest()) {
      try {
        CompileResponse response = _cachedCompile;
        if (executionService != null) {
          executionService.execute(
              _context.htmlSource, _context.cssSource, response.result);
        }
      } catch (e) {
        _showOutput('Error compiling to JavaScript:\n${e}', error: true);
        _showError('Error compiling to JavaScript', '${e}');
      }
      _runButton.disabled = false;
      if (_editProgress != null) {
        _editProgress.hidden(true);
        _editProgress.indeterminate = false;
      }
      return;
    }

    var input = new CompileRequest()..source = context.dartSource;
    _setLastRunCondition();
    _cachedCompile = null;
    dartServices
        .compile(input)
        .timeout(longServiceCallTimeout)
        .then((CompileResponse response) {
      _cachedCompile = response;
      if (executionService != null) {
        return executionService.execute(
            _context.htmlSource, _context.cssSource, response.result);
      }
    }).catchError((e) {
      _showOutput('Error compiling to JavaScript:\n${e}', error: true);
      _showError('Error compiling to JavaScript', '${e}');
    }).whenComplete(() {
      _runButton.disabled = false;
      if (_editProgress != null) {
        _editProgress.hidden(true);
        _editProgress.indeterminate = false;
      }
    });
  }

  bool _hasStoredRequest() {
    return (_lastRun != null && _lastRun[_FileType.DART] == context.dartSource && _lastRun[_FileType.CSS] == context.htmlSource 
        &&  _lastRun[_FileType.CSS] == context.cssSource && _cachedCompile != null);
  }
 
  void _storePreviousResult() {
    var input = new CompileRequest()..source = context.dartSource;
    _setLastRunCondition();
    dartServices
        .compile(input)
        .timeout(longServiceCallTimeout)
        .then((CompileResponse response) {
      _cachedCompile = response;
    });
  }
  
  void _performAnalysis() {
    var input = new SourceRequest()..source = _context.dartSource;
    Lines lines = new Lines(input.source);
    
    Future<AnalysisResults> request =
        dartServices.analyze(input).timeout(serviceCallTimeout);

    _analysisRequest = request;

    request.then((AnalysisResults result) {
      // Discard if we requested another analysis.
      if (_analysisRequest != request) return;

      // Discard if the document has been mutated since we requested analysis.
      if (input.source != _context.dartSource) return;

      _displayIssues(result.issues);

      _context.dartDocument.setAnnotations(result.issues
          .map((AnalysisIssue issue) {
        int startLine = lines.getLineForOffset(issue.charStart);
        int endLine =
            lines.getLineForOffset(issue.charStart + issue.charLength);

        Position start = new Position(
            startLine, issue.charStart - lines.offsetForLine(startLine));
        Position end = new Position(endLine, issue.charStart +
            issue.charLength -
            lines.offsetForLine(startLine));

        return new Annotation(issue.kind, issue.message, issue.line,
            start: start, end: end);
      }).toList());
    }).catchError((e) {
      _context.dartDocument.setAnnotations([]);
      _logger.severe(e);
    });
  }

  void _clearErrors() {
    _errorsToast.hide();
  }

  void _clearOutput() {
    _output.text = '';
    _output.element.innerHtml =
        "<div class='consoleTitle default-text-div layout horizontal center-center'><span class='default-text'>Console output</span></div>";
  }

  void _showOutput(String message, {bool error: false}) {
    if (message == null) return;
    Element title = $('.consoleTitle');
    if (title != null) title.hidden = true;
    message = message + '\n';
    SpanElement span = new SpanElement();
    span.classes.add(error ? 'errorOutput' : 'normal');
    span.text = message;
    _output.add(span);
    _output.element.scrollTop = _output.element.scrollHeight;
  }

  void _setGistDescription(String description) {
    if (description == null || description.isEmpty) {
      description = 'DartPad';
    }

    for (Element e in querySelectorAll('.sample-titles')) {
      e.text = description == null ? '' : description;
    }
  }
  
  void _setLastRunCondition () {
    _lastRun = new Map<_FileType, String>();
    _lastRun[_FileType.DART] = context.dartSource;
    _lastRun[_FileType.HTML] = context.htmlSource;
    _lastRun[_FileType.CSS] = context.cssSource;
  }
  
  void _setGistId(String id) {
    if (id == null || id.isEmpty) {
      _gistId = null;
    } else {
      _gistId = id;
    }
  }

  void _displayIssues(List<AnalysisIssue> issues) {
    if (issues.isEmpty) {
      _clearErrors();
    } else {
      Element errorElement = _errorsToast.element;

      errorElement.children.clear();

      issues.sort((a, b) => a.charStart - b.charStart);

      // Create an item for each issue.
      for (AnalysisIssue issue in issues) {
        DivElement error = new DivElement();
        error.classes.add('issue');
        error.classes.add('layout');
{'fraction_non_alphanumeric': 0.14355541643591535, 'fraction_numerical': 0.0018299583443692664, 'mean_word_length': 3.1284294234592447, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 51, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2056417', 'n_tokens_mistral': 12751, 'n_tokens_neox': 12296, 'n_words': 2490}