أعجوبة

البرمجيات الحُرة والمفتوحة المصدر

أدوات المستخدم

أدوات الموقع


Action disabled: recent
thawab-pri:webapp-editor

التحرير عبر الويب

الطريقة في HTML5

طريقة التحرير في HTML 5 كما في المحرر http://nicedit.com/ وهي مدعومة في:

  • Internet Explorer 5.5+
  • Firefox 3+
  • Safari 2+
  • Opera 9+
  • Chrome

تتم عبر عمل contenteditable=“true” على صندوق div

<div id="editor" contenteditable="true">
هذه <b>تجربة</b>
</div>

للمزيد انظر

وأفضل وأقصر مثال عليها هو

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head>
    <title>Untitled Page</title>
    <script language="javascript">
      function doFormat(cmd) {
        document.execCommand(cmd, null, null);
      }
    </script>
</head>
<body>
<h1>Testing contenteditable</h1>
<hr />
<button onclick="doFormat('bold');"><strong>B</strong></button>
<button onclick="doFormat('italic');"><em>I</em></button>
 
<button onclick="doFormat('underline');"><span style="text-decoration:underline">U</span></button>
<hr />
<div id="editor1" contenteditable="true" style="height:200px; border:1px solid black">
This is the content
</div>
<div id="editor2" contenteditable="true" style="height:200px; border:1px solid black">
This is more editable content in a different div
</div>
</body>
</html>

يمكن التعامل مع التحديد عبر الدالة window.getSelection() التي تعيد

interface Selection {
  readonly attribute Node anchorNode;
  readonly attribute long anchorOffset;
  readonly attribute Node focusNode;
  readonly attribute long focusOffset;
  readonly attribute boolean isCollapsed;
  void collapse(in Node parentNode, in long offset);
  void collapseToStart();
  void collapseToEnd();
  void selectAllChildren(in Node parentNode);
  void deleteFromDocument();
  readonly attribute long rangeCount;
  Range getRangeAt(in long index);
  void addRange(in Range range);
  void removeRange(in Range range);
  void removeAllRanges();
  stringifier DOMString ();
};

وهي قائمة من كائنات ال range عددها rangeCount

الطريقة القديمة

الطريقة القديمة هي إخفاء ال textarea وعمل iframe يحتوي على وثيقة أخرى (يعني فيها body …إلخ) ثم وضعه في طور “design mode” وذلك عبر وضع القيمة on للخاصية designMode DOM attribute على document الموجودة داخل iframe

وهي الطريقة المستخدمة في tinymce

الفرق بين designMode و contenteditable هو أن الأول يكون عاما على كل الوثيقة لهذا نحتاج iframe بها وثيقة جديدة. وأفضل شرح مبسط لها تجده في:

<html>
<head>
  <title>Midas HTML Editor Demo</title>
 
  <script type="text/javascript">
      var editor; /* global var */
      function onload() {
          editor = document.getElementById("content-frame");
          editor.contentDocument.designMode = "on";
          setFocus();
      }
 
      function setFocus() {
        setTimeout(function() { editor.contentWindow.focus(); }, 100);
      }
 
      function doShowHTML() {
          var source = editor.contentDocument.body.innerHTML;
          alert(source);
      }
 
      function doInsertLink() {
          var url = prompt("URL:", "http://");
          if (url)
              editor.contentDocument.execCommand("createlink", false, url);
          setFocus();
      }
 
      function doInsertTable() {
          var size = prompt("Enter desired rows and columns: (rows|cols)", "2|2");
          if (size) {
              var rowcol = size.split("|");
              if (rowcol.length == 2 && rowcol[0] > 0 && rowcol[0] < 10&& rowcol[1] > 0 && rowcol[1] < 10) {
                  var html = "<table border='1'>";
                  for (var row=0; row<rowcol[0]; row++) {
                      html += "<tr>";
                      for (var col=0; col<rowcol[1]; col++) {
                          html += "<td width='20'></td>";
                      }
                      html += "</tr>";
                  }
                  html += "</table>";
                  editor.contentDocument.execCommand('inserthtml', false, html);
              }
          }
          setFocus();
      }
 
      window.addEventListener("load", onload, false);
  </script>
</head>
<body>
 
    <div id="main-toolbar">
      <button id="bold" label="Bold" onclick="editor.contentDocument.execCommand('bold', false, null); setFocus();">Bold</button>
 
      <button id="italic" label="" onclick="editor.contentDocument.execCommand('italic', false, null); setFocus();">Italic</button>
      <button id="underline" label="" onclick="editor.contentDocument.execCommand('underline', false, null); setFocus();">Underline</button>
      <button id="link" label="" onclick="doInsertLink();">Link</button>
      <button id="table" label="" onclick="doInsertTable();">Table</button>
      <button id="showhtml" label="View Source" onclick="doShowHTML();">View Source</button>
    </div>
 
  <div id="main">
    <iframe id="content-frame" src="about:blank"></iframe>
  </div>
 
</body>
</html>

الفكرة بسيطة وهي أن يتم تحديد عنصر التحرير عبر

editor=document.getElementById("content-frame");

وظيفة الأزرار هنا أنها تستدعي editor.contentDocument.execCommand

عيب هذه الطريقة أن كل متصفح يفهمها بطريقة مختلفة بعض المتصفحات قد تعمل <b> وبعضها الآخر <strong> وهكذا

كما أن الناتج قد لا يكون xhtml سليم.

قائمة بالأوامر:

أمثلة أخرى:

دالة document.execCommand في بعض التطبيقات

إليك هذا الكود على بايثون webkit gtk

#!/usr/bin/env python
#
# Example Text Editor
# Ryan Paul (SegPhault) - 07/12/2009
#
 
import gtk, webkit, os
 
class ExampleEditor(gtk.Window):
  def __init__(self):
    gtk.Window.__init__(self)
    self.set_title("Example Editor")
    self.connect("destroy", gtk.main_quit)
    self.resize(500, 500)
    self.filename = None
 
    self.editor = webkit.WebView()
    self.editor.set_editable(True)
    self.editor.load_html_string("This is a test", "file:///")
 
    scroll = gtk.ScrolledWindow()
    scroll.add(self.editor)
    scroll.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
 
    self.ui = self.generate_ui()
    self.add_accel_group(self.ui.get_accel_group())
    self.toolbar1 = self.ui.get_widget("/toolbar_main")
    self.toolbar2 = self.ui.get_widget("/toolbar_format")
    self.menubar = self.ui.get_widget("/menubar_main")
 
    self.layout = gtk.VBox()
    self.layout.pack_start(self.menubar, False)
    self.layout.pack_start(self.toolbar1, False)
    self.layout.pack_start(self.toolbar2, False)
    self.layout.pack_start(scroll, True, True)
    self.add(self.layout)
 
  def generate_ui(self):
    ui_def = """
    <ui>
      <menubar name="menubar_main">
        <menu action="menuFile">
          <menuitem action="new" />
          <menuitem action="open" />
          <menuitem action="save" />
        </menu>
        <menu action="menuEdit">
          <menuitem action="cut" />
          <menuitem action="copy" />
          <menuitem action="paste" />
        </menu>
        <menu action="menuInsert">
          <menuitem action="insertimage" />
        </menu>
        <menu action="menuFormat">
          <menuitem action="bold" />
          <menuitem action="italic" />
          <menuitem action="underline" />
          <menuitem action="strikethrough" />
          <separator />
          <menuitem action="font" />
          <menuitem action="color" />
          <separator />
          <menuitem action="justifyleft" />
          <menuitem action="justifyright" />
          <menuitem action="justifycenter" />
          <menuitem action="justifyfull" />
        </menu>
      </menubar>
      <toolbar name="toolbar_main">
        <toolitem action="new" />
        <toolitem action="open" />
        <toolitem action="save" />
        <separator />
        <toolitem action="undo" />
        <toolitem action="redo" />
        <separator />
        <toolitem action="cut" />
        <toolitem action="copy" />
        <toolitem action="paste" />
      </toolbar>
      <toolbar name="toolbar_format">
        <toolitem action="bold" />
        <toolitem action="italic" />
        <toolitem action="underline" />
        <toolitem action="strikethrough" />
        <separator />
        <toolitem action="font" />
        <toolitem action="color" />
        <separator />
        <toolitem action="justifyleft" />
        <toolitem action="justifyright" />
        <toolitem action="justifycenter" />
        <toolitem action="justifyfull" />
        <separator />
        <toolitem action="insertimage" />
        <toolitem action="insertlink" />
      </toolbar>
    </ui>
    """
 
    actions = gtk.ActionGroup("Actions")
    actions.add_actions([
      ("menuFile", None, "_File"),
      ("menuEdit", None, "_Edit"),
      ("menuInsert", None, "_Insert"),
      ("menuFormat", None, "_Format"),
 
      ("new", gtk.STOCK_NEW, "_New", None, None, self.on_new),
      ("open", gtk.STOCK_OPEN, "_Open", None, None, self.on_open),
      ("save", gtk.STOCK_SAVE, "_Save", None, None, self.on_save),
 
      ("undo", gtk.STOCK_UNDO, "_Undo", None, None, self.on_action),
      ("redo", gtk.STOCK_REDO, "_Redo", None, None, self.on_action),
 
      ("cut", gtk.STOCK_CUT, "_Cut", None, None, self.on_action),
      ("copy", gtk.STOCK_COPY, "_Copy", None, None, self.on_action),
      ("paste", gtk.STOCK_PASTE, "_Paste", None, None, self.on_paste),
 
      ("bold", gtk.STOCK_BOLD, "_Bold", "<ctrl>B", None, self.on_action),
      ("italic", gtk.STOCK_ITALIC, "_Italic", "<ctrl>I", None, self.on_action),
      ("underline", gtk.STOCK_UNDERLINE, "_Underline", "<ctrl>U", None, self.on_action),
      ("strikethrough", gtk.STOCK_STRIKETHROUGH, "_Strike", "<ctrl>T", None, self.on_action),
      ("font", gtk.STOCK_SELECT_FONT, "Select _Font", "<ctrl>F", None, self.on_select_font),
      ("color", gtk.STOCK_SELECT_COLOR, "Select _Color", None, None, self.on_select_color),
 
      ("justifyleft", gtk.STOCK_JUSTIFY_LEFT, "Justify _Left", None, None, self.on_action),
      ("justifyright", gtk.STOCK_JUSTIFY_RIGHT, "Justify _Right", None, None, self.on_action),
      ("justifycenter", gtk.STOCK_JUSTIFY_CENTER, "Justify _Center", None, None, self.on_action),
      ("justifyfull", gtk.STOCK_JUSTIFY_FILL, "Justify _Full", None, None, self.on_action),
 
      ("insertimage", "insert-image", "Insert _Image", None, None, self.on_insert_image),
      ("insertlink", "insert-link", "Insert _Link", None, None, self.on_insert_link),
    ])
 
    actions.get_action("insertimage").set_property("icon-name", "insert-image")
    actions.get_action("insertlink").set_property("icon-name", "insert-link")
 
    ui = gtk.UIManager()
    ui.insert_action_group(actions)
    ui.add_ui_from_string(ui_def)
    return ui
 
  def on_action(self, action):
    self.editor.execute_script(
      "document.execCommand('%s', false, false);" % action.get_name())
 
  def on_paste(self, action):
    self.editor.paste_clipboard()
 
  def on_new(self, action):
    self.editor.load_html_string("", "file:///")
 
  def on_select_font(self, action):
    dialog = gtk.FontSelectionDialog("Select a font")
    if dialog.run() == gtk.RESPONSE_OK:
      fname, fsize = dialog.fontsel.get_family().get_name(), dialog.fontsel.get_size()
      self.editor.execute_script("document.execCommand('fontname', null, '%s');" % fname)
      self.editor.execute_script("document.execCommand('fontsize', null, '%s');" % fsize)
    dialog.destroy()
 
  def on_select_color(self, action):
    dialog = gtk.ColorSelectionDialog("Select Color")
    if dialog.run() == gtk.RESPONSE_OK:
      gc = str(dialog.colorsel.get_current_color())
      color = "#" + "".join([gc[1:3], gc[5:7], gc[9:11]])
      self.editor.execute_script("document.execCommand('forecolor', null, '%s');" % color)
    dialog.destroy()
 
  def on_insert_link(self, action):
    dialog = gtk.Dialog("Enter a URL:", self, 0,
      (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK))
 
    entry = gtk.Entry()
    dialog.vbox.pack_start(entry)
    dialog.show_all()
 
    if dialog.run() == gtk.RESPONSE_OK:
      self.editor.execute_script(
        "document.execCommand('createLink', true, '%s');" % entry.get_text())
    dialog.destroy()
 
  def on_insert_image(self, action):
    dialog = gtk.FileChooserDialog("Select an image file", self, gtk.FILE_CHOOSER_ACTION_OPEN,
      (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
 
    if dialog.run() == gtk.RESPONSE_OK:
      fn = dialog.get_filename()
      if os.path.exists(fn):
        self.editor.execute_script(
          "document.execCommand('insertImage', null, '%s');" % fn)
    dialog.destroy()
 
  def on_open(self, action):
    dialog = gtk.FileChooserDialog("Select an HTML file", self, gtk.FILE_CHOOSER_ACTION_OPEN,
      (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OPEN, gtk.RESPONSE_OK))
 
    if dialog.run() == gtk.RESPONSE_OK:
      fn = dialog.get_filename()
      if os.path.exists(fn):
        self.filename = fn
        with open(fn) as fd:
          self.editor.load_html_string(fd.read(), "file:///")
    dialog.destroy()
 
  def on_save(self, action):
    if self.filename:
      with open(self.filename) as fd: fd.write(self.get_html())
    else:
      dialog = gtk.FileChooserDialog("Select an HTML file", self, gtk.FILE_CHOOSER_ACTION_SAVE,
        (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_SAVE, gtk.RESPONSE_OK))
 
      if dialog.run() == gtk.RESPONSE_OK:
        self.filename = dialog.get_filename()
        with open(self.filename, "w+") as fd: fd.write(self.get_html())
      dialog.destroy()
 
  def get_html(self):
    self.editor.execute_script("document.title=document.documentElement.innerHTML;")
    return self.editor.get_main_frame().get_title()
 
gtk.gdk.threads_init()
gtk.gdk.threads_enter()
e = ExampleEditor()
gtk.gdk.threads_leave()
e.show_all()
gtk.main()

محرر ب textarea وأزرار وتكون عملية preview منفصلة

محرر كالذي في dokuwiki فيه أزرار تعرف التحديد من خلال

element . select()

    Selects everything in the text field.
element . selectionStart [ = value ]

    Returns the offset to the start of the selection.

    Can be set, to change the start of the selection.
element . selectionEnd [ = value ]

    Returns the offset to the end of the selection.

    Can be set, to change the end of the selection.
element . setSelectionRange(start, end)

    Changes the selection to cover the given substring.
thawab-pri/webapp-editor.txt · آخر تعديل: 2015/04/23 03:21 بواسطة 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki