Эх сурвалжийг харах

[spark] Initial support for Markdown

Enrico Berti 11 жил өмнө
parent
commit
2f40e42

+ 82 - 531
apps/spark/src/spark/templates/editor.mako

@@ -24,571 +24,122 @@ ${ commonheader(_('Query'), app_name, user) | n,unicode }
 
 ${ common.navbar('editor') }
 
-<div class="container-fluid">
-
-  <div class="row-fluid">
-    <div class="card card-small">
-      <div style="margin-bottom: 10px">
-        % if can_edit_name:
-        <h1 class="card-heading simple">
-          <a href="javascript:void(0);"
-             id="query-name"
-             data-type="text"
-             data-name="name"
-             data-value="${design.name}"
-             data-original-title="${ _('Application name') }"
-             data-placement="right">
-          </a>
-
-          <a href="javascript:void(0);"
-             id="query-description"
-             data-type="textarea"
-             data-name="description"
-             data-value="${design.desc}"
-             data-original-title="${ _('Application description') }"
-             data-placement="right" style="font-size: 14px; margin-left: 10px">
-          </a>
-
-        </h1>
-        % endif
-      </div>
-      <div class="card-body">
-        <p>
-          <span class="pull-right">
-            <button type="button" class="btn btn-primary uploadAppModalBtn">${ _('Upload app') }</button>
-            <button type="button" class="btn btn-primary createContextModalBtn">${ _('Create context') }</button>
-          </span>
-          <div></div><!-- this is to fix IE's quirkiness /-->
-          <form class="form-inline">
-            <span class="dropdown">
-              ${_('App name')}&nbsp;
-              <!-- ko if: $root.appNames().length == 0 -->
-              <a class="uploadAppModalBtn" href="javascript:void(0);">
-                ${ _("Missing, add one?") }
-              </a>
-              <!-- /ko -->
-              <a data-bind="if: $root.appName" data-toggle="dropdown" href="javascript:void(0);">
-                <strong data-bind="text: $root.appName().nice_name"></strong>
-                <i class="fa fa-caret-down"></i>
-              </a>
-              <ul data-bind="foreach: $root.appNames" class="dropdown-menu">
-                <li data-bind="click: $root.chooseAppName, text: nice_name" class="selectable"></li>
-              </ul>
-            </span>
-
-            <label style="margin-left: 20px; margin-right: 20px">
-              ${_('Class path')}
-              <input type="text" data-bind="value: $root.classPath" class="input-xlarge" placeholder="spark.jobserver.WordCountExample" />
-            </label>
-
-            ${_('Context')}&nbsp;
-            <label class="radio" style="margin-top:-2px">
-              <input type="radio" name="autoContext" value="true" data-bind="checked:$root.autoContext.forEditing" style="margin-right: 5px" />
-              ${ _('Automatic') }
-            </label>
-            <label class="radio" style="margin-left: 5px;margin-top:-2px">
-              <span class="dropdown" style="padding-left: 5px">
-                <!-- ko if: $root.contexts().length == 0 -->
-                <a class="createContextModalBtn" href="javascript:void(0);">
-                  ${ _("Create one?") }
-                </a>
-                <!-- /ko -->
-                <a data-bind="if: $root.context" data-toggle="dropdown" href="javascript:void(0);">
-                  <strong data-bind="text: $root.context().nice_name"></strong>
-                  <i class="fa fa-caret-down"></i>
-                </a>
-                <ul data-bind="foreach: $root.contexts" class="dropdown-menu">
-                  <li data-bind="click: $root.chooseContext, text: nice_name" class="selectable"></li>
-                </ul>
-              </span>
-              <input type="radio" name="autoContext" value="false" data-bind="checked:$root.autoContext.forEditing"/>
-            </label>
-          </form>
-        </p>
-      </div>
-    </div>
-  </div>
 
+<div class="container-fluid">
   <div class="row-fluid">
-    <div class="card card-small">
-      <h1 class="card-heading simple">
-        <a id="collapse-editor" href="javascript:void(0)" class="pull-right"><i class="fa fa-caret-up"></i></a>
-        ${_('Parameters')}
-      </h1>
-      <div class="card-body">
-        <p>
-          <div id="param-pane">
-          <div data-bind="css: {'hide': query.errors().length == 0}" class="hide alert alert-error">
-            <p><strong>${_('Your application has the following error(s):')}</strong></p>
-            <div data-bind="foreach: query.errors">
-              <p data-bind="text: $data" class="queryErrorMessage"></p>
-            </div>
-          </div>
-
-          <div data-bind="visible: query.params().length == 0">${_('No parameters defined yet.')}</div>
-
-          <table class="table-condensed">
-            <thead data-bind="visible: query.params().length > 0">
-              <tr>
-                <th>${ _('Name') }</th>
-                <th>${ _('Value') }</th>
-                <th/>
-              </tr>
-            </thead>
-            <tbody data-bind="foreach: query.params">
-              <tr>
-                <td><input type="text" class="input-large required propKey" data-bind="value: name" /></td>
-                <td>
-                <div class="input-append">
-                  <input type="text" data-bind="value: value" class="input-xxlarge" />
-                  <button class="btn fileChooserBtn" data-bind="click: $root.showFileChooser">..</button>
-                </div>
-                </td>
-                <td style="vertical-align: top">
-                  <a class="btn" href="#" data-bind="click: $root.removeParam">${ _('Delete') }</a>
-                </td>
-              </tr>
-            </tbody>
-          </table>
-          <div style="margin-top: 10px">
-            <a class="btn" href="#" data-bind="click: $root.addParam">${ _('Add') }</a>
-          </div>
-
-          <div class="actions" style="margin-top: 50px; margin-bottom: 20px">
-            <button data-bind="click: tryExecuteQuery, enable: $root.appNames().length > 0 && $root.classPath()"
-              type="button" id="executeQuery" class="btn btn-primary disable-feedback"
-              tabindex="2" data-loading-text="${ _("Executing...") }">
-              ${_('Execute')}
-            </button>
-            <button data-bind="click: trySaveQuery, visible: ($root.query.id() && $root.query.id() != -1)" type="button" class="btn">${_('Save')}</button>
-            <button data-bind="click: trySaveAsQuery, visible: $root.appNames().length > 0" type="button" class="btn">${_('Save as...')}</button>
-            &nbsp; ${_('or create a')} &nbsp;<a type="button" class="btn" href="${ url('spark:editor') }">${_('New application')}</a>
-            <span class="pull-right">
-              <a type="button" class="btn" data-bind="visible: rows().length != 0, attr: {'href': '${ url('spark:download_result') }' + $root.query.jobId()}">${_('Download')}</a>
-            </span>
-          </div>
+    <div class="span12">
+
+      <div class="card card-home">
+        <div id="snippets"></div>
+
+        <div class="question">
+          ${ _('What would you like to type?') }
+          <select id="codeMode">
+            <option value="markdown">Markdown</option>
+            <option value="text/x-impalaql">Impala</option>
+            <option value="text/x-hiveql">Hive</option>
+            <option value="text/x-pig">Pig</option>
+          </select>
         </div>
-          <div data-bind="css: {'hide': rows().length == 0}" class="hide">
-            <div class="scrollable">
-              <table id="resultTable" class="table table-striped table-condensed resultTable" cellpadding="0" cellspacing="0" data-tablescroller-min-height-disable="true" data-tablescroller-enforce-height="true">
-                <thead>
-                  <tr>
-                    <th>${ _('Key') }</th>
-                    <th>${ _('Value') }</th>
-                  </tr>
-                </thead>
-              </table>
-            </div>
-          </div>
-
-          <div data-bind="visible: resultsEmpty()">
-            <div class="scrollable">
-              <div class="row-fluid">
-                <div class="span10 offset1 center empty-wrapper">
-                  <i class="fa fa-frown-o"></i>
-                  <h1>${_('The server returned no results.')}</h1>
-                  <br/>
-                </div>
-              </div>
-            </div>
-          </div>
-
-          <div id="wait-info" class="hide">
-            <div class="scrollable">
-              <div class="row-fluid">
-                <div class="span10 offset1 center" style="padding: 30px">
-                  <!--[if !IE]><!--><i class="fa fa-spinner fa-spin" style="font-size: 60px; color: #DDD"></i><!--<![endif]-->
-                  <!--[if IE]><img src="/static/art/spinner.gif" /><![endif]-->
-                </div>
-              </div>
-            </div>
-          </div>
-        </p>
+        <textarea id="mainEditor"></textarea>
       </div>
-    </div>
-  </div>
-
-
 
-<div id="saveAsQueryModal" class="modal hide fade">
-  <div class="modal-header">
-    <a href="#" class="close" data-dismiss="modal">&times;</a>
-    <h3>${_('Save your query')}</h3>
-  </div>
-  <div class="modal-body">
-    <form class="form-horizontal">
-      <div class="control-group" id="saveas-query-name">
-        <label class="control-label">${_('Name')}</label>
-        <div class="controls">
-          <input data-bind="value: $root.query.name" type="text" class="input-xlarge">
-        </div>
-      </div>
-      <div class="control-group">
-        <label class="control-label">${_('Description')}</label>
-        <div class="controls">
-          <input data-bind="value: $root.query.description" type="text" class="input-xlarge">
-        </div>
-      </div>
-    </form>
-  </div>
-  <div class="modal-footer">
-    <button class="btn" data-dismiss="modal">${_('Cancel')}</button>
-    <button data-bind="click: modalSaveAsQuery" class="btn btn-primary">${_('Save')}</button>
-  </div>
-</div>
-
-<div id="chooseFile" class="modal hide fade">
-  <div class="modal-header">
-      <a href="#" class="close" data-dismiss="modal">&times;</a>
-      <h3>${_('Choose a file')}</h3>
-  </div>
-  <div class="modal-body">
-      <div id="filechooser">
-      </div>
-  </div>
-  <div class="modal-footer">
+    </div>
   </div>
 </div>
 
+<textarea id="tempEditor"></textarea>
 
-${ common.uploadAppModal() }
-${ common.createContextModal() }
-
-
-<style type="text/css">
-  h1 {
-    margin-bottom: 5px;
-  }
-
-  #filechooser {
-    min-height: 100px;
-    overflow-y: auto;
-  }
-
-  .control-group {
-    margin-bottom: 3px!important;
-  }
-
-  .control-group label {
-    float: left;
-    padding-top: 5px;
-    text-align: left;
-    width: 40px;
-  }
-
-  .hueBreadcrumb {
-    padding: 12px 14px;
-  }
-
-  .hueBreadcrumbBar {
-    padding: 0;
-    margin: 12px;
-  }
-
-  .hueBreadcrumbBar a {
-    color: #338BB8 !important;
-    display: inline !important;
-  }
-
-  .divider {
-    color: #CCC;
-  }
-
-  .param {
-    padding: 8px 8px 1px 8px;
-    margin-bottom: 5px;
-    border-bottom: 1px solid #EEE;
-  }
-
-  .remove {
-    float: right;
-  }
-
-  .selectable {
-    display: block;
-    list-style: none;
-    padding: 5px;
-    background: white;
-    cursor: pointer;
-  }
-
-  .selected, .selectable:hover {
-    background: #DDDDDD;
-  }
-
-  .editorError {
-    color: #B94A48;
-    background-color: #F2DEDE;
-    padding: 4px;
-    font-size: 11px;
-  }
-
-  .editable-empty, .editable-empty:hover {
-    color: #666;
-    font-style: normal;
-  }
-
-  .tooltip.left {
-    margin-left: -13px;
-  }
-
-  .scrollable {
-    overflow-x: auto;
-  }
-
-  .resultTable td, .resultTable th {
-    white-space: nowrap;
-  }
-
-  #navigatorTables li {
-    width: 95%;
-    white-space: nowrap;
-    overflow: hidden;
-    text-overflow: ellipsis;
-  }
+<link rel="stylesheet" href="/static/ext/css/codemirror.css">
+<link rel="stylesheet" href="/spark/static/css/spark.css">
 
-  #navigatorSearch, #navigatorNoTables {
-    display: none;
-  }
+<script src="/static/ext/js/codemirror-3.11.js"></script>
+<script src="/static/js/codemirror-pig.js"></script>
+<script src="/static/js/codemirror-hql.js"></script>
+<script src="/static/ext/js/codemirror-sql.js"></script>
+<script src="/static/ext/js/codemirror-markdown.js"></script>
+<script src="/static/ext/js/markdown.min.js"></script>
 
-  #navigator .card {
-    padding-bottom: 30px;
-  }
-
-</style>
-
-<script src="/static/ext/js/knockout-min.js" type="text/javascript" charset="utf-8"></script>
-<script src="/static/ext/js/knockout.mapping-2.3.2.js" type="text/javascript" charset="utf-8"></script>
-<script src="/spark/static/js/spark.vm.js"></script>
+<script type="text/javascript" charset="utf-8">
 
-<link href="/static/ext/css/bootstrap-editable.css" rel="stylesheet">
-<script src="/static/ext/js/bootstrap-editable.min.js"></script>
-<script src="/static/ext/js/bootstrap-editable.min.js"></script>
+  var mainCodeMirror, tempCodeMirror, tempCodeMirrorUpdateFn;
 
-<script src="/static/ext/js/jquery/plugins/jquery-fieldselection.js" type="text/javascript"></script>
-
-<script type="text/javascript" charset="utf-8">
-  var viewModel;
 
   $(document).ready(function(){
-    $("*[rel=tooltip]").tooltip({
-      placement: 'bottom'
-    });
 
-    $("#help").popover({
-      'title': "${_('Did you know?')}",
-      'content': $("#help-content").html(),
-      'trigger': 'hover',
-      'html': true
+    $("#codeMode").on("change", function(){
+      mainCodeMirror.setOption("mode", $("#codeMode").val());
     });
 
-    // Knockout
-    viewModel = new sparkViewModel();
-    viewModel.fetchAppNames();
-    viewModel.fetchContexts();
-    % if design_json:
-      viewModel.loadDesign(${ design_json | n,unicode });
-    % endif
-    % if job_id:
-      viewModel.openQuery("${ job_id }");
-    % endif
-    var hash = window.location.hash;
-    if (hash != "" && hash.indexOf("=") > -1) {
-      var hashKey = hash.split("=")[0].substr(1);
-      var hashValue = hash.split("=")[1];
-      if (hashKey == 'jobId') {
-        viewModel.openQuery(hashValue);
-      } else if (hashKey == 'applicationId') {
-        viewModel.query.appName(hashValue);
-      }
-
-    }
-    ko.applyBindings(viewModel);
-
-    $("#collapse-editor").on("click", function () {
-      if ($("#param-pane").is(":visible")) {
-        $("#param-pane").slideUp(100, function () {
-          $(".dataTables_wrapper").jHueTableScroller();
-          $(".resultTable").jHueTableExtender({
-            hintElement: "#jumpToColumnAlert",
-            fixedHeader: true,
-            firstColumnTooltip: true
-          });
+    function replaceWithEditor(snippet) {
+      snippet.html("");
+      snippet.off("click");
+      $(tempCodeMirror.getWrapperElement()).appendTo(snippet);
+      $(tempCodeMirror.getWrapperElement()).show();
+      tempCodeMirror.setOption("mode", snippet.data("mode"));
+      tempCodeMirror.setValue(snippet.data("source"))
+      tempCodeMirror.setSize("100%", "50px");
+      tempCodeMirrorUpdateFn = function(){
+        var _source = tempCodeMirror.getValue();
+        snippet.html(markdown.toHTML(_source)).data("source", _source);
+        snippet.on("click", function(){
+          replaceWithEditor($(this));
         });
-        $("#collapse-editor i").removeClass("fa-caret-up").addClass("fa-caret-down");
       }
-      else {
-        $("#param-pane").slideDown(100, function () {
-          $(".dataTables_wrapper").jHueTableScroller();
-          $(".resultTable").jHueTableExtender({
-            hintElement: "#jumpToColumnAlert",
-            fixedHeader: true,
-            firstColumnTooltip: true
-          });
-        });
-        $("#collapse-editor i").removeClass("fa-caret-down").addClass("fa-caret-up");
-      }
-    });
-  });
-
-
-  function modal(el) {
-    var el = $(el);
-    return function() {
-      el.modal('show');
-    };
-  }
-
-  function tryExecuteQuery() {
-    viewModel.executeQuery();
-  }
-
-  function trySaveQuery() {
-    if (viewModel.query.id() && viewModel.query.id() != -1) {
-      viewModel.saveQuery();
     }
-  }
 
-  function trySaveAsQuery() {
-    if (viewModel.appName() != null && viewModel.appName() != ""){
-      $('#saveAsQueryModal').modal('show');
-    }
-  }
-
-  $('.uploadAppModalBtn').click(function(){
-    $('#uploadAppModal').modal('show');
-  });
-
-  $('.createContextModalBtn').click(function(){
-    $('#createContextModal').modal('show');
-  });
-
-  function modalSaveAsQuery() {
-    if (viewModel.query.name()) {
-      viewModel.query.id(-1);
-      viewModel.saveQuery();
-      $('#saveas-query-name').removeClass('error');
-      $('#saveAsQueryModal').modal('hide');
-    } else {
-      $('#saveas-query-name').addClass('error');
+    function addSnippet(value, mode) {
+      var _snippet = $("<div>").addClass("snippet").html(markdown.toHTML(value)).appendTo($("#snippets")).data("source", value).data("mode", mode);
+      _snippet.on("click", function(){
+        replaceWithEditor($(this));
+      });
+      mainCodeMirror.setValue("");
     }
-  }
 
-  // Editables
-  $("#query-name").editable({
-    validate: function (value) {
-      if ($.trim(value) == '') {
-        return "${ _('This field is required.') }";
+    var mainEditor = $("#mainEditor")[0];
+    var tempEditor = $("#tempEditor")[0];
+
+    mainCodeMirror = CodeMirror(function (elt) {
+      mainEditor.parentNode.replaceChild(elt, mainEditor);
+    }, {
+      value: mainEditor.value,
+      readOnly: false,
+      lineNumbers: true,
+      mode: "markdown",
+      extraKeys: {
+        "Ctrl-Enter": function () {
+          addSnippet(mainCodeMirror.getValue(), mainCodeMirror.getOption("mode"));
+        }
+      },
+      onKeyEvent: function (e, s) {
+        if (s.type == "keyup") {
+        }
       }
-    },
-    success: function(response, newValue) {
-      viewModel.query.name(newValue);
-    },
-    emptytext: "${ _('Application name') }"
-  });
-
-  $("#query-description").editable({
-    success: function(response, newValue) {
-      viewModel.query.description(newValue);
-    },
-    emptytext: "${ _('Empty description') }"
-  });
-
-
-  // Events and datatables
-  $(document).on('saved.query', function() {
-    $.jHueNotify.info("${_('Application saved successfully!')}")
-  });
-
-  $(document).on('savedas.query', function() {
-    window.location.href = "${ url('spark:editor') }" + viewModel.query.id();
-  });
-
-  var dataTable = null;
-
-  function cleanResultsTable() {
-    if (dataTable) {
-      dataTable.fnClearTable();
-      dataTable.fnDestroy();
-      viewModel.rows.valueHasMutated();
-      dataTable = null;
-    }
-  }
-
-  function addResults(viewModel, dataTable, index, pageSize) {
-    $.each(viewModel.rows.slice(index, index + pageSize), function(row_index, row) {
-      dataTable.fnAddData(row);
     });
-  }
 
-  function resultsTable() {
-    if (! dataTable) {
-      dataTable = $(".resultTable").dataTable({
-        "bPaginate": false,
-        "bLengthChange": false,
-        "bInfo": false,
-        "aaSorting": [],
-        "oLanguage": {
-          "sEmptyTable": "${_('No data available')}",
-          "sZeroRecords": "${_('No matching records')}"
-        },
-        "fnDrawCallback": function( oSettings ) {
-          $(".resultTable").jHueTableExtender({
-            hintElement: "#jumpToColumnAlert",
-            fixedHeader: true,
-            firstColumnTooltip: true
-          });
+    tempCodeMirror = CodeMirror(function (elt) {
+      tempEditor.parentNode.replaceChild(elt, tempEditor);
+    }, {
+      value: tempEditor.value,
+      readOnly: false,
+      lineNumbers: true,
+      mode: "markdown",
+      extraKeys: {
+        "Ctrl-Enter": function () {
+          tempCodeMirrorUpdateFn();
         }
-      });
-      $(".dataTables_filter").hide();
-      $(".dataTables_wrapper").jHueTableScroller();
-
-      // Automatic results grower
-      var dataTableEl = $(".dataTables_wrapper");
-      var index = 0;
-      var pageSize = 100;
-      dataTableEl.on("scroll", function (e) {
-        if (dataTableEl.scrollTop() + dataTableEl.outerHeight() + 20 > dataTableEl[0].scrollHeight && dataTable) {
-          addResults(viewModel, dataTable, index, pageSize);
-          index += pageSize;
+      },
+      onKeyEvent: function (e, s) {
+        if (s.type == "keyup") {
         }
-      });
-      addResults(viewModel, dataTable, index, pageSize);
-      index += pageSize;
-
-      $(".resultTable").width($(".resultTable").parent().width());
-    }
-  }
-
-  $(document).on('execute.query', function() {
-    $('#wait-info').show();
-    $("#executeQuery").button("loading");
-    cleanResultsTable();
-  });
-
-  $(document).on('executed.query', function() {
-    $('#wait-info').hide();
-    $("#executeQuery").button("reset");
-    resultsTable();
-  });
-
-  $(document).on("created.context", function() {
-      $("#createContextModal").modal("hide");
-      $("#createContextBtn").button("reset");
-      $("input[data-default]").each(function(){
-        $(this).val($(this).data("default"));
-      });
+      }
     });
 
-  // Server error handling.
-  $(document).on('server.error', function(e, data) {
-    $(document).trigger('error', "${_('Server error occured: ')}" + data.error);
-  });
+    $(tempCodeMirror.getWrapperElement()).hide();
 
-  $(document).on('server.unmanageable_error', function(e, responseText) {
-    $(document).trigger('error', "${_('Unmanageable server error occured: ')}" + responseText);
   });
 
+
 </script>
 
 ${ commonfooter(messages) | n,unicode }

+ 2 - 20
apps/spark/src/spark/views.py

@@ -41,26 +41,8 @@ LOG = logging.getLogger(__name__)
 
 
 @view_error_handler
-def editor(request, design_id=None, query_history_id=None):
-  api = get_api(request.user)
-  jobs = api.jobs()
-  if design_id is not None and not design_id.isdigit():
-    job_id, design_id = design_id, None
-  else:
-    job_id = None
-
-  action = request.path
-  app_name = get_app_name(request)
-  query_type = beeswax_models.SavedQuery.TYPES_MAPPING[app_name]
-  design = safe_get_design(request, query_type, design_id)
-
-  return render('editor.mako', request, {
-    'action': action,
-    'design': design,
-    'design_json': json.dumps(design_to_dict(design)),
-    'can_edit_name': design.id and not design.is_auto,
-    'job_id': job_id,
-  })
+def editor(request):
+  return render('editor.mako', request, {})
 
 @view_error_handler
 def list_jobs(request):

+ 26 - 0
apps/spark/static/css/spark.css

@@ -0,0 +1,26 @@
+/*
+ Licensed to Cloudera, Inc. under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  Cloudera, Inc. 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.
+*/
+
+.question {
+  margin: 10px;
+}
+
+.snippet {
+  border-bottom: 2px solid #F0F0F0;
+  margin: 10px;
+}

+ 765 - 0
desktop/core/static/ext/js/codemirror-markdown.js

@@ -0,0 +1,765 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
+
+  var htmlFound = CodeMirror.modes.hasOwnProperty("xml");
+  var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
+
+  function getMode(name) {
+    if (CodeMirror.findModeByName) {
+      var found = CodeMirror.findModeByName(name);
+      if (found) name = found.mime || found.mimes[0];
+    }
+    var mode = CodeMirror.getMode(cmCfg, name);
+    return mode.name == "null" ? null : mode;
+  }
+
+  // Should characters that affect highlighting be highlighted separate?
+  // Does not include characters that will be output (such as `1.` and `-` for lists)
+  if (modeCfg.highlightFormatting === undefined)
+    modeCfg.highlightFormatting = false;
+
+  // Maximum number of nested blockquotes. Set to 0 for infinite nesting.
+  // Excess `>` will emit `error` token.
+  if (modeCfg.maxBlockquoteDepth === undefined)
+    modeCfg.maxBlockquoteDepth = 0;
+
+  // Should underscores in words open/close em/strong?
+  if (modeCfg.underscoresBreakWords === undefined)
+    modeCfg.underscoresBreakWords = true;
+
+  // Turn on fenced code blocks? ("```" to start/end)
+  if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false;
+
+  // Turn on task lists? ("- [ ] " and "- [x] ")
+  if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
+
+  // Turn on strikethrough syntax
+  if (modeCfg.strikethrough === undefined)
+    modeCfg.strikethrough = false;
+
+  var codeDepth = 0;
+
+  var header   = 'header'
+  ,   code     = 'comment'
+  ,   quote    = 'quote'
+  ,   list1    = 'variable-2'
+  ,   list2    = 'variable-3'
+  ,   list3    = 'keyword'
+  ,   hr       = 'hr'
+  ,   image    = 'tag'
+  ,   formatting = 'formatting'
+  ,   linkinline = 'link'
+  ,   linkemail = 'link'
+  ,   linktext = 'link'
+  ,   linkhref = 'string'
+  ,   em       = 'em'
+  ,   strong   = 'strong'
+  ,   strikethrough = 'strikethrough';
+
+  var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/
+  ,   ulRE = /^[*\-+]\s+/
+  ,   olRE = /^[0-9]+\.\s+/
+  ,   taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
+  ,   atxHeaderRE = /^#+/
+  ,   setextHeaderRE = /^(?:\={1,}|-{1,})$/
+  ,   textRE = /^[^#!\[\]*_\\<>` "'(~]+/;
+
+  function switchInline(stream, state, f) {
+    state.f = state.inline = f;
+    return f(stream, state);
+  }
+
+  function switchBlock(stream, state, f) {
+    state.f = state.block = f;
+    return f(stream, state);
+  }
+
+
+  // Blocks
+
+  function blankLine(state) {
+    // Reset linkTitle state
+    state.linkTitle = false;
+    // Reset EM state
+    state.em = false;
+    // Reset STRONG state
+    state.strong = false;
+    // Reset strikethrough state
+    state.strikethrough = false;
+    // Reset state.quote
+    state.quote = 0;
+    if (!htmlFound && state.f == htmlBlock) {
+      state.f = inlineNormal;
+      state.block = blockNormal;
+    }
+    // Reset state.trailingSpace
+    state.trailingSpace = 0;
+    state.trailingSpaceNewLine = false;
+    // Mark this line as blank
+    state.thisLineHasContent = false;
+    return null;
+  }
+
+  function blockNormal(stream, state) {
+
+    var sol = stream.sol();
+
+    var prevLineIsList = (state.list !== false);
+    if (state.list !== false && state.indentationDiff >= 0) { // Continued list
+      if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
+        state.indentation -= state.indentationDiff;
+      }
+      state.list = null;
+    } else if (state.list !== false && state.indentation > 0) {
+      state.list = null;
+      state.listDepth = Math.floor(state.indentation / 4);
+    } else if (state.list !== false) { // No longer a list
+      state.list = false;
+      state.listDepth = 0;
+    }
+
+    var match = null;
+    if (state.indentationDiff >= 4) {
+      state.indentation -= 4;
+      stream.skipToEnd();
+      return code;
+    } else if (stream.eatSpace()) {
+      return null;
+    } else if (match = stream.match(atxHeaderRE)) {
+      state.header = match[0].length <= 6 ? match[0].length : 6;
+      if (modeCfg.highlightFormatting) state.formatting = "header";
+      state.f = state.inline;
+      return getType(state);
+    } else if (state.prevLineHasContent && (match = stream.match(setextHeaderRE))) {
+      state.header = match[0].charAt(0) == '=' ? 1 : 2;
+      if (modeCfg.highlightFormatting) state.formatting = "header";
+      state.f = state.inline;
+      return getType(state);
+    } else if (stream.eat('>')) {
+      state.indentation++;
+      state.quote = sol ? 1 : state.quote + 1;
+      if (modeCfg.highlightFormatting) state.formatting = "quote";
+      stream.eatSpace();
+      return getType(state);
+    } else if (stream.peek() === '[') {
+      return switchInline(stream, state, footnoteLink);
+    } else if (stream.match(hrRE, true)) {
+      return hr;
+    } else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
+      var listType = null;
+      if (stream.match(ulRE, true)) {
+        listType = 'ul';
+      } else {
+        stream.match(olRE, true);
+        listType = 'ol';
+      }
+      state.indentation += 4;
+      state.list = true;
+      state.listDepth++;
+      if (modeCfg.taskLists && stream.match(taskListRE, false)) {
+        state.taskList = true;
+      }
+      state.f = state.inline;
+      if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
+      return getType(state);
+    } else if (modeCfg.fencedCodeBlocks && stream.match(/^```[ \t]*([\w+#]*)/, true)) {
+      // try switching mode
+      state.localMode = getMode(RegExp.$1);
+      if (state.localMode) state.localState = state.localMode.startState();
+      state.f = state.block = local;
+      if (modeCfg.highlightFormatting) state.formatting = "code-block";
+      state.code = true;
+      return getType(state);
+    }
+
+    return switchInline(stream, state, state.inline);
+  }
+
+  function htmlBlock(stream, state) {
+    var style = htmlMode.token(stream, state.htmlState);
+    if ((htmlFound && state.htmlState.tagStart === null && !state.htmlState.context) ||
+        (state.md_inside && stream.current().indexOf(">") > -1)) {
+      state.f = inlineNormal;
+      state.block = blockNormal;
+      state.htmlState = null;
+    }
+    return style;
+  }
+
+  function local(stream, state) {
+    if (stream.sol() && stream.match("```", false)) {
+      state.localMode = state.localState = null;
+      state.f = state.block = leavingLocal;
+      return null;
+    } else if (state.localMode) {
+      return state.localMode.token(stream, state.localState);
+    } else {
+      stream.skipToEnd();
+      return code;
+    }
+  }
+
+  function leavingLocal(stream, state) {
+    stream.match("```");
+    state.block = blockNormal;
+    state.f = inlineNormal;
+    if (modeCfg.highlightFormatting) state.formatting = "code-block";
+    state.code = true;
+    var returnType = getType(state);
+    state.code = false;
+    return returnType;
+  }
+
+  // Inline
+  function getType(state) {
+    var styles = [];
+
+    if (state.formatting) {
+      styles.push(formatting);
+
+      if (typeof state.formatting === "string") state.formatting = [state.formatting];
+
+      for (var i = 0; i < state.formatting.length; i++) {
+        styles.push(formatting + "-" + state.formatting[i]);
+
+        if (state.formatting[i] === "header") {
+          styles.push(formatting + "-" + state.formatting[i] + "-" + state.header);
+        }
+
+        // Add `formatting-quote` and `formatting-quote-#` for blockquotes
+        // Add `error` instead if the maximum blockquote nesting depth is passed
+        if (state.formatting[i] === "quote") {
+          if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
+            styles.push(formatting + "-" + state.formatting[i] + "-" + state.quote);
+          } else {
+            styles.push("error");
+          }
+        }
+      }
+    }
+
+    if (state.taskOpen) {
+      styles.push("meta");
+      return styles.length ? styles.join(' ') : null;
+    }
+    if (state.taskClosed) {
+      styles.push("property");
+      return styles.length ? styles.join(' ') : null;
+    }
+
+    if (state.linkHref) {
+      styles.push(linkhref);
+      return styles.length ? styles.join(' ') : null;
+    }
+
+    if (state.strong) { styles.push(strong); }
+    if (state.em) { styles.push(em); }
+    if (state.strikethrough) { styles.push(strikethrough); }
+
+    if (state.linkText) { styles.push(linktext); }
+
+    if (state.code) { styles.push(code); }
+
+    if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }
+
+    if (state.quote) {
+      styles.push(quote);
+
+      // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
+      if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
+        styles.push(quote + "-" + state.quote);
+      } else {
+        styles.push(quote + "-" + modeCfg.maxBlockquoteDepth);
+      }
+    }
+
+    if (state.list !== false) {
+      var listMod = (state.listDepth - 1) % 3;
+      if (!listMod) {
+        styles.push(list1);
+      } else if (listMod === 1) {
+        styles.push(list2);
+      } else {
+        styles.push(list3);
+      }
+    }
+
+    if (state.trailingSpaceNewLine) {
+      styles.push("trailing-space-new-line");
+    } else if (state.trailingSpace) {
+      styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
+    }
+
+    return styles.length ? styles.join(' ') : null;
+  }
+
+  function handleText(stream, state) {
+    if (stream.match(textRE, true)) {
+      return getType(state);
+    }
+    return undefined;
+  }
+
+  function inlineNormal(stream, state) {
+    var style = state.text(stream, state);
+    if (typeof style !== 'undefined')
+      return style;
+
+    if (state.list) { // List marker (*, +, -, 1., etc)
+      state.list = null;
+      return getType(state);
+    }
+
+    if (state.taskList) {
+      var taskOpen = stream.match(taskListRE, true)[1] !== "x";
+      if (taskOpen) state.taskOpen = true;
+      else state.taskClosed = true;
+      if (modeCfg.highlightFormatting) state.formatting = "task";
+      state.taskList = false;
+      return getType(state);
+    }
+
+    state.taskOpen = false;
+    state.taskClosed = false;
+
+    if (state.header && stream.match(/^#+$/, true)) {
+      if (modeCfg.highlightFormatting) state.formatting = "header";
+      return getType(state);
+    }
+
+    // Get sol() value now, before character is consumed
+    var sol = stream.sol();
+
+    var ch = stream.next();
+
+    if (ch === '\\') {
+      stream.next();
+      if (modeCfg.highlightFormatting) {
+        var type = getType(state);
+        return type ? type + " formatting-escape" : "formatting-escape";
+      }
+    }
+
+    // Matches link titles present on next line
+    if (state.linkTitle) {
+      state.linkTitle = false;
+      var matchCh = ch;
+      if (ch === '(') {
+        matchCh = ')';
+      }
+      matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
+      var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
+      if (stream.match(new RegExp(regex), true)) {
+        return linkhref;
+      }
+    }
+
+    // If this block is changed, it may need to be updated in GFM mode
+    if (ch === '`') {
+      var previousFormatting = state.formatting;
+      if (modeCfg.highlightFormatting) state.formatting = "code";
+      var t = getType(state);
+      var before = stream.pos;
+      stream.eatWhile('`');
+      var difference = 1 + stream.pos - before;
+      if (!state.code) {
+        codeDepth = difference;
+        state.code = true;
+        return getType(state);
+      } else {
+        if (difference === codeDepth) { // Must be exact
+          state.code = false;
+          return t;
+        }
+        state.formatting = previousFormatting;
+        return getType(state);
+      }
+    } else if (state.code) {
+      return getType(state);
+    }
+
+    if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
+      stream.match(/\[[^\]]*\]/);
+      state.inline = state.f = linkHref;
+      return image;
+    }
+
+    if (ch === '[' && stream.match(/.*\](\(| ?\[)/, false)) {
+      state.linkText = true;
+      if (modeCfg.highlightFormatting) state.formatting = "link";
+      return getType(state);
+    }
+
+    if (ch === ']' && state.linkText) {
+      if (modeCfg.highlightFormatting) state.formatting = "link";
+      var type = getType(state);
+      state.linkText = false;
+      state.inline = state.f = linkHref;
+      return type;
+    }
+
+    if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
+      state.f = state.inline = linkInline;
+      if (modeCfg.highlightFormatting) state.formatting = "link";
+      var type = getType(state);
+      if (type){
+        type += " ";
+      } else {
+        type = "";
+      }
+      return type + linkinline;
+    }
+
+    if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
+      state.f = state.inline = linkInline;
+      if (modeCfg.highlightFormatting) state.formatting = "link";
+      var type = getType(state);
+      if (type){
+        type += " ";
+      } else {
+        type = "";
+      }
+      return type + linkemail;
+    }
+
+    if (ch === '<' && stream.match(/^\w/, false)) {
+      if (stream.string.indexOf(">") != -1) {
+        var atts = stream.string.substring(1,stream.string.indexOf(">"));
+        if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) {
+          state.md_inside = true;
+        }
+      }
+      stream.backUp(1);
+      state.htmlState = CodeMirror.startState(htmlMode);
+      return switchBlock(stream, state, htmlBlock);
+    }
+
+    if (ch === '<' && stream.match(/^\/\w*?>/)) {
+      state.md_inside = false;
+      return "tag";
+    }
+
+    var ignoreUnderscore = false;
+    if (!modeCfg.underscoresBreakWords) {
+      if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
+        var prevPos = stream.pos - 2;
+        if (prevPos >= 0) {
+          var prevCh = stream.string.charAt(prevPos);
+          if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
+            ignoreUnderscore = true;
+          }
+        }
+      }
+    }
+    if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
+      if (sol && stream.peek() === ' ') {
+        // Do nothing, surrounded by newline and space
+      } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
+        if (modeCfg.highlightFormatting) state.formatting = "strong";
+        var t = getType(state);
+        state.strong = false;
+        return t;
+      } else if (!state.strong && stream.eat(ch)) { // Add STRONG
+        state.strong = ch;
+        if (modeCfg.highlightFormatting) state.formatting = "strong";
+        return getType(state);
+      } else if (state.em === ch) { // Remove EM
+        if (modeCfg.highlightFormatting) state.formatting = "em";
+        var t = getType(state);
+        state.em = false;
+        return t;
+      } else if (!state.em) { // Add EM
+        state.em = ch;
+        if (modeCfg.highlightFormatting) state.formatting = "em";
+        return getType(state);
+      }
+    } else if (ch === ' ') {
+      if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
+        if (stream.peek() === ' ') { // Surrounded by spaces, ignore
+          return getType(state);
+        } else { // Not surrounded by spaces, back up pointer
+          stream.backUp(1);
+        }
+      }
+    }
+
+    if (modeCfg.strikethrough) {
+      if (ch === '~' && stream.eatWhile(ch)) {
+        if (state.strikethrough) {// Remove strikethrough
+          if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
+          var t = getType(state);
+          state.strikethrough = false;
+          return t;
+        } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
+          state.strikethrough = true;
+          if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
+          return getType(state);
+        }
+      } else if (ch === ' ') {
+        if (stream.match(/^~~/, true)) { // Probably surrounded by space
+          if (stream.peek() === ' ') { // Surrounded by spaces, ignore
+            return getType(state);
+          } else { // Not surrounded by spaces, back up pointer
+            stream.backUp(2);
+          }
+        }
+      }
+    }
+
+    if (ch === ' ') {
+      if (stream.match(/ +$/, false)) {
+        state.trailingSpace++;
+      } else if (state.trailingSpace) {
+        state.trailingSpaceNewLine = true;
+      }
+    }
+
+    return getType(state);
+  }
+
+  function linkInline(stream, state) {
+    var ch = stream.next();
+
+    if (ch === ">") {
+      state.f = state.inline = inlineNormal;
+      if (modeCfg.highlightFormatting) state.formatting = "link";
+      var type = getType(state);
+      if (type){
+        type += " ";
+      } else {
+        type = "";
+      }
+      return type + linkinline;
+    }
+
+    stream.match(/^[^>]+/, true);
+
+    return linkinline;
+  }
+
+  function linkHref(stream, state) {
+    // Check if space, and return NULL if so (to avoid marking the space)
+    if(stream.eatSpace()){
+      return null;
+    }
+    var ch = stream.next();
+    if (ch === '(' || ch === '[') {
+      state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
+      if (modeCfg.highlightFormatting) state.formatting = "link-string";
+      state.linkHref = true;
+      return getType(state);
+    }
+    return 'error';
+  }
+
+  function getLinkHrefInside(endChar) {
+    return function(stream, state) {
+      var ch = stream.next();
+
+      if (ch === endChar) {
+        state.f = state.inline = inlineNormal;
+        if (modeCfg.highlightFormatting) state.formatting = "link-string";
+        var returnState = getType(state);
+        state.linkHref = false;
+        return returnState;
+      }
+
+      if (stream.match(inlineRE(endChar), true)) {
+        stream.backUp(1);
+      }
+
+      state.linkHref = true;
+      return getType(state);
+    };
+  }
+
+  function footnoteLink(stream, state) {
+    if (stream.match(/^[^\]]*\]:/, false)) {
+      state.f = footnoteLinkInside;
+      stream.next(); // Consume [
+      if (modeCfg.highlightFormatting) state.formatting = "link";
+      state.linkText = true;
+      return getType(state);
+    }
+    return switchInline(stream, state, inlineNormal);
+  }
+
+  function footnoteLinkInside(stream, state) {
+    if (stream.match(/^\]:/, true)) {
+      state.f = state.inline = footnoteUrl;
+      if (modeCfg.highlightFormatting) state.formatting = "link";
+      var returnType = getType(state);
+      state.linkText = false;
+      return returnType;
+    }
+
+    stream.match(/^[^\]]+/, true);
+
+    return linktext;
+  }
+
+  function footnoteUrl(stream, state) {
+    // Check if space, and return NULL if so (to avoid marking the space)
+    if(stream.eatSpace()){
+      return null;
+    }
+    // Match URL
+    stream.match(/^[^\s]+/, true);
+    // Check for link title
+    if (stream.peek() === undefined) { // End of line, set flag to check next line
+      state.linkTitle = true;
+    } else { // More content on line, check if link title
+      stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
+    }
+    state.f = state.inline = inlineNormal;
+    return linkhref;
+  }
+
+  var savedInlineRE = [];
+  function inlineRE(endChar) {
+    if (!savedInlineRE[endChar]) {
+      // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
+      endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
+      // Match any non-endChar, escaped character, as well as the closing
+      // endChar.
+      savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
+    }
+    return savedInlineRE[endChar];
+  }
+
+  var mode = {
+    startState: function() {
+      return {
+        f: blockNormal,
+
+        prevLineHasContent: false,
+        thisLineHasContent: false,
+
+        block: blockNormal,
+        htmlState: null,
+        indentation: 0,
+
+        inline: inlineNormal,
+        text: handleText,
+
+        formatting: false,
+        linkText: false,
+        linkHref: false,
+        linkTitle: false,
+        em: false,
+        strong: false,
+        header: 0,
+        taskList: false,
+        list: false,
+        listDepth: 0,
+        quote: 0,
+        trailingSpace: 0,
+        trailingSpaceNewLine: false,
+        strikethrough: false
+      };
+    },
+
+    copyState: function(s) {
+      return {
+        f: s.f,
+
+        prevLineHasContent: s.prevLineHasContent,
+        thisLineHasContent: s.thisLineHasContent,
+
+        block: s.block,
+        htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
+        indentation: s.indentation,
+
+        localMode: s.localMode,
+        localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
+
+        inline: s.inline,
+        text: s.text,
+        formatting: false,
+        linkTitle: s.linkTitle,
+        em: s.em,
+        strong: s.strong,
+        strikethrough: s.strikethrough,
+        header: s.header,
+        taskList: s.taskList,
+        list: s.list,
+        listDepth: s.listDepth,
+        quote: s.quote,
+        trailingSpace: s.trailingSpace,
+        trailingSpaceNewLine: s.trailingSpaceNewLine,
+        md_inside: s.md_inside
+      };
+    },
+
+    token: function(stream, state) {
+
+      // Reset state.formatting
+      state.formatting = false;
+
+      if (stream.sol()) {
+        var forceBlankLine = !!state.header;
+
+        // Reset state.header
+        state.header = 0;
+
+        if (stream.match(/^\s*$/, true) || forceBlankLine) {
+          state.prevLineHasContent = false;
+          blankLine(state);
+          return forceBlankLine ? this.token(stream, state) : null;
+        } else {
+          state.prevLineHasContent = state.thisLineHasContent;
+          state.thisLineHasContent = true;
+        }
+
+        // Reset state.taskList
+        state.taskList = false;
+
+        // Reset state.code
+        state.code = false;
+
+        // Reset state.trailingSpace
+        state.trailingSpace = 0;
+        state.trailingSpaceNewLine = false;
+
+        state.f = state.block;
+        var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, '    ').length;
+        var difference = Math.floor((indentation - state.indentation) / 4) * 4;
+        if (difference > 4) difference = 4;
+        var adjustedIndentation = state.indentation + difference;
+        state.indentationDiff = adjustedIndentation - state.indentation;
+        state.indentation = adjustedIndentation;
+        if (indentation > 0) return null;
+      }
+      return state.f(stream, state);
+    },
+
+    innerMode: function(state) {
+      if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
+      if (state.localState) return {state: state.localState, mode: state.localMode};
+      return {state: state, mode: mode};
+    },
+
+    blankLine: blankLine,
+
+    getType: getType,
+
+    fold: "markdown"
+  };
+  return mode;
+}, "xml");
+
+CodeMirror.defineMIME("text/x-markdown", "markdown");
+
+});

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 0 - 0
desktop/core/static/ext/js/markdown.min.js


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно