المحتوى عن 'html'.



مزيد من الخيارات

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المُحتوى


التصنيفات

  • التخطيط وسير العمل
  • التمويل
  • فريق العمل
  • دراسة حالات
  • نصائح وإرشادات
  • التعامل مع العملاء
  • التعهيد الخارجي
  • التجارة الإلكترونية
  • الإدارة والقيادة
  • مقالات ريادة أعمال عامة

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
  • HTML
    • HTML5
  • CSS
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • Sass
    • إطار عمل Bootstrap
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة Swift
  • لغة R
  • لغة TypeScript
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات برمجة عامة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • مقالات تصميم عامة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات DevOps عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • التسويق بالرسائل النصية القصيرة
  • استسراع النمو
  • المبيعات
  • تجارب ونصائح

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عمل حر عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

تمّ العثور على 32 نتائج

  1. يمثّل كلٌّ من الضّغط والتّصغيرأمرًا تقوم بتنفيذه على الأصول الموجودة في موقعك (مثل ملفات css. وملفات js.). تستطيع من خلال كِلَيهما تقليص حجم الملف وبالتّالي جعلَه أكثر فعاليّةً في عبور الشبكة بين الخواديم والمتصفّحات. بكلمات أخرى، يصبح الأداء أفضل بتقليص حجم أصولك. تمثّل الشبكة نقطةَ اختناقٍ لسرعة الويب. لهذا السّبب، يساعد تخفيض حجم الملف وتقليصه في تفادي تحميل عبء زائد على الشّبكة. لكنّهما - أي الضغط والتصغير - يختلفان عن بعضهما بوضوح. إن كنت لا تعرف هذا بالفعل، فالأمر يستحق الاطّلاع. يقوم التّصغير بأمورٍ مثل حذف الفواصل، وحذف التّعليقات، وحذف الفواصل المنقوطة غير الضّروريّة وتقليل طول الرّموز السّت عشريّة وغيرها من الأمور المشابهة. يبقى الملف شيفرةً صالحةً تمامًا. لن ترغبَ في محاولة قراءتها أو العمل عليها، لكنها لا تخرق أيًّا من القواعد. يستطيع المتصفّح قراءتها واستخدامها كما هو الحال في الملف الأصليّ. يُنشئ التّصغير ملفًا جديدًا تقومُ أنتَ باستخدامه في نهاية المطاف. على سبيل المثال، تستطيع إنشاء ملف style.css لتعمل عليه، ومن ثمّ بإمكانك تصغيره إلى style.min.css. يتحرّى الضّغط جميع المقاطع المكرّرة ويستبدلها بمؤشّرات إلى موضع الورود الأول للمقطع المعنيّ. قدّمت جوليا إيفانز Julia Evans طريقةً رائعة لفهم ما سبق (راجع منشورها والفيديو). إليك الفقرة الأولى من القصيدة: .red { color: red; } Once upon a midnight dreary, while I {pon}dered weak an{d wea}{ry,} Over many{ a }quaint{ and }curious volume of forgotten lore, W{hile I }nodded, n{ear}ly napping, su{dde}n{ly }th{ere} ca{me }a t{apping,} As{ of }so{me o}ne gent{ly }r{apping, }{rapping} at my chamb{er }door. `'Tis{ some }visitor,'{ I }mu{tte}r{ed, }`t{apping at my chamber door} - O{nly th}is,{ and }no{thi}{ng }m{ore}. وَجد gzip أن النّصّ الوارد ضمن أقواس متعرّجة هو نصٌّ مكرّر. لهذا السبب، سيُستبدل بمؤشّر يستهلك مساحةً أقل مما يستهلكه النّصّ نفسه. تظهر فعاليّة هذا الأمر في إنقاص حجم الملف، خاصّةً في حالة الشّيفرة البرمجيّة على اعتبار أنّها تحتوي بطبيعتها على الكثير من المقاطع المكرّرة. تخيّل فقط عدد مرّات ورود ‎<div في ملف HTML أو عدد مرّات ورود { في ملف CSS. يمكنك إنشاء إصدارات مضغوطة من الملفات، على سبيل المثال style.css.zip لكنّك نادرًا ما ستضطّر لذلك ولن يعرف المتصفّح ماذا يفعل بهذه الإصدارات. تتّم عمليّة الضّغط على الويب من قبل الخادم مباشرةً ذلك إذا ما قمت بإعداده للقيام بها. حالما تُطبّق الإعدادات، يحدث الضّغط تلقائيًّا ولا حاجةَ بك للقيام بأي عمل آخر. يضغط الخادم الملف ويرسله عبر الشبكة مضغوطًا. يستقبل المتصفح الملف ويفكّ ضغطه قبل استخدامه. لم أسمع قطُّ أحدًا يذكر أعباء عمليّتَي الضّغط وفكّ الضّغط، لذا سأفترض أنّها مهملةٌ وأنّ فوائدها تفوقُ أعباءها بكثيرٍ. يجري عادةً أتمتة عملية الضغط عبر أدوات البناء مثل Gulp مما يسهل عليك العملية. إليك كيفيّة تفعيل عمليّة الضّغط على خادم Apache من خلال استخدام الوحدة mod_deflate . كما يّقدّم H5BP إعدادات الخواديم لجميع الخواديم الشّائعة التي تدعم الضّغط. مثال سنستخدم ملف CSS من Bootstrap على اعتباره مرجعًا شائعًا. ستُوفّر ما يقارب 17% عند تصغير ملف CSS، أو 85% عند ضغطه أو 86% عند قيامك بكليهما. الوضع المثاليّ عند التأكد من عمل كلِّ شيءٍ كما ينبغي من خلال أدوات المطور (DevTools) موضّح كما يلي: الضّغط أكثر فعاليّة، لكنّ القيام بكليهما هو الأفضل بالمطلق. تُخفّض عمليّة الضّغط حجم الملف أكثر بخمسة أضعاف من عمليّة التّصغير. لكنّك تحصل على دفعة صغيرة إضافيّة عند القيام بالتّصغير أيضًا. هناك أيضًا دليلٌ على أنّ المتصفّحات تقرأ وتحلّل الملف المُصغّر أسرع: شرَعَت مايكروسوفت هي الأخرى بتحسين مُحلّلاتها لهذا الأمر: يندرج التّخزين المؤقّت للأصول في هذا السّياق أيضًا حيث لا يوجد ما هو أسرع من مُتصفّح لا يحتاج إلى طلب الأصول على الإطلاق. يوجد الكثير من المعلومات حول هذا الموضوع على الوِيب (أو في الكتب)، وقد نقوم بنشر مقال حول هذا الموضوع قريبًا. ترجمة -وبتصرف- للمقال The Difference Between Minification and Gzipping لصاحبه Chris Coyier
  2. نواصل اليوم دروسنا حول jQuery Mobile، في هذا الدرس، سنطّلع على أمثلة تهيئة، إنشاء صفحات، معاجلات الحدث (event handlers)، وتنقلات الصفحة وغيرها. تضمين JQUERY MOBILE في موقعك من السهل إضافة إطار jQuery إلى موقعك كسهولة إضافة أي ملف جافا سكربت خارجي. <!DOCTYPE html> <html> <head> <title>jQuery Mobile on Script Tutorials</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"> </script> </head> <body> <p>Site contents will be here</p> </body> </html> إنشاء صفحة بسيطة باستخدام JQUERY MOBILE تحدثنا بالفعل على الشيفرة البرمجية لبناء هيكل بسيط لموقع ويب للهواتف، لكننا لا يشبه مواقع الويب للهواتف، لننشئ إذا موقع يتكون من صفحة واحدة وزر حتى نفهم طريقة عمل jQuery Mobile. يعين jQuery Mobile الصفحات باستخدام سمة data-role. في ما وراء الكواليس، يحدد jQuery Mobile العناصر المبنية على هذه السمة ويعززها بشكل تدريجي بإضافة: أصناف CSS ،أي ترميز لازم ،إدارة للأحداث. تبدوا هذه وكأنها طريقة معقدة للتعامل مع الأشياء، لماذا لا يكون لدينا صفحات عادية مرتبطة كالعادة؟ لكننا في هذا الدرس سنغطي عدة مميزات هامة لـ jQuery Mobile: تنقلات الصفحة من خلال التعامل مع الصفحات كمناطق محتوى منفصلة في ملف واحد، يمكن لـ jQuery Mobile إنشاء تنقلات سلسة للصفحات، مما يؤدي إلى مظهر مشابه للتطبيقات. إدارة التصفح يمكن لـ jQuery Mobile التعامل تلقائيًا مع تصفح الصفحة عن طريق توفير مميزات مثل أزرار العودة والربط العميق. الكفاءة لن يحتاج المتصفح إلى الوصول إلى الشبكة مرارًا وتكرارًا بما أن جميع الموارد موجودة في ملف واحد، كما هو الحال مع الملفات الفردية الأصغر حجمًا. سيساعد هذا على تخفيف بطء التطبيق واستنزاف بطارية الهاتف، لكن من جهة أخرى، سيكون وقت تحميل أطول لصفحات HTML الكبيرة التي تحتوي على العديد من المناظر (views) الفردية لصفحة jQuery Mobile. ومع ذلك، بمجرد تحميل الملف، فإن السلوك سيصبح أسرع ولن يعتمد بالضرورة على الوصول إلى الشبكة. <!DOCTYPE html> <html> <head> <title>Script-tutorial: jQuery Mobile</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"> </script> </head> <body> <div data-role="page"> <div data-role="header"><h1>Single Page Site</h1></div> <div data-role="content"> <p>Look at the button!</p> <a href="https://www.script-tutorials.com" data-role="button">I am a button</a> </div> </div> </body> </html> المثال الأول: إنشاء صفحة بسيطة باستخدام jQuery mobile <!DOCTYPE html> <html> <head> <title>Script-tutorial: jQuery Mobile</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"> </script> </head> <body> <div data-role="page"> <div data-role="header"><h1>Single Page Site</h1></div> <div data-role="content"> <p>Look at the button!</p> <a href="https://www.script-tutorials.com" data-role="button">I am a button</a> </div> </div> </body> </html> صفحة بسيطة مع زر واحد مرتبط إلى صفحة الحوار (dialog page) المثال الثاني: إنشاء صفحة بسيطة باستخدام jQuery Mobile مع زر يعمل وصفحة ثانية. <!DOCTYPE html> <html> <head> <title>Script-tutorials: jQuery Mobile</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"> </script> </head> <body> <div data-role="page"> <div data-role="header"><h1>Single Page Site</h1></div> <div data-role="content"> <p>Look at the button!</p> <a href="#dpop" data-role="button" data-rel="dialog">I am a button</a> </div> </div> <div data-role="page" id="dpop" data-theme="d"> <div data-role="header"><h1>Clicked!</h1></div> <div data-role="content"> <p>clicked content!</p> <a href="#" data-rel="back" data-role="button">Go back</a> </div> </div> </body> </html> القسم 2.2: إطار عمل JQUERY MOBILE حدث تهيئة الهاتف يستخدم إطار عمل jQuery دالة $(document).ready() للتحايل على مشاكل التلاعب والتحميل عن طريق إعطائك صلاحية الوصول إلى دوالك في أقرب وقت. في حين أن هذا الأمر رائع للمواقع المتكونة من صفحة واحدة، لكنه سيصبح مشكلة صغيرة لإطار عمل jQuery Mobile. يستخدم jQuery Mobile AJAX لتحميل محتويات كل صفحة بدلا من إعادة تحميل بنية DOM كاملة. تعمل دالة $(document).ready() مرة واحدة عند تحميل الصفحة، وليس عن طريق نداء AJAX، في jQuery Mobile، لا تعمل دالة $(document).ready() مرة واحدة في الصفحة، لكن مرة في الموقع ما لم يطلب المستخدم تحديث الصفحة، وهذا يعني أن بعض الإعدادات الافتراضية التي يجب تعيينها عن طريق jQuery Mobile لا يمكن تعيينها في دالة $(document).ready() لأنها لن تُطبّق في الصفحات الأخرى المتضمنة عن طريق AJAX. الحل هو تعيين وتغيير هذه الافتراضات باستخدام حدث mobileinit لأنه يعمل قبل دالة $(document).ready(). يجب عليك تضمين إطار عمل jQuery لاستخدام حدثmobileinit ومن ثم إما عن طريق تضمين مباشر أو تضمين خارجي لملف جافا سكربت يحتوي على حدث الربط لحدث mobileinit وفي النهاية تضمين jQuery Mobile. مثال 3: تضمين jQuery، سكربت mobileinit مضمن وjQuery Mobile. <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script type="text/javascript"> $(document).on("mobileinit", function() { $.extend( $.mobile , { pageLoadErrorMessage: 'Either the page cannot be found or it cannot be loaded.' }); }); </script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script> تهيئة صفحة في JQUERY MOBILE يجب عليك اتباع نهج أقل حيوية (dynamic) وأكثر تخطيط لاستخدام حدث pageinit على صفحتك، فتوجد عدة طرق لإرفاق حدث pageinit في شيفرتك البرمجية ففي نسخ jQuery Mobile قبل 1.1، ستستخدم jQuery 1.6.4 وهذا يعني أنه يجب عليك استخدام دالة .bind() بدلا من دالة .on()،وعند استخدام jQuery Mobile 1.1+ ستستخدم دالة .on() لربط الحدث. دالة .on() الموجودة في jQuery 1.7 هي توحيد لعدة دوال تُستخدم في ربط الأحداث، فبدلا من القلق على استخدام .bind()، .live() أو .delegate() يمكنك الآن استخدام دالة .on() لإيجاد الأحداث، يمكنك معرفة المزيد من المعومات حول هذه الدالة عن طريق زيارة التوثيق. إذا كنت تستخدم إصدار jQuery Mobile قبل 1.1، تجنب استخدام أسلوب .on()، ويجب عليك بدلا من ذلك استخدام دالة .delegate() أو .live(). المثال 4: استخدام حدث سكربت pageinit وmobileinit بدلا من $(document).ready() ستظهر لك الصورة في الأعلى إذا كانت صفحة multipage_two.html غير متوفرة في رابط المباشر للمجلد. <!DOCTYPE html> <html> <head> <title>Script-Tutorials jQuery Mobile</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <script type="text/javascript"> $(document).on("mobileinit", function() { $.extend( $.mobile , { pageLoadErrorMessage: 'Either the page cannot be found or it cannot be loaded.' }); }); //THE TIED EVENT OF THE MULTIPAGE_TWO.HTML BEGINS $(document).on("pageinit","#pageinit2", function() { alert("pageinit is bound!"); }); //THE TIED EVENT OF THE MULTIPAGE_TWO.HTML ENDS </script> </head> <body> <div data-role="page"> <div data-role="header"><h1>pageinit event example</h1></div> <div data-role="content"> <p>The button below will use AJAX to load another page and trigger a bound event</p> <a href="multipage_two.html" data-role="button">Click to open a new page</a> </div> </div> </body> </html> السكربت أدناه يحتوي على حدث مرتبط في السكربت أعلاه لـ pageinit.html، وهذا سيُطلق (Trigger) تحميل الصفحة مباشرة من ذلك الملف. هذه multipage_two.html <!DOCTYPE html> <html> <head> <title>Script-tutorials: jQuery mobile</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0-rc.1/jquery.mobile-1.1.0-rc.1.min.css"/> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script src="http://code.jquery.com/mobile/1.1.0-rc.1/jquery.mobile-1.1.0-rc.1.min.js"> </script> </head> <body> <div data-role="page" id="pageinit2"> <div data-role="header"><h1>pageinit event example </h1></div> <div data-role="content"> <p>Fantastic! I am a new page and was loaded through AJAX.</p> <a href="pageinit.html" data-role="button" data-rel="back">Amazing, now take me back</a> </div> </div> </body> </html> <img src="https://www.script-tutorials.com/demos/511/img6.png" data-lazy-src="https://www.script-tutorials.com/demos/511/img6.png" class=" lazyloaded"> <!DOCTYPE html> <html> <head> <title>Script-Tutorials jQuery Mobile</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" /> <script src="http://code.jquery.com/jquery-1.7.1.min.js"></script> <script type="text/javascript"> $(document).on("mobileinit", function() { $.extend( $.mobile , { pageLoadErrorMessage: 'Either the page cannot be found or it cannot be loaded.' }); }); $(document).on("pageinit","#pageinit2", function() { alert("pageinit is bound!"); }); </script> <script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script> </head> <body> <div data-role="page"> <div data-role="header"><h1>pageinit event example</h1></div> <div data-role="content"> <p>The button below will use AJAX to load another page and trigger a bound event</p> <a href="multipage_two.html" data-role="button">Click to open a new page</a> </div> </div> </body> </html> التنقل بين الصفحات تتكون العملية من صفحتين: صفحة "من” وصفحة "إلى”، هذه التنقلات تغيّر الصفحة من الصفحة الحالية (fromPage) إلى صفحة جديدة (toPage). إخفاء الصفحة وأحداث الظهور يفرق jQuery Mobile بين أحداث صفحة التحويل وأحداث إظهار وإخفاء الصفحة بسبب طبيعته غير المتزامنة، تحدث أحداث تحميل الصفحة عند تحميل الملف إلى المتصفح بطرية متزامنة، وعند تحميل الصفحة بهذه الطريقة، سيصبح أسلوب jQuery(document).ready() متاحًا للاستعمال وسيشغل jQuery Mobile أحداث التهيئة الأخرى كذلك. كما رأينا، قد يحتوي ملف صفحة HTML الواحدة على عدة مناظر (views) لـ jQuery Mobile، ويمكن للمستخدم الانتقال بين مناظر هذه الصفحة عدة مرات. هذه الصفحة لا تطلق أحداث تحميل الصفحة، وبدلا من ذلك، يوفر jQuery Mobile مجموعة من الأحداث التي تعمل في كل مرة يحدث فيها انتقال للصفحة، وكل واحدة من هذه الأحداث توفر مرجع إلى الحدث وكائنات واجهة المستخدم: pagebeforehide ينطلق هذا الحدث في الصفحة التي يُنتقل منها قبل بدء عملية الانتقال. ستكون ui.nextPage الصفحة التي يُنتقل إليها أو كائن jQuery فارغ إذا لم يكن هناك أي صفحة. Pagebeforeshow ينطلق هذا الحدث في الصفحة التي يُنتقل إليها قبل بدء عملية الانتقال. ستكون ui.prevPage الصفحة التي يُنتقل منها أو كائن jQuery فارغ إذا لم يكن هناك أي صفحة. Pagehide ينطلق هذا الحدث في الصفحة التي يُنتقل منها بعد انتهاء عملية الانتقال. سيكون ui.nextPage كائن jQuery للصفحة التي يُنتقل إليها أو كائن jQuery فارغ إذا لم يكن هناك أي صفحة. Pageshow ينطلق هذا الحدث في الصفحة التي يُنتقل إليها بعد انتهاء عملية الانتقال. سيحتوي ui.prevPage كائن jQuery للصفحة التي يُنتقل منها أو كائن jQuery فارغ إذا لم يكن هناك أي صفحة. توفر هذه الأحداث الأربعة نظير لنداء jQuery(document).ready() لمناظر صفحة التطبيق. لاستخدام هذه الأحداث، ارفق مستمع الحدث (event listeners) إلى الصفحة المناسبة باستخدام jQuery.bind()، jQuery.live()، أو jQuery.delegate(). jQuery.bind()، jQuery.live()، و jQuery.delegate() هي أساليب التي يملكها jQuery لربط المعالجات (handlers) إلى مستمعي الأحداث. للمزيد من التفاصيل، راجع توثيق jQuery، وهنا نستخدم jQuery.bind(): <script> $("#page1").bind("pagehide", function(event, ui) { var strAlert = ""; for (var thing in event) { strAlert += thing + " : " + event[thing] + "\n"; } alert(strAlert); }); </script> بالنسبة للصفحات المتكونة من نفس الملف، سيكون jQuery.bind() كافيا. بالنسبة للصفحات التي ستُحمّل بشكل غير متزامن عن طريق jQuery Mobile، استخدم jQuery.delegate() أو jQuery.live(). المثال 4: حدث pagebeforehide <!DOCTYPE html> <html> <head> <title>Script-tutorials: pagebeforehide Event</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css"> <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script> <script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script> <script> $(document).on("pagebeforehide","#pagetwo",function(){ alert("pagebeforehide event fired - pagetwo is about to be hidden"); }); </script> </head> <body> <div data-role="page" id="pageone"> <div data-role="header"><h1>pagebeforehide Event</h1></div> <div data-role="main" class="ui-content"> <p>Page One</p> <a href="#pagetwo" data-role="button">Go to Page Two</a> </div> <div data-role="footer"> <h1>Header</h1> </div> </div> <div data-role="page" id="pagetwo"> <div data-role="header"><h1>pagebeforehide Event</h1></div> <div data-role="main" class="ui-content"> <p>Page Two</p> <a href="#pageone" data-role="button">Go to Page One</a> </div> <div data-role="footer"> <h1>Footer</h1> </div> </div> </body> </html> المثال 5: حدث pagebeforeshow <!DOCTYPE html> <html> <head> <title>Script-tutorials: pagebeforehide Event</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css"> <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script> <script> $(document).on("pagebeforeshow","#pagetwo",function(){ // When entering pagetwo alert("pagetwo is about to be shown"); }); $(document).on("pageshow","#pagetwo",function(){ // When entering pagetwo alert("pagetwo is now shown"); }); $(document).on("pagebeforehide","#pagetwo",function(){ // When leaving pagetwo alert("pagetwo is about to be hidden"); }); $(document).on("pagehide","#pagetwo",function(){ // When leaving pagetwo alert("pagetwo is now hidden"); }); </script> <script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script> </head> <body> <div data-role="page" id="pageone"> <div data-role="header"><h1>pagebeforehide Event</h1></div> <div data-role="main" class="ui-content"> <p>Page One</p> <a href="#pagetwo" data-role="button">Go to Page Two</a> </div> <div data-role="footer"> <h1>Footer 1</h1> </div> </div> <div data-role="page" id="pagetwo"> <div data-role="header"><h1>pagebeforehide Event</h1></div> <div data-role="main" class="ui-content"> <p>Page Two</p> <a href="#pageone" data-role="button">Go to Page One</a> </div> <div data-role="footer"> <h1>Footer 2</h1> </div> </div> </body> </html> أحداث تحميل JQUERY MOBILE أحداث تحميل الصفحة للصفحات الخارجية. كلما تُحمّل صفحة خارجية في DOM، سيعمل حدثين، الأول هو pagecontainerbeforeload والثاني إما pagecontainerload (نجاح) أو pagecontainerloadfailed (فشل). ويرد في الجدول أدناه شرح لهذه الأحداث: الحدث الوصف pagecontainerbeforeload تشتغل قبل أي طلب لتحميل صفحة. pagecontainerload تشتغل بعد تحميل الصفحة بنجاح وتُدرج في DOM. pagecontainerload تشتغل إذا فشل طلب تحميل الصفحة، افتراضيًا ستُظهر رسالة “Error Loading Page”. تشتغل إذا فشل طلب تحميل الصفحة، افتراضيًا ستُظهر رسالة “Error Loading Page”. ستجد في الأسفل الشيفرة البرمجية لملف main.html المثال 6: حدث تحميل الصفحة عند وجود زر لتحميل الصفحة الخارجية، ستظهر هذه الصورة في المتصفح <!DOCTYPE html> <html> <head> <title>Script-tutorials: pagebeforehide Event</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css"> <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script> <script> $(document).on("pagecontainerload",function(event,data){ alert("pagecontainerload event fired!\nURL: " + data.url); }); $(document).on("pagecontainerloadfailed",function(event,data){ alert("Sorry, requested page does not exist."); }); </script> <script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script> </head> <body> <div data-role="page" id="pageone"> <div data-role="header"><h1>Load Events</h1></div> <div data-role="main" class="ui-content"> <p>Content data loaded or faile to load</p> <a href="pageexist.html" data-role="button">External Page Exist</a> <a href="page_notexist.html" data-role="button">No Page Exist</a> </div> <div data-role="footer"> <h1>Footer</h1> </div> </div> </body> </html> ترجمة -وبتصرّف- للمقال jQuery Mobile Lesson 2 لصاحبه Andrew
  3. في هذه السلسلة من المقالات، سنتحدث عن منصة jQuery Mobile التي تساعدك على إنشاء مواقع ويب متجاوبة للهواتف. بدايتك مع jQuery Mobile ما هو jQuery Mobile المحمول يوفرjQuery Mobile مجموعة وفيرة من أدوات التطوير التي تجعل موقع ويب الموجه للهواتف متجاوب ومرن وبسيط. يُعرّف jQuery ببساطة على أنه مجموعة من إضافات وودجات (widgets) jQuery تهدف إلى توفير API متعدد المنصات لإنشاء تطبيقات ويب للهواتف. من حيث الشيفرات البرمجية، jQuery Mobile يشبه jQuery UI، لكن في حين أن jQuery UI موجه لتطبيقات سطح المكتب، فإن jQuery Mobile موجه للهواتف النقالة. ما يجب معرفته حول إنشاء مواقع ويب للهواتف يجب عليك معرفة ثلاثة كلمات رئيسية عند تطوير مواقع ويب للهواتف، وهي أنه يجب أن يكون الموقع الموجه للهواتف: متجاوبًا: يتضمن كيف يبدو شكل الصفحة عند التنقل، وكيف يُضْغط على الزر وكيف تُحمّل البيانات على الهاتف. مرنًا: يعني أنه يمكن تخصيص الموقع عن طريق المستخدم ليناسب رغباته. بسيطًا: يعني أن الموقع يسهل تصفحه عن طريق الهاتف. ما يجب معرفته حول jQuery Mobile للمطورين أثّرت jQuery بشكل كبير على العالم الناشئ من تطوير وتصميم الويب، فلقد غيّرت من طريق تنفيذ واستخدام جافا سكريبت على مواقع وتطبيقات الويب ولذلك اختارتها مايكروسوفت كإطار جافا سكربت المفضل، وضمّنت دعم الإكمال التلقائي (auto-completion) لـ jQuery داخل visual studio، وبالتالي فإن JQuery mobile هي امتداد مدعوم وموثوق في مكتبة jQuery. لماذا jQuery Mobile؟ (تحتاج إلى معرفة jQuery MOBILE كمطور) كما أشرت في "ما يجب معرفته حول jQuery Mobile للمطورين”، أدى ظهور جيل أجهزة الهواتف إلى الحاجة إلى تطبيقات ويب متجاوبة ومرنة وبسيطة وبالتالي ارتفعت بسرعة الحاجة والطلب على jQuery mobile (إطار عمل jQuery)، وحان الآن الوقت بالنسبة لك كمعلم أو مبتدئ في API مواقع الهواتف الجوالة للقفز والمشاركة مع هذا المشروع. الجهاز الذي يُشغل jQuery Mobile يعتمد عمل jQuery في مختلف الأجهزة على المتصفح ونظام التشغيل. فالمتصفحات التي تدعم jQuery Mobile هي المتصفحات الحديثة التي تدعم وسوم HTML5 والقليل من CSS3. فالأجهزة التي تدعم jQuery mobile هي الحواسيب المكتبية والمحمولة،أنظمة الكل في واحد (all-in-one)، الأجهزة اللوحيّة، الهواتف الذكية، أندرويد، iOS، بلاك بيري (النسخ الحديثة)، webOS، ويندوز للهواتف، وأجهزة القراءة الإلكترونية، فيدعم jQuery mobile أجهزة كيندل kindle التي تتضمن متصفح مبني على Webkit تجريبي. ما يحتاجه المطور المحترف لإنشاء مواقع ويب للهواتف باستخدام jQuery Mobile لتطوير التطبيقات ستستخدم: بالنسبة لنظام ويندوز التطبيقات: Komodo IDE، eclipse، iWeb، Microsoft FrontPage، Aptana studio، Dreamweaver، notepad++. وسنستخدم Dreamweaver في هذا الدرس. بالنسبة لنظام OsX التطبيقات: Coda، espresso، textwrangler، kod. لغات البرمجية التي يجب على مطور الويب معرفتها هي HTML5 و CSS3 وجافا سكربت. يتكون jQuery Mobile من أربعة ملفات: ملف جافا سكربت، ملف CSS، وملفي PNG. يُحمل ملف جافا سكربت بشكل مقصود بعد تحميل مكتبة jQuery الأساسية، ويقوم هذا الملف بمهام كثيرة مثل إنشاء الويدجات وتطبيق مستمعي الحدث (event listeners) وتفعيل API. ورقة أنماط CSS تحدد مخطط ومظهر عناصر صفحة jQuery Mobile، بالإضافة إلى تحديد الانتقالات (transitions) والرسوم المتحركة مع تحويلات CSS3. يمكنك تحميل كامل حزمة jQuery Mobile (مكتبة جافا سكربت، CSSـ والرسومات) أو يمكنك الوصول إليها عن طريق مشروع CDN، راجع صفحة تحميل مشروع jQuery Mobile للتفاصيل، سنستخدم CDN في أمثلتنا لهذا الدرس. DOCTYPE لبناء محتوى HTML لموقع الهاتف DOCTYPE انتقالي لـ HTML4 بالنسبة للمطورين الراضين باستخدام وسوم HTML المهملة لكن يريدون الانتقال إلى HTML 4 يمكنهم استخدام DOCTYPE الانتقالي والذي يسمح للمتصفح تحليل الوسوم المهملة وعرضها دون رمي (throw) أية أخطاء. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> DOCTYPE الصارم لـ HTML4 يمكن للمطورين الذي لا يرغبون بتضمين دعم للوسوم المهملة وإمكانية وجود أخطاء تنسيق عند استخدام وضع التوافق ‘compatibility’ استخدام DOCTYPE الصارم والذي يخبر المتصفح برمي أخطاء عند تضمين وسوم مهملة في ملف HTML عند محاولة تحميله، وسيكون هذا مفيدًا لضمان عملك على معيار HTML4. HTML5 DOCTYPE يعمل HTML5 DOCTYPE تقريبًا على جميع المتصفحات القديمة وهو يُستخدم بالفعل على مواقع عديدة اليوم. لاستخدام HTML5 DOCTYPE في ملف HTML، استخدم المقتطف التالي: <!DOCTYPE html> هيكل الموقع الأساسي باستخدام HTML5 DOCTYPE <!DOCTYPE html> <html> <head><title></title></head> <body></body> </html> العمل مع هيكل HTML5، CSS و جافا سكربت بناء وتقديم المحتوي في HTML5 <!DOCTYPE html> <html> <head> <title>Titles should be short descriptions of the page</title> <meta charset="utf-8"> </head> <body> <div> <h1>h1 tags should contain the most important information on your site</h1> <p>Paragraph tags should be where most of your text content lives, or used as a separation between sections.</p> <ul> <li>This is a list element</li> <li>This is another element, notice the bullets and indentation </li> </ul> <p>You probably noticed that I used a "div" tag to wrap these other tags in. I'm using it as a container.</p> </div> </body> </html> نعرف بالفعل ما تفعله هذه الشيفرة البرمجية، لكن دعنا نلقي نظرة حول شكله في الواقع. ستظهر لك الصورة في الأسفل شكل HTML الذي كتبناه: CSS في بناء وتقديم محتوى HTML5 اضغط على زر الإدخال بعد وسوم <title> </title> واضف المقتطف كما هو موضح في رابط الصورة التالية لربط ملف الأنماط .css إلى ملف .html الرئيسي. يجب وضع الشيفرة البرمجية التالية في ملف hist.css @charset "utf-8"; /* CSS Document */ body { background:#0CF; font-family: Helvetica, Arial, sans-serif; font-size: 0.75em; } h1 { font-size: 200% } p { margin: 5px 0; padding: 0 3px; } #year{ text-align:center; font-weight:bold; font-size:200%; } .small { font-size: 80%; } تُظهر لك الصورة في الأسفل مظهر HTML الجديد بعد ربط ملف CSS . جافا سكربت في بناء وتقديم محتوى HTML5 ملخص لوضع وسوم جافا سكربت في ملفك: تحميل السكربتات في عنصر head : يجعلها تُحمّل أولا لكن قد تُؤثّر سلبا على وقت التحميل. تحميل السكربتات في body : تبدو فكرة جديدة لكنها تخلق مشاكل مع الصيانة وترمي (trows) تأخيرات التحميل المحتملة. تحميل السكربتات قبل إغلاق وسم body : سيبدو تحميل موقع أسرع لكن قد يؤدي ذلك إلى نتيجة غير مقصودة لنقص وظائف الموقع في الثواني الأولى للتحميل. دعونا نضع السكربتات في وسم head والتي هي الحالة المفضلة لكل مطور. <script src="js/javascript.js" type="text/javascript"></script> </head> يجب وضع السكربت التالي في ملف javascript.js // JavaScript Document window.onload = setYear; function setYear() { var currentYear = new Date(); document.getElementById('year').innerHTML = currentYear.getFullYear(); } ستوضح لك الصورة أدناه شكل HTML الجديد بعد ربط ملف .js . تعلمنا قليلا جول استخدام الحالي لهيكل HTML وربط CSS وتحميل السكربت (جافا سكربت)، والآن لنتعلم كيف نستخدم المهارات التي تعلمناها في jQuery mobile. ترجمة -وبتصرّف- للمقال jQuery Mobile Lesson 1 لصاحبه Andrew
  4. يظهر الخطأ 404 عندما تحاول زيارة صفحة غير موجودة في الموقع، ويميل أغلب الناس إلى ترك هذه الصفحة وعدم التفكير في تصميمها. لكن أخذ بعض الوقت وإنشاء تصميم لهذه الصفحة من الممكن أن يعطي تجربة استخدام مختلفة لزوار موقعك ويحدد قرار بقائهم في موقعك أو مغادرته في أسرع وقت. في الغالب عندما قمت بتصميم الموقع، قمت بتفقد صحة كل الروابط في الموقع حتى لا تظهر صفحة الخطأ للمستخدم لكن مع الوقت ربما تقوم بحذف منشور ما أو تغيّر اسمه. وأيضًا من الممكن أن يقوم المستخدم بزيارة الصفحة مباشرةً من دون تصفح الموقع مما سيقوده إلى صفحة الخطأ. يمكن تصميم هذه الصفحة ببساطة وسهولة بمساعدة قوالب صفحات ووردبريس الجاهزة. فهم الخطأ 404 رسالة الخطأ 404 هي عبارة عن رسالة ستظهر للزائر عند عدم وجود الصفحة التي يحاول زيارتها في الموقع. وهذه الصفحة موجودة بشكل افتراضي في ووردبريس لكن ليست موجودة في كل قوالب ووردبريس الموجودة على الإنترنت، إذا قمت بإنشاء قالب ووردبريس مخصص لموقعك، فذلك يزيد من فرصة بقاء المستخدم في موقعك حتى عندما لا يجد الصفحة التي يبحث عنها، عندما يجد صفحة 404 مبهجة وجميلة. الصفحة الافتراضية للخطأ 404 يتم تضمين صفحة الخطأ 404 مع بعض قوالب ووردبريس ولكن ليس بالضرورة كل القوالب التي تجدها على شبكة الإنترنت، مع أن ووردبريس تبحث بشكل افتراضي عن قالب الصفحة 404.php في القالب المفعّل في الموقع لكن إذا لم تجد هذا القالب سوف تظهر رسالة الخطأ الافتراضية لووردبريس والتي تعد غير ملائمة للمستخدم. إذا لم يكن قالب هذه الصفحة موجودًا، فيمكنك إنشاء قالب هذه الصفحة بسهولة. قم بإنشاء صفحة فارغة باسم 404.php. هذه هي التعليمات البرمجية التي سنبدأ بها: ملف 404.php <?php get_header(); ?> <h2>Error 404 - Page Not Found.</h2> <?php get_sidebar(); ?> <?php get_footer(); ?> التعليمات البرمجية السابقة ستقوم بإظهار مخرجات بسيطة، ضمن وسم h2. كما تستدعي أيضًا رأس الموقع (header)، الشريط الجانبي (sidebar)، وتذييل الموقع (footer) وكل هذه العناصر افتراضية ويمكنك تعديلها طبعًا لتناسب القالب الذي تقوم بتصميمه. العمل على قالب الصفحة أولًا، سنضيف مربع البحث إلى الصفحة لجعلها أكثر فائدة للمستخدم. وبهذه الطريقة، حتى إذا ظهرت هذه الصفحة لأحد الزوار، فسيكون لديه خيار البحث في موقعك بدلًا من المغادرة. ملف 404 – مع إضافة مربع البحث <?php get_header(); ?> <h2>Error 404 - Page Not Found.</h2> Search: <?php include(TEMPLATEPATH . "/searchform.php"); ?> <?php get_sidebar(); ?> <?php get_footer(); ?> جعل الصفحة أكثر ديناميكية كي نجعل صفحة 404 أكثر ديناميكية يمكننا استخدام ميزة إعادة التوجيه بحيث تظهر صفحة الخطأ للمستخدم لفترة وجيزة من الزمن ومن ثم يتم إعادة توجيه المستخدم إلى الصفحة الرئيسية للموقع. كما يمكن الاستفادة من هذه الصفحة في ارتفاع ترتيب الموقع في محركات البحث. في المثال التالي يمكننا البدء بتعديل ملف header.php من القالب الخاص بك. وإضافة ما يلي: <?php if (is_404()) { $redirectHome = get_option('home'); ?> <?php echo $redirectHome; ?> بعد إضافة هذه التعليمات، سنقوم الآن بتعديل ملف 404.php ليصبح بالشكل التالي: <?php get_header(); ?> <h1>Error 404 - File Not Found.</h1> <h3>Please <a href="<?php bloginfo('home'); ?>" Click here</a> to return to our home page, or you can wait to be redirected in 15 seconds.</h3> <?php get_footer(); ?> في المثال السابق ستظهر صفحة 404 للمستخدمين، ولكن بعد ذلك سيتم توجيههم تلقائيًا إلى الصفحة الرئيسية. بدلًا من تركهم محبطين من هذه الصفحة ومغادرتهم للموقع مع تجربة سيئة. قد لا يكون هذا المثال أفضل حل دائمًا، ولكن يمكن أن يكون مفيدًا لشخص يبحث عن شيء معين على موقعك. التأكد من عمل الصفحة يمكن التأكد أن صفحة 404 تعمل بكتابة رابط موقعك وإلحاقها بعنوان صفحة أو مقال تعرف أنه غير متواجد في موقعك. مثال: http://www.yourwebsitedomain.com/test404page.php إذا لم تعمل إذا لم يتم توجيهك إلى الصفحة هذا يعني أنه علينا تعديل ملف htaccess. على الخادم (server) كي نجعل صفحة 404 تعمل ابحث عن ملف htaccess. في تنصيب ووردبريس الخاص بك وأضف السطر التالي له: ErrorDocument 404 /index.php?error=404 لكن إذا لم يكن تنصيب الووردبريس في المجلد الجذر للخادم الخاص بك يجب الإشارة إلى المجلد الفرعي الذي نصبت عليه ووردبريس: ErrorDocument 404 /YOURSUBFOLDERNAME/index.php?error=404 هذا سيجبر الخادم على استخدام ملف 404.php مثال كامل التعليمات البرمجية أدناه تعطينا بعض الأفكار الجيدة التي يمكن استخدامها في صفحة 404 في حال لم يجد المستخدم ما يرغب في الوصول إليه، يمكنك ملاحظة هذه الخيارات التي تزيد من فرص بقائهم في الموقع، والوصول إلى ما يرغبون به. <?php get_header(); ?> <h1>404 Error</h1> We cannot seem to find what you were looking for. Maybe we can still help you. <ul> <li>You can search our site using the form provided below.</li> <li>You can visit <a href="<?php bloginfo?>"</a></li> <a href="<?php ('url'); ?>" the homepage.</a> <li>Or you can view some of our recent posts.</li> </ul> Search: TEMPLATEPATH . "/searchform.php"); ?> <h3>Recent Posts</h3> <ul> <?php query_posts('posts_per_page=5'); if (have_posts()) : while (have_posts()) : the_post(); ?> <li><a href="<?php the_permalink() ?>" title="Permalink for : <?php the_title(); ?>"><?php the_title(); ?></a> endwhile; endif; ?> </ul> <?php get_footer(); ?> يمكنك تغيير هذا المثال وتنسيقه ليناسب احتياجاتك. كل هذا المزيج من الخيارات يضمن بأن المستخدمين لن يغادروا الموقع مباشرًة، إذا لم يجدوا ما كانوا يبحثون عنه. جعل الصفحة أكثر ملائمة وبهجة للمستخدم لجعل هذه الصفحة أكثر جاذبية للمستخدم، يمكن استخدام العديد من الأفكار كوضع صور خلفية لهذه الصفحة أو كتابة تعليمات html خاصة لجعل هذه الصفحة أكثر قوة وتنوعًا وإليك بعض الأمثلة لصفحات 404 مميزة: Mundofox.com CSSRemix.com CSSRemix.com ChrisJennings.com ook.co.uk Youcastr.com Mixx.com JustCreativeDesign.com Agens.no Mushroomdigital.co.uk استخدمها بحكمة من الجيد دائمًا استخدام صفحة الخطأ 404 سواءً في ووردبريس أو في مواقع html الثابتة (static)، والاستفادة منها بوضع إعلانات مثلًا. هناك العديد من الطرق لتصميم صفحة 404 الخاصة بك، والعديد من الطرق أيضًا لمساعدة زوار الموقع في الوصول إلى ما يبحثون عنه، استخدم الأمثلة المذكورة أعلاه كما ترغب واختر ما يناسبك للوصول إلى أفضل نتيجة. ترجمة -وبتصرّف- للمقال How to Create a Custom WordPress 404 Error Page حقوق الصورة البارزة محفوظة لـ Freepik
  5. إذا كنت مهتمًا في تطوير ووردبريس فمن المؤكد أنك قمت بمحاولة تعديل قوالب ووردبريس سواء من خيارات القالب أو قمت بعمل قالب ابن من أجل تعديلات أكثر. تبعًا لتعريف موقع ووردبريس للقالب: مجموعة من الملفات التي تعمل معًا من أجل تشكيل واجهة للمستخدم. في هذا الدرس سوف نتعلم تطوير قوالب ووردبريس، سنبدأ مع الملفات الأساسية التي تشكل قالب ووردبريس وسوف نتقدم لنتعلم عن الحلقات وملفات ووردبريس الافتراضية، لتتمكن من عرض منشوراتك أو صفحات موقعك. بنهاية هذا الدرس سوف تكون قادرًا على بناء قالب ووردبريس الخاص بك. البداية إذا كنت جاهزًا و تملك نسخة ووردبريس تم تنصيبها على خادم محلي على حاسبك الشخصي، لنبدأ و نتعرف أين مكان القوالب في ووردبريس، توجد قوالب ووردبريس عادة في المسار wp-content/themes إذا ذهبت إلى هذا المسار داخل نسخة ووردبريس المثبتة على حاسبك فستجد عدة قوالب افتراضية من ووردبريس. لنقم بإنشاء قالب ووردبريس جديد. هناك بضع خطوات سهلة للقيام بذلك: أنشئ مجلد جديد في المجلد themes وأعطِه اسمًا مميزًا يشبه التالي my-awesome-theme. والآن في هذا المجلد قم بإنشاء ملفين باسم style.css و index.php الآن قم بفتح ملف style.css وانسخ التالي إليه: /* Theme Name: My Awesome Theme Theme URI: https://myawesometheme.awesome Author: Daniel Pataki Author URI: https://danielpataki.com Description: The theme for my awesome site Version: 1.0 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: my-awesome-theme This theme, like WordPress, is licensed under the GPL. Use it to make something cool, have fun, and share what you've learned with others. */ هذه التفاصيل ستظهر بشكل أوتوماتيكي في قسم تفاصيل القالب في شاشة Appearance->Themes في لوحة تحكم ووردبريس. إذا قمت بزيارة قسم Appearance->Themes سوف ترى قالبك بين القوالب المعروضة وأيضًا يمكنك تفعيله. لكن هذا القالب لا يملك أي تعليمات برمجية لذلك سترى شاشة بيضاء عند تفعيله وسنضيف التعليمات البرمجية قريبًا. كيف تعمل ملفات قوالب ووردبريس تعمل قوالب ووردبريس باستخدام صفحات ووردبريس الافتراضية. لأنها بذلك تقلل عدد الملفات التي نحتاجها لكل موقع مقارنة مع html التي تتطلب بناء صفحة مكررة كثيرة. لكن منذ ظهور php ومعالجتها للملفات على الخادم(server) يمكننا أن نوفر مساحة كبيرة في الملفات فمن الممكن أن يكون ملف واحد مسؤول عن عرض المقال المنشور أو عرض الصفحة ويمكن ل php أن تعرف ماذا نريد وبناء على ذلك تستبدل المعلومات في هذا الملف. ها هو مثال لنرى كيف يعمل ذلك: <!DOCTYPE html> <html> <head> <title>A single post template</title> </head> <body> <div id="site-header"> <h1>Welcome to my site</h1> <nav> <ul> <li><a href=''>Home</a></li> <li><a href=''>About</a></li> <li><a href=''>Contact</a></li> </ul> </nav> </div> <div id="article"> <h2 class="article-title"><?php the_title() ?></h2> <div class="article-content"><?php the_content() ?></div> <div class="article-meta">Published on <?php the_time( "Y-m-d" ) ?> by <?php the_author() ?></div> </div> <div id="site-footer"> <nav> <ul> <li><a href=''>Home</a></li> <li><a href=''>About</a></li> <li><a href=''>Contact</a></li> </ul> </nav> <div id="copyright">&copy; Daniel Pataki</div> </div> </body> </html> لاحظ أنك لا ترى محتوى ثابت مكتوب في هذه الملف، بدلًا من أن ترى عنوان المقال سترى دالة ()the_title و هي مسؤولة عن إخراج نص العنوان. هذه الدالة تحدد أي مقال تم طلبه حاليًا من خلال رابط الصفحة وتجلب المعلومات المناسبة من قاعدة البيانات لعرضها. وبذلك قمنا بعمل ملف لأي مقال في الموقع، وتأخذ ووردبريس هذه المرحلة إلى مستوى آخر وتقوم بفصل رأس وتذييل الموقع ووضعها في ملفات منفصلة واستدعائها عندما نريدها وليست نسخ رأس وتذييل الموقع كل مرة نحتاجهم فيها: <?php get_header() ?> <div id="article"> <h2 class="article-title"><?php the_title() ?></h2> <div class="article-content"><?php the_content() ?></div> <div class="article-meta">Published on <?php the_time( "Y-m-d" ) ?> by <?php the_author() ?></div> </div> <?php get_sidebar() ?> <?php get_footer() ?> كيف تعمل قوالب ووردبريس سوف نستعمل مجموعة من ملفات قالب ووردبريس في قالبنا الذي نقوم بصنعه، ما نحتاج لمعرفته هو أسماء الملفات التي تلزمنا لعمل الصفحات الأساسية للموقع وأسماء هذه الملفات محددة في وردبريس من التسلسل الهرمي لملفات قوالب ووردبريس. لنلقي نظرة على أنواع الصفحات التي نحتاجها: الصفحات الأرشيفية مثل (التصنيفات، الوسوم، أرشيفات الوسوم، الناشرين في الموقع) الصفحات الواحدة (المقال الواحد، الصفحة الواحدة، نوع المقال) الصفحة الرئيسية وصفحة كل المقالات صفحة الخطأ صفحة نتائج البحث تسمى هرمية لأن ووردبريس تبحث عن مجموعة من الملفات وتعرض الذي له هرمية أعلى. لنأخذ مثلًا صفحة المؤلف أولاً ووردبريس تبحث عن صفحة باسم هذه المؤلف إذا وجدتها سوف تقوم باستخدامها، لكن إن لم تجدها سوف تقوم بالبحث عن ملف برقم المستخدم وإذا لم تجدها ستتابع البحث عن ملف باسم author.php وإن لم تجده ايضًا ستبحث عن achive.php وإذا فشلت كل المحاولات السابقة سوف تعرض سوف تستخدم ملف index.php والذي هو حتمًا موجود لأنه مطلوب لإنشاء القالب. لاحظ أن ووردبريس تبدأ مع الملفات الخاصة ثم تقوم بالتعميم. وهذا أفضل لأنه يمكنك إنشاء ملف واحد من أجل أن يقوم باستخدامه لعرض المستخدم وهو author.php ولكن ربما تريد عمل ملف خاص لأحد المستخدمين وبذلك يمكنك استخدام اسم المستخدم مثال author-danielpataki.php. لنقم ببناء قالب عند بناء قالب جديد، فمن الأفضل بناء الأساسيات التي سأحتاجها في كل الصفحات. مثل أقسام رأس الموقع(header) وتذييل الموقع(footer). يجب أن يكون ما زال لديك ملفي index.php و style.css اللذان قمنا بإنشائهما، لذا لتبدأ. بناء الأساسيات عادة أقوم بعمل صورة للقالب كي يتم عرضه في لوحة تحكم ووردبريس في تفاصيل القالب. هذ ليس مطلوبًا بشدة عند بناء قالب لكنه يعطيني انطلاقة جيدة وإلهام لما سيكون عليه القالب إذا كان لديك صورة للتصميم فمن الأفضل أن تضعها، أو يمكنك الاستعانة بموقع Unsplash. عندما تحصل على صورة تعجبك، قم بقصها لتصبح بعرض 880px وبطول 660px، قم بتسميتها screenshot.png وضعها في مجلد القالب إذا أردت صورة بسرعة فيمكنك الاستعانة بهذه الصورة التي قم بعملها وتنزيلها من هنا. الخطوة القادمة هي كتابة تعليمات html الأساسية التي ستظهر في كل الصفحات مثل عنصر . لنبدأ بإنشاء ملف header.php داخل هذا الملف سنقوم بلصق تعليمات html كالتالي: <!DOCTYPE html> <html <?php language_attributes(); ?> class="no-js"> <head> <meta charset="<?php bloginfo( 'charset' ); ?>"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="profile" href="http://gmpg.org/xfn/11"> <?php wp_head(); ?> </head> <body <?php body_class(); ?>> الآن سنقوم بإنشاء ملف footer.php الذي سيحتوي وسوم الإغلاق للوسوم المفتوحة في ملف header.php : <?php wp_footer() ?> </body> </html> يجب أن أشير إلى دالتين هامتين و هما ()wp_head و ()wp_footer . عندما تقوم بإنشاء أي قالب يجب وضع ()wp_head مباشرة قبل إغلاق وسم <head> و دالة ()wp_footer مباشرة قبل إغلاق وسم <body> لأن هذا يساعد ووردبريس و الإضافات التي تنصبها على موقعك بوضع روابط ملفاتهم الخاصة هنا. الآن لنعد إلى ملف index.php إذا قمت بزيارة موقعك فستجد صفحة فارغة. هذا لأن ملف index.php فارغ ولم تقم باستعمال ملفات header.php و footer.php بعد. قم بإضافة التعليمات التالية إلى ملف index.php: <?php get_header() ?> My Awesome Theme <?php get_footer() ?> إذا زرت الصفحة مجددًا فيجب أن ترى عبارة "My Awesome Theme" وإذا اطلعت على الشيفرة المصدرية للصفحة فستجد الكثير من الأشياء هناك وليس عليك القلق بشأنها في الوقت الحالي. والآن من المؤكد بأنك ترغب بإضافة تنسيقات بما أن ملف style.css لا يتم استدعائه بشكل تلقائي، وربما تفكر بإضافة ملف style.css كما كنت تفعل عندما تصميم مواقع html ولكن في ورردبريس لا يجب عليك فعل ذلك أبدًا دع هذا الأمر لووردبريس. أنشئ ملف functions.php وأضف التعليمات التالية: <?php add_action( 'wp_enqueue_scripts', 'mat_assets' ); function mat_assets() { wp_enqueue_style( 'my-awesome-theme', get_stylesheet_uri() ); } التعليمات في ملف functions.php تخبر ووردبريس عن ملفات css الخاصة بك و بعدها تقوم ووردبريس باستدعاء ملفات في مكان وضعك لدالة ()wp_head و هذه هي وظيفة دالة ()wp_head أن تستدعي ملفاتك و بعض ملفات ووردبريس في رأس الموقع. الآن يمكنك تجربة وضع بعض التعليمات في ملف style.css ورؤية النتيجة مثل: body {background:red} قبل المتابعة لنضف بعض التعليمات البسيطة إلى ملفاتنا لتصبح أفضل وأجمل: في ملف header.php: <!DOCTYPE html> <html <?php language_attributes(); ?> class="no-js"> <head> <meta charset="<?php bloginfo( 'charset' ); ?>"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="profile" href="http://gmpg.org/xfn/11"> <?php wp_head(); ?> </head> <body <?php body_class(); ?>> <div id="site-header"> <h1><?php bloginfo('title') ?></h1> </div> <div id='site-content'> في ملف footer.php: </div> <!-- site content --> <div id='site-footer'> <p>&copy; My Awesome Theme</p> </div> <?php wp_footer() ?> </body> </html> في ملف style.css: /* Theme Name: My Awesome Theme Theme URI: https://myawesometheme.awesome Author: Daniel Pataki Author URI: https://danielpataki.com Description: The theme for my awesome site Version: 1.0 License: GNU General Public License v2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html Text Domain: my-awesome-theme This theme, like WordPress, is licensed under the GPL. Use it to make something cool, have fun, and share what you've learned with others. */ html { height: 100%; background:#444; font-family: "Helvetica Neue", Arial, sans-serif; line-height: 1.5em; } #site-header { text-align: center; } #site-header h1 { font-size:32px; color: #ffffff; font-weight: 300; letter-spacing: 1px; } #site-content { max-width:625px; background: #fff; margin: 0 auto; padding: 22px; border-radius:5px; } #site-footer { color: #fff; text-align:center; font-size:12px; text-transform: uppercase; } الآن كل صفحة ستقوم بزيارتها في موقع ستبدو نفسها لأننا فقط نملك index.php والذي سترجع إليه ووردبريس عند عدم وجود أي صفحات. فهم حلقة ووردبريس الحلقة هي أساس عمل كل الصفحات في ووردبريس. لأن الحلقة تحوي المعلومات التي ستعرض على الصفحة. ووردبريس تعرف ماذا يجب أن تحتوي كل صفحة، وتعرف هذه من روابط الصفحات مثلًا صفحة المقال الواحد يجب أن تحتوي مقال واحد وصفحتك الرئيسية يجب أن تحتوي أحدث 10 مقالات وهذا كله تعرفه ووردبريس. هذه المعلومات يتم جلبها من قاعدة البيانات بشكل أوتوماتيكي وكل ما عليك القيام به هو تفعيل الحلقة من أجل عرض البيانات من الأفضل شرح هذا بمثال أضف هذه التعليمات إلى ملف index.php: <?php if( have_posts() ) : ?> <?php while( have_posts() ) : the_post() ?> <h2><a href='<?php the_permalink() ?>'><?php the_title() ?></a></h2> <div class="content"> <?php the_content() ?> </div> <?php endwhile ?> <?php else : ?> <p>Oh No, there are no posts!</p> <?php endif ?> عندما تنسخ هذه التعليمات وتزور صفحتك الرئيسية ستكتشف قوة ووردبريس وسحر قوالبها، سوف ترى قائمة مقالاتك التي قمت بإضافتها في لوحة تحكم ووردبريس. إذا قمت بالضغط على عنوان المقال سوف تذهب إلى صفحة تعرض لك هذا المقال بالرغم من أنك لم تقم بعمل صفحة للمقال الواحد. لنحلل التعليمات البرمجية ونرى كيف تعمل. كل هذا يبدأ مع عبارة if التي تتحقق من قيمة دالة have_posts(). هذه الدالة ستعيد قيمة true إذا كان هناك مقالات وقيمة false إذا لم يكن هناك مقالات لعرضها. كما ترى من عبارة else إذا لم يكن هناك مقالات فإننا نعرض رسالة للمستخدم بأنه ليس هناك مقالات. إذا كان هناك مقالات فإننا ننشئ حلقة while والتي لن تتوقف عن تنفيذ التعليمات التي بداخلها ما دامت دالة ()have_posts ترجع قيمة true. الدالة الأولى التي نستخدمها هي دالة ()the_post والتي تقوم بإعداد بعض البيانات لنا وتكمل الحلقة بعد ذلك، إذا كنا قد وصلنا للمقال الأخير هذا يعني أن دالة have_posts() ستعيد لنا false في المرة القادمة وبذلك تتوقف الحلقة و يتم تنفيذ التعليمات البرمجية التالية إذا كان هناك تعليمات باقية. عند عرض المقال، قمت بإضافة العنوان والمحتوى الكامل للمقال. لقد قمت باستخدام دالة ()the_permalink للحصول على رابط المقال، دالة ()the_title لعرض العنوان ودالة ()the_content لعرض محتوى المقال كاملًا. هذه الدوال تستخدم داخل حلقات ووردبريس وسوف تحدد المقال المطلوب كما هو متوقع. هكذا يبدو موقعي الآن: من الآن وصاعدًا عرض المقال بطرق مختلفة هي مسألة معرفة الدوال التي تستخدمها ووردبريس وإضافة بعض تعليمات CSS لجعل قالبك رائع، إليك بعض الدوال المفيدة التي تساعدك في عرض التصنيفات، الوسوم، تاريخ المقالات وأكثر من ذلك. القوالب وعبارة if ماذا تفعل إذا أردت عرض مقتطف من المقالات في الصفحة الرئيسية وكامل المقال في حال عرض المقال الواحد؟ في هذه الحالة لديك خيارين استخدام الدوال الشرطية التي توفرها ووردبريس أو عمل ملف قالب جديد. الدوال الشرطية التي توفرها ووردبريس تستخدم للتحقق من أشياء عديدة، على سبيل المثال إذا أردت معرفة إذا كنت تعرض مقال واحد أو عدة مقالات، إليك اللائحة الكاملة لهذه الدوال على موقع ووردبريس بمساعدة دالة ()is_singular يمكننا فعل التالي: <?php get_header() ?> <?php if( have_posts() ) : ?> <?php while( have_posts() ) : the_post() ?> <h2><a href='<?php the_permalink() ?>'><?php the_title() ?></a></h2> <div class="content"> <?php if( is_singular() ) : ?> <?php the_content() ?> <?php else : ?> <?php the_excerpt() ?> <?php endif ?> </div> <?php endwhile ?> <?php else : ?> <p>Oh No, there are no posts!</p> <?php endif ?> <?php get_footer() ?> الحل الآخر هو استخدام ملفين منفصلين index.php لعرض مقتطفات من المقالات وملف single.php لعرض المقال كاملًا. من المؤكد أنك تسأل أي طريقة هي الأفضل؟ ليس هناك إجابة واضحة لهذا السؤال لأنه يعتمد بالدرجة الأولى على ما تريد إنجازه. في الواقع، عرض جميع المقالات وعرض المقالة الواحدة مختلف بما يكفي لعمل ملفين منفصلين، ولكن هناك بعض القوالب التي تطبق الطريقتين. وبعض القوالب تذهب أبعد من ذلك وتقوم بإنشاء ملف خاص بالحلقة فقط. لأن هذا سيجعل التعليمات نفسها تستخدم من قبل جميع الصفحات بدلًا من نسخ الحلقة في كل مكان نريد تواجد الحلقة فيه. لنلقي نظرة على ملف index.php المعدل: <?php get_header() ?> <?php if( have_posts() ) { while( have_posts() ) { the_post(); get_template_part( 'template-parts/content', '' ); } } else { get_template_part( 'template-parts/content', 'none' ); } ?> <?php get_footer() ?> قمت بوضع كل محتوى المقال في ملف مختلف. دالة ()get_template_part تجلب المحتوى من ملف وتجمع الوسيط الثاني مع الأول بإضافة “-” للمسار. في حالة الاستدعاء الثاني داخل عبارة else الدالة ستحاول استدعاء ملف template-parts/content-none.php، في الاستدعاء الأول لهذه الدالة تركت الوسيط الثاني فارغًا وهذا سيستدعي template-parts/content.php. في ملف single.php، سأضيف نفس التعليمات ولكن سأضيف كلمة single للوسيط الثاني من دالة ()get_template_part . أخيرًا لنقم بعمل مجلد template-parts وبداخله 3 ملفات: content.php و content-single.php وcontent.php وأقوم بنسخ تعليمات الحلقة المناسبة لكل ملف على سبيل المثال ملف content.php سيبدو كالتالي: <h2><a href='<?php the_permalink() ?>'><?php the_title() ?></a></h2> <div class="content"> <?php the_excerpt() ?> </div> غطينا في هذا الدرس ما يكفي لعمل قالب بسيط، وهذا سيمكّنك من عرض مقالاتك بالطريقة التي تعجبك بتنسيقاتك الخاصة. ترجمة -وبتصرّف- للمقال WordPress Development for Beginners: Building Themes لصاحبه Daniel Pataki
  6. إذا أردت أن تصبح مطوّر ووردبريس فهذا يتطلب أيضًا تعلم لغة البرمجة الشهيرة PHP، والتي بنيت من خلالها منصة ووردبريس. طورت بشكل أساسي في عام 1994، PHP لغة برمجة قوية ومفتوحة المصدر لإنشاء مواقع ويب ديناميكية وتفاعلية. سوف تتعلم القواعد الأساسية للغة PHP وكيفية كتابتها، وكيفية عملها، والاطلاع على بعض الأمثلة. ما هي PHP؟ PHP هي لغة برمجة نصية من طرف الخادم (server). لفهم ما يعني هذا، لنقارنها مع Html. عندما تزور صفحة Html بسيطة، يقوم متصفحك بإرسال طلب للخادم الذي يحتوي هذه الصفحة التي تحاول الوصول إليها. الخادم يكتشف أي ملف تريد الوصول إليه ويقوم بإرساله لك. متصفحك يترجم شيفرة Html المرسلة ويعرضها لك. بالمقارنة، عندما تزور صفحة PHP هناك خطوة إضافية متصفحك يقوم بإرسال طلب والخادم يجد الملف الذي تريده. قبل إرساله لمتصفحك، الخادم يعالج الملف، منتجًا مخرجات Html نهائية (Html output) ثم يقوم بإرسال هذه المخرجات لمتصفحك الذي يقوم بعرضها كالمعتاد. لهذا السبب عندما تنظر إلى الشيفرة المصدرية لأي موقع لا ترى أبدًا أي شيفرات PHP، فقط Html، حتى لو كان الموقع مكتوبًا بلغة PHP. إذا لماذا يحتاج الخادم إلى عملية المعالجة؟ ما المغزى من ذلك؟ لأنه يمكننا من عمل مواقع ديناميكية وكتابة شيفرات بفعالية كبيرة. لنستعرض مثالين بسيطين. في Html كل شيء ثابت (static). يمكنك إنشاء صفحة تعرض كلمة “Good morning” أو “Good evening” لكنك لا تستطيع إظهار واحدة وإخفاء الأخرى اعتمادًا على التوقيت الحقيقي لليوم. في PHP تستطيع عمل ذلك لأن الخادم يمكنه معالجة التعليمات البرمجية آخذًا التوقيت الحقيقي لليوم بعين الاعتبار. والمنطق لهذا المثال يكون كالتالي: if it is before 10am { <h1>Good Morning</h1> } if it is after 6pm { <h1>Good Evening</h1> } otherwise { <h1>Good Day</h1> } طبعًا هذه ليست التعليمات البرمجية الحقيقية، لكنها تتبع منطق PHP. الخادم سيحدد أي حالة هي الحالة المحققة للشرط وسيعرض واحدة فقط من العناوين. مثال آخر هو موقع تويتر أو أي موقع آخر الذي يقدم المحتوى المكتوب من قبل المستخدمين. إذا كان تويتر يستخدم Html فقط، أحد ما يجب عليه أن يقوم بوضع تغريدتك في ملف Html في كل مرة تقوم بتغريدة جديدة وهذا طبعًا ليس استغلالاً جيدًا للوقت. يوجد حوالي 1 مليار حساب على موقع تويتر لذلك الشركة ستحتاج إلى 1 مليار ملف Html، ملف لكل مستخدم. مع PHP، مشكلة إعادة توليد ما ينشره المستخدم يمكن أن يحل بعدة ملفات. سنعود إلى هذا المثال لاحقًا في هذه المقالة. البداية قبل أن نبدأ، أود التنبيه على أنه في المراحل الأولى من تعلم PHP ربما لن تكون على قادرًا على إنشاء موقع ويب. هذه ردة فعل طبيعية وهذا أيضًا ما شعرت به عند بداية تعلم PHP. الخطوات الأولى تجريدية قليلاً، لكن في نهاية هذا المقال سترى النور. من أجل اختبار وممارسة ما سوف تتعلم في هذه المقالة، سوف تحتاج إلى خادم أباتشي (Apache server). يمكن أن يكون هذا موقع اختبار عبر الإنترنت ولكن يمكن أيضًا أن يكون خادم محلي. أوصي بإنشاء خادم محلي (local server). وإليك كيفية القيام بذلك: أولاً، عليك تحميل وتثبيت Virtualbox وVagrant. إنشاء مسار في أي مكان على كمبيوترك لتخزين ملفات المشروع. لدي مجلد “websites” في مسار المستخدم الخاص بي، قمت بإنشاء مسار “PHPtutorial”. وسوف أشير إلى هذا باسم “مجلد المشروع الرئيسي” طوال بقية هذا المقال. بمجرد إنشاء المسار، افتح terminal على linux أو osx، أو cmd على windows، وانتقل إلى المجلد. إذا قمت بإنشاء بنية المجلد نفسه كما لدي، على linux أو osx يمكنك كتابة cd ~/websites/PHPtutorial على windows cd %HOMEPATH%/websites/PHPtutorial عندما تكون في المسار الصحيح، ألصق الأمر التالي: curl -L -o 'install.sh' http://bit.ly/1hBfq57 && curl -L -o 'Vagrantfile' http://bit.ly/1mE3Qt9 && vagrant up وبمجرد تنفيذ الأمر، سوف تكون قادرًا على الوصول إلى الموقع الخاص بك على http://192.168.33.21. إذا قمت بإعادة تشغيل جهاز الكمبيوتر الخاص بك، سوف تحتاج أيضًا إلى إعادة تشغيل الخادم الخاص بك، والتي يمكنك القيام به عن طريق الانتقال إلى مجلد المشروع الرئيسي في سطر الأوامر وكتابة vagrant up. ملف المشروع الرئيسي هو الآن المجلد الجذر الخاص بك. قم بإنشاء ملف index.html هناك مع أي محتوى، وينبغي أن يتم عرضه بشكل صحيح عند زيارة موقع الويب الخاص بك على http://192.168.33.21. الخطوات الأولى في البداية، لنلقِ نظرة على الوسوم(tags)، المتغيرات(variables)، القيم (values)، والإظهار (echoing). أنشئ ملف اسمه index.php في ملف المشروع الرئيسي واكتب”Hello HTML” بداخله. للتأكد بأنه يعمل قم بزيارة http://192.168.33.21، ويجب رؤية النص الذي قمت بكتابته على شاشة المتصفح. قاعدة مهمة جدًا وهي أن ما يكتب داخل وسوم php يعتبر شيفرة PHP، وأي شيء آخر يعتبر Html. لذلك ظهر نص Hello HTML. وسوم php تبدأ ب<?php وتنتهي ب ?>. أي شيء بين هذين الوسمين يعتبر PHP. Hello HTML <?PHP echo "Hello PHP"; ?> التعليمات البرمجية السابقة أظهرت كيف يتم استخدام وسوم php وكيفية إظهار القيم على الشاشة. عندما تُظهر قيمة أنت تخبر PHP لعرض هذه القيمة على الشاشة. نتيجة تنفيذ التعليمات البرمجية السابقة سيكون ” Hello Html Hello PHP” . الجزء الأول يعتبر Html لأنه لم يكن داخل وسوم PHP بينما الجزء الثاني قد تم إظهاره بواسطة شيفرة PHP. لاحظ أنه يجب إحاطة النص بإشاراتي اقتباس. المتغيرات(variables) تسمح لك بتخزين القيم بدون عرضها. المتغيرات يتم استخدامها بكثرة لتخزين العديد من الأشياء. لنلقي نظرة على مثال بسيط: <?PHP $name = "Daniel"; echo $name; ?> في التعليمات البرمجية السابقة قمت بتخزين قيمة “Daniel” في متغير يدعى name$. بدلاً من إظهار “Daniel” بشكل مباشر، الآن أنا أظهر قيمة name$ مهما كانت. من الوهلة الأولى تبدو لك كزيادة في التعليمات البرمجية بدون فائدة لكن إذا كنا نستعمل هذا المتغير في أكثر من مكان يمكننا تغيير كل شيء بتغيير قيمة هذا المتغير في مكان واحد، بدلاً من تغيير تلك القيم في كل مكان وُضِعت فيه. لاحظ أن الفصل بين أسطر الشيفرات يكون بوضع فاصلة منقوطة (;). إذا أنتجت شيفرتك خطأ فهناك احتمال أنك نسيت وضع فاصلة منقوطة في مكان ما، خطأ شائع ومحبط لكثير من المبرمجين في كل مكان. القيم(values) يمكن أن تكون عدة أنواع. لحد الآن تعرفنا على واحد من هذه الآنواع وهو النوع النصي. النص هو سلسلة من المحارف ويجب إحاطته بإشارتيَ اقتباس (“). نوع آخر وهو النوع الرقمي. الرقم هو أي عدد كامل ولا يجب وضعه ضمن اشارتي اقتباس وإلا سيعتبر نصًا. واحدة من أهم أنواع القيم المصفوفات(array). المصفوفات تخزن عدة قيم، والتي من الممكن أن تكون قيمًا من أنواع مختلفة. <?PHP $name = 'Daniel'; $age = 30; $interests = array( 'guitar', 'singing', 'squash', 'running', 'board games' ); ?> كما ترى يمكن تعريف المصفوفة على الشكل التالي (array(value1 , value2 , value3. يجب كتابة القيم دائمًا في المصفوفة كما تكتب خارج المصفوفة. النص يجب أن يكون محاطًا بإشارتيَ اقتباس (“)، الرقم غير محاط بشيء وهكذا. مثال بسيط إذا بماذا يمكن استخدام ما تعلمناه للآن، ما تعلمناه للآن لا يمكنه إنشاء موقع بعد ولكن حتمًا نحتاج لمثال عملي الآن. لنلقِ نظرة على شيفرة لتطبيق توقعات الطقس: <?PHP $unit = "C"; $temp = 29; if( $unit == 'F' ) { $temp = $temp * 9 / 5 + 32; } $forecast = "The weather today will be great, a Sunny " . $temp . $unit; echo $forecast; ?> هناك بعض الأشياء الجديدة في هذه التعليمات البرمجية، لكن أعتقد أن المثال يشرح نفسه بوضوح. لنراجعه سطرًا بسطر. في البداية، نقوم بإنشاء متغيرين: أول متغير عبارة عن نص يحمل القيمة “C” للمئوية، والمتغير التالي هو عدد صحيح، 29، والذي يهدف إلى تخزين درجة الحرارة الفعلية. الأهداف هنا بسيطة: عرض النص الذي يخبرنا درجة الحرارة الحالية باستخدام القيمة من متغير درجة الحرارة لدينا في وحدة قياس حسب الاختيار، مئوية أو فهرنهايت. السطر التالي هو عبارة if، التي يتم تنفيذ ما بداخلها فقط إذا كانت القيمة داخل الأقواس صحيحة. الشرط هو: $unit == 'F' الذي يترجم منطقيًا كالتالي: إذا كانت قيمة واحدة القياس هي F و هي ليست كذلك و بالتالي سيتم تخطي كتلة هذا الشرط و ما بداخله بدون تنفيذ. في السطر الأخير متغير forecast$ الذي يحوي عبارة “The weather today will be great, a Sunny “ . اعتمادًا على قيمة متغيري unit$ و temp$ ستكون النهاية مختلفة، النقاط بين النصوص والمتغيرات تجمع المتغيرات مع النصوص أو تدمجها. لذا ببساطة سيتم استبدال قيمة temp$ بـ 29 و unit$ بـ C والمخرجات النهائية ستكون: The weather today will be great, a Sunny 29C الآن، ماذا يحدث إذا غيرنا قيمة unit$ إلى F، في البداية سوف يتم تنفيذ التعليمات البرمجية داخل if لأن شرطها تحقق وأصبحت صحيحة، وهذا يعني أن كل شيء بين الأقواس المجعدة سيتم تنفيذه أيضًا. في المثال، اسندت قيمة جديدة لمتغير temp$. أخذت القيمة الأصلية، وتم ضربها في 9، وتقسيمها من على 5 وإضافة 32 وهذه هي المعادلة لتحويل مئوية إلى فهرنهايت. يجب أن تكون القيمة الجديدة لـ temp$ هي 84.2، والقيمة الجديدة unit$ هي F الآن سيتم استبدال المتغيرات بقيمها في الجملة النهائية فتصبح: The weather today will be great, a Sunny 84.2F هذا المثال لا يزال غير مجدٍ بعض الشيء بالنسبة لنا، لكنه يبدأ في إظهار قوة PHP. لاحظ كيف وصلنا إلى الناتج النهائي عن طريق استبدال المتغيرات ببساطة مع القيم. فهو يساعد على الوصول إلى عقلية “replacing placeholder” لأنه هو أساسًا ما تدور حوله لغة PHP. في مثال حقيقي، سنقوم باستخدام القيم الجوية الحالية وبدلًا من كتابة جملة The weather today will be great, a Sunny سوف نستخدم الظروف الفعلية. وقد نرغب أيضًا في التنبؤ غدًا أو في اليوم التالي، مما سيؤدي إلى استبدال “today” بنص آخر. وبالإضافة إلى ذلك، من الواضح أن درجة الحرارة الحالية من الصعب أن تكون مكتوبة يدويًا في الشيفرة البرمجية، فإنه سيكون من الضروري أن يتم استرجاعها من مكان ما. على الرغم من أن هذا يضيف بضع طبقات من التعقيد، لكن المبدأ سيكون نفسه وهو أن نجلب البيانات من مكان ما ونضعها في مخرجاتنا. عبارة if والحلقات الشرطية loops لقد رأينا بالفعل عبارة if في مثال حقيقي. تقوم عبارة if بالتأكد من الشرط داخل الاقواس. إذا كانت النتيجة صحيحة تتم معالجة التعليمات البرمجية داخل الأقواس المجعدة وإلا فأنه لن يتم تنفيذ التعليمات. عبارة if أيضًا ذات صلة مع else if و else. عبارة else if تكتب دائمًا بعد عبارة if. إذا كانت عبارة if غير محققة سيتم التأكد تاليًا إذا كانت عبارة else if محققة، و يمكن أن يكون لدينا عدد غير محدد من عبارات else if بناءً على نفس المنطق. و أخيرًا لدينا عبارة else التي سيتم تنفيذها عندما تكون جميع الشروط التي تسبقها غير محققة، و ليس من الضروري أن يسبق عبارة else عبارة else if يمكنك فقط استعمال عبارة if و else مع بعضهما. لنلقِ نظرة على الأمثلة التالية للتوضيح: // Simple if Statement if( $speed > 50 ) { echo "You are over the speed limit"; } // If and else statement if( $age < 21 ) { echo "You may not buy alcohol"; } else { echo "You may buy alcohol"; } // If, else if and else statement if( $speed < 25 ) { echo "You are too slow"; } elseif( $speed > 120 ) { echo "You are too fast"; } else { echo "Your speed is just right!"; } شيء آخر هنا علينا أن نلاحظه وهو الأسطر التي تبدأ مع مائلة مزدوجة “//”. المائلة المزدوجة تسمح لك بإضافة تعليق سطر واحد في أي مكان في التعليمات البرمجية. يمكنك إضافة تعليقات متعددة الاسطر من خلال البدء مع / * writing whatever you’d like and ending it with * /. الحلقات (loops) تأتي بالعديد من الأشكال والأحجام، أربعة على وجه الدقة. تسمح لك الحلقات بتشغيل نفس التعليمات البرمجية عدة مرات بناءً على استمرار الشرط الذي بدأت به الحلقة. قد تكره الحلقات الآن، لكنها سوف تصبح أقرب حلفائك عندما تصبح أكثر احترافية في كتابة التعليمات البرمجية. لنبدأ مع حلقة foreach. حلقة foreach تمر من كل عناصر المصفوفة وتنفذ تعليمات برمجية معينة. فيما يلي مثال سريع يظهر قائمة Html: <?PHP $names = array( "Daniel Pataki", "Raelene Morey", "James Farmer" ); echo "<ul>"; foreach( $names as $name ) { echo "<li>" . $name . "</li>"; } echo "</ul>"; ?> المفتاح هنا هو فهم العبارة داخل الأقواس، وهي $names as $name، الجزء الأول هو اسم المصفوفة التي ترغب في المرور من خلالها. والثاني هو اسم المتغير الذي يمكن الرجوع إليه من أجل الحصول على قيمة عنصر في هذه المصفوفة الذي تمر عليه حاليًا من خلال الحلقة ولك الحرية في تسمية هذا المتغير أي أنه ليس من الضروري أن تسميه name$ هنا بل يمكن تسميته أي اسم تريده. في مثالنا، سيتم تنفيذ الحلقة ثلاث مرات. عندما تمر الحلقة بالمصفوفة للمرة الأولى ستكون قيمة المتغير name$ هي “Daniel Pataki”، وفي المرة الثانية ستكون “Raelene Morey,” وفي المرة الثالثة ستكون “James Farmer.” يعرض المثال التالي تفاصيل في قائمة، من مصفوفة مترابطة: <?PHP $details = array( "name" => "Daniel Pataki", "Age" => "30", "Twitter" => "http://twitter.com/danielpataki" ); echo "<ul>"; foreach( $details as $label => $value ) { echo "<li><strong>" . $label . "</strong>: " . $value . "</li>"; } echo "</ul>"; ?> الأقواس تحتوي الآن على ثلاثة متغيرات: $details as $label => $value ، ستمر الحلقة الآن من المصفوفة details$. عند كل مرور للحلقة على المصفوفة سيتم مناداة المفتاح label$ والقيمة المرتبطة بهذا المفتاح value$ ، عند المرور الثاني ستكون قيمة label$ هي Age و قيمة value$ هي 30 و ستكون نتيجة تنفيذ التعلمية هي: • Name: Daniel Pataki • Age: 30 • Twitter: http://twitter.com/danielpataki كيف تعمل قوائم مقالات ووردبريس: لنلقِ نظرة الآن على مثال أكثر ارتباطًا بووردبريس: كيف تعمل قوائم مقالات ووردبريس. لا تحاكي التعليمات البرمجية أدناه ووردبريس بالضبط، ولكن جوهر العملية موجود. تخيل أن المنشورات أو المقالات جزء من مصفوفة ارتباطية كبيرة. التعليمات البرمجية الخاصة بصفحتك الرئيسية ببساطة تمر على المصفوفة وتضع البيانات المناسبة في المكان المناسب في بعض وسوم Html: $posts = array( 0 => array( "post_title" => "My First Post", "post_excerpt" => "This is a short snippet of text from the first post", "post_date" => "2015 December 1", "post_author" => "Daniel Pataki" ), 1 => array( "post_title" => "Second Post", "post_excerpt" => "Short exceprt for post number 2", "post_date" => "2015 December 4", "post_author" => "Raelene Morey" ) ); foreach( $posts as $post ) { echo "<div class='post'>"; echo "<h1>" . $post['post_title'] . "</h1>"; echo "<p>" . $post['post_excerpt'] . "</p>"; echo "<div class='post-meta'>Published on " . $post['post_date'] . " by " . $post['post_author'] . "</div>"; echo "</div>"; } الدوال (Functions) بطريقة ما، الدوال تخدم نفس الغرض كما المتغيرات - وسيلة لاستبدال التعليمات البرمجية. يمكن أن تأخذ الدوال كتلة من التعليمات البرمجية وتخزّنها لوقت لاحق وتستخدمها عدة مرات. دعونا ننظر إلى مثال بسيط جدًا باستخدام حلقة المقالات بالمثال السابق. حيث أنه من المحتمل أن يتم استخدام الجزء الذي نظهر فيه عناصر المنشورات في عدة صفحات من موقع الويب - كالصفحة الرئيسية، وصفحات الأرشيف، وصفحات المؤلفين، وما إلى ذلك. سنستخدم نفس التعليمات البرمجية مرارًا وتكرارًا. هذا هو بالضبط الوقت الذي نحتاج فيه إلى الدوال. <?PHP function display_post( $postdata ) { echo "<div class='post'>"; echo "<h1>" . $postdata['post_title'] . "</h1>"; echo "<p>" . $postdata['post_excerpt'] . "</p>"; echo "<div class='post-meta'>Published on " . $postdata['post_date'] . " by " . $postdata['post_author'] . "</div>"; echo "</div>"; } $posts = array( 0 => array( "post_title" => "My First Post", "post_excerpt" => "This is a short snippet of text from the first post", "post_date" => "2015 December 1", "post_author" => "Daniel Pataki" ), 1 => array( "post_title" => "Second Post", "post_excerpt" => "Short exceprt for post number 2", "post_date" => "2015 December 4", "post_author" => "Raelene Morey" ) ); foreach( $posts as $post ) { display_post( $post ); } التعليمة البرمجية أعلاه ليست أقصر، ولكن في أي وقت نريد عرض منشورات في المستقبل يمكننا فقط أن نكتب display_post( $post ); مما يجعلها أكثر كفاءة في المستقبل. كيف يعمل كل هذا؟ دعونا نفهم ذلك من خلال النظر إلى الدالة في الجزء العلوي من التعليمات البرمجية. قمت بتسمية هذه الدالة بـ display_post(); و في القوسين نلاحظ أن هذه الدالة تتوقع وسيط واحد. و نحن نعلم أن هذا الوسيط يجب أن يحتوي معلومات المنشور لذا سيكون هذا الوسيط عبارة عن مصفوفة مترابطة تحتوي معلومات منشور واحد أسميته postdata$. ضمن الأقواس المجعدة ندرج كل التعليمات البرمجية التي نريد تنفيذها عند استدعاء الدالة. هذه هي نفس التعليمات البرمجية التي كانت لدينا من قبل، أنا فقط بحاجة إلى إعادة تسمية كافة المتغيرات إلى postdata$، اسم الوسيط الذي تتوقعه الدالة، والتي يمكننا استخدامها في أي مكان داخل الدالة. في الجزء السفلي من التعليمة البرمجية، يمكنك أن ترى أن عرض المقال الآن أصبح أكثر تنظيمًا للتعليمات البرمجية، لقد استخدمنا للتو display_post($post). أنا متأكد أنك تشعر ببعض الضياع الآن حول التسمية، لماذا استخدمنا post$ في مكان و postdata$ في مكان آخر؟ لنراجع التعليمات البرمجية يتم تشغيل الدوال فقط عندما يتم استدعاؤها، لذا أكثر ما يهمنا في تعليماتنا البرمجية هو مصفوفة $posts، بعد ذلك نقوم بإنشاء الحلقة، كما فعلنا سابقًا. بداخل الحلقة المتغير الذي يحوي بيانات المقال الواحد post$، لحد الآن لم يتغير شيء من مثال الدالة. الآن، بدلًا من وجود كتلة من التعليمات البرمجية هناك نحن ببساطة سنقوم بإسناد هذه التعليمات البرمجية لدالة. نستدعي دالة ()display_post، نعلم أننا نحتاج لتمرير بيانات لهذه الدالة و هذه البيانات هي بيانات مقال واحد. أي متغير يحوي هذه البيانات الآن؟ المتغير post$ ، هذا يأتي من تعريف الحلقة. بعد ذلك، انتقل للأعلى إلى الدالة. سيتم الآن تنفيذها. في هذه الدالة تم تسمية الوسيط postdata$ هذا يعني ببساطة أنه ضمن هذه الدالة سيتم الإشارة إلى البيانات التي يتم تمريرها إلى هذه الدالة كوسيط بـ.postdata$ إنشاء مقتطفات يمكن أن تحتوي الدوال على وسائط متعددة وقيم افتراضية لهذه الوسائط. دعونا ننشئ دالة يمكن أن تحول أي نص إلى مقتطف عن طريق قطعه إلى قطع نصية. نريد دالة لتكون مرنة قدر الإمكان، وقادرة على السيطرة على طول المقتطف، وإلحاق السلسلة إلى النهاية. سنجعل الوسيط الأول النص الذي نريد تقصيره، الوسيط الثاني سيكون لتحديد مقدار المحارف التي نريد اقتطاعها، والثالث النص المُلحق. دعونا نبدأ بإعطاء الدالة إطار للعمل ضمنه: function make_excerpt( $text, $length = 200, $append = '...' ) { } لاحظ أنني أعطيت قيمًا افتراضية لهذه الوسائط. هذا يعني أنه يمكنك استدعاء الدالة دون تمرير الوسيطين الثاني والثالث. إذا قمت بذلك سوف تفترض الدالة أنك تريد إنشاء مقتطف طويل من 200 حرف وإلحاق ثلاث نقاط بعد ذلك. دعونا نكتب محتوى هذه الدالة: function get_excerpt( $text, $length = 200, $append = '...' ) { if( strlen( $text ) < $length ) { return $text; } else { $excerpt = substr( $text, 0, $length ); $excerpt .= $append; return $excerpt; } } لقد أضفت الكثير من الأشياء الجديدة في هذه الدالة، لذلك دعونا نشرح كل إضافة جديدة بالتفصيل. يبدأ كل هذا بعبارة if، الذي تتحقق مما إذا كان النص المعطى أطول من طول معين. وذلك باستخدام دالة مبنية مسبقًا في لغة PHP تدعى ()substr. تأخذ هذه الدالة سلسلة حرفية كوسيط وتعيد طولها. إذا كان طول text$ أقل من length$ يتم إرجاع النص ببساطة - سنتحدث عما يعني الإرجاع خلال لحظات. وإلا، فإننا ننشئ مقتطفًا باستخدام الدالة ()substr. هذه الدالة تقطع سلسلة حرفية وتتطلب ثلاثة وسائط: النص الذي سنقتطع منه، ومن أين تبدأ بالاقتطاع، وطول النص الذي نرد اقتطاعه. نريد أن نبدأ من بداية النص وقطع أحرف length$، وهذا هو السبب لإضافتي رقم 0 كي يقتطع من بداية السلسلة الحرفية. وأخيرًا، نضيف النص الملحق إلى النهاية. لقد استخدمت .= للقيام بذلك. هذه هي نفس كتابة $excerpt = $excerpt. $append في السطر الأخير، قمت بإعادة متغير excerpt$. ما هو الإرجاع؟ حتى الآن كنا في معظم الأحيان نظهر المخرجات، ولكن في كثير من الحالات نحن لا نريد عرض نتيجة حساباتنا. الدالة ()strlen هي مثال جيد على ذلك. أنت لا تريد فعلاً عرض طول متغير text$ أنت ترغب فقط في استخدامه في التعليمات البرمجية لتحقيق شيء ما. ()strlenترجع هذه القيمة، مما يسمح لك باستخدامها في مكان آخر. عند إرجاع قيمة باستخدام دالة، يمكنك إما استخدامها مباشرة مثل دالة ()strlen، أو تعيينها لمتغير $short_text = get_excerpt( $content ) يمكنك بعد ذلك إظهار short_text$ في وقت لاحق عند الحاجة. لمزيد من القراءة والدراسة PHP هي الأساس لجميع قوالب و إضافات WordPress لذلك سوف تحتاج إلى الاعتماد بشكل كبير جدًا على هذه اللغة. عند العمل في بيئة ووردبريس، ستستخدم دوال PHP (مثل strlen و substr)، ولكن هناك أيضًا مئات من الدوال التي تم إنشاؤها خصيصًا لووردبريس من قبل فريق التطوير الأساسي لمساعدتك. ستكتشف بعض هذه الوظائف في الدروس القادمة من هذه السلسلة. لقد غطينا الأساسيات التي تحتاج إلى معرفتها للبدء بتطوير ووردبريس، ولكن الحقيقة هي أننا فقط خدشنا السطح! من المهم أن تقرأ وتستكشف كل المواضيع المتعلقة بووردبرس وPHP لمزيد من العمق والممارسة. ترجمة -وبتصرّف- للمقال WordPress Development for Beginners: Learning PHP لصاحبه Daniel Pataki
  7. قد يبدو مفهوم البرمجة غرضية التوجه (object-oriented programming)، صعبًا ومعقدًا في البداية لكنه مفهوم مهم يجب تعلمه، إذا أردت زيادة مهاراتك وقدراتك في تطوير إضافات ووردبريس.سوف نشرح طريقتين مفيدتين تمكنك من جعل إضافاتك غرضية التوجه، الأمر الذي سيقلل من فرص تعقيد التعليمات البرمجية وتضاربها مع بعضها البعض، وتمكنك من كتابة تعليمات برمجية مرتبة وقوية وتشكل وحدات منفصلة. ملاحظة: هذه المقالة حول البرمجة غرضية التوجه، لم يتم التطرق فيها إلى طرق الكتابة المتقدمة للتعليمات البرمجية أو تم تبسيط بعضها لتصبح مفهومة بشكل أكبر للمبتدئين، الغرض من هذه المقالة تعليمك المبادئ الأساسية للبرمجة غرضية التوجه، وليس جعلك محترفًا في ليلة وضحاها، لكن إذا واصلت شغفك على التعلم والتجربة فستصبح محترفًا في وقت قصير. ما هي البرمجة غرضية التوجه؟ البرمجة غرضية التوجه بأبسط مفهوم هي طبقة أخرى من التجريد(abstraction)، إذا كتبت تعليمات php عند عملك في ووردبريس من قبل ففي الغالب أنك مررت بمرحلة التجريد، بدلًا من كتابة أسطر من التعليمات البرمجية سطرًا تلو سطر، البرمجة الغرضية تستخدم بواني (constructs) كالدوال (functions). الدوال يمكن ان تجعل التعليمات البرمجية أكثر مركزية ووحدة. لنأخذ هذا المثال: <div class='post' id='post-23'> <h2><?php the_title() ?></h2> <div class='excerpt'> <?php $content = get_the_content(); if( strlen($content) < 250 ) { echo $content; } else { $content = substr( $content, 0, 250 ); echo $content . '...'; } ?> </div> </div> قمت يدويًا بإنشاء مقتطف عند جلب البيانات مباشرة، لكن تخيل أن أفعل ذلك في كل مرة أعرض منشور ووردبريس، ليست هذه بداية المشكلة حتى، لكن ماذا لو أردت بعد فترة تغيير طول محارف المقتطف. الآن هي معدة لتقتطع 250 محرفًا و إذا أردت تغيير هذا العدد عليك أن تمر على جميع الأماكن التي كتبت هذه التعليمات فيها و تغييرها واحدًا تلو الآخر، لهذا استخدام الدوال مفيد جدًا، ووردبريس أوجدت حلًا لهذه المشكلة عن طريق استخدام دالة ()the_excerpt. <div class='post' id='post-23'> <h2><?php the_title() ?></h2> <div class='excerpt'> <?php the_excerpt() ?> </div> </div> البرمجة غرضية التوجه هي طبقة أخرى من التجريد(abstraction)، مشابهة كثيرًا للدوال. في هذه المقالة، سنقوم بإنشاء صنف(class)، الذي يمكن تشبيهه بوعاء حاوي للدوال. كتابة صنفك الأول لنقم بإنشاء صنف باسم “Post”. سيكون الهدف منه هو التلاعب بمنشورات ووردبريس: class Post { function get_excerpt( $content ) { if( strlen($content) < 250 ) { return $content; } else { $excerpt = substr( $content, 0, 250 ); return $excerpt . '...'; } } function the_excerpt( $content ) { echo get_excerpt( $content ); } function the_title( $title ) { echo $title; } } صنفنا الآن يحتوي على 3 دوال بسيطة، واحدة لجلب المقتطف والثانية من أجل عرض المقتطف، الثالثة لعرض عنوان المقال. في الوقت الحالي الصنف يحتوي الآن فقط دوال، لكن هذا ليس كل شيء يمكن للصنف أن يفعله. لنرى القوة الحقيقية للأصناف سنلقي نظرة على دالة خاصة في الأصناف تدعي الباني __construct() ، ومتغير خاص يدعى this$، والمتغيرات داخل الأصناف. عندما ننشأ صنف، يكون الهدف منه معظم الأحيان هو إنشاء كائنات متعددة، يمكنك إنشاء عدة منشورات، لنرى كيف سنقوم بها من خلال هذا المثال: class Post { var $title; var $content; function __construct( $data ) { $this->title = $data['title']; $this->title = $data['content']; } function get_excerpt() { if( strlen($this->content) < 250 ) { return $this->content; } else { $excerpt = substr( $content, 0, 250 ); return $excerpt . '...'; } } function the_excerpt() { echo $this->get_excerpt(); } function the_title() { echo $this->title; } } $postdata = array( 'title' => 'Post 1 Title', 'content' => 'Content of Post 1' ); $post = new Post( $postdata ); $post->the_excerpt(); هناك الكثير من الأشياء غير المفهومة بالنسبة لك في هذا المثال، لكن سيتم شرح كل شيء خلال لحظات، عندما تريد إنشاء كائن من صنف تستعمل هذه التعليمات كما في المثال $post = new Post ($postdata)، عندما تقوم بإنشاء الكائن ستنفذ الدالة البانية بشكل مباشر. الوسيط postdata$ الذي تم تمريره للكائن عند إنشائها سيصبح متاحًا لدالة الباني التي تم تعريفها في الصنف. لتعطي قيم للمتغيرين اللذين تم تعريفهما داخل الصنف. تم إسناد قيمة للمتغير title$ عن طريق this->$title وللمتغير content$ عن طريق this->$content . المتغير this$ هو متغير خاص يشير إلى الصنف نفسه، في أي وقت تريد الإشارة فيه إلى متغير تم تعريفه داخل الصنف بـ this->$property_name أو إلى دالة ()this->$function_name تم تعريفها داخل الصنف. ملاحظة: الدوال داخل الصنف تدعى طرائق(methods)، والمتغيرات تدعى خصائص(properties) وسيتم من الآن الإشارة إليهم بهذه الأسماء بهذا المقال. إن أفضل طريقة لفهم كيفية عمل الأصناف هو تتبع التعليمات البرمجية، بعد أن قمنا بإنشاء الكائن قمنا بتشغيل الدالة ()post->the_excerpt$ لكن الآن نستعمل متغير post$ للإشارة إلى الدالة بدلًا من this$ لأننا نستعمل هذه الدالة خارج الصنف. الآن لنعرف بماذا تقوم هذه الدالة يمكننا التوجه إلى تعريف الصنف لأننا نعلم أن التعريف الأصلي والتعليمات البرمجية لهذه الدالة هي في الصنف الذي تم تعريفها فيه. كما نرى أن دالة get_excerpt لا تحتاج لتمرير أي محتوى لأنها تأخذ المحتوى من المتغير content$ مراجعة ما تعلمناه لنراجع ما تعلمناه عن الأصناف قبل أن نرى كيف نستعملها في برمجة إضافات ووردبريس. الصنف هو وعاء حاوي للدوال يمكن تعريف الصنف بكتابة كلمة class وبعدها اسم الصنف، الدوال داخل الصنف تدعى طرائق، والمتغيرات تدعى خصائص. إذا كنا نكتب التعليمات البرمجية داخل الصنف فيمكن الإشارة إلى الخصائص والدوال بالمتغير this$. يمكن إنشاء كائن من صنف باستخدام الصيغة $object = new class( $params ) . الدالة البانية يتم تنفيذها مباشرة عند إنشاء الكائن. استخدام الكائنات في إضافات ووردبريس بما أن الأصناف تقوم بتغليف التعليمات البرمجية ضمن وعاء أو وحدة واحدة فهي تمنع التضارب مع التعليمات البرمجية المتواجدة خارج هذا الصنف يمكنني تعريف الدالة get_excerpt في إضافة ووردبريس التي أعمل عليها على الرغم أن هذه الدالة متواجدة بنفس الاسم في التعليمات البرمجية الأساسية لووردبريس. البرمجية غرضية التوجه تعتبر ميزة رائعة لأنها تجبرك استخدام معايير برمجية جيدة بالإضافة أنها تجعلك مبرمجًا أفضل. عندما تكون البرمجة غرضية التوجه مكتوبة بشكل جيد، فإنها دائمًا تكون الحل الأمثل لتتمكن من معرفة مزاياها بشكل جيد نحن نحتاج إلى أكثر من عدة أسطر برمجية، برمجة إضافة ووردبريس ستكون مثالًا رائعًا. هذا مثال هو عبارة عن إضافة تضيف نصًا إلى نهاية كل مقال يتم نشره بشكل تلقائي: <?php /* Plugin Name: Written By Awesome Description: Adds a nice author tagline Version: 1.0.0 Author: Daniel Pataki Author URI: http://danielpataki.com */ class Written_By_Awesome { function __construct() { add_filter( 'the_content', array( $this, 'add_author_line_to_content' ) ); } function add_author_line_to_content( $content ) { return $content . '<br> Written by someone awesome'; } } $written_by_awesome = new Written_By_Awesome(); عندما تفهم كيف تعمل الأصناف يمكنك تخطي الكثير من العوائق والتفكير بتعليماتك البرمجية كوحدات منفصلة، على سبيل المثال يمكنك إنشاء أصناف لتتولى التعليمات البرمجية الخاصة بالتفاعل مع المستخدم وإظهار النتائج، وأصناف آخرى للتعامل مع الخادم (server). لنلقي نظرة على هذه الإضافة، التي ترتب المنشورات بناء عدد التعليقات: <?php class PostsByCommentBackend { function construct() { add_action( 'pre_get_posts', array( $this, 'backend_query' ) ); } function backend_query( $query ) { if ( ! $query->is_admin() ) { $query->set( 'orderby', 'comment_count' ); $query->set( 'order', 'DESC' ); } } } class PostsByCommentFrontend { function construct() { add_action( 'pre_get_posts', array( $this, 'frontend_query' ) ); } function frontend_query( $query ) { if ( $query->is_admin() ) { $query->set( 'orderby', 'comment_count' ); $query->set( 'order', 'DESC' ); } } } $backend = new PostsByCommentBackend(); $frontend = new PostsByCommentFrontend(); ربما تجد هذه التعليمات بالغة الصعوبة، لكن النقطة التي أحاول الوصول إليها أن فصل تعليماتك البرمجية إلى وحدات منفصلة، يسهل عملية تطوير هذه الوحدات والبناء عليها. مثال كامل إذا كنت مهتمًا بمزيد من الأمثلة التوضيحية يمكنك الاطلاع على مشروع WordPress Plugin Boilerplate وهو عبارة عن قاعدة لبناء إضافات ووردبريس عن طريق البرمجة غرضية التوجه. لقد استخدمتها في العديد من مشاريعي وأنصح بها بشدة، خاصة للإضافات التي تتطلب درجة عالية من التعقيد. الخاتمة منصة ووردبريس تحتاج إلى إضافات مكتوبة بشكل أفضل، والحل الوحيد هو استخدام البرمجة غرضية التوجه، إنها تجعل التعليمات البرمجية أكثر قابلية للقراءة، مرتبة بشكل أفضل وذات جودة عالية وسهلة التطوير مستقبلًا. ترجمة -وبتصرّف- للمقال An Introduction to Object-Oriented Code for WordPress Plugins لصاحبه Daniel Pataki
  8. البيانات الوصفية ليست محدودةً لنوع اصطلاحاتٍ وحيد. صفحة «About» التي أنشأناها في الدرس السابق جيدة، لكن من المرجح أنَّ لديك صفحةً واحدةً اسمها «About»، ولكنك متعطشٌ للمزيد؟ لنتعلم كيف نوصِّف المنظمات والشركات. هذه نموذجٌ لصفحة شركةٍ ما، لنلقِ نظرةً على شيفرة HTML دون البيانات الوصفية. <article> <h1>Google, Inc.</h1> <p> 1600 Amphitheatre Parkway<br> Mountain View, CA 94043<br> USA </p> <p>650-253-0000</p> <p><a href="http://www.google.com/">Google.com</a></p> </article> وصفٌ قصيرٌ ومنظَّم، فجميع المعلومات حول هذه المنظمة موجودةٌ ضمن عنصر <article>، فلنبدأ هناك. <article itemscope itemtype="http://schema.org/Organization"> وكما فعلنا عند توصيف الأشخاص في الدرس السابق، سنحتاج إلى ضبط الخاصيتين itemscope و itemtype في العنصر الحاوي لبقية العناصر، وهو في حالتنا العنصر <article>. خاصية itemtype تُصرِّح ما هو نوع الاصطلاحات التي تستعملها (في هذه الحالة http://schema.org/Organization)، وخاصية itemscope تُصرِّح أنَّ كل الخاصيات التي تضبطها للعناصر الأبناء للعنصر الحالي ترتبط بنوع الاصطلاحات هذا. إذًا، ماذا يوجد في نوع الاصطلاحات Organization؟ بضع خاصياتٍ بسيطة، التي يكون بعضها مألوفًا لديك. الخاصية الشرح name اسم المنظمة (على سبيل المثال: «Initech») url رابط URL لصفحة ويب، مثل الصفحة الرئيسية للمنظمة address العنوان الفيزيائي للمنظمة، يمكن أن يحتوي على خاصيات أخرى مثل streetAddress وaddressLocality و addressRegion و postalCode و addressCountry telephone رقم هاتف المنظمة geo تحديد الإحداثيات الجغرافية لموقع المنظمة، ويملك خاصيتين دائمًا: latitude و longitude أول قطعة من الشيفرات في عنصر <article> هي <h1>. يحتوي عنصر <h1> على اسم الشركة، ولهذا سأضيف خاصية itemprop="name"‎ إليه مباشرةً. <h1 itemprop="name">Google, Inc.</h1> ووفقًا للنموذج الهيكلي للبيانات الوصفية في HTML5، عناصر <h1> ليس لها معالجة خاصة، وستكون قيمة خاصية البيانات الوصفية هي المحتوى النصي للعنصر. أي أننا قلنا بالعربية: «اسم المنظمة هو "Google, inc.‎"». المعلومة التالية التي نريد توصيفها هي العنوان. توصيف عنوان المنظمة مماثل تمامًا لتوصيف عنوان شخصٍ ما. أضف أولًا خاصية itemprop="address"‎ إلى العنصر الذي يحتوي على العنوان (العنصر <p> في هذه الحالة). وهذا يُصرِّح أنَّ هذه هي خاصية address للمنظمة، لكن ماذا عن خاصيات العنوان نفسه؟ سنحتاج إلى الخاصيتين itemtype و itemscope لكي نقول أنَّ عنصر العنوان هذا له خاصياتٌ تابعة له. <p itemprop="address" itemscope itemtype="http://schema.org/PostalAddress"> في النهاية، علينا وضع كل قطعة من المعلومات في عنصر <span> لكي نتمكن من وضع الخاصية الملائمة (streetAddress وaddressLocality و addressRegion و postalCode و addressCountry) في كل عنصر من تلك العناصر. <p itemprop="address" itemscope itemtype="http://schema.org/PostalAddress"> <span itemprop="streetAddress">1600 Amphitheatre Parkway</span><br> <span itemprop="addressLocality">Mountain View</span>, <span itemprop="addressRegion">CA</span> <span itemprop="postalCode">94043</span><br> <span itemprop="addressCountry">USA</span> </p> بالعربية: «هذه المنظمة تملك عنوانًا بريديًا. اسم الشارع لذاك العنوان البريدي هو "1600 Amphitheatre Parkway"، أما البلدة (locality) فهي "Mountain View"، والإقليم (region) هو "CA"، والرمز البريدي (postal code) هو "94043"، واسم الدولة هو "USA"». الخطوة التالية هي توصيف رقم الهاتف للمنظمة، بُنيةُ أرقامِ الهواتفِ معقدةٌ بعض الشيء، والصيغة المُحدَّدة لها خاصةٌ بكل دولة (والأمر أسوأ إذا أردت الاتصال بدولةٍ أخرى). لدينا في مثالنا رقمٌ هاتفيٌ من الولايات المتحدة، وهو مكتوبٌ بصيغةٍ ملائمةٍ للاتصال من أي مكان داخل الولايات المتحدة. <p itemprop="telephone">650-253-0000</p> (في حال لم تنتبه، لقد خرج نوع الاصطلاحات Address من المجال [scope] عندما أُغلِق العنصر <p>. لقد عدنا الآن إلى تعريف الخاصيات لنوع الاصطلاحات Organization.) إذا كنت تريد وضع أكثر من رقم هاتف –ربما واحد للزبائن في الولايات المتحدة وآخر للزبائن من بقية دول العالم– فيمكنك فعل ذلك. إذ يمكن تكرار أي خاصية من خاصيات البيانات الوصفية أكثر من مرة. كل ما عليك فعله هو التأكد أنَّ لكل رقمٍ عنصرُ HTML خاصٌ به مفصولٌ عن أيّة لافتة وضعتَها له. <p> US customers: <span itemprop="telephone">650-253-0000</span><br> UK customers: <span itemprop="telephone">00 + 1* + 6502530000</span> </p> ووفقًا للنموذج الهيكلي للبيانات الوصفية في HTML5، عناصر <p> أو عناصر <span> ليس لها معالجةٌ خاصةٌ، وستكون قيمة خاصية telephone هي المحتوى النصي للعنصر. لا يحاول نوع الاصطلاحات Organization أن يُقسِّم ويُصنِّف مختلف أجزاء رقم الهاتف، فخاصية telephone هي نصٌ عاديٌ. حيث تستطيع وضع رمز المنطقة بين قوسين، أو أن تستعمل الفراغات بدلًا من الشرطات للفصل بين الأرقام. وإذا حاول العميل الذي يُفسِّر البيانات الوصفية أن يُفسِّر رقم الهاتف، فآلية ذلك عائدةٌ تمامًا إليه. لدينا بعد ذلك خاصيةٌ نألفها: url. ومثل روابط URL المتعلقة بشخصٍ ما، يمكن لروابط URL أن تتعلق بمنظمة. وقد تكون هذه الروابط هي الصفحة الرئيسية للشركة، أو صفحة التواصل، أو صفحة المنتجات، أو أيّة صفحة أخرى. بعبارةٍ أخرى، إذا كان لديك رابط URL عن أو من أو يتعلق بالمنظمة، فوصِّفه بخاصية itemprop="url"‎. <p><a itemprop="url" href="http://www.google.com/">Google.com</a></p> ووفقًا للنموذج الهيكلي للبيانات الوصفية في HTML5، عنصر <a> له معالجةٌ خاصةٌ، فقيمة خاصية البيانات الوصفية هي قيمة الخاصية href، وليس المحتوى النصي للرابط. بالعربية: «لدى هذه المنظمة رابط URL الآتي http://www.google.com/‎». لكن الخاصية لا تُحدِّد مزيدًا من المعلومات عن هذا الارتباط، ولا تُضمِّن السلسلة النصية للرابط Google.com». في النهاية، أريد الحديث عن الموقع الجغرافي. لا أقصد هنا الواجهة البرمجية لتحديد الموقع الجغرافي؛ وإنما أقصد كيفية توصيف الموقع الجغرافي للمنظمات باستخدام البيانات الوصفية. لحد هذه اللحظة، كل الأمثلة التي رأيناها ركَّزَت على توصيف البيانات المرئية. بعبارةٍ أخرى، لديك عنصر <h1> فيه اسم الشركة، لذا ستُضيف خاصية itemprop إلى عنصر <h1> لتُصرِّح أنَّ النص (المرئي) الموجود في تلك الترويسة هو اسم المنظمة. أو ربما لديك عنصر <img> الذي يُشير إلى صورةٍ ما، وتستطيع إضافة الخاصية itemprop لعنصر <img> لكي تُصرِّح أنَّ تلك الصورة (المرئية) هي صورةٌ لشخصٍ ما. أما في هذا المثال، لن تكون معلومات الموقع الجغرافي على هذا النحو؛ فلا يوجد نصٌ مرئيٌ يُعطي إحداثيات الطول والعرض (بدقة أربعة أرقام عشرية!) لموقع المنظمة. وفي الواقع، الصفحة التي نعمل عليها لا تحتوي على معلومات عن الموقع الجغرافي إطلاقًا. وحتى لو وُجِدَ رابطٌ إلى خرائط Google، لكنه لا يحتوي على إحداثيات الطول والعرض (يحتوي على معلومات مشابهة في صيغةٍ خاصةٍ بخرائط Google). لكن حتى لو افترضنا وجود رابط URL لإحدى خدمات الخرائط التي تضع إحداثيات خطوط الطول والعرض كوسائط في رابط URL، فلا توجد طريقة في البيانات الوصفية لفصل أجزاء URL عن بعضها؛ فلا تستطيع أن تقول أنَّ «أول وسيطٍ في طلبية URL هو إحداثيات الطول والوسيط الثاني هو إحداثيات العرض وبقية وسائط الطلبية غير مهمة بالنسبة إلينا». توفِّر HTML5 طريقةً لتوصيف البيانات غير المرئية للتعامل مع الحالات الخاصة مثل هذه الحالة. لا تَستعمل هذه التقنية إلا كملاذٍ أخيرٍ لك. فلو كانت هنالك طريقةٌ لعرض البيانات التي تريد توصيفها، فافعل ذلك. أرى أنَّ البيانات المخفية التي تستطيع «الآلات» قراءتها فقط «ستتعفن» بسرعة. وهذا يعني أنَّ أحدهم سيأتي عاجلًا أم آجلًا وسيُحدِّث النص المرئي الموجود في الصفحة لكنه سينسى تحديث البيانات غير المرئية. هذا يحدث بوتيرة كبيرة أكثر مما تتوقع، وأنا واثقٌ أنَّه سيحدث لك أيضًا. لكن مع هذا، هنالك وضع معلومات للموقع الجغرافي لكنه لا يريد أن تعم الفوضى في الواجهة بوجود زوجين من الأرقام غير المفهومة ذات ست خانات. الخيار الوحيد الذي أمامك هنا هو البيانات المخفية. كل ما تستطيع فعله هنا هو وضع البيانات المخفية بعد النص المرئي الذي يصفها مباشرةً، لعل ذلك يُذكِّر الشخص الذي أتى ليحدث النص المرئي لكي يُحدِّث البيانات المخفية بعده مباشرةً. في هذا المثال، أنشأنا عنصر <span> ضمن عنصر <article> الذي يحوي بقية خاصيات المنظمة، ثم وضعنا بيانات الموقع الجغرافي المخفية داخل عنصر <span> (هذه هي الطريقة التنظيمية لنوع الاصطلاحات Organization، راجع صفحة http://schema.org/Organization لمزيدٍ من المعلومات). <span itemprop="areaServed" itemscope itemtype="http://schema.org/Place"> <span itemprop="geo" itemscope itemtype="http://schema.org/GeoCoordinates"> <meta itemprop="latitude" content="37.4149" /> <meta itemprop="longitude" content="-122.078" /> </span> </span> </article> معلومات الموقع الجغرافي مُعرَّفةٌ في نوع الاصطلاحات الخاص بها، مثل «عنوان» (Address) الشخص أو المنظمة، وبالتالي، سيحتاج عنصر <span> إلى ثلاث خاصيات: itemprop="geo"‎ يقول أنَّ هذا العنصر يُمثِّل خاصية geo للمنظمة التي يتبع لها itemtype="http://schema.org/GeoCoordinates"‎ يُحدِّد أيُّ نوعِ اصطلاحاتٍ ستخضع له خاصيات هذا العنصر itemscope يقول أنَّ هذا العنصر هو عنصرٌ حاوٍ يملك نوع اصطلاحات خاص به (مُحدَّدٌ في خاصية itemtype). جميع الخاصيات الموجودة ضمن هذا العنصر هي خاصياتٌ لنوع الاصطلاحات http://schema.org/GeoCoordinates، وليس لنوع الاصطلاحات للعنصر الرئيسي http://schema.org/Organization السؤال المهم في هذا المثال هو: «كيف تستطيع توصيف البيانات غير المرئية؟» يمكنك استخدام العنصر <meta>. لم تكن في السابق تستطيع استخدام العنصر <meta> إلا داخل ترويسة صفحتك؛ أما في HTML5، فتستطيع استخدام العنصر <meta> في أيِّ مكانٍ، وهذا ما سنفعله تمامًا. <meta itemprop="latitude" content="37.4149" /> ووفقًا للنموذج الهيكلي للبيانات الوصفية في HTML5، عنصر <meta> له معالجةٌ خاصة، فقيمة خاصية البيانات الوصفية هي قيمة الخاصية content؛ ولأن هذه الخاصية لا تُعرَض أبدًا، فهي مثاليةٌ لضبط عددٍ غيرِ محدودٍ من البيانات المخفية. لكن ستزداد المسؤولية الملقاة على عاتقك هنا، حيث عليك التأكد أنَّ المعلومات المخفية ستبقى متوافقةً مع ما حولها من النص المرئي. لا يوجد دعمٌ مباشرٌ لنوع الاصطلاحات Organization في المُقتطفات المنسقة في Google، لذا لا أملك نتيجةَ بحثٍ جميلة لعرضها لك. لكن لها تأثيرٌ على الأحداث (events) والمراجعات (reviews)، اللتان تدعمهما المقتطفات المنسقة في Google (تقبل بعض خاصياتهما أن يكون نوع الخاصية Organization). الشكل 1: معلومات البيانات الوصفية كما تُظهِرها أداة اختبار البيانات المنظّمة ترجمة -وبتصرّف- لجزء من فصل «Microdata» من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim
  9. سنتعلّم مبادئ HTML و CSS لإنشاء مشاريع ويب خاصة بنا. سيتميّز هذا الدرس بتركيزه على الجانب العملي بشكل كبير، حيث سنبدأ فورًا بإنشاء مشروع خاص بنا. أمّا بالنسبة للنواحي النظرية فسنشرحها عند الحاجة أثناء تقدّمنا ببناء المشروع. بهذه الطريقة سنكتسب أساسًا متينًا وسريعًا في مجال تطوير الويب. سأوجّهك خلال هذا المشروع إلى روابط خارجية تُشير إلى مصادر أخرى للتوسُّع في نقاط معيّنة إن أحببت ذلك. المشروع سيكون المشروع عبارة عن بناء ملف شخصي portfolio على الويب. سيضم صفحة بداية Home ومدوّنة Blog وصفحة تُظهر مشاريع ويب المستقبلية التي تنوي إنشائها Projects، بالإضافة إلى صفحة للتواصل Contact. الهدف من المشروع الهدف منه هو جعلك تدخل عالم برمجة الويب ومساعدتك كي تتعلّم كيفية إيجاد المزيد من المعلومات حول أي موضوع لوحدك. ستصبح قادرًا بعد ذلك على معالجة مشاريع ويب أكثر تعقيدًا. ما هو HTML و CSS؟ لغة الرُماز المعلَّم Hypertext Markup Language والتي ندعوها اختصاراً HTML هي لغة مسؤولة عن بُنية structure صفحة الويب. فمثلًا يمكنك تعريف عناوين headings وفقرات paragraphs ونصوص texts وصور images في HTML. أمّا بالنسبة لأوراق الأنماط المتتالية Cascading Style Sheet وندعوها اختصاراً CSS فهي مسؤولة عن تنسيق (تنميط) style وتخطيط layout صفحة الويب. يمكنك تعريف تنسيقات جديدة خاصة بالألوان والخطوط وطرق المحاذاة وحتى يمكنك إنشاء بعض التحريكات animations البسيطة في CSS. تذكّر: تزوّدنا HTML بالمحتوى في حين تُنسّق CSS هذا المحتوى. موقع ويب أم تطبيق ويب يمكننا بناء مواقع ويب websites معقّدة جدًّا باستخدام HTML و CSS فحسب. لكن ستكون مواقع الويب هذه ساكنة static، ويعني ذلك أنّ زوّار الموقع يمكنهم استعراض الصفحات لكنّهم لا يمكنهم التفاعل مع هذه الصفحات (باستثناء النقر على الروابط الموجودة في الصفحات). لبرمجة مواقع ويب ديناميكية dynamic تكون تفاعلية مع المستخدم، سنحتاج إلى لغة برمجة إضافية مثل JavaScript أو Dart. ويمكننا باستخدامهما تطوير (هو مفهوم أوسع من البرمجة) تطبيقات ويب Web Applications كاملة مثل التطبيقات التي تهتم بإجراء الحسابات المختلفة أو تطبيقات الألعاب أو تطبيقات المحادثة وغيرها الكثير. تعمل هذه التطبيقات في الواقع ضمن متصفّح الويب. كما توجد لغات برمجة وتقنيات أخرى مثل PHP وRuby وASP.NET تسمح ببناء تطبيقات ويب ديناميكية أيضًا ولكن تعمل هذه التطبيقات ضمن مزوّد خدمة الاستضافة (على المخدّم). كما يمكن المزج بين هذين الأسلوبين لإنشاء مواقع ويب فعّالة للغاية. يمكنك بعد الانتهاء من هذه الدروس، البدء بتعلّم مثل هذه اللغات والتقنيات وإنشاء تطبيقات ويب ديناميكية. الأجهزة المحمولة يُشكّل الوصول إلى مواقع الويب عن طريق الأجهزة المحمولة كالهواتف الذكيّة أو الأجهزة اللوحية نسبة كبيرة من عمليات الوصول العامة. وهكذا فإنّه من الضروري أن يظهر موقعنا بشكل جيّد على الشاشات الصغيرة. على العموم سنولي هذا الأمر اهتمامًا خلال هذه السلسلة التعليمية. الأدوات المستخدمة نحتاج إلى تطبيقين للدخول في عالم تطوير وبرمجة الويب، محرّر نصوص لإنشاء الملفات الخاصة بموقع الويب، ومتصفّح ويب لعرض وتجربة الموقع. 1- المحرر Editor سيكون كافيًا استخدام محرّر نصوص عادي (كبرنامج المفكرة Notepad في ويندوز مثلًا). ولكن من الأفضل استخدام محرّر نصوص يسهّل عملنا إلى حدٍّ كبير. لهذا الغرض أنصح باستخدام محرّر نصوص عصري ومخصّص لتحرير HTML. محرّر النصوص الذي أنصح به حاليًا هو Brackets (مجّاني من شركة أدوبي Adobe). يمكنك أن تستخدم أي محرّر تريده، ولكنّني في هذه السلسلة سأُشير أحيانًا إلى بعض وظائف تطبيق Brackets. يوجد محرّر نصوص جيّد أيضاً ويمكنك استخدامه وهو Atom، ويوجد محرّر نصوص آخر أيضًا، قديم قليلًا لكنّه جيّد وهو ++Notepad. حمّل ونصّب المحرّر Brackets إذا أردت ذلك. 2- المتصفح Browser يتوجّب على موقعنا أن يعمل على جميع المتصفحات الرئيسية بالطبع (Internet Explorer و Firefox و Chrome و Safari). على أية حال، أنصح باستخدام المتصفح Chrome لأغراض التطوير والبرمجة. يضم Chrome أدوات مفيدة للغاية لمطوّر الويب والتي سيستخدمها على نحو متكرّر. بالإضافة إلى ذلك، يدعم المحرّر Brackets المتصفّح Chrome بشكل جيّد، بحيث أنّ التغييرات التي تحدث في النص ستظهر مباشرةً ضمن Chrome (عن طريق ميزة اسمها Live Reload). حمّل ونصّب المتصفح Chrome الآن من هنا. إنشاء مستند HTML لنبدأ الآن بإنشاء مستند HTML الأوّل لنا من أجل موقع الويب: أنشئ مجلّدًا من أجل المشروع. سمّ المجلّد بالاسم Portfolio (أو أي اسم آخر ترغبه). افتح برنامج Brackets. من القائمة File اختر الأمر …Open Folder لتحديد وفتح المجلّد الذي أنشأناه قبل قليل. انقر بزر الفأرة الأيمن أسفل اسم المجلّد واختر New File لإنشاء ملف جديد، سمّه index.html. لديك الآن ملف نصّي فارغ اسمه index.html لماذا الملف index.html؟ في الواقع للملف index.html معنى خاص، فعندما نطلب من المتصفّح عنوان موقع ويب ما، وليكن مثلًا http://code.makery.ch، فعند ذلك سيُعرَض الملف index.html أولاً بشكل تلقائي، أي كأنّنا طلبنا العنوان http://code.makery.ch/index.html. بالنسبة إلينا، سيمثّل الملف index.html الصفحة الرئيسية. عمليتا العرض والتحديث يمكننا الآن تعبئة مستند HTML (الملف index.html) بالمحتوى. اكتب الأسطر التالية ضمن مستند HTML. لعرض الصفحة ضمن المتصفّح، انقر الأيقونة التي تُشبه البرق في الطرف الأيمن من البرنامج (المعاينة الحية Live Preview). سيُفتَح المتصفّح Chrome وسيُعرض المستند ضمنه، وفي حال أجريت أي تغيير جديد على مستند HTML فسترى نتيجة ذلك على المتصفّح مباشرةً، وهي في الحقيقة ميزة هامة وفعّالة. إذا لم تظهر الصفحة، اذهب إلى المكان الذي يوجد فيه الملف index.html وافتحه مباشرةً باستخدام Chrome أو أي متصفّح آخر. أمّا لم تُحدّث الصفحة تلقائياً، فاحفظ جميع الملفات المفتوحة (سننشئ ملفات أخرى تباعاً) ثم من نافذة المتصفح اضغط F5 على لوحة المفاتيح. تهانينا، لقد أنشأت موقع ويب الأوّل الخاص بك. التحييد Indentation لكي نُبقي الشيفرة نظيفة وواضحة، من المهم أن تعمل على تحييد النص بشكل صحيح باستخدام مفتاح الجدولة Tab من لوحة المفاتيح. يأخذ مفتاح Tab في برنامج Brackets أربعة فراغات بشكل افتراضي. بالنسبة لي أجد ذلك كثيراً بعض الشيء، لذلك أنصح أن تغيّر هذا لتصبح الفراغات spaces التي يأخذها المفتاح Tab تساوي 2، وذلك من أسفل نافذة Brackets. من المهم جدًّا أن ننتبه إلى تحييد الشيفرة من البداية، لكي نكتب شيفرة نظيفة وواضحة من بداية تعلّمنا للبرمجة عموماً. تلميح 1: استخدام Shift+Tab لكي تنقل المحاذاة إلى اليسار بدلاً من اليمين. تلميح 2: يمكنك محاذاة عدة أسطر بنفس الوقت إذا اخترتهم وضغطت Tab أو حتى Shift+Tab. عناصر HTML 1- الوسوم Tags في المثال السابق رأيت رموزًا مكتوبة ضمن رمزي <> تشكل هذه الرموز كلمات نُسميها الوسوم Tags. تتألف عناصر HTML عادةً (ولكن ليس دائماً) من وسمين، وسم للفتح opening tag ووسم للإغلاق closing tag. في مثالنا السابق كان <html> هو وسم للفتح، أمّا <html/> هو وسم للإغلاق حيث نلاحظ وجود محرف slash قبل اسم الوسم. أي نص موجود بين وسمي الفتح والإغلاق يُعتبر محتوى لعنصر HTML هذا. فمثلاً بالنسبة للوسمين <html> و <html/> نُخبر متصفّح الويب عن بداية ونهاية مستند HTML لصفحة الويب الخاصة بنا. أمّا الوسم الثاني الذي صادفناه هو الوسم <body>. يُحدّد هذا الوسم أنّ جميع المحتوى الواقع بين وسم الفتح <body> والإغلاق <body/> سيظهر بشكل مرئي للمستخدم في المنطقة الرئيسية من نافذة المتصفّح. 2- السمات Attributes تُعرّف السمات معلومات إضافية لعنصر HTML. وتقع هذه السمات ضمن وسم الفتح لعنصر HTML، ويكون لها دائماً اسم name وقيمة value. كمثال على السمات، دعنا ننظر إلى عنصر HTML الخاص بالروابط. وهو ربما من أهم العناصر على الإطلاق. يضم عنصر الروابط <a> السمة href (وهي اختصار للكلمتين hypertext reference) التي تحمل القيمة في هذا المثال http://code.makery.ch.ولكن سيعرض المتصفّح هذا الرابط على شكل النص التالي: My Website. البنية الأساسية لصفحة HTML لقد رأينا قبل قليل عنصري HTML وهما <html> و <body>. ولكن بنية صفحة (مستند) HTML تتكوّن عادةً من المزيد من العناصر. استبدل محتويات الملف index.html بالشيفرة التالية، بعد ذلك سنناقش كل عنصر HTML موجود فيها. بنية ملف (مستند) HTML <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Web Portfolio of Marco</title> </head> <body> <h1>Web Portfolio of Marco</h1> <p>Write anything you want to tell the world.</p> </body> </html> الشرح نضع في السطر الأوّل <DOCTYPE html!> دوماً. فهي تخبر المتصفّح عن نوع المستند. يشير الوسم <html> إلى بداية المستند والوسم <html/> إلى نهايته. يحتوي العنصر <head> (بين وسمي الفتح والإغلاق له) على معلومات إضافية حول الصفحة. وبشكل مختلف عن العنصر <body>، لا تظهر هذه المعلومات في نافذة المتصفح الرئيسية. يجب أن تكون هناك إشارة ضمن العنصر <head> حول الترميز character set المستَخدَم في هذا المستند: <"meta charset="utf-8>. إذا لم تحدّد الترميز المستَخدَم فإنّ بعض الرموز قد لا تظهر بصورة صحيحة. ربما قد لاحظت أنّ العنصر <meta> لا يمتلك وسم إغلاق. توجد بعض عناصر HTML التي لا تمتلك أيضًا وسوم إغلاق، ولكنها تعتبر استثناءً. نرى بعد ذلك الوسم <title> الذي يضم عنوان المستند والذي سيظهر على شريط العنوان لنافذة المتصفّح. كل شيء ضمن الوسم <body> سيظهر ضمن نافذة المتصفّح الرئيسية. يُعرّف العنصر <h1> العنوان الرئيسي الذي سيظهر للمستخدم ضمن صفحة الويب. ويمكن إنشاء عناوين فرعية أيضاً باستخدام العناصر <h2> <h3> <h4> <h5> <h6>. النص الموجود بين الوسمين <p> و <p/> يُعتبر فقرة مستقلة، وهذا ما يُعبّر عنه العنصر <p>. بعد كل وسم فتح لعنصر ما، يجب أن نحاذي العنصر التالي (بمفتاح الجدولة Tab) لتحسين عرض الشيفرة. ورغم أنّ ذلك ليس ضرورياً ولا يؤثّر أصلاً في عرض المستند، ولكن تأكّد من امتلاكك لهذه العادة الجيّدة. تلميح 1: يمكنك استخدام بنية HTML السابقة لأي صفحة HTML جديدة. تلميح 2: استخدم الاختصار Ctrl+S من لوحة المفاتيح لحفظ الملف الحالي. تلميح 3: استخدم الاختصار Ctrl+Z من لوحة المفاتيح للتراجع عن العمليات التي أجريتها. نحن مستعدّون الآن وبعناصر HTML البسيطة هذه، أن نرتقي بموقعنا إلى مستوى أعلى. في البداية لنُضِف صورة بحيث تبدو الصفحة الرئيسية لمشروعنا أكثر جمالًا. إدراج صورة لإدراج صورة نستخدم العنصر <img>. المثال التالي سيُدرج صورة موجودة ضمن ملف اسمه marco.jpg: <img src="marco.jpg" alt="Picture of me"> للعنصر <img> وسم فتح فقط ولا يوجد له وسم إغلاق. وهو يحتوي على سمتين src و alt. السمة src تُحدّد عنوان URL الذي يُعبّر عن اسم ملف الصورة ومساره. أمّا السمة alt فتمثّل النص البديل، وهو النص الذي يصف محتويات الصورة. يُستخدم هذا النص من قِبل محرّكات البحث، وفي حال تعذّر عرض الصورة ضمن الصفحة سيُعرض هذا النص بدلاً عنها. 1- عناوين URL النسبية والمطلقة تُستخدم عناوين URL من أجل السمة src الخاصة بعنصر الصورة، وأيضاً من أجل السمة href الخاصة بعنصر الارتباط. يُحدّد عنوان URL عنوان (مسار) ملف، وبصورة عامة العنوان هو مصدر resource قد يكون صورة أو صفحة ويب من موقع آخر. بالاعتماد على موقع الملف نستخدم إمّا العنوان النسبي relative أو العنوان المطلق absolute. فإذا كان الملف موجودًا ضمن نفس موقع الويب، عندها يمكن استخدام عنوان نسبي. فكما رأينا في المثال السابق اسم الملف وحده موجود دون عنوانه الفعلي (المطلق). يكون عنوان URL من النوع النسبي، نسبيًا دومًا إلى صفحة HTML الحالية، فإذا كان الملف المستهدف وصفحة HTML التي ستستخدمه موجودان ضمن نفس المجلّد فعندها يكفي الإشارة إلى اسم الملف فقط، أمّا إذا كانا ضمن مجلّدين مختلفين فعندها يحب أن يؤخذ ذلك بعين الاعتبار. فإذا فرضنا مثلًا أنّ ملف الصورة من المثال السابق موجودة ضمن مجلّد فرعي اسمه images سيكون عنوان URL النسبي لملف الصورة images/marco.jpg. أمّا إذا كان ملف الصورة موجود ضمن المجلّد الأب، فيمكنك عندئذ الوصول إليه باستخدام البادئة ( /.. ) أي سيصبح عنوان الملف في هذه الحالة marco.jpg/.. أمّا إذا كان الملف موجوداً ضمن موقع ويب آخر، فعندها يجب استخدام عنوان URL مطلق. تحتوي عناوين URL المطلقة على الاسم والمسار الكاملين للملف. حقائق عن عناوين URL: عنوان URL الذي يبدأ بـ //:http هو عنوان URL مطلق. عنوان URL بدون //:http هو عنوان URL نسبي بالنسبة إلى صفحة ويب الحالية. تُشير النقطة ( . ) إلى المجلّد الحالي. تُشير النقطتان ( .. ) إلى المجلّد الأب. أمثلة عن عناوين ويب النسبية والمطلقة: <!-- عناوين نسبية --> <a href="image-gallery.html">Image Gallery</a> <a href="blog/first-blog-entry.html">My First Blog Entry</a> <a href="../image-gallery.html">Back to Image Gallery</a> <!-- عناوين مُطلقة --> <a href="http://www.my-colleague.com/blog.html">Blog of a Colleague</a> 2- إضافة صورة إلى موقعنا لندرج صورة ضمن الصفحة، ينبغي نسخ ملف الصورة إلى المجلّد Portfolio. من الممكن أن تستخدم نفس الصورة الموجودة مع هذا الدرس، أو أن تستخدم صورة من عندك، ولكن احرص في جميع الأحوال على أنّك ستُحدّد اسم الملف مع الامتداد بدقّة. يجب أن تحصل على شيفرة شبيهة بما يلي (لاحظ أنّني أضفت عناوين فرعية بالإضافة إلى مزيد النصوص): الملف index.html مع الشيفرة اللازمة: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Web Portfolio of Marco</title> </head> <body> <h1>Web Portfolio of Marco</h1> <h2>Welcome!</h2> <p>Thanks for stopping by.</p> <p>Please have a look around. In the blog section I document my experiences in programming. You may also look at my web projects. Have Fun.</p> <img src="marco.jpg" alt="Picture of me"> <p>Marco :-)</p> </body> </html> والصورة التالية لما ستبدو عليه الصفحة في متصفّح الويب: سنتعلّم في الدرس الثاني كيف ننشر موقعنا على الانترنت. ترجمة -وبتصرّف- للدّرس HTML & CSS Tutorial - Part 1: Your First Website لصاحبه Marco Jakob.
  10. إن كنت قد قرأت المقال السّابق الذي يشرح ماهية metadata وكيفية استخدامها فأود أن أنوّه إلى أنني لم أخترع الأمثلة في الدرس السابق من عندي تمامًا، فهنالك اصطلاحات للبيانات الوصفية لتوصيف المعلومات الخاصة بالأشخاص، ومن السهل فعل ذلك. لنلقي نظرةً أقرب. أسهل طريقة لدمج البيانات الوصفية في موقعك الشخصي تكون في صفحة About، من المرجح وجود صفحة About لديك، أليس كذلك؟ إن لم تكن لديك صفحة، فيمكنك المتابعة معي في توسعة صفحة About الآتية ببعض البنى الهيكلية. لننظر أولًا إلى الشيفرة المصدرية، قبل إضافة أيّة خاصيات لها علاقة بالبيانات الوصفية: <section> <img width="204" height="250" src="http://diveintohtml5.org/examples/2000_05_mark.jpg" alt="[Mark Pilgrim, circa 2000]"> <h1>Contact Information</h1> <dl> <dt>Name</dt> <dd>Mark Pilgrim</dd> <dt>Position</dt> <dd>Developer advocate for Google, Inc.</dd> <dt>Mailing address</dt> <dd> 100 Main Street<br> Anytown, PA 19999<br> USA </dd> </dl> <h1>My Digital Footprints</h1> <ul> <li><a href="http://diveintomark.org/">weblog</a></li> <li><a href="http://www.google.com/profiles/pilgrim">Google profile</a></li> <li><a href="http://www.reddit.com/user/MarkPilgrim">Reddit.com profile</a></li> <li><a href="http://www.twitter.com/diveintomark">Twitter</a></li> </ul> </section> أول شيء عليك فعله دائمًا هو التصريح عن نوع الاصطلاحات الذي ستستعمله، ومجال (scope) الخاصيات التي تريد إضافتها. يمكنك القيام بذلك عبر إضافة خاصيتَي itemtype و itemscope إلى العنصر الأب الذي يحتوي على بقية العناصر التي تريد توصيف البيانات فيها، وهو في حالتنا العنصر <section>. <section itemscope itemtype="http://schema.org/Person"> يمكنك الآن البدء بتعريف خاصيات البيانات الوصفية من نوع الاصطلاحات http://schema.org/Person، لكن ما هي هذه الخاصيات؟ كما هو واضح، تستطيع رؤية كامل قائمة الخاصيات بزيارة الصفحة http://schema.org/Person في متصفحك. لا تتطلب مواصفة البيانات الوصفية أن توضع قائمة الخاصيات هناك، لكنني أرى أنَّ ذلك مستحسنٌ. فلو أردت مثلًا أن تجعل المطورين يستعملون نوع اصطلاحات البيانات الوصفية الذي أنشأته، فستحتاج إلى توثيقه. ولا يوجد مكانٌ أفضل لوضع التوثيق فيه من رابط نوع الاصطلاحات نفسه، أليس كذلك؟ الخاصية الشرح name الاسم additionalName الاسم الإضافي، قد يكون الاسم الأوسط أو اللقب image رابطٌ لصورةٍ له jobTitle المُسمَّى الوظيفي (مثلًا، مدير مالي [Financial Manager]) url رابط URL لصفحة ويب، مثل الصفحة الرئيسية لمدونة ذاك الشخص affiliation المنظمة التي يرتبط بها هذا الشخص (أن يكون -على سبيل المثال- موظفًا أو طالبًا فيها) address العنوان الفيزيائي للشخص. يمكن أن يحتوي على خاصيات أخرى مثل streetAddress وaddressLocality و addressRegion و postalCode و addressCountry knows علاقة اجتماعية بين الشخص الموصوف وشخصٍ آخر أول شيء نصادفه في صفحة About السابقة هي صورةٌ موضوعةٌ ضمن عنصر <img>، ولكي نُصرِّح أنَّ الصورة الموجودة هي صورة الشخص الموصوف، فكل ما نحتاج له هو إضافة itemprop="image"‎ إلى عنصر <img>. <img itemprop="image" width="204" height="250" src="http://diveinto.html5doctor.com/examples/2000_05_mark.jpg" alt="[Mark Pilgrim, circa 2000]"> أين هي قيمة خاصية البيانات الوصفية؟ إنها موجودةٌ في خاصية src، وإذا كنتَ تتذكر من النموذج الهيكلي للبيانات الوصفية في HTML5، "قيمة" خاصية البيانات الوصفية في عنصر <img> هي محتوى الخاصية src. ولكل عنصر <img> خاصية src -وإلا فلن تُعرَض الصورة- وخاصية src تحتوي على رابط URL دائمًا، أترى؟ إذا كنت تكتب HTML بشكلٍ صحيح، فاستعمال البيانات الوصفية سهلٌ جدًا. علاوةً على ذلك، العنصر <img> ليس موجودًا لوحده في الصفحة، فهو عنصر ابن للعنصر <section>، الذي عرَّفناه مع الخاصية itemscope. تُعيد البيانات الوصفية استعمال علاقة "الأب-الابن" بين العناصر في الصفحة لتعريف مجال (scope) خاصيات البيانات الوصفية. أي أننا نقول بالعربية: "العنصر <section> يُمثِّل شخصًا، وأيّة خاصيات للبيانات الوصفية التي تجدها في العناصر التي تكون ابنًا للعنصر <section> هي خاصياتٌ تابعةٌ لذاك الشخص". يمكنك تخيل الأمر على أنَّ العنصر <section> هو الفاعل في الجملة، والخاصية itemprop تمثِّل الفعل (وهو يُشبِه: "مُصوَّرٌ في")، وقيمة خاصية البيانات الوصفية هي المفعول به في الجملة. يجب تعريف "الفاعل" مرةً واحدةً فقط، وذلك بوضع الخاصيتين itemscope و itemtype في عنصر <section> الأب. أما "الفعل" فيُعرَّف بوضع الخاصية itemprop="image"‎ في عنصر <img>. أما "المفعول به" فلا يحتاج إلى أيّة شيفرات خاصة، لأنَّ النموذج الهيكلي للبيانات الوصفية في HTML5 يقول أنَّ قيمة خاصية البيانات الوصفية في عنصر <img> هي في خاصية src. سننتقل الآن إلى القسم التالي من الشيفرة، سنشاهد ترويسة <h1> وبداية قائمة <dl>. ليس من الضروري إضافة خاصيات البيانات الوصفية إلى عنصرَي <h1> و <dl>، فلا حاجة إلى وضع خاصية من خاصيات البيانات الوصفية في كل عنصر من عناصر HTML. الغرض من البيانات الوصفية هو "توصيف" البيانات وليس الشيفرات أو الترويسات التي تحيط بها. ترويسة <h1> لا تحتوي على قيمة، فهي مجرد ترويسة. وكذلك الأمر لعنصر <dt> الذي يحتوي على السلسلة النصية "Name» التي لا تمثل خاصية، وإنما لافتة (label) فقط. <h1>Contact Information</h1> <dl> <dt>Name</dt> <dd>Mark Pilgrim</dd> أين توجد المعلومات الحقيقية؟ في عنصر <dd>، وهنالك سنحتاج إلى وضع خاصية itemprop، لكن أيّ خاصية منها؟ إنها خاصية name، وأين قيمة الخاصية؟ هي النص الموجود ضمن العنصر <dd>، لكن ألا نحتاج إلى وضع القيمة في شيفرة خاصة؟ النموذج الهيكلي للبيانات الوصفية في HTML5 يقول لا، فلا يوجد معنى خاص لعناصر <dd>، وستكون قيمة الخاصية هي النص الموجود ضمن العنصر. <dd itemprop="name">Mark Pilgrim</dd> كيف نستطيع التعبير عما سبق بالعربية؟ "اسم هذا الشخص هو Mark Pilgrim". حسنًا، لنتابع. إضافة الخاصيتين التاليتين صعبٌ قليلًا، هذه هي الشيفرة قبل إضافة البيانات الوصفية: <dt>Position</dt> <dd>Developer advocate for Google, Inc.</dd> إذا نظرتَ إلى نوع اصطلاحات Person، فستجد أنَّ النص "Developer advocate for Google, Inc.‎" يحتوي على خاصيتين: jobTitle ‏(قيمتها "Developer advocate") و affiliation‏ ( قيمتها "Google, Inc.‎")، لكن كيف تستطيع أن تُعبِّر عن ذلك عبر البيانات الوصفية؟ الجواب المختصر: لا يمكنك فعل ذلك. لا توجد طريقة في البيانات الوصفية تمكِّنك من تقسيم سلسلة نصية إلى عدِّة خاصيات. لا يمكنك القول "أول 18 محرفًا من هذه السلسلة النصية هي خاصيةُ بياناتٍ وصفية، وآخر 12 محرفًا هي خاصيةٌ أخرى". لكن هذا لا يعني أنَّ الأمر مستحيلٌ. تخيل أنك تريد أن تُنسِّق النص "Developer advocate" بنوع خطٍ مختلف عن النص "Google, Inc.‎". حسنًا، CSS لا تستطيع فعل ذلك أيضًا، لكن ماذا كنتَ ستفعل؟ ستحتاج أولًا إلى وضع كل قسم من السلسلة النصية في حاويات مختلفة، مثل <span>، ثم تُطبِّق أنماط CSS على كل عنصر <span> على حدة. يمكنك تطبيق هذه التقنية أيضًا على البيانات الوصفية، فهنالك معلومتان منفصلتان هنا: jobTitle و affiliation. إذا وضعت كل معلومة في عنصر <span>، فستستطيع أن تقول أنَّ كل عنصر <span> هو خاصيةٌ مستقلةٌ من خاصيات البيانات الوصفية. <dt>Position</dt> <dd><span itemprop="jobTitle">Developer advocate</span> for <span itemprop="affiliation">Google, Inc.<span></dd> هذا يعني: "وظيفة هذا الشخص هي "Developer advocate". هذا الشخص يعمل لدى "Google, Inc.‎"". تلك جملتان، وخاصيتا بياناتٍ وصفية. صحيحٌ أننا وضعنا مزيدًا من الشيفرات، لكننا استفدنا منها خيرَ استفادة. سنستفيد أيضًا من نفس التقنية لتوصيف معلومات العنوان، يُعرِّف نوع الاصطلاحات Person الخاصية address، التي هي بدورها عنصرٌ من عناصر البيانات الوصفية، وهذا يعني أنَّ للعنوان نوعُ اصطلاحاتٍ خاصٌ به (http://schema.org/PostalAddress)، وله خاصياتٌ متعلقةٌ به. يُعرِّف نوع الاصطلاحات PostalAddress خمسَ خاصياتٍ: streetAddress و addressLocality و addressRegion و postalCode و addressCountry. إذا كنتَ مبرمجًا، فمن المرجح أنَّك تعرف كيف تفصل النقطة بين الكائنات وخاصياتها، تخيل أنَّ العلاقة كالآتي: Person Person.PostalAddress Person.PostalAddress.streetAddress Person.PostalAddress.addressLocality Person.PostalAddress.addressRegion Person.PostalAddress.postalCode Person.PostalAddress.addressCountry لنعد إلى مثالنا. العنوان بأكمله موجودٌ في عنصر <dd> وحيد (أكرِّر مرةً أخرى أنَّ العنصر <dt> هو لافتة، ولا يلعب دورًا في إضافة معلومات إلى البيانات الوصفية). من السهل الإشارة إلى خاصية address، كل ما عليك فعله هو إضافة الخاصية itemprop في عنصر <dd>. <dt>Mailing address</dt> <dd itemprop="address"> لكن تذكَّر أنَّ خاصية address هي بدورها عنصرٌ من عناصر البيانات الوصفية، هذا يعني أننا نحتاج إلى وضع الخاصيتين itemscope و itemtype أيضًا. <dt>Mailing address</dt> <dd itemprop="address" itemscope itemtype="http://schema.org/PostalAddress"> لقد رأينا هذا من قبل، لكن للعناصر من المستوى الأول (top-level). عنصر <section> يحتوي على itemtype و itemscope، وجميع العناصر الموجودة ضمن العنصر <section> التي لديها خاصيات للبيانات الوصفية هي ضمن "مجال" (scope) نوع اصطلاحات البيانات الوصفية. لكن هذه هي أول مرة نرى فيها "تشعّب" المجالات، أي تعريف itemtype و itemscope (في عنصر <dd>) داخل مجال موجود مسبقًا (في عنصر <section>). المجالات المتشعبة تعمل تمامًا كما تعمل شجرة DOM في HTML. العنصر <dd> يحتوي على عددٍ معيّنٍ من العناصر الأبناء، ويكون مجالها هو نوع الاصطلاحات المُعرَّف في العنصر <dd>، وبعد أن ينتهي العنصر <dd> عبر وسم الإغلاق <‎/dd> فسيرجع المجال إلى نوع الاصطلاحات المُعرَّف في العنصر الأب (الذي هو <section> في حالتنا). تُعاني خاصيات العنوان من نفس المشكلة التي واجهناها عند تعريف الخاصيتين jobTitle و affiliation، لكن السلسلة النصية للعنوان أطول قليلًا، وعلينا تقسيمها إلى خمس خاصيات للبيانات الوصفية. وسنستعمل نفس الآلية التي اتبعناها سابقًا: وضع كل قطعة من المعلومات في عنصر <span>، ثم توصيف تلك المعلومات عبر خاصيات البيانات الوصفية. <dd itemprop="address" itemscope itemtype="http://schema.org/PostalAddress"> <span itemprop="streetAddress">100 Main Street</span><br> <span itemprop="addressLocality">Anytown</span>, <span itemprop="addressRegion">PA</span> <span itemprop="postalCode">19999</span> <span itemprop="addressCountry">USA</span> </dd> بالعربية: "هذا الشخص يملك عنوانًا بريديًا. اسم الشارع لذاك العنوان البريدي هو "100 Main Street"، أما البلدة (locality) فهي "Anytown"، والإقليم (region) هو "PA"، والرمز البريدي (postal code) هو "19999"، واسم الدولة هو "USA"". س: هل صيغة العنوان البريدي خاصةٌ بالولايات المتحدة؟ ج: لا. خاصيات نوع الاصطلاحات PostalAddress عامةٌ لتتمكن من وصف أي عنوان بريدي في العالم. لكن لن يكون لجميع العناوين قيمٌ لكل خاصية من الخاصيات، لكن لا بأس بهذا. وقد تتطلب بعض العناوين وضع أكثر من "سطر" واحد في خاصية معيّنة، ولا بأس بهذا أيضًا. فمثلًا، لو كان يحتوي العنوان البريدي على عنوان الشارع ورقم البناء، فسيمثِّل كلاهما الخاصية streetAddress: <p itemprop="address" itemscope itemtype="http://schema.org/PostalAddress"> <span itemprop="streetAddress"> 100 Main Street Suite 415 </span> ... </p> بقي شيءٌ أخيرٌ في صفحة About: قائمةٌ بروابط URL. لدى نوع الاصطلاحات Person خاصيةٌ لهذا الغرض اسمها url، التي يمكن أن تحتوي على أيّ نوعٍ من أنواع الروابط (المهم أن يكون "رابطًا"). ما أقصده هو أنَّ تعريف الخاصية url غير مُحدَّد، ويمكن أن تحتوي على أيّة روابط متعلقة بالشخص: مدونة، أو معرض صور، أو حساب شخصي على موقعٍ آخر مثل فيسبوك أو تويتر. من المهم أن تلاحظ أنَّ الشخص الواحد قد يمتلك أكثر من خاصية url. تقنيًا، يمكن لأي خاصية أن تتكرر، لكن إلى الآن لم نستفد من هذا. على سبيل المثال، قد يكون لديك أكثر من خاصية image تُشير إلى روابط URL لصورتين مختلفتين. أريد هنا أن أذكر أربعة روابط URL مختلفة: المدونة، وحساب Google، وحساب Reddit، وحساب تويتر. هنالك قائمة في HTML فيها أربعة روابط موجودة في أربعة عناصر <a>، كلُ واحدٍ منها موجودٌ في عنصر <li> خاص به. سنُضيف الخاصية itemprop="url"‎ إلى كل عنصر من عناصر <a>. <h1>My Digital Footprints</h1> <ul> <li><a href="http://diveintomark.org/" itemprop="url">weblog</a></li> <li><a href="http://www.google.com/profiles/pilgrim" itemprop="url">Google profile</a></li> <li><a href="http://www.reddit.com/user/MarkPilgrim" itemprop="url">Reddit.com profile</a></li> <li><a href="http://www.twitter.com/diveintomark" itemprop="url">Twitter</a></li> </ul> وفقًا للنموذج الهيكلي للبيانات الوصفية في HTML5، سيُعامَل العنصر <a> معاملةً خاصةً، فقيمة خاصية البيانات الوصفية تؤخذ من الخاصية href، وليس من المحتوى النصي للعنصر. وسيتم تجاهل المحتوى النصي لكل رابط من قِبَل مُفسِّر البيانات الوصفية. وهذا يعني -بالعربية-: "هذا الشخص لديه رابط URL في http://diveintomark.org/‎ وهذا الشخص لديه رابط URL آخر في http://www.google.com/profiles/pilgrim وهذا الشخص لديه رابط URL آخر في http://www.reddit.com/user/MarkPilgrim وهذا الشخص لديه رابط URL آخر في http://www.twitter.com/diveintomark" المقتطفات المنسقة Rich Snippets ربما تتساءل : "لماذا نفعل هذا؟" هل نُضيف البنى الهيكلية عبثًا؟ لماذا نأبه للبيانات الوصفية ونستعملها؟ هنالك نوعان رئيسيان من التطبيقات التي تستخدم HTML، وبطريقها تستخدم البيانات الوصفية أيضًا: متصفحات الويب محركات البحث أما للمتصفحات، فهنالك واجهةٌ برمجيةٌ في DOM لاستخلاص عناصر البيانات الوصفية وخاصياتها وقيم تلك الخاصيات من صفحة الويب، لكن للأسف هذه الواجهة البرمجية غير مدعومة إلا من الإصدارات الحديثة لبعض المتصفحات، لهذا اعتبر أنَّ هذا الطريق مسدودٌ إلى أن تدعم جميع المتصفحات هذه الواجهة البرمجية. مستهلكٌ آخر لشيفرات HTML هو محركات البحث. ماذا يمكن لمحركات البحث فعله مع خاصيات البيانات الوصفية التي تتحدث عن شخصٍ ما؟ تخيل هذا: بدلًا من عرض عنوان الصفحة ومُلخَّص عن محتواها، فسيعرض محرِّك البحث بعض المعلومات الهيكلية الموجودة فيها، مثل الاسم الكامل، والمُسمى الوظيفي، والشركة التي يعمل بها، والعنوان، وربما سيعرض أيضًا صورةً مُصغَّرةً له. هل جذب ذلك انتباهك؟ يدعم محرك البحث Google البيانات الوصفية كجزءٍ من برنامج "المقتطفات المنسقة" (Rich Snippets)، فعندما يُفسِّر عنكبوت البحث في Google صفحتك ويجد خاصيات للبيانات الوصفية التي تتطابق مع نوع الاصطلاحات http://schema.org/Preson، فسيحاول تفسير تلك الخاصيات ويُخزِّن قيمها بجانب بقية بيانات الصفحة. لدى Google أداةٌ رائعةٌ لكي ترى كيف "يرى" Google خاصيات البيانات الوصفية في صفحتك، واختبارها على صفحة About التي نعمل عليها سيُعطي النتيجة: الشكل 1: معلومات البيانات الوصفية كما تُظهِرها أداة اختبار البيانات المنظّمة كل البيانات الوصفية موجودٌ هنا: خاصية image من <img src>، جميع روابط URL من قائمة عناصر <a href>، وحتى كائن العنوان (مذكورٌ في "address") والخاصيات الخمس المتعلقة به. الآن، كيف يستعمل Google كل هذه المعلومات؟ الأمر نسبيٌ، فلا توجد قواعد مُلزِمَة لكيفية عرض خاصيات البيانات الوصفية، ولا أيُّها سيُعرَض، وحتى لا توجد قواعد تحكم إذا كانت ستُعرَض هذه الخاصيات أم لا. إذا بحث أحدهم عن "Mark Pilgrim" ورأى Google أنَّ صفحة "About" تستحق الظهور في نتائج البحث، وقرر Google أنَّ خاصيات البيانات الوصفية الموجودة في تلك الصفحة تستحق أن تُعرَض، فعندها ستبدو نتيجة البحث مشابهةً لما يلي: الشكل 2: مثالٌ عن نتيجة البحث عن صفحة فيها بياناتٌ وصفيةٌ أول سطر "About Mark Pilgrim" هو عنوان الصفحة الموجود في عنصر <title>، ولكن هذا ليس أمرًا مثيرًا للاهتمام؛ لأن محرك Google يفعل هذا لكل صفحة، لكن السطر الثاني مليءٌ بالمعلومات المأخوذة مباشرةً من البيانات الوصفية التي أضفناها إلى الصفحة. "Anytown PA" هو جزءٌ من العنوان البريدي، الموصوف عبر نوع الاصطلاحات http://schema.org/PostalAddress، أما "Developer advocate" و "Google, Inc.‎" هما الخاصيتان من نوع الاصطلاحات http://schema.org/Person (الخاصية jobTitle و affiliation على التوالي وبالترتيب). هذا رائع! لا تحتاج إلى أن تكون شركةً كبيرةً تُبرِمُ اتفاقياتٍ خاصةً مع شركات محركات البحث لتخصيص طريقة عرض نتيجة البحث. كل ما تحتاج له هو عشر دقائق وبعض خاصيات HTML لكي توصِّف فيها بياناتك التي ستنشرها في صفحتك. س: فعلتُ كل ما قلتَه لي، لكن لم تتغير طريقة عرض نتائج البحث عن صفحتي في Google، ما الخطب؟ ج: "لا تضمن Google أنَّ الشيفرة الموجودة في أيّة صفحة أو موقع ستُستخدَم في نتائج البحث"، لكن بغض النظر أنَّ محرك Google قرر ألّا يستعمل البيانات الوصفية في صفحتك، فقد يستعملها محركُ بحثٍ آخر. فمعيار البيانات الوصفية (Microdata) هو معيارٌ مفتوحٌ يستطيع أيُّ شخصٍ توظيفه -كما هي بقية HTML5-. من واجبك توفير أكبر قدر من البيانات تستطيع تقديمه. ثم اترك الأمر للآخرين لكي يُقرِّروا ماذا يفعلون معها. ربما يفاجئوك! ترجمة -وبتصرّف- لجزء من فصل "Microdata" من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  11. يحتوي مشروع الملف الشخصي الخاص بنا على صفحة واحدة حتى الآن. من الواضح أنّ معظم مواقع الويب تمتلك أكثر من صفحة. سنُضيف في هذا الدرس صفحات إضافيّة. إنشاء صفحة جديدة سننشئ ثلاث صفحات جديدة، صفحة من أجل مدوّنة الموقع Blog، وصفحة من أجل المشاريع Projects وأخرى من أجل معلومات التواصل Contact. ستكون هذه الصفحات الرئيسيّة في الموقع. يجب أن نتذكّر دائمًا أنّه يمكن أن نضيف صفحات فرعيّة أخرى لاحقًا. فمثلًا سيكون هناك صفحة فرعيّة لكل تدوينة ضمن المدوّنة. لامتلاك بنية جيّدة ضمن الموقع من الأفضل إنشاء مجلّدات فرعيّة من أجل كلّ صفحة. سيكون لكل مجلّد فرعي ملف index.html خاص به يُعرَض افتراضيًّا عندما يفتح المتصفّح هذا المجلّد. يُعتبر إنشاء صفحة جديدة أمر سهلًا للغاية. من الأفضل نسخ ملف index.html الذي أنشأناه مسبقًا بحيث يكون لدينا البنية الأساسية جاهزة. بعد ذلك نُجري بالطبع بعض التعديلات المناسبة لكلّ صفحة جديدة. ملاحظة: يجب أن نتأكّد بأنّنا لا نستخدم أيّ رمز خاص أو فراغات عند تسمية أي مجلّد فرعي أو ملف. يجب أن نستخدم الأحرف القياسيّة ويُفضّل أن تكون بحالة صغيرة lower case، ومن الجيّد أيضًا فصل الكلمات عن بعضها باستخدام إشارة الناقص (-). صفحة المدونة أنشئ مجلّدًا فرعيًّا ضمن مجلّد portfolio وسمّه blog. انسخ الملف index.html إلى هذا المجلّد الفرعي الجديد. يجب الآن أن تبدو بنية الملفات لديك على الشكل التالي: افتح الملف المنسوخ blog/index.html في المتصفّح (إذا كنت تستخدم Brackets انقر زر Live Preview). ستلاحظ وجود أمرين لا يعملان بشكل صحيح: صور الصفحة لا تظهر. الألوان المعرّفة ضمن ملف css غير مُطبّقة في هذه الصفحة. يعود السبب في ذلك هو أنّنا ضمن مجلّد فرعي، فالمسار الخاص بملفات الصور لم يعد صحيحًا. لكي نسمح للصور بالظهور يجل أن نستخدم المسار marco.jpg/.. بدلًا من marco.jpg ضمن السمة src لعنصر الصورة img. ولكن من المؤكّد أنّنا لن نريد استخدام نفس الصور ضمن صفحة المدوّنة. لذلك يمكن إزالة العنصر img بشكل كامل. جرت العادة أن نعرّف قواعد css بحيث تكون شاملة لكل صفحات موقع الويب. وهكذا فإنّه من الضروري أن نشير إلى نفس ملف css الذي يحوي هذه القواعد وذلك ضمن ملف المدوّنة. يمكننا ذلك بتغيير عنوان URL من main.css إلى main.css/.. ضمن عنصر link. يوضّح السطر التالي شكل عنصر link بعد تعديل الملف blog/index.html: <link rel="stylesheet" href="../main.css"> سيؤدي تعديل عنوان ملف css ضمن عنصر link إلى تطبيق تنسيقات css الموجودة ضمن الملف main.css على صفحة المدوّنة. سنغيّر الآن عنوان ومحتوى صفحة المدوّنة. blog/index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="../main.css"> <title>Blog - Web Portfolio of Marco</title> </head> <body> <h1 class="title">Blog</h1> <p>I write about things I encounter while learning web programming.</p> <h2>Blog Entries</h2> <!-- Here will be a list of all the blog entries. --> </body> </html> يمكننا رؤية بعض الوسوم الجديدة في الشيفرة السابقة وهي <-- و --!> حيث يمكننا أن نكتب تعليق comment بين الرمزين السابقين. الهدف من التعليقات هو كتابة بعض الملاحظات التوضيحيّة لنا فقط، فهي لا تظهر لمستخدم الصفحة. التدوينة كصفحة فرعية تحتاج مدوّنتنا بالطبع إلى بعض المُدخلات (التدوينات). سننشئ صفحة HTML مستقلّة من أجل كل تدوينة. أنشئ مجلّدًا فرعيًّا ضمن المجلّد blog وسمّه first-entry ثمّ انسخ الملف blog/index.html إلى المجلّد الفرعي first-entry: افتح ملف التدوينة وغيّر محتوياته كما يلي: blog/first-entry/index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="../../main.css"> <title>First Entry - Web Portfolio of Marco</title> </head> <body> <h1 class="title">First Entry</h1> <p>April 7, 2015</p> <hr> <p>This is my very first blog entry.</p> </body> </html> لقد وضعت في الشيفرة السابقة عنصر HTML جديد وهو <hr> حاول أن تعرف وظيفته بمفردك، حاول مثلًا إزالته ومن ثمّ انظر إلى تأثير ذلك على الصفحة. أو يمكنك البحث عن معلومات عنه في الانترنت. لنبدأ بكتابة التدوينات أنصح دائمًا أن تبدأ فورًا بكتابة تدويناتك. يمكنك كتابة تدوينة قصيرة في كل مرّة تتعلّم شيئًا جديدًا حول البرمجة. يمكنك الاطلاع على بعض الأفكار التالية التي قد تساعدك في البدء بالتدوين: ماذا تعلّمت اليوم؟ أضف صور توضيحية. أضف روابط إلى مواقع مفيدة. ما هي المشاكل التي واجهتها؟ كيف حللت تلك المشاكل؟ ما العمل الذي يتوجّب عليّ إنجازه في المرّة القادمة؟ بمثل هذه التدوينات السابقة ستتقدّم سريعًا في احتراف البرمجة، وأسباب ذلك: ستكون ملّمًا بما تعلّمته بالتدريج. يمكنك أن تبحث عن المعلومات ضمن وثائقك الشخصيّة. ستتدرّب على HTML وCSS بينما تكتب هذه التدوينات. إذا نشرت موقعك على الانترنت يمكنك أن تجعل تدويناتك متاحة للآخرين. وهذا يساعد على مشاركة المعرفة التي اكتسبتها أو حتى أن تطلب من أحد الأشخاص أن يساعدك على حل مشكلة ما. إذا نشرت ملفّك الشخصي سيكون من الرائع مشاركته مع الآخرين للاطّلاع على إمكانيّاتك وأعمالك. التدوينة الثانية تلميح: أنشئ مجلّدًا فرعيًّا من أجل كل ملف تدوينة جديد. سيسمح لك هذا التنظيم بوضع الصور والملفات الأخرى لكل تدوينة بشكل منفصل عن ملفات التدوينات الأخرى. صفحة المشاريع من المفيد الاحتفاظ بصفحة خاصّة للمشاريع والمهام التي ننجزها. سنحضّر صفحة خاصّة لمثل هذه المشاريع على الرغم من عدم وجود أي محتوى بعد. لنكمل كما فعلنا من أجل صفحة المدوّنة ولننشئ مجلّدً فرعيً اسمه projects ضمن المجلّد الرئيسي portfolio ولننسخ الملف index.html إليه. projects/index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="../main.css"> <title>Projects - Web Portfolio of Marco</title> </head> <body> <h1 class="title">Projects</h1> <p>Here you will soon find my web projects.</p> </body> </html> صفحة التواصل سننشئ الآن الصفحة الأخيرة من الصفحات الرئيسيّة في الموقع وهي صفحة التواصل Contact. أنشئ مجلّدًا اسمه contact ضمن المجلّد الرئيسي portfolio وانسخ الصفحة index.html إليه. ملاحظة مهمّة: كُن حذرًا فيما يتعلّق بالمعلومات التي ترغب بنشرها للعموم. فمثلًا لا تنشر بريدك الإلكتروني الرئيسي، لأنّه من الممكن أن تستقبل بريدًا مزعجًا. contact/index.html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <link rel="stylesheet" href="../main.css"> <title>Contact - Web Portfolio of Marco</title> </head> <body> <h1 class="title">Contact</h1> <p> You can contact me by email: <a href="mailto:spammails@gmx.ch">spammails@gmx.ch</a> </p> <p> Marco Jakob<br> Switzerland </p> </body> </html> يمكننا أن نلاحظ وجود عنصر HTML جديد بجوار اسم Marco Jakob وهو العنصر <br>. يعمل هذا العنصر على الانتقال إلى سطر جديد ضمن الصفحة. نلاحظ أيضًا الرّابط الخاص mailto الذي يوجد على يسار البريد الإلكتروني (ضمن السمة href). سيعمل هذا الرّابط في حال نقره على فتح برنامج البريد الإلكتروني الافتراضي المثبّت على حاسوبك، مع فتح رسالة جديدة بعنوان البريد الإلكتروني المطلوب. لدينا الآن خمس صفحات HTML: سنزوّد موقعنا في الدرس السادس بوسيلة للتنقّل بين صفحاته. ترجمة -وبتصرّف- للمقال HTML & CSS Tutorial - Part 5: Blog and Other Pages لصاحبه Marco Jakob.
  12. هنالك أكثر من 100 عنصر في HTML5، بعضها هيكليٌ تمامًا وبعضها مجردُ حاويةٍ لواجهةٍ برمجيةٍ. وعبر تاريخ HTML، تجادل كاتبو المعايير حول العناصر التي يجب تضمينها في اللغة، فهل يجب أن تحتوي HTML على عنصر <figure>؟ أو عنصر <person>؟ ماذا عن عنصر <rant>؟ اُتخِذَت القرارات، وكُتِبَت المعايير، وطوَّر المطورون تطبيقاتهم، وأضاف صانعو المتصفحات الميزات لمتصفحاتهم، ودُفِعَت عجلة تطوير الويب إلى الأمام. من المؤكَّد أنَّ HTML لن تستطيع إرضاء الجميع، إذ لا يستطيع أيُّ معيارٍ فعل هذا. لم تصل بعض الأفكار المقترحة إلى المستوى المطلوب، فمثلًا، لا يوجد عنصر <person> في HTML5 (وكذلك الأمر لعنصر <rant>)؛ لا يوجد شيءٌ يمنعك من إضافة عنصر <person> إلى صفحات الويب التي تكتبها، لكنها لن تكون سليمةً بنيويًا، ولن تعمل بشكلٍ متماثل في جميع المتصفحات، وقد تتعارض مع معايير HTML المستقبلية إن أضافتها لاحقًا. حسنًا، إن لم يكن الحل كامنًا في إنشاء عناصر جديدة، فماذا على مطوِّر الويب الذي يحب اتباع القواعد الهيكلية أن يفعل؟ كانت هنالك محاولاتٌ لتوسعة الإصدارات القديمة من HTML. أشهر طريقة هي microformats، التي تستعمل الخاصيتين class و rel في HTML. خيارٌ آخر هو RDFa، التي صُمِّمَت لتعمل في XHTML لكنها صُدِّرَت لاحقًا إلى HTML أيضًا. لدى Microformats و RDFa نقاطُ قوةٍ وضعف. إذ تأخذان طريقًا مختلفًا تمامًا لتحقيق نفس الهدف: توسعة صفحات الويب بإضافة بُنى هيكلية جديدة لا تُمثِّل جزءًا من أساس لغة HTML. لا أنوي أن أقلب هذا الدرس إلى حربٍ بين الصيغ؛ وإنما أريد أن أُركِّز على خيارٍ ثالثٍ طوِّرَ بعد تعلم الدروس من microformats و RDFa، وصُمِّمَ ليندمج جيدًا مع HTML5: إنه "البيانات الوصفية" (microdata). ما هي البيانات الوصفية؟ كل كلمة في الجملة الآتية مهمة، لذا انتبه جيدًا إليها: حسنًا، ماذا تعني الجملة السابقة العجيبة؟ لنبدأ من نهايتها إلى بدايتها. تتمحور البيانات الوصفية (microdata) حول "أنواع الاصطلاحات المخصصة" (custom vocabularies). تخيّل أنَّ جميع عناصر HTML5 هي نوعٌ وحيدٌ من الاصطلاحات. وهذا النوع يستطيع تمثيل "قسم" (section) أو "مقالة" (article)، لكنه لا يستطيع تضمين عناصر لتمثيل "شخص" أو "حدث". فإذا أردت تمثيل "شخص" في صفحة الويب، فعليك تعريف نوع اصطلاحاتٍ خاص بك. تسمح لك البيانات الوصفية (microdata) بذلك، حيث يستطيع أيُّ شخصٍ أن يُعرِّف اصطلاحات microdata خاصة به ويبدأ بتضمين خاصياته (properties) في صفحات الويب التي يطورها. النقطة الثانية التي عليك أن تعرفها عن البيانات الوصفية أنَّه تعمل وفق ثنائيات "الاسم/القيمة". فكل نوع اصطلاحات يُعرِّف مجموعةً من الخاصيات التي لها أسماءٌ معيّنة. على سبيل المثال، قد يتضمن نوع الاصطلاحات "person" خاصياتٍ مثل name و image. ولتضمين خاصية من خاصيات البيانات الوصفية (microdata) في صفحة الويب، عليك وضع اسم الخاصية في مكانٍ معيّن. واعتمادًا على العنصر الذي تضع فيه خاصيتك، هنالك قواعد حول كيفية استخلاص قيمة الخاصية (سنأتي على ذكرها في القسم التالي). بالإضافة إلى الخاصيات التي لها أسماء، تعتمد البيانات الوصفية كثيرًا على مفهوم "المجال" (scope). أبسط طريقة تستطيع تخيّل المجالات في البيانات الوصفية هي أن تتخيل علاقة "الأب-الابن" بين عناصر DOM. العنصر <html> يحتوي عادةً على عنصرين: <head> و <body>. العنصر <body> يحتوي عادةً على عدِّة أبناء وقد يحتوي كلٌ منها على أبناء خاصة به. على سبيل المثال، قد تحتوي صفحة الويب على عنصر <h1> موجودٌ ضمن عنصر <hgroup> موجودٌ داخل عنصر <header> الموجود داخل عنصر <body>. وقد يحتوي جدولٌ ما على خلية <td> موجودة ضمن <tr> الموجود ضمن <table> (الذي يتفرّع من <body>). تُعيد البيانات الوصفية استخدام هذه البنية الهيكلية لشجرة DOM لتوفير طريقة لقول "جميع الخاصيات ضمن هذا العنصر مأخوذةٌ من نوع الاصطلاحات المُحدَّد". وهذا يسمح لك باستخدام أكثر من نوع من أنواع الاصطلاحات في البيانات الوصفية في نفس الصفحة. تستطيع أيضًا أن تضع نوعًا من أنواع اصطلاحات البيانات الوصفية ضمن نوعٍ آخر، وذلك عبر إعادة استخدام البنية الهيكلية لشجرة DOM (سأريك عدِّة أمثلة عن تداخل أنواع الاصطلاحات في دروسٍ قادمة). تحدثتُ سابقًا عن موضوع DOM، لكن دعني أستفيض قليلًا فيه. مهمة البيانات الوصفية هي إضافةُ مزيدٍ من الهيكلية إلى البيانات الظاهرة في صفحة الويب. ليس الغرض من البيانات الوصفية أن تكون صيغةُ بياناتٍ تعملُ بمفردها، وإنما هي مكمِّلةٌ للغة HTML وتعتمد عليها. وكما سترى في القسم التالي، تعمل البيانات الوصفية بأفضل صورة ممكنة عندما تستعمل HTML استعمالًا سليمًا، لكن اصطلاحات HTML غير كافية للتعبير عن كل ما نريده، لذلك أتت البيانات الوصفية (microdata) للتحكم الدقيق في البُنى الهيكلية للبيانات الموجودة في شجرة DOM. إذا كانت البيانات التي تحاول توصيفها غير موجودةٍ في شجرة DOM، فربما عليك أن تتراجع وتعيد التفكير فيما إذا كانت البيانات الوصفية هي الحل الصحيح لمشكلتك. هل أصبحت هذه الجملة واضحةً الآن؟ "توصِّف البيانات الوصفية شجرة DOM بثنائياتٍ على شكل "الاسم/القيمة" آتيةٌ من أنواع اصطلاحاتٍ مُخصَّصة". أرجو ذلك، ولنرى تطبيقاتٍ عمليةً عليها في بقية هذا السلسلة. النموذج الهيكلي للبيانات الوصفية تعريف نوع اصطلاحات البيانات الوصفية الخاص بك سهلٌ. تحتاج أولًا إلى مجال أسماء (namespace) الذي هو URL. رابط URL لمجال الأسماء يمكن أن يُشير إلى صفحة ويب موجودة، لكن هذا ليس ضروريًا. لنقل أنَّك تريد إنشاء نوع اصطلاحات لوصف "شخص ما". إذا كنتُ أملك النطاق schema.org فسأستعمل رابط URL الآتي http://schema.org/Person كمجال أسماءٍ لنوع اصطلاحات البيانات الوصفية. هذه طريقة سهلة لإنشاء مُعرِّف فريد عالمي: اختر رابط URL في نطاقٍ تملكه. سأحتاج إلى تعريف بعض الخاصيات في نوع الاصطلاحات، لنبدأ بثلاث خاصيات أساسية: name (اسمك الكامل) image (رابطٌ لصورةٍ لك) url (رابطٌ لموقعٍ يتعلق بك، مثل مدونتك أو حسابك على Google) بعض الخاصيات السابقة هي روابط URL، وبعضها الآخر مجرد نص بسيط. وتربُط كلُ واحدةٍ منها نفسها بنوعٍ معيّن من الشيفرات، وحتى قبل أن تبدأ بالتفكير عن البيانات الوصفية أو الاصطلاحات أو إلى ما هنالك… تخيّل أنَّ لديك صفحة لحساب المستخدم أو صفحة "About"، من المحتمل أنَّك ستضع اسمك كترويسة (ربما في عنصر <h1>)، أما صورتك ففي عنصر <img> ذلك لأنَّك تريد أن يراها زوار صفحتك، وستضع أيّة روابط URL مرتبطة بك في عناصر <a> ذلك لأنَّك تريد أن يتمكن زوار صفحتك من النقر عليها. ولنقل أيضًا أنَّ كامل معلوماتك الشخصية موجودةٌ في عنصر <section> لفصلها عن بقية محتويات الصفحة. إذًا: <section> <h1>Mark Pilgrim</h1> <p><img src="http://www.example.com/photo.jpg" alt="[me smiling]"></p> <p><a href="http://diveintomark.org/">weblog</a></p> </section> النموذج الهيكلي للبيانات الوصفية هو ثنائيات على شكل "الاسم/القيمة". يُعرَّف اسم الخاصية التي تتبع لبيانات الوصفية (مثل name أو image أو url في مثالنا) دومًا ضمن عنصر HTML. ثم تؤخذ قيمة تلك الخاصية من شجرة DOM للعنصر. ولأغلبية عناصر HTML، تكون قيمة الخاصية هي المحتوى النصي للعنصر، لكن هنالك عددٌ لا بأس به من الاستثناءات. العنصر القيمة <meta> الخاصية content <audio> الخاصية src <embed> <iframe> <img> <source> <video> <a> الخاصية href <area> <link> <object> الخاصية data <time> الخاصية datetime جميع العناصر الأخرى المحتوى النصي "إضافة البيانات الوصفية" إلى صفحتك هي مسألة إضافة بعض الخاصية إلى عناصر HTML التي لديك. أول شيء عليك فعله هو التصريح عن نوع الاصطلاحات الذي ستستخدمه، وذلك عبر الخاصية itemtype، أما الشيء الثاني الذي عليك دائمًا فعله هو التصريح عن مجال (scope) نوع الاصطلاحات، وذلك عبر الخاصية itemscope. جميع البيانات التي نريد هيكلتها في المثال الآتي موجودةٌ في عنصر <section>، لذا سنُعرِّف الخاصيتين itemtype و itemscope في العنصر <section>. <section itemscope itemtype="http://schema.org/Person"> اسمك هو أول المعلومات الموجودة ضمن عنصر <section>، وهو موجودٌ ضمن عنصر <h1>، وعنصر <h1> لا يملك أيّ معنى خاص في النموذج الهيكلي للبيانات الوصفية في HTML5، لذلك سيُصنَّف تحت بند "جميع العناصر الأخرى" حيث تكون قيمة الخاصية هي المحتوى النصي الموجود ضمن العنصر (سيعمل ما سبق بشكلٍ مماثل إن كان اسمك موجودًا ضمن عنصر <p> أو <div> أو <span>). <h1 itemprop="name">Mark Pilgrim</h1> السطر السابق يقول بالعربية: "هذه هي خاصية name لنوع الاصطلاحات http://schema.org/Person، وقيمة تلك الخاصية هي Mark Pilgrim". الخاصية التالية هي خاصية image، التي من المفترض أن تكون رابط URL، ووفقًا للنموذج الهيكلي للبيانات الوصفية في HTML5، "قيمة" خاصية البيانات الوصفية الموجودة في العنصر <img> هي قيمة الخاصية src، لكن لاحظ أنَّ رابط URL لصورتك الشخصية موجودٌ في خاصية <img src>، فكل ما علينا فعله هو التصريح أنَّ العنصر <img> يُمثِّل خاصية image. <p> <img itemprop="image" src="http://www.example.com/photo.jpg" alt="[me smiling]"> </p> السطر السابق يقول بالعربية: "هذه هي قيمة خاصية image لنوع الاصطلاحات http://schema.org/Person، وقيمة الخاصية هي http://www.example.com/photo.jpg". في النهاية، الخاصية url هي رابط URL أيضًا، ووفقًا للنموذج الهيكلي للبيانات الوصفية في HTML5، "قيمة" خاصية البيانات الوصفية الموجودة في العنصر <a> هي قيمة الخاصية href، وهذا يتوافق توافقًا مثاليًا مع الشيفرة الموجودة عندك؛ فكل ما تحتاج له هو أن تقول أنَّ عنصر <a> الموجود مسبقًا يُمثِّل الخاصية url: <a itemprop="url" href="http://diveintomark.org/">dive into mark</a> السطر السابق يقول بالعربية: "هذه هي قيمة خاصية url لنوع الاصطلاحات http://schema.org/Person، وقيمة الخاصية هي http://diveintomark.org/‎". إذا كانت شيفراتك مختلفةً قليلًا فلن تُشكِّل لك مشكلةً بكل تأكيد. يمكنك إضافة خاصيات البيانات الوصفية وقيمتها إلى أيّ شيفرة من شيفرات HTML، حتى الشيفرات من القرن العشرين التي كانت تستعمل الجداول لإنشاء تخطيط الصفحة. وبغض النظر عن أنني لا أنصحك بتاتًا بكتابة مثل هذه الشيفرات، لكنها للأسف ما تزال شائعةً، وما يزال بإمكاننا إضافة خاصيات البيانات الوصفية إليها. <table> <tr><td>Name<td>Mark Pilgrim <tr><td>Link<td> <a href=# onclick=goExternalLink()>http://diveintomark.org/</a> </table> أضف خاصية itemprop في خلية الجدول التي تحتوي على الاسم لإنشاء الخاصية name. خلايا الجدول لا تملك أيّ معنى خاص في النموذج الهيكلي للبيانات الوصفية في HTML5، لذلك ستكون قيمة الخاصية هي المحتوى النصي الموجود ضمن الخلية. <tr> <td>Name<td itemprop="name">Mark Pilgrim إضافة الخاصية url أصعب بقليل، حيث لا تستعمل الشيفرة السابقة العنصر <a> استعمالًا سليمًا، فبدلًا من وضع رابط للصفحة الهدف في خاصية href، فستستعمل JavaScript والخاصية onclick لاستدعاء دالة (غير معروضة هنا) التي تستخلص رابط URL ثم تنتقل إليه. ولكي تُصاب بالغثيان، لنقل أنَّ تلك الدالة ستفتح الرابط في نافذة منبثقة صغيرة دون شريط تمرير. ألم يكن الويب مسليًا في القرن الماضي؟ على أيّة حال، ما يزال متوجبًا عليك إضافة خاصية البيانات الوصفية، لكن عليك أن تكون مبدعًا قليلًا. لا يمكنك استخدام عنصر <a> إذ أنَّ الرابط الهدف ليس موجودًا في خاصية href، ولا توجد طريقةٌ تستطيع فيها تجاوز القاعدة التي تقول "في العنصر <a>، ابحث عن قيمة خاصية البيانات الوصفية في href"، لكنك تستطيع إضافة عنصر ليحتوي الفوضى السابقة، وتُضيف الخاصية url إليه. <table itemscope itemtype="http://schema.org/Person"> <tr><td>Name<td>Mark Pilgrim <tr><td>Link<td> <span itemprop="url"> <a href=# onclick=goExternalLink()>http://diveintomark.org/</a> </span> </table> ولعدم وجود قاعدة خاصة تنطبق على العنصر <span>، فستُستعمَل القاعدة الافتراضية "قيمة الخاصية هي المحتوى النصي الموجود ضمن العنصر". "المحتوى النصي" لا يعني "جميع الشيفرات داخل العنص" (كالتي تحصل عليها عبر خاصية innerHTML في DOM على سبيل المثال)، وإنما تعني "النص فقط". وفي هذه الحالة يكون المحتوى النصي لعنصر <a> الموجود ضمن العنصر <span> هو http://diveintomark.org/‎. الخلاصة يمكنك إضافة خاصية البيانات الوصفية إلى أي شيفرة. وإذا كنتَ تستعمل HTML بشكلٍ صحيح، فستجد أنَّ إضافة البيانات الوصفية أسهل وأيسر فيما لو كانت شيفرة HTML مشوهةً، لكنك تستطيع إضافة البيانات الوصفية إليها على أيّة حال. ترجمة -وبتصرّف- لفصل "Microdata" من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  13. هل صنعت قالب HTML وأحببته لكنّك لم تعرف كيف تُحوّله إلى قالب ووردبريس؟ ليس بعد الآن، ستتعلم في هذا الدرس كيف تقوم بذلك. هل تعلم بأنّ هناك الكثير من المُطوّرين الذين يكسبون رزقهم عن طريق إنشاء قوالب وإضافات ووردبريس (Wordpress) وبيعها؟ وهل تعلم أنّه بإمكان مطوّري ووردبريس الحصول على آلاف الدولارات سنويًَّا من خلال بيع مُنتجاتهم؟ هذا الدّرس مُخصّص لمطوّري الويب، على فرض أن القارئ على اطّلاع (ولو بشكل مُبسّط) بما يتعلّق بلغة PHP وMySQL ومعلومات عامّة عن كيفية تنصيب واستخدام ووردبريس. هذا الدرس جزء من سلسلة تعلم كيفية تطوير قوالب ووردبريس: مقدمة إلى تطوير قوالب ووردبريس: تحويل صفحة HTML إلى قالب ووردبريس (هذا الدرس) التصفيح (Pagination) في قوالب ووردبريس إضافة قوائم التنقل (Navigation Menu) إلى قالب ووردبريس صف وتسجيل ملفات Javascript و CSS في قوالب ووردبريس متطلبات التّشغيل نحتاج إلى تحميل نسخة ووردبريس من الموقع الرسمي ، سنستخدم النّسخة العربية من هذا الرّابط. تحتاج ووردبريس إلى خادوم ويب، سواء كان Apache أو Nginx أو الخادوم المُدمج مع لغة PHP (بإصدار5.4 وما فوق). وتحتاج أيضًا إلى خادوم قواعد البيانات MySQL. لنقم بإنشاء مُجلّد جديد wordpress_tutorials ونضع فيه نسخة ووردبريس النّاتجة عن فكّ ضغط الملف الذي قمنا بتحميله. ملاحظة: يمكن أن تستخدم البرامج المُجهّزة خصّيصًا بخواديم Apache و MySQL مثل MAMP ،WAMP أو XAMPP. مع مُراعاة الاختلاف بين رابط خادوم الويب ومعلومات خادوم MySQL مع ما سنستخدمه في هذا الدّرس. سنقوم باستخدام الخادوم المُدمج مع PHP لتجنّب تنصيب خادوم Apache أو Nginx وتجنب الخوض في الإعدادات الخاصّة بكل واحد منها. لتشغيل الخادوم المُدمج نتوجه باستخدام سطر اﻷوامر إلى المجلّد الذي يحوي نسخة ووردبريس. ثم نكتب أمر: php -S localhost:8000 يتمّ تشغيل خادوم الويب المُدمج مع PHP على الحاسوب، يمكن التّوجّه عن طريق المُتصفّح إلى الرّابط localhost:8000 للتّأكد من أن الخادوم يعمل. تهيئة قالب HTML سنقوم بالاعتماد على قالبٍ قمتُ بإعداده مُسبقاً، يمكن تحميله من هذا الرابط، التّصميم عبارة عن قالب عادي يحوي بضع ملفّات HTML ،CSS و JavaScript. وهو مبني اعتمادًا على إطار عمل Foundation مع بعض التّعديلات والإضافات. هذا القالب تجريبيّ فقط لرؤية كيفية ظهور الشكل العامّ له. نتوجه إلى مجلّد المشروع الذي يحوي ملفات ووردبريس، ومنه نتوجّه إلى مجلد wp-content ثم إلى مُجلّد themes. نقوم بإنشاء مُجلّد اسمه my_theme بمثابة مُجلّد القالب الذي سنقوم بإنشائه. ملفات القالب الأساسية نحتاج إلى وجود ملفّين على الأقل ضمن مجلد القالب الذي أنشأناه، وهما: - style.css - index.php وهذا أقل ما يجب توفّره في القالب ليكون قالبًا صالحًا للاستخدام. نتجه إلى صفحة تغيير القوالب من لوحة تحكم ووردبريس لنرى ما يشبه: كما تلاحظون لا نرى القالب الجديد الذي أنشأناه ضمن القوالب، وذلك ﻷن هناك شيئًا آخر علينا فعله قبل أن نرى القالب الجديد في هذه الصفحة. نحتاج إلى إضافة تعليق في ملف style.css كالتّالي (يمكن تعديل أيّ قيمة أيضًا): /* Theme Name: قالبي الجديد Author: عمار العقاد Author URI: http://aalakkad.me Description: قالب جديد، نقوم من خلاله بالتّدرّب على إنشاء قوالب WordPress. Version: 1.0 */ تقوم ووردبريس بالدخول إلى كل مجلد في مجلد القوالب، ثم تبحث عن ملف style.css بداخله تعليق يشبه التّعليق السابق، ثم تقوم بتفسير هذه التّعليقات ووضعها في صفحة القوالب التي تظهر للمُستخدم من خلالها أسماء وتوصيف القوالب. وهناك تفصيلات أخرى للتعليقات في ملف style.css يمكن الاطلاع عليها كاملة من ملفات التوثيق. يمكن التوجه إلى صفحة القوالب من خلال لوحة التحكم، لنرى شيئًا مُشابهًا لما يلي: لنقم بتفعيل هذا القالب الجديد، ثم نتوجّه إلى الصّفحة الرّئيسية للمُدوّنة (الصفحة التي تُظهر المقالات وليست صفحة لوحة التّحكم). إن كنت تستخدم الخادوم المُدمج مع PHP بالطريقة التي ذكرناها، فيكون رابط الصّفحة الرئيسية هو: localhost:8000. سنرى صفحة بيضاء دون أن تحوي أيّ شيء، وذلك ﻷن القالب ما زال فارغًا تمامًا. سيتغيّر الوضع بعد قليل. نقل قالب HTML إلى قالب ووردبريس ما سنفعله في الخطوة الأولى هو وضع قالب HTML في ملف index.php ثم نقوم بتعديل مسارات ملفات CSS و JavaScript. ستكون جميع الصفحات التي نستخدمها من ووردبريس بعد تفعيل القالب الجديد مُتطابقة، وكأنها قالب HTML نفسه. وهذا هو المطلوب في هذه المرحلة، سنقوم بإضافة التّفاصيل فيما بعد وعلى عدة مراحل. نقوم الآن بنسخ محتوى ملف index.html كاملًا من قالب HTML ونضعه في ملف index.php في قالبنا الجديد my_theme ضمن ووردبريس. ثم ننسخ مجلد assets كما هو إلى داخل مجلد قالبنا الجديد. لنرى كيف أصبح شكل الموقع بعد أن وضعنا محتوى ملف index.html في ملف index.php: يظهر الآن القالب بهذا الشّكل ﻷن ملفات CSS وJavaScript أصبحت في مسار مختلف عن المسار الذي كان موجودًا في قالب HTML. علينا الآن أن نقوم بتعديل مسارات هذه الملفات كي يُصبح شكل قالب ووردبريس الجديد مُطابقًا لشكل قالب HTML. توفّر لنا ووردبريس عددًا من الدّوالّ (functions) مهمتها تسهيل إنشاء القوالب، سنستخدم منها get_template_directory_uri()، تقوم هذه الدالّة بإرجاع قيمة نصّيّة هي رابط القالب. مثلاً، إن كان مجلد القالب هو my_theme ورابط خادوم الويب الذي نعمل عليه هو http://localhost:8000 ستكون نتيجة الدالّة هي: http://localhost:8000/wp-content/my_theme يجب أن نستخدم هذه الدالّة أو ما يشابهها لنحصل على الرّوابط، ولا نستخدم روابط نضعها بشكل يدوي، فيُمكن لرابط خادوم الويب أن يتغير، وإن كانت الروابط موضوعةً بشكل يدوي فسيظهر شكل القالب كما في الأعلى خاليًّا من ملفات CSS و .JavaScript. نقوم بتعديل ملف index.html في قسم head، نبحث عن سطر استدعاء ملفات CSS، ثم نقوم بالتّعديل عليها لتصبح بالشّكل: في قسم head أيضًا نُريد استبدال رابط استدعاء ملف modernizr.js ليستخدم دالّة get_template_directory()، فيُصبح: <script src="<?php echo get_template_directory_uri(); ?>/assets/js/modernizr.js"></script> أخيرًا نبحث في نهاية ملف index.php عن سطر استدعاء ملف JavaScript، ونقوم بالتّعديل عليه ليُصبح كما يلي: <script src="<?php echo get_template_directory_uri(); ?>/assets/js/vendor.js"></script> الآن عندما يتم طلب المُدوّنة من المُتصفّح، ستكون النّتيجة النّهائية للتعديلات الثلاثة التي قمنها بها كالتالي: <link rel="stylesheet" href="http://localhost:8000/wp-content/themes/my_theme/assets/css/main.css"> <script src="http://localhost:8000/wp-content/themes/my_theme/assets/js/modernizr.js"></script> <script src="http://localhost:8000/wp-content/themes/my_theme/assets/js/vendor.js"></script> نذهب الآن إلى الصّفحة الرّئيسية للمُدوّنة لنرى كيف أصبح القالب. في آخر فقرة من هذا الدرس سنقوم بتعديل ترويسة الموقع التي نصّها الحالي هو: "عنوان الموقع"، والذي يظهر في الجُزء العُلويّ. نريد تغيير هذا النصّ ليصبح اسم المدونة التي قمنا بإدخالها أثناء مرحلة التّنصيب، يمكنك تغيير اسم المدونة من لوحة التحكم ثم الإعدادات. لنفتح صفحة index.php في المُحرّر، ثم نبحث عن السطر الذي يحوي: <h1 class="logo">عنون الموقع</h1> ,نقوم باستبداله بما يلي بعض إضافة وسم PHP له: <h1 class="logo"><?php bloginfo('name'); ?></h1> وسم: <?php php bloginfo('name'); ?> يقوم بإظهار/طباعة نصّ يتم جلبه من قاعدة البيانات، النصّ هو اسم المُدوّنة. نلاحظ أن الدّالّة bloginfo() تقوم بالإظهار وليست الإرجاع return كما تفعل دالّة ()get_template_directory_uri. الآن عندما نتوجّه إلى الصفحة الرئيسية للمدونة تكون الترويسة شبيهة بالصورة: فصل محتوى الملفات من قواعد البرمجة الهامة إلغاء التّرابط أو الفصل (decoupling)، فنحاول فصل العناصر المُترابطة قدر الإمكان ووضعها في ملفات مُنفصلة. مثلًا سنقوم لاحقًا بإنشاء صفحة خاصّة لعرض المواضيع المُنفردة، وستحتاج تلك الصّفحة إلى وجود الترويسة (header) والتذييل (footer) كي تحوي ملفات CSS و JavaScript وتكون متناغمة مع باقي القالب. توفّر لنا ووردبريس عدة طُرق لإلغاء التّرابط في الملفات، إمّا أن يتم استخدام التّسلسل الهرمي لملفّات القالب (theme hierarchy) أو يتم استخدام قِطَع القالب (template parts). التّسلسل الهرمي لملفّات القالب (أو ملفات القالب للاختصار) هي ملفات بأسماء ثابتة، تقوم ووردبريس باستخدام الملف المُتوفّر منها حسب الطلب الذي يأتيها من المتصفح؛ مثلاً طلبتَ من المدونة رابط مقالٍ معيّن، تقوم ووردبريس بتحليل الطّلب ومعرفة أن المطلوب هو مقال، فتبحث عن ملف باسم single.php، إن كان موجودًا يتمّ استخدامه، إن لم يكن موجودًا تبحث ووردبريس عن ملف index.php. وهكذا الأمر بالنسبة لجميع الطلبات. أما قِطع القالب، فهي ملفات يقوم المستخدم بإنشائها دون أن يلتزم باستخدام أسماء مُحدّدة كما في ملفات القالب. ثم يقوم المطوّر باستخدام هذه القِطع في أي مكان يريده ضمن القالب. يقوم المُطوّر مثلًا بإنشاء قطعة مُخصّصة لعرض أزرار المُشاركة، يقوم بتسمية الملف: sharing.php، ويضع فيه الوسوم التي يُريدها. ثم يقوم باستدعاء هذا الملف في الصّفحة الرّئيسيّة index.php وصفحة المقال المُنفرد single.php على النّحو التّالي: <?php get_template_part('sharing'); ?> هناك تفصيلات حول كيفية إنشاء قطع القالب والتّعامل معها، ما سنذكره هنا فقط لإعطاء فكرة عامة عنها وليس لشرحها بالتّفصيل. سنستخدم في هذا الدرس التّسلسل الهرمي فقط لسهولته وشهرته. إن كان الاسم غريبًا بالنّسبة إليك فستألفه بمُجرد أن تشرع في استخدامه. سنقوم بإنشاء 3 ملفّات في القالب هي: - header.php - footer.php - sidebar.php ومن ثم يمكننا استخدام هذه الملفّات في أي مكان ضمن القالب عن طريق الدّوال: - get_header() - get_footer() - get_sidebar() هذه الدّوال تعمل بشكل مُشابه لـ require التّي نعرفها في لغة PHP. بهذه الطّريقة لن نحتاج إلى القيام بالنّسخ واللصق في كل مرة نريد إدراج الترويسة في ملفات القالب، وفي حال أردنا تعديل شيء في الترويسة فلن نضطرّ إلى تطبيق التعديل في جميع ملفات القالب. الخطوة التّالية هي نقل المحتوى المُناسب لكل ملفّ من الملفّات السّابقة من ملف index.php إلى الملفّات الثّلاثة الجديدة التي أنشأناها. سنقوم حرفيًا بالنّسخ واللّصق من ملف index.php إلى الملفات الثّلاثة، ثم نقوم بوضع إحدى الدّوال الثلاثة في ملف index.php بدل النصّ الذي قمنا باقتصاصه. ملف الترويسة header.php نقوم باقتصاص مُحتوى ملف index.php من بدايته وحتى نهاية وسم </header> في السطر 57. نضع ما اقتصصناه في ملف header.php. ثم نضع مكان الاقتصاص في ملف index.php ما يلي: <?php get_header(); ?> فتصبح بداية ملف index.php مشابهةً لما يلي: <?php get_header(); ?> <div class="main-content"> <div class="row"> ملف التذييل footer.php نقوم الآن باقتصاص الجزء الخاص بالتذييل من ملف index.php، في السطر 71 بدءاً من وسم: <div class="site-footer"> وحتى نهاية الملف. نقوم بوضع المُحتوى في ملف footer.php، ثم نستبدل مكان الوسوم المقصوصة في ملف index.php بما يلي: <?php get_footer(); ?> ملف الشريط الجانبي sidebar.php الوسوم الخاصّة بالشريط الجانبي ما هي إلا أربعة أسطر، تبدأ من السطر 64، المحتوى هو: <div class="large-4 columns sidebar"> <div class="card"> Sidebar </div> </div> نقوم بنقل المحتوى السّابق من ملف index.php إلى ملف sidebar.php، ونضع مكانه في ملف index.php: <?php get_sidebar(); ?> بهذا ننتهي من المرحلة الأولى من إلغاء الترابط بين الوسوم، وضعنا كل جزءٍ منها في ملفٍّ خاصٍّ به، وكلما أردنا استخدام ذلك الجزء نقوم باستدعاء الدّالّة المناسبة لذلك. أصبح ملف index.php أقصر طولًا، ويؤدّي نفس الغرض الذي كان يؤدّيه قبل أن نقوم بالتّعديلات، محتوى الملف كالتالي: <?php get_header(); ?> <div class="main-content"> <div class="row"> <div class="large-8 right columns news"> <div class="row"> <div class="medium-12 columns"> <article class="card"> <header> <h1>Post title</h1> </header> <div class="date">4 كانون الأول 2014</div> <p>خلافاَ للاعتقاد <a href="#">السائد</a> فإن لوريم إيبسوم ليس نصاَ عشوائياً، بنذ العام 45 قبل الميلاد، مما يجعله أكثر من 2000 عام في القدم. قام البروفيسور "ريتشارد ماك لينتوك" (Richard McClintock) وهو بروفيسور اللغة اللاتينية في جامعة هامبدن-سيدني في فيرجينيا بالبحث عن أصول كلمة لاتينية غامضة في نص لوريم إيبسوم وهي "consectetur"، وخلال تتبعه لهذه الكلمة في الأدب اللاتيني اكتشف المصدر الغير قابل للشك. فلقد اتضح أن كلمات نص لوريم إيبسوم تأتي من الأقسام 1.10.32 و 1.10.33 من كتاب "حول أقاصي الخير والشر" (de Finibus Bonorum et Malorum) للمفكر شيشيرون (Cicero) والذي كتبه في عام 45 قبل الميلاد. هذا الكتاب هو بمثابة مقالة علمية مطولة في نظرية الأخلاق، وكان له شعبية كبيرة في عصر النهضة. السطر الأول من لوريم إيبسوم "Lorem ipsum dolor sit amet.." يأتي من سطر في القسم 1.20.32 من هذا الكتاب.</p> <div class="read-more text-left"> <a href="#">تابع قراءة المقال &raquo;</a> </div> <footer class="tags"> <span class="label">تقني</span> <span class="label">تنمية</span> <span class="label">منوعات</span> </footer> </article> </div> <div class="medium-12 columns"> <article class="card"> <header> <h1>Post title</h1> </header> <div class="date">4 كانون الأول 2014</div> <p>خلافاَ للاعتقاد <a href="#">السائد</a> فإن لوريم إيبسوم ليس نصاَ عشوائياً، بنذ العام 45 قبل الميلاد، مما يجعله أكثر من 2000 عام في القدم. قام البروفيسور "ريتشارد ماك لينتوك" (Richard McClintock) وهو بروفيسور اللغة اللاتينية في جامعة هامبدن-سيدني في فيرجينيا بالبحث عن أصول كلمة لاتينية غامضة في نص لوريم إيبسوم وهي "consectetur"، وخلال تتبعه لهذه الكلمة في الأدب اللاتيني اكتشف المصدر الغير قابل للشك. فلقد اتضح أن كلمات نص لوريم إيبسوم تأتي من الأقسام 1.10.32 و 1.10.33 من كتاب "حول أقاصي الخير والشر" (de Finibus Bonorum et Malorum) للمفكر شيشيرون (Cicero) والذي كتبه في عام 45 قبل الميلاد. هذا الكتاب هو بمثابة مقالة علمية مطولة في نظرية الأخلاق، وكان له شعبية كبيرة في عصر النهضة. السطر الأول من لوريم إيبسوم "Lorem ipsum dolor sit amet.." يأتي من سطر في القسم 1.20.32 من هذا الكتاب.</p> <div class="read-more text-left"> <a href="#">تابع قراءة المقال &raquo;</a> </div> <footer class="tags"> <span class="label">تقني</span> <span class="label">تنمية</span> </footer> </article> </div> </div> <div class="row"> <div class="medium-12 columns"> <ul class="pagination"> <li class="arrow unavailable"><a href="">&laquo;</a></li> <li class="current"><a href="">1</a></li> <li><a href="">2</a></li> <li><a href="">3</a></li> <li><a href="">4</a></li> <li class="unavailable"><a href="">&hellip;</a></li> <li><a href="">12</a></li> <li><a href="">13</a></li> <li class="arrow"><a href="">&raquo;</a></li> </ul> </div> </div> </div> <?php get_sidebar(); ?> </div> </div> <?php get_footer(); ?> جلب المقالات من قاعدة البيانات نحتاج الآن إلى عرض المقالات المحفوظة في قاعدة البيانات (والتي يتم التّحكم فيها من لوحة التّحكم الخاصّة بووردبريس)، سنقوم بعرضها في ملف index.php وفي دروس لاحقة سنستخدم نفس طريقة عرض المقالات في باقي ملفات القالب، سواءً في ملف single.php أو في archive.php أو في غيرها. ضمن ملف index.php نجد أن كل مقال مُحاط بالوسوم: <div class="medium-12 columns"> <article class="card"> كل مقال يجب أن يبدأ بهذه الوسوم، وينتهي بإغلاق هذه الوسوم. نلاحظ أن قالب HTML يحوي مقالين شكليين، سنحذف واحدًا منها ونكتفي بالآخر لنقوم باستخدامه كمعيار لعرض المقالات مثله. نحيط وسوم بداية المقال بالحلقة الرّئيسية التي تكلّمنا عنها سابقًا، فتُصبح الوسوم كما يلي: <?php while(have_posts()) { the_post(); ?> <div class="medium-12 columns"> <article class="card"> وبعد إغلاق الوُسوم التي بدأ المقال عندها نضع نهاية الحلقة (قوس إغلاق الحلقة) كما يلي: </article> </div> <?php } ?> إن قمنا بحفظ التّعديلات والتجربة، سنجد أن المقال الشّكليّ سيتكرّر بعدد المقالات الموجودة في قاعدة بيانات ووردبريس لدينا. لكن جميع المقالات ستكون بنفس الشّكل وبنفس المُحتوى. سنقوم الآن باستبدال المُحتوى الشّكليّ بالمُحتوى المُناسب من قاعدة البيانات حسب كل مقال. تقدم ووردبريس دالّة the_title لطباعة عُنوان المقال الحالي، سنستخدمها بدل عنوان المقال الشّكلي، فتصبح الوسوم كما يلي: <h1><?php the_title();?></h1> وبنفس الطريقة نستخدم دالّة the_date لعرض تاريخ نشر المقال كما يلي: <div class="date"><?php the_date();?></div> ثم لعرض محتوى المقال نستخدم دالّة the_content بدل وسم <p></p> الذي يحوي المحتوى الشكليّ، كما يلي: <p><?php the_content();?></p> يُمكن أن نقوم بحفظ التّعديلات على الملف ومشاهدة النّتيجة في المُتصفّح لنتأكد من أن التّعديلات التي قمنا بها تم تطبيقها وتعرض النّتيجة الصّحيحة. بقي لدينا تعديلان مُشابهان لما قمنا به، الأول لتعديل رابط “تابع قراءة المقال” والثاني لعرض الوسوم الخاصّة بالمقال. لتعديل رابط المقال نستخدم دالّة the_permalink التي وظيفتها عرض رابط الويب الخاصّ بالمقال، لكن لا تقوم الدّالّة سوى بطباعة الرّابط دون وضعه في وسم <a></a> كي يصبح قابلًا للنّقر والاستخدام. علينا إذًا وضع الرّابط في مكانه المُناسب كما يلي: <a href="<?php the_permalink();?>">تابع قراءة المقال »</a> قمنا باستبدال قيمة حقل href="#" بدالّة طباعة رابط المقال. الآن أصبح بإمكاننا مُشاهدة الصّفحة الرّئيسيّة للقالب التي تعرض المقالات ويمكن الضّغط على رابط “تابع قراءة المقال” للوصول إلى المقال بعينه ضمن صفحة مُستقلّة. بقي الآن أن نعرض الوسوم الخاصّة بكل مقال. مكان الوسوم هو داخل وسم: <footer class="tags"> </footer> سنقوم باستخدام دالّة wp_get_post_tags التي مهمتها إرجاع مصفوفة تحوي جميع الوسوم الخاصّة بالمقال على شكل كائنات (Objects) وليس مُجرّد نصّ عادي. وعليه فإنه سيكون بإمكاننا الوصول إلى اسم كل وسم عن طريق: tag->name$ كما يلي: <footer class="tags"> <?php $tags = wp_get_post_tags(get_the_ID()); foreach($tags as $tag) { echo '<span class="label"> ' . $tag->name . '</span> '; } </footer> يُمكننا الآن استعراض المقالات في الصّفحة الرّئيسية للمُدوّنة والوصول إلى كل مقال عن طريق الرّابط الخاص به ورؤية الوسوم الخاصّة بكل مقال أيضًا. سنتوقف هنا في هذا الدرس كي لا تكون المعلومات كثيفة يصعب استيعابها، وسنتابع في دروس أخرى استخدام أرقام الصّفحات في الأسفل بشكل صحيح. (يمكن الآن إضافة تعليق حول وسوم الصفحات كي لا تظهر بشكل خاطئ وبدون روابط صحيحة). خلاصة تعلّمنا في هذا الدّرس كيف نقوم بتجهيز قالب HTML ليُصبح قالبَ ووردبريس، وكيف نُعدّل مسارات ملفات JavaScript وCSS لتُصبح متناغمةً مع رابط الويب الخاص بقالب ووردبريس. وفي القسم الثاني من الدرس تعلّمنا كيف نقوم بفصل المحتوى عن ملف index.php إلى ملفّات فرعية أخرى، لنقوم بإعادة استخدامها في أي ملف ضمن القالب. وفي القسم الأخير رأينا كيف يمكن جلب المقالات من قاعدة البيانات وإظهارها للمُستخدم مع عرض الوسوم الخاصّة بكل مقال. أرجو أن يكون الدرس سهلاً بسيطاً، في حال وجود أي استفسار أو مداخلة نرحّب به في التعليقات.
  14. مدخل إلى html5

    كلنا نعرف نماذج الويب، أليس كذلك؟ أنشِئ <form>، وبعض عناصر <input type="text"‎>، وربما عنصر <input type="password"‎> وإنهِ النموذج بزر <input type="submit"‎>. معلوماتك السابقة منقوصة، إذ تُعرِّف HTML5 ثلاثة عشر نوع إدخالٍ جديدٍ التي تستطيع استعمالها في نماذجك. لاحظ أنني ذكرتُ كلمة "تستطيع" بصيغة المضارع، أي أنَّك تستطيع استخدامها الآن دون أيّة أمور التفافية أو إضافات مُخصَّصة. لكن لا تتحمس كثيرًا؛ فلم أكن أقصد أنَّ جميع تلك الميزات الرائعة مدعومة في كل متصفح؛ ما أقصده هو أنَّ تلك النماذج ستعمل بشكلٍ رائع في المتصفحات الحديثة، لكنها ستبقى تعمل في المتصفحات القديمة (بما في ذلك IE6) وإن لم يكن سلوكها مماثلًا لسلوكها في المتصفحات الحديثة. النص البديل placeholder IE Fiefox Safari Chrome Opera iPhone Android 10+ 3.7+ 4.0+ 4.0+ 11.0+ 4.0+ 2.3+ أول تحسين لنماذج الويب أتت به HTML5 هو القدرة على ضبط نص بديل (placeholder text) في حقل الإدخال. "النص البديل" هو النص الذي سيُعرَض داخل حقل الإدخال لطالما كان حقل الإدخال فارغًا، وسيختفي النص البديل بعد بدء الكتابة في الحقل. من المرجَّح أنَّك شاهدت نصًا بديلًا من قبل، فمتصفح Firefox فيه نصٌ بديلٌ في شريط العنوان مكتوبٌ فيه "Search Bookmarks and History" (النص البديل في الإصدارات الحديثة هو "Search" فقط): وعندما تهمُّ بكتابة أيّ شيءٍ فيه، فسيختفي النص البديل: هذه آلية تضمين النص البديل في نماذج الويب الخاصة بك: <form> <input name="q" placeholder="Search Bookmarks and History"> <input type="submit" value="Search"> </form> ستتجاهل المتصفحاتُ التي لا تدعم النص البديل الخاصيةَ placeholder ببساطة. انظر إلى الجدول أعلاه لمعرفة ما هي المتصفحات التي تدعم النص البديل. س: هل يمكنني وضع وسوم HTML في خاصية placeholder؟ أريد أن أضيف صورةً أو أن أغيِّرَ الألوان. ج: لا يمكن أن تحتوي خاصية placeholder إلا على نصٍ بسيط، ولا يُسمَح بوضع وسوم HTML فيها. لكن هنالك إضافات CSS تسمح لك بتنسيق النص البديل في بعض المتصفحات. التركيز التلقائي على الحقول autofocus IE Fiefox Safari Chrome Opera iPhone Android 10+ 4.0+ 5.0+ 5.0+ 10.1+ . 3.0+ يمكن لمواقع الويب استخدام JavaScript للتركيز على حقلٍ للإدخال في نموذج الويب تلقائيًا. على سبيل المثال، الصفحة الرئيسية لمحرك البحث Google ستُركِّز تلقائيًا (auto-focus) على حقل البحث لكي تستطيع البدء في كتابة عبارة البحث مباشرةً؛ وعلى الرغم من أنَّ هذا الأمر ملائمٌ للكثيرين، لكنه قد يُزعِج المستخدمين المتقدمين أو أولي الاحتياجات الخاصة؛ فلو ضغطتَ على زر المسافة (space) متوقعًا أن تُمرَّر الصفحة إلى الأسفل، فستفاجأ بعدم التمرير، لأنَّ مؤشر الكتابة موجودٌ في حقلٍ من حقول النموذج (سيُكتَب فراغ في ذاك الحقل بدلًا من التمرير). وإن ضغطت على حقل إدخالٍ مختلف أثناء تحميل الصفحة وبدأت الكتابة فيه، فسيأتي سكربت التركيز التلقائي (بعد اكتمال تحميل الصفحة) و"يُساعِدُك" وينقل التركيز إلى حقل الإدخال الأصلي، مما يجعلك تكتب في حقلٍ مختلف، ويقطع عليك «سلسلة أفكارك». ولمّا كان التركيز التلقائي يُنفَّذ عبر JavaScript، فمن الصعب التعامل مع كل الحالات الغريبة؛ وليس هنالك منقذٌ لِمَن يريدون تعطيل ميزة التركيز التلقائي. لحل هذه المشكلة، وفَّرَت HTML5 خاصية autofocus لجميع عناصر التحكم في نماذج الويب. عمل الخاصية autofocus واضحٌ من اسمها: نقل التركيز إلى حقل إدخال مُعيّن في أقرب فرصة ممكنة عند تحميل الصفحة. ولكن لمّا كانت هذه الخاصية موجودة في HTML وليست مُطبَّقة عبر JavaScript، فسيكون سلوكها متماثلًا في جميع مواقع الويب وعلى جميع المتصفحات. وسيتمكن مطورو المتصفحات (أو مطورو الإضافات) من منح المستخدمين إمكانية تعطيل التركيز التلقائي تمامًا. هذا مثالٌ عن كيفية التركيز التلقائي عبر الخاصية autofocus: <form> <input name="q" autofocus> <input type="submit" value="Search"> </form> المتصفحات التي لا تدعم الخاصية autofocus ستتجاهلها تمامًا. انظر إلى الجدول أعلاه لتعرف ما هي المتصفحات التي تدعم خاصية autofocus. هل تريد أن تعمل ميزة التركيز التلقائي في جميع المتصفحات، وليس تلك التي تدعم HTML5 فقط؟ يمكنك الاستمرار في استخدام سكربت التركيز التلقائي، لكن عليك إجراء تعديلين بسيطين: أضف الخاصية autofocus إلى شيفرة HTML اكتشف إذا كان المتصفح يدعم الخاصية autofocus، وشغِّل سكربت التركيز التلقائي إن لم يكن يدعم المتصفح الخاصية autofocus داخليًا. <form name="f"> <input id="q" autofocus> <script> if (!("autofocus" in document.createElement("input"))) { document.getElementById("q").focus(); } </script> <input type="submit" value="Go"> </form> ضبط التركيز التلقائي في أقرب فرصة ممكنة تنتظر العديد من صفحات الويب إلى أن يقع الحدث window.onload لكي تضبط التركيز؛ لكن الحدث window.onload لا يُفعَّل إلا بعد تحميل جميع الصور؛ وإذا حوَت صفحتك على الكثير من الصور، فمن المحتمل أن السكربت الذي ستستخدمه سيؤدي إلى إعادة التركيز على حقل الإدخال المُعيّن بعد أن يبدأ المستخدم تفاعله مع قسمٍ آخر في صفحتك. هذا هو سبب كره المستخدمين المتقدمين لسكربتات التركيز التلقائي. وضعنا سكربت التركيز التلقائي في المثال الموجود في القسم السابق بعد حقل الإدخال مباشرةً؛ وهذا حلٌ مثاليٌ لمشكلتنا، لكن قد ترى أنَّ وضع شيفرة JavaScript في منتصف الصفحة هو أمرٌ سيئ (أو قد لا يسمح لك السند الخلفي [back-end] بذلك)؛ فإن لم تستطع وضع السكربت في منتصف الصفحة، فعليك أن تضبط التركيز أثناء وقوع حدث مخصص مثل ‎$(document).ready()‎ في jQuery بدلًا من window.onload. <head> <script src=jquery.min.js></script> <script> $(document).ready(function() { if (!("autofocus" in document.createElement("input"))) { $("#q").focus(); } }); </script> </head> <body> <form name="f"> <input id="q" autofocus> <input type="submit" value="Go"> </form> تُفعِّل مكتبة jQuery الحدث الخاص ready في أقرب فرصة تتوفر فيها شجرة DOM للصفحة، أي أنها تنتظر إلى أن ينتهي تحميل النص في الصفحة، لكنها لن تنتظر تحميل جميع الصور فيها. لكن هذا ليس حلًا مثاليًا، فإن كانت الصفحة كبيرةً جدًا أو كان الاتصال بطيئًا للغاية، فقد يبدأ المستخدم تفاعله مع الصفحة قبل تنفيذ سكربت التركيز التلقائي؛ إلا أنَّ هذا الحل أفضل بكثير من انتظار وقوع الحدث window.onload. إذا كنت قادرًا على إضافة تعبير JavaScript وحيد في شيفرة صفحتك، فهنالك حلٌ وسطٌ بين الأمرين. يمكنك استخدام الأحداث الخاصة في jQuery لتعريف حدث خاص بك، ولنقل أنَّ اسمه هو autofocus_ready. ثم تستطيع تفعيل هذا الحدث يدويًا بعد أن تُتاح خاصية autofocus مباشرةً. <head> <script src=jquery.min.js></script> <script> $(document).bind('autofocus_ready', function() { if (!("autofocus" in document.createElement("input"))) { $("#q").focus(); } }); </script> </head> <body> <form name="f"> <input id="q" autofocus> <script>$(document).trigger('autofocus_ready');</script> <input type="submit" value="Go"> </form> هذا الحل مثاليٌ مثل الحل الأول، حيث يضبط التركيز إلى الحقل المُحدَّد في أقرب فرصة ممكنة، وذلك أثناء تحميل بقية نص الصفحة. لكنه ينقل تنفيذ التسلسل المنطقي لتطبيقك (التركيز على حقل الإدخال) من جسم الصفحة إلى ترويستها. يعتمد المثال السابق على مكتبة jQuery، لكن مفهوم الأحداث الخاصة ليس مقتصرًا على jQuery فحسب، فلدى مكتبات JavaScript الأخرى مثل YUI و Dojo إمكانياتٌ مشابهة. الخلاصة هي: من المهم ضبط التركيز التلقائي ضبطًا سليمًا. يُفضَّل أن تدع المتصفح يضبط التركيز التلقائي عبر خاصية autofocus في حقل الإدخال الذي تريد التركيز تلقائيًا عليه إذا كان ذلك ممكنًا. إذا كنتَ تريد حلًا للمتصفحات القديمة، فاكتشف أولًا دعم المتصفح للخاصية autofocus لكي تتأكد أنَّ السكربت الذي كتبتَه سيُنفَّذ على المتصفحات القديمة فقط. حاول ضبط التركيز التلقائي في أقرب فرصة ممكنة، حاول مثلًا أن تضع سكربت التركيز في شيفرة HTML بعد حقل الإدخال مباشرةً. فإن لم تستطع، فاستعمل مكتبة JavaScript تدعم الأحداث المُخصَّصة، وفعِّل الحدث المُخصَّص مباشرةً بعد شيفرة النموذج؛ وإن لم يكن ذلك ممكنًا، فاعتمد على حدثٍ مثل الحدث ‎$(document).ready()‎ في مكتبة jQuery. لا تنتظر الحدث window.onload لكي يضبط التركيز تحت أيّ ظرفٍ كان. عناوين البريد الإلكتروني لفترةٍ تجاوزت العقد من الزمن، احتوت نماذج الويب على عدِّد قليلٍ من الحقول، وكان أكثرها شيوعًا: نوع الحقل شيفرة HTML ملاحظات مربع تأشير (checkbox) "input type="checkbox يمكن تفعيله أو تعطيله زر انتقاء (radio button) "input type="radio يمكن تجميعه مع حقول أخرى حقل كلمة مرور input type="password"‎ يُظهِر نقطًا بدلًا من المحارف التي تكتبها قائمة منسدلة <select> </select> - مُنتقي الملفات "input type="file يُظهِر مربع حوار "اختيار ملف" زر الإرسال "input type="submit - نص عادي ‎input type="text"‎‎ يُمكن حذف الخاصية type تعمل جميع أنواع الحقول السابقة في HTML5، فلو أردت "التحديث إلى HTML5" (ربما بتغيير نوع المستند DOCTYPE)، فلن تحتاج إلى إجراء أيّة تعديلات على نماذج الويب عندك، والفضل بذلك يعود إلى توافقية HTML5 مع الإصدارات التي تسبقها. لكن HTML5 أضافت ثلاثة عشر نوعًا جديدًا من الحقول، ولأسبابٍ ستتضح لك بعد قليل، لا يوجد سببٌ يمنعك من استعمالها الآن. أول أنواع المدخلات الجديدة مخصصٌ لعناوين البريد، ويبدو كما يلي: <form> <input type="email"> <input type="submit" value="Go"> </form> أوشكتُ على كتابة جملةٍ مطلعها: "أما في المتصفحات التي لا تدعم type="email"‎..." لكنني تداركتُ نفسي وتوقفت. لكن ما السبب؟ لأنني لستُ متأكدًا من ماذا يعني عدم دعم المتصفح للحقل type="email"‎، حيث «تدعم» جميع المتصفحات type="email"‎‎، لكنها قد لا تفعل شيئًا خاصًا لها (سترى بعد قليل بعض الأمثلة عن المعاملة الخاصة لهذا الحقل)؛ لكن المتصفحات التي لا تتعرف على type="email"‎ ستعامله على أنَّه type="text"‎ وسيظهر كحقلٍ نصيٍ عاديٍ. لا تسعني الكلمات للتعبير عن مدى أهمية ما سبق، لأنَّ في الويب ملايين النماذج التي تسألك أن تدخِل عنوان بريدك الإلكتروني، وجميعها تستعمل <input type="text"‎> وستشاهد مربعًا نصيًا، ثم تُدخِل فيه عنوان بريدك، وانتهى. ثم أتت HTML5 التي أضافت type="email"‎، فهل سترتبك المتصفحات؟ لا، يُعامِل كل متصفح على وجه هذا الكوكب القِيم غير المعروفة لخاصية type على أنها type="text"‎، بما في ذلك IE 6. لذلك تستطيع استعمال type="email"‎ حالًا دون القلق حول دعم المتصفحات. لكن ماذا يعني أنَّ المتصفح "يدعم" الحقل type="email"‎؟ حسنًا، قد يعني هذا عدِّة أشياء. لم تُحدِّد مواصفة HTML5 أيّة توصية حول الواجهة الرسومية التي تظهر للمستخدم لأنواع المدخلات الجديدة. ستعرضه أغلبية متصفحات سطر المكتب مثل Safari و Chrome و Opera و Firefox كحقلٍ نصيّ؛ ولهذا لن يُلاحِظ مستخدمو موقعك الفرق (إلى أن يُحاولوا إرسال النموذج). ثم ها قد أتت الهواتف المحمولة… ليس للهواتف المحمولة لوحة مفاتيح فيزيائية، فكل "الكتابة" تتم بالضغط على لوحة مفاتيح ظاهرة على الشاشة التي تُعرَض عند الحاجة لها (عند التركيز على حقل من حقول أحد النماذج في صفحة الويب على سبيل المثال). تتعرف متصفحات الهواتف المحمولة الذكية على العديد من أنواع المدخلات الجديدة في HTML5، وتُجري تعديلات ديناميكية على لوحة المفاتيح الظاهرة على الشاشة لكي تُلائم نوع المدخلات. مثلًا، عناوين البريد الإلكتروني هي نصوص، صحيح؟ بالطبع، لكنها نوع خاص من النصوص، فمثلًا يحتوي كل عنوان بريد إلكتروني (نظريًا) على رمز @ وعلى نقطة (.) واحدة على الأقل؛ ومن غير المحتمل أن يحتوي على فراغات؛ لذا لو كنتَ تستعمل هاتف iPhone وحاولت الكتابة في عنصر <input type="email‎"‎>، فستظهر لوحة مفاتيح تحتوي على زر مسافة أصغر من المعتاد، بالإضافة إلى أزرار مخصصة لمحرفَي @ و . . لا توجد سلبيات لتحويل جميع الحقول التي تُمثِّل عناوين البريد الإلكتروني إلى type="email"‎ في الحال. فلن يلاحظ ذلك أحدٌ إلا مستخدمي الهواتف المحمولة، الذين أظن أنَّهم لن ينتبهوا لذلك أيضًا. لكن من سيلاحظ ذلك سيبتسم بصمت ويشكرك في قلبه لأنَّك سهلت عليه الأمر قليلًا. عناوين الويب عناوين مواقع الويب -التي يُسمّيها مهووسو المعايير "URLs"، إلا بعض المتحذلقين الذين يدعونها "URIs"- هي نوعٌ آخرٌ من النص المُخصَص؛ البنية العامة لعناوين الويب مرتبطة بمعايير الإنترنت ذات الصلة. إذا طلب منك أحدهم كتابة عنوان لموقع ويب في نموذج، فسيتوقعون منك كتابة شيءٍ مثل "http://www.google.com/‎" وليس "125 Farwood Road"؛ ومن الشائع استخدام الخط المائل / في العناوين (الخط المائل مذكورٌ ثلاث مرات في عنوان صفحة Google الرئيسية)؛ وينتشر استخدام النقط . أيضًا، لكن يُمنَع وضع فراغات في العنوان. لدى جميع عناوين الويب لاحقة للنطاق مثل "‎.com" أو "‎.org". تعرض أغلبية متصفحات الويب لسطح المكتب الحديثة حقل type="url"‎ كحقلٍ نصيّ عادي، لذلك لن يُلاحظ مستخدمو موقعك ذلك إلى أن يحاولوا أن يُرسِلوا النموذج. ستُعامِل المتصفحات التي لا تدعم HTML5 الحقل type="url"‎ كحقل type="text"‎ تمامًا، فلا بأس من استعمال هذا الحقل في صفحات الويب الحديثة عند الحاجة. ستُعدِّل الهواتف الذكية من طريقة عرض لوحة المفاتيح كما في حقل عنوان البريد الإلكتروني، لكن لوحة المفاتيح في هذه المرة ستُخصَّص لتسهيل إدخال عناوين الويب. ففي هواتف iPhone سيُزال زر المسافة تمامًا وسيُستعاض عنه بنقطة وخط مائل وزر "‎.com" (يمكنك الضغط مطولًا على زر "‎.com" للاختيار بين اللاحقات الشهيرة الأخرى مثل "‎.org" أو "‎.net")؛ وستُخصِّص هواتف أندرويد لوحة مفاتيحها بشكلٍ مشابه. إدخال الأرقام طلب إدخال الأرقام أصعب من طلب كتابة عنوان بريد إلكتروني أو موقع ويب؛ لأنَّ الأرقام معقدة أكثر مما تظن. اختر رقما ما بسرعة. -1؟ لا، كنت أقصد رقمًا بين 1 و 10. ‎7½‎؟ لا، ليس رقمًا كسريًا. π؟ لماذا تختار الأرقام العجيبة؟! الفكرة التي أود إيصالها هي أنَّك لا تسأل عن "رقمٍ ما"، فمن المحتمل أنَّك ستطلب من المستخدم إدخال رقم في مجال معيّن، ولا تريد إلا نوعًا محدَّدًا من الأرقام ضمن ذلك المجال، وقد تريد استبعاد الأعداد الكسرية أو العشرية، أو أن تسمح بإدخال الأرقام التي تقبل القسمة على 10. ستسمح لك HTML5 بكل هذا. <input type="number" min="0" max="10" step="2" value="6"> لنتحدث عن الخاصيات السابقة كلًا على حدة (يمكنك المتابعة مع المثال الحي إن شئت). الخاصية type="number"‎ تعني أنَّ الحقل يقبل الأرقام. الخاصية min="0"‎ تُحدِّد القيمة الدنيا المقبولة لهذا الحقل. الخاصية max="10"‎ تُحدِّد القيمة القصوى المقبولة. الخاصية step="2"‎ مجتمعةً مع قيمة الخاصية min ستُعرِّف ما هي الأرقام المسموحة في المجال: 0 و 2 و 4 وهكذا إلى أن تصل إلى قيمة الخاصية max. الخاصية value="6"‎ تُحدِّد القيمة الافتراضية. يُفترَض أن تكون هذه الخاصية مألوفةً لديك، فهي نفس الخاصية التي تستعملها دومًا لتحديد قيم حقول النموذج (ذكرتُ هذه النقطة هنا لكي أذكِّرك أنَّ HTML5 مبنية على إصدارات HTML السابقة، فلا حاجة أن تعيد تعلم كل الأمور التي تعرفها من قبل!). هذه هي الشيفرة الخاصة بحقل الأرقام. ابقِ في ذهنك أنَّ جميع الخاصيات السابقة اختيارية. فإذا كانت لديك قيمة دنيا للمجال المقبول لكن دون وجود حد أقصى للأرقام، فيمكنك ضبط خاصية min وعدم ضبط الخاصية max. الخطوة الافتراضية هي 1، ويمكنك عدم ذكر الخاصية step إلا إذا كانت قيمة الخطوة عندك مختلفةً عن 1. تستطيع إسناد سلسلة نصية فارغة إلى الخاصية value إن لم تكن هنالك قيمةٌ افتراضيةٌ، أو بإمكانك حذف الخاصية تمامًا. لكن HTML5 لا تقف عند هذا الحد، حيث توفِّر لك دوال JavaScript للتحكم بهذا الحقل: الدالة (input.stepUp(n تزيد قيمة الحقل بمقدار n. الدالة (input.stepDown(n تنقص من قيمة الحقل مقدار n. الخاصية input.valueAsNumber تُعيد القيمة الحالية للحقل كعدد ذي فاصلةٍ عشرية (تذكَّر أنَّ الخاصية input.value تُعيد سلسلةً نصيةً دومًا). هل صَعُبَ عليك تخيل شكل هذا الحقل؟ حسنًا، طريقة عرض هذا الحقل عائدة تمامًا لمتصفحك، ويدعم مختلف مُصنِّعي المتصفحات هذا الحقل بطرائق مختلفة. فعلى هواتف iPhone -التي يصعب فيها كتابة المدخلات عمومًا- سيُحسِّن المتصفح من لوحة المفاتيح مرةً أخرى لكتابة الأرقام. أما في نسخة سطح المكتب من متصفح Opera، سيظهر نفس الحقل type="number"‎ كعنصر "spinbox" الذي يملك أسهمًا صغيرةً للأعلى والأسفل تستطيع الضغط عليها لتغيير القيمة. تحترم المتصفحات قيم الخاصيات min و max و step، لذلك ستكون قيمة ذاك الحقل مقبولة دومًا، فلو وصلت إلى القيمة القصوى، فسيُعطَّل زر السهم العلوي ولن تستطيع زيادة الرقم الموجود. وكما في بقية حقول الإدخال التي شرحناها سابقًا في هذا الدرس، المتصفحات التي لا تدعم type="number"‎ ستُعامِل الحقل وكأنَّه type="text"‎، وستظهر القيمة الافتراضية في الحقل النصي (لأنها مُخزَّنة في الخاصية value)، لكن سيتجاهل المتصفح الخاصيات الأخرى مثل min و max؛ لكنك تستطيع إنشاء spinbox بنفسك، أو قد تستعمل مكتبة JavaScript تحتوي على هذا العنصر؛ لكن تذكَّر أن تتحقق من دعم المتصفح لهذا الحقل أولًا، على سبيل المثال: if (!Modernizr.inputtypes.number) { //لا يوجد دعم لحقول type=number //ربما تجرِّب Dojo أو مكتبة JavaScript أخرى } تحديد الأرقام عبر المزلاج هنالك آليةٌ أخرى لتمثيل المدخلات الرقمية، فمن المحتمل أنَّك رأيك "مزلاجًا" (slider) من قبل يُشبِه: يمكنك وضع المزلاج في نماذج HTML5 أيضًا، والشيفرة الخاصة به شبيهة جدًا بحقل spinbox: <input type="range" min="0" max="10" step="2" value="6"> جميع الخاصيات المتوفرة مماثلة لحقل type="number"‎ (أي min و max و step و value)، ولها نفس المعنى. الفرق الوحيد هو في واجهة الاستخدام؛ فبدلًا من وجود حقل لكتابة الرقم، ستعرض المتصفحات الحديثة الحقل type="range"‎ كمزلاج، بينما ستعرضه المتصفحات القديمة التي لا تدعم HTML5 كحقل type="text"‎، لذا لا يوجد سبب يمنعك من البدء باستخدامه مباشرةً. منتقي التاريخ Date picker لم تتضمن HTML 4 حقلًا لاختيار التاريخ، لكن مكتبات JavaScript تداركت الأمر (Dojo و jQuery UI و YUI و Closure Library) لكن هذه الحلول كانت تتطلب الخوض في مكتبة JavaScript التي تدعم حقل مُنتقي التاريخ (date picker). أضافت HTML5 أخيرًا حقلًا لانتقاء التاريخ دون الحاجة إلى كتابته يدويًا عبر JavaScript؛ وفي الواقع، أضفات ستة حقول: واحد للتاريخ (date) وآخر للشهر (month) وآخر للأسبوع (week) وآخر للوقت (time) وآخر للتاريخ والوقت (date + time) وآخر للتاريخ والوقت لكن دون ذكر المنطقة الزمنية (date + time – timezone). لكن للأسف، هذا الحقل غير مدعوم من أغلبية المتصفحات. إذ يدعمه متصفح Opera منذ الإصدار التاسع و Chrome من الإصدار 20. هذه هي طريقة عرض متصفح Opera لحقل <input type="date"‎>: وإذا أردت من المستخدم انتقاء الوقت والتاريخ، فهنالك حقل <input type="datetime"‎>: أما لو كنت تحتاج إلى الشهر والسنة فقط (ربما تريد إدخال تاريخ انتهاء البطاقة الائتمانية)، فهنالك حقل <input type="month"‎>: ويتوفر أيضًا حقلٌ لانتقاء أسبوع معيّن في السنة –وإن لم يكن ذلك شائعًا– عبر الحقل <input type="week"‎>: أخيرًا وليس آخرًا، يمكنك اختيار الوقت عبر الحقل <input type="time"‎>: من المحتمل أن تدعم المتصفحات حقول الإدخال السابقة تباعًا، لكن كما في حقل type="email"‎ وغيره، ستُعرَض هذه الحقول كحقولٍ نصيةٍ بسيطةٍ في المتصفحات التي لا تدعم الحقل type="date"‎ وأخوته. يمكنك ببساطة أن تستعمل الحقل <input type="date"‎> وأخوته لتوفِّر مُنتقي التاريخ لمستخدمي متصفحَي Opera و Chrome وتنتظر دعم بقية المتصفحات. أو أن تعتمد حلًا عمليًا هو استعمال <input type="date"‎> ثم تكتشف إن كان المتصفح يدعم مُنتقي التاريخ، ثم تستعمل حلًا برمجيًا إن لم يكن يدعمه (مثل Dojo و jQuery UI و YUI و Closure Library أو حلًا آخر). <form> <input type="date"> </form> ... <script> var i = document.createElement("input"); i.setAttribute("type", "date"); if (i.type == "text") { //لا يوجد دعم لمنتقي التاريخ :-( //استخدام مكتبة Dojo/jQueryUI/YUI/Closure لإنشاء واحد //ثم استبدل حقل <input> ديناميكيًا } </script> حقول البحث حسنًا، وظيفة هذا الحقل واضحة من اسمه، لكن قد نحتاج إلى شرح آلية تطبيقه في المتصفحات. البحث لا يكون فقط في محركات البحث مثل Google أو Yahoo!‎، فمن الممكن أن يكون حقل البحث في أيّ صفحة وفي أيّ موقع؛ فهنالك واحدٌ في موقع أمازون، وآخر في موقع CNN، ويتواجد أيضًا في أغلبية المدونات. لكن ما هو الوسم المستخدم لتلك الحقول؟ <input type="text"‎>، مثل بقية حقول النص الموجودة في الويب. لنحاول تصحيح الأمر… <form> <input name="q" type="search"> <input type="submit" value="Find"> </form> ما رأيك بتجربة حقل <input type="search"‎> في متصفحك. قد لا تلاحظ أيّة اختلافات بينه وبين الحقل النصي العادي؛ لكن إن كنتَ تستعمل Safari على نظام Mac OS X، فسيبدو الحقل كما يلي: هل لاحظت الفرق؟ لدى حقل البحث زوايا مدورة! أعلم أنَّك لا تستطيع احتواء نفسك من الفرح، لكن انتظر، فهنالك المزيد! عندما تبدأ الكتابة في حقل type="search"‎، فسيضع المتصفح زر "x" صغير في الجانب الأيمن من الحقل؛ ويؤدي الضغط عليه إلى حذف محتويات الحقل (متصفح Chrome، الذي يتشارك مع Safari في البنية الداخلية، له نفس السلوك السابق). الغرض من التعديلات البسيطة السابقة هي إعطاء حقول البحث شكلًا وسلوكًا شبيهًا بحقول البحث في تطبيقات Mac OS X المكتبية مثل iTunes. يستعمل موقع Apple.com الحقل <input type="search"‎> لحقل البحث في الموقع لإعطائه شكلًا مألوفًا لمستخدمي Mac، لكن ذلك ليس خاصًا بنظام Mac فقط؛ إذ أنَّه شيفرة HTML فحسب، وبهذا يستطيع كل متصفح على كل منصة (أو نظام تشغيل) أن يعرض الحقل بشكلٍ مشابه لعناصر الواجهة الرسومية الخاصة بالمنصة. وكما هو الحال في بقية أنواع الحقول، المتصفحات التي لا تتعرف على حقل type="search"‎ ستعامِله كأنَّه type="text"‎؛ فلا يوجد سببٌ يمنعك من استخدام type="search"‎ في حقول البحث حالًا. منتقي الألوان تُعرِّف HTML5 حقل <input type="color"‎> الذي يسمح لك باختيار لونٍ ما ويُعيد التمثيل الست عشري للون المُختار؛ تأخرت المتصفحات في دعم هذا الحقل، حيث يدعمه Opera منذ الإصدار 17، و Firefox منذ الإصدار 29، و Chrome منذ الإصدار 20، وما زلنا في انتظار دعم بقية المتصفحات له. يندمج هذا الحقل جيدًا مع منتقي الألوان الموجود في نظامَيّ ويندوز و Mac، أما في لينُكس فهو يعرض مُنتقي ألوان أساسي. وهو يُعيد قيمة ست عشرية للون RGB الذي يمكن استخدامه في أي مكان يقبل ألوان CSS (جرِّب مُنتقي الألوان في متصفحك). التحقق من صحة مدخلات المستخدم IE Fiefox Safari Chrome Opera iPhone Android 10+ 4.0+ 5.0+ 10.0+ 9.0+ 4.1+ 4.0+ تحدثت في هذا الفصل عن عددٍ من حقول الإدخال الجديدة وبعض الميزات المحدثة مثل التركيز التلقائي لحقلٍ من حقول النموذج، لكنني لم أذكر ما أعتبره أهم جزء من النماذج الحديثة في HTML5: التحقق التلقائي من صحة مدخلات المستخدم. خذ على سبيل المثال مشكلةً شائعةً هي إدخال عنوان بريد إلكتروني في نموذج ويب؛ ربما ستجري تحققًا من مدخلات المستخدم من طرف العميل عبر JavaScript، متبوعًا بتحققٍ من جهة الخادوم عبر PHP أو Python أو أيًّا كانت لغة البرمجة التي تستعملها. لن يُشكِّل التحقق من مدخلات المستخدم عبر HTML5 بديلًا عن التحقق من جهة الخادوم، لكن من المحتمل أن تشكِّل بديلًا عن سكربتات JavaScript التي تستعملها. هنالك مشكلتين كبيرتين في التحقق من البريد الإلكتروني عبر JavaScript: عددٌ كبيرٌ جدًا من زوار موقعك (حوالي 10% تقريبًا) يُعطِّلون JavaScript في متصفحهم ستفشل في التحقق من صحة البريد الإلكتروني أنا آسف لقول هذا، لكنك ستفشل في ذلك. فعملية تحديد فيما إذا كانت سلسلة نصية ما هي عنوان بريد إلكتروني معقدةٌ بشكلٍ لا يُصدَّق. فكلما أمعنت النظر في الأمر، لوجدت مدى تعقيده. هل ذكرتُ لتوي أنَّ الأمر معقدٌ جدًا جدًا؟ أليس من الأسهل إلقاء ذاك الحِمل والصداع الناتج عنه على عاتق المتصفح؟ لقطة الشاشة السابقة مأخوذة من متصفح Opera 10، إلا أنَّ إمكانية التحقق من حقول النماذج متوفرة منذ الإصدار 9. ولدى Firefox 4 و Chrome 10 آليةٌ مشابهة. كل ما عليك فعله هو ضبط الخاصية type إلى "email". وعندما يحاول المستخدم إرسال (submit) نموذج فيه حقل <input type="email"‎>، فسيتحقق المتصفح تلقائيًا من عنوان البريد الإلكتروني حتى لو كانت JavaScript معطَّلةً في المتصفح. توفِّر HTML5 أيضًا تحققًا من عناوين الويب المُدخَلة في حقول <input type="url"‎>، والأرقام المدخلة في حقول <input type="number"‎>؛ ستؤخذ قيم الخاصية min و max بعين الاعتبار عند التحقق من الأرقام، فلن تسمح لك المتصفحات بإرسال النموذج إذا أدخلتَ رقمًا كبيرًا أكبر من الحد الأقصى. لا حاجة إلى وضع شيفرات لتفعيل التحقق من المدخلات في HTML5؛ حيث تكون مفعَّلة افتراضيًا، إلا أنَّك تستطيع تعطيلها عبر وضع الخاصية novalidate. <form novalidate> <input type="email" id="addr"> <input type="submit" value="Subscribe"> </form> الحقول المطلوبة required IE Fiefox Safari Chrome Opera iPhone Android 10+ 4.0+ 5.0+ 10.0+ 9.0+ 4.1+ 4.0+ التحقق من مدخلات المستخدم في HTML5 ليس محدودًا بنوع الحقل، إذ تستطيع أيضًا أن تُشير إلى أنَّ بعض الحقول "مطلوبةٌ"؛ يجب تحديد قيم للحقول المطلوبة قبل أن ترسِل النموذج. شيفرة الحقول المطلوبة بسيطةٌ جدًا: <form> <input id="q" required> <input type="submit" value="Search"> </form> يمكنك تجربة حقل <input required> في متصفحك. قد تُغيّر بعض المتصفحات الشكل الافتراضي للحقول المطلوبة. وإذا حاول المستخدم إرسال النموذج دون تعبئة الحقل المطلوب، فسيظهر إشعار يخبر المستخدم أنَّ من الضروري إدخال قيمة في الحقل وعدم تركه فارغًا (الصورة الآتية من متصفح Chrome): ترجمة -وبتصرّف- للفصل "HTML5 Forms" من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  15. مدخل إلى html5

    كانت البرمجيات المكتبية تتفوق على تطبيقات الويب بإمكانية تخزين المعلومات محليًا تخزينًا دائمًا؛ حيث يوفِّر نظام التشغيل عادةً طبقةً وسيطةً لتخزين وقراءة بيانات خاصة بالتطبيق مثل الإعدادات وحالة التشغيل، وقد تُخزَّن هذه القيم في سجل النظام (registry) أو ملفات ini أو ملفات XML أو في مكانٍ آخر وفقًا للتقاليد المُتبَعة في نظام التشغيل؛ أما لو احتاج التطبيق المكتبي إلى تخزينٍ محليٍ أكثر تعقيدًا من مجرد تخزين البيانات على شكل "المفتاح/القيمة"، فيمكنك أن تُضمِّن قاعدة البيانات الخاصة بتطبيقك، أو أن تبتكر صيغة ملفات للتخزين، أو غيره ذلك من الحلول. لكن على مرِّ التاريخ، لم تملك تطبيقات الويب هذا الامتياز، وعلى الرغم من ابتكار الكعكات (Cookies) في بدايات الويب لكن كان الغرض منها هو التخزين المحلي لكميةٍ قليلةٍ من البيانات، إلا أنَّ هنالك ثلاثة أسباب تمنعنا من استخدامها لهذا الغرض: ستُضمَّن الكعكات في كل طلبية HTTP، مما يؤدي إلى حدوث بطء في تطبيق الويب بسبب نقل نفس البيانات مرارًا وتكرارًا دون داعٍ ستُضمَّن الكعكات في كل طلبية HTTP، وهذا يعني إرسال البيانات دون تشفير عبر الإنترنت (إلا إذا كان يُخدَّم تطبيق الويب عندك عبر طبقة SSL) المساحة التخزينية للكعكات محدودة إلى حوالي 4 كيلوبايت من البيانات، وهي كافية لإبطاء تطبيقك (انظر أعلاه)، لكنها ليست كافية لتخزين شيءٍ مفيدٍ ما نحتاج له حقًا هو: مساحة تخزينية كبيرة موجودة على جهاز العميل يمكن أن تبقى حتى بعد تحديث الصفحة لن تُنقَل طوال الوقت إلى الخادوم جميع المحاولات -قبل HTML5- لتحقيق ما سبق كانت غير مرضية لمختلف الأسباب. لمحة تاريخية عن محاولات تخزين البيانات محليا قبل HTML5 لم يكن هنالك سوى متصفح Internet Explorer في بدايات الويب، أو على الأقل هذا ما حاولت مايكروسوفت إيهام العالم به، ولتحقيق هذه الغاية، وكجزءٍ من الحرب الكبرى الأولى للمتصفحات، ابتكرت مايكروسوفت ميزاتٍ كثيرة ووضعتها في متصفحها -Internet Explorer- الذي أنهى تلك الحرب. واحدة من تلك الميزات تُسمى "DHTML Behaviors" وكان أحد خصائصها يُدعى userData. تسمح ميزة userData لصفحات الويب أن تُخزِّن 64 كيلوبايت كحد أقصى لكل نطاق (domain)، وذلك عبر هيكلية تعتمد على XML (أما النطاقات الموثوقة، مثل مواقع إنترانت [intranet]، فتستطيع تخزين 10 أضعاف الكمية؛ وكانت 640 كيلوبايت في ذاك الوقت أكثر من كافية). لم يوفِّر IE أي مربع حوار لأخذ إذن المستخدم، ولم تكن هنالك إمكانية لزيادة كمية البيانات التي يمكن تخزينها محليًا. في عام 2002، أضافت شركة Adobe ميزةً في Flash 6 التي اكتسبت الاسم "Flash cookies"، لكن هذه الميزة كانت معروفةً ضمن بيئة Flash بالاسم Local Shared Objects؛ باختصار، تسمح هذه الميزة لكائنات Flash أن تُخزِّن 100 كيلوبايت من البيانات كحد أقصى لكل نطاق. طوَّر Brad Neuberg نموذجًا أوليًا لجسرٍ يربط تقنية Flash بلغة JavaScript أسماه AMASS (اختصار للعبارة AJAX Massive Storage System)، لكنه كان محدودًا بسبب بعض المشكلات في تصميم صيغة Flash. لكن في 2006، ومع مجيء ExternalInterface في Flash 8، أصبح من الممكن بسهولة وسرعة الوصول إلى الكائنات المشتركة المخزنة محليًا (Local Shred Objects أو اختصارًا LSOs) من JavaScript؛ ولهذا السبب أعاد Brad كتابة AMASS ودمجها مع Dojo Toolkit تحت الاسم dojox.storage. وبهذا مَنَحَ Flash كل نطاق 100 كيلوبايت من التخزين المحلي "مجانًا"، وستُطلَب موافقة المستخدم عند كل زيادة في تخزين البيانات (1 ميغابايت، 10 ميغابايت، وهكذا). في عام 2007، أصدرت Google إضافة Gears، التي هي إضافة مفتوحة المصدر للمتصفحات غرضها هو توفير إمكانيات إضافية إليها (تحدثنا سابقًا عن Gears في سياق /توفير واجهة برمجية لتحديد الموقع الجغرافي لمتصفح IE/). توفِّر Gears واجهة برمجية (API) للوصول إلى قاعدة بيانات SQL مدمجة فيها مبنيةٌ على محرك قواعد البيانات SQLite. يمكن لإضافة Gears تخزين كمية غير محدودة من البيانات لكل نطاق في جداول قاعدة بيانات SQL بعد أخذ إذن المستخدم. في تلك الأثناء، أكمل Brad Neuberg وآخرون مشوارهم في تطوير dojox.storage لتوفير واجهة موحَّدة لمختلف الإضافات، وبحلول 2009 أصبح بمقدور dojox.storage أن تكتشف دعم (وتوفر واجهة موحدة) لبرمجية Adobe Flash و Gears و Adobe AIR والنموذج الأولي من التخزين المحلي في HTML5 الذي كان مُطبَّقًا في الإصدارات القديمة من Firefox فقط. عندما تنظر إلى تلك الحلول، فستكتشف أنَّ جميعها كان خاصًا بمتصفح معيّن أو كان يتبع لإضافة خارجية. وعلى الرغم من الجهود البطولية لتوحيد تلك الاختلافات (dojox.storage) إلا أنَّ تلك الحلول تملك واجهات برمجية مختلفة جذريًا عن بعضها، ولكلٍ منها حدود قصوى لمقدار المساحة التخزينية المتوفرة، ولكلٍ منها تجربة مستخدم مختلفة. هذه هي المشكلة التي أتت HTML5 لحلها: توفير واجهة برمجية معيارية، ومطبَّقة في جميع المتصفحات، دون الحاجة إلى استخدام إضافات خارجية. مدخل إلى التخزين المحلي في HTML5 ما أشير إليه على أنَّه "التخزين المحلي في HTML5"‏ (HTML5 Storage) هو مواصفة باسم "Web Storage" التي كانت جزءًا من معيار HTML5، لكنها انقسمت وأصبحت معيارًا مستقلًا لأسباب ليست مهمة. بعض الشركات المسؤولة عن المتصفحات تطلِق عليها الاسم "التخزين المحلي" (Local Storage) أو "تخزين DOM" ‏(DOM Storage). ازداد تعقيد موضوع التسميات خصوصًا بعد ظهور عدد من المعايير الجديدة التي سأناقشها في نهاية هذا الدرس. إذًا، ما هو التخزين المحلي في HTML؟ بشكل مبسّط: هو طريقة تتمكن صفحات الويب من خلالها أن تُخزِّن البيانات على شكل "المفتاح/القيمة" محليًا داخل متصفح الويب في حاسوب العميل. ومثل الكعكات، ستبقى البيانات موجودةً حتى بعد إغلاقك للسان الصفحة في المتصفح، أو إغلاق المتصفح. لكن على عكس الكعكات، لن تُرسَل البيانات تلقائيًا إلى خادوم الويب البعيد؛ وعلى النقيض من كل المحاولات السابقة لتوفير ميزة التخزين المحلي، هذه الميزة موجودة داخليًا في متصفحات الويب، لذلك ستكون متاحة للاستخدام حتى لو لم تتوفر إضافاتٌ خارجيةٌ للمتصفح. ما هي المتصفحات التي تدعمها؟ حسنًا، التخزين المحلي في HTML5 مدعومٌ من أغلبية المتصفحات، وحتى القديمة منها. IE Firefox Safari Chrome Opera iPhone Android 8.0+ 3.5+ 4.0+ 4.0+ 10.5+ 2.0+ 2.0+ تستطيع الوصول إلى التخزين المحلي في HTML5 في شيفرات JavaScript عبر الكائن localStorage الموجود في الكائن العام window؛ لكن قبل أن تستخدمها، عليك أن تكتشف دعم المتصفح لها. function supports_html5_storage() { try { return 'localStorage' in window && window['localStorage'] !== null; } catch (e) { return false; } } لكن بدلًا من كتابة الدالة السابقة يدويًا، يمكنك استخدام Modernizr لاكتشاف دعم التخزين المحلي في HTML5. if (Modernizr.localstorage) { // window.localStorage متوفرة! } else { // لا يوجد دعم للتخزين المحلي :( // ربما تجرب dojox.storage أو مكتبة أخرى } استخدام التخزين المحلي في HTML5 يعتمد التخزين المحلي في أساسه على تخزين البيانات على شكل "مفتاح/قيمة". أي أنَّك تُخزِّن البيانات في مفتاح له اسم مُميِّز، ثم تستطيع الحصول على تلك البيانات مرةً أخرى باستخدام نفس المفتاح. ذاك المفتاح هو سلسلة نصية، ويمكن أن تكون البيانات المُخزَّنة من أي نوع تدعمه لغة JavaScript بما في ذلك السلاسل النصية والقيم المنطقية (true و false) أو الأعداد الصحيحة أو الأعداد العشرية؛ لكن في الواقع، ستُخزَّن البيانات كسلسلة نصية، وهذا يعني أنَّه لو لم تكن القيمة المُخزَّنة نصيةً فستحتاج إلى استعمال دوال مثل parseInt()‎ أو parseFloat()‎ لكي تحوِّل البيانات التي حصلت عليها إلى نوع البيانات الذي تريده. interface Storage { getter any getItem(in DOMString key); setter creator void setItem(in DOMString key, in any data); }; سيؤدي استدعاء الدالة setItem()‎ مع تمرير مفتاح موجود مسبقًا إلى إعادة الكتابة فوق القيمة السابقة دون إشعار. وسيؤدي استدعاء الدالة getItem()‎ مع تمرير مفتاح غير موجود إلى إعادة null بدلًا من رمي استثناء (throw an exception). وكما هو الحال مع بقية الكائنات في JavaScript، يمكنك أن تُعامِل الكائن localStorage على أنَّه مصفوفة ترابطية (associative array). فبدلًا من استخدام الدالتين getItem()‎ و setItem()‎، تستطيع بكل بساطة أن تستعمل الأقواس المربعة (التي تستعملها للوصول إلى عناصر المصفوفات). يمكن على سبيل المثال أن نُعيد كتابة هذه الشيفرة: var foo = localStorage.getItem("bar"); localStorage.setItem("bar", foo); var foo = localStorage["bar"]; localStorage["bar"] = foo; هنالك دوالٌ أخرى لحذف قيمة مرتبطة بمفتاح معيّن، ولحذف كل ما هو مُخزَّنٌ محليًا (وهذا يعني حذف كل المفاتيح والقيم معًا). interface Storage { deleter void removeItem(in DOMString key); void clear(); }; لن يؤدي استدعاء الدالة removeItem()‎ مع تمرير مفتاح غير موجود إلى فعل أي شيء. وأخيرًا، هنالك خاصية للحصول على العدد الكلي للقيم المُخزَّنة محليًا، ودالة للحصول على اسم كل مفتاح عبر تمرير فهرسه المكاني*. interface Storage { readonly attribute unsigned long length; getter DOMString key(in unsigned long index); }; لو استدعيتَ الدالة key()‎ مع فهرس لا يقع بين 0 - 1 فستُعيد الدالة null. تتبّع التغييرات في مساحة التخزين المحلي إذا أردت أن تتبَّع التغييرات في مساحة التخزين (storage area) برمجيًا، فعليك أن تستعمل الحَدَث storage، الذي يُفعَّل (fired) في الكائن العام window في كل مرة تُستدعى فيها الدالة setItem()‎ أو removeItem()‎ أو clear()‎ وتجري تلك الدالة تغييرًا ما. فعلى سبيل المثال، لو أعدتَ ضبط قيمة موجودة مسبقًا وكانت القيمة الجديدة مساوية للقيمة القديمة، أو استدعيت الدالة clear()‎‎ لكن لم تكن هنالك أيّة قيم في مساحة التخزين، فلن يُفعَّل الحدث storage، لعدم تغيّر شيء في مساحة التخزين. الحدث storage مدعوم في كل متصفح يدعم الكائن localStorage، وهذا يتضمن Internet Explorer 8، لكن IE 8 لا يدعم الدالة المعيارية من W3C لمراقبة الأحداث addEventListener (لكنها أُضيفت في نهاية المطاف في IE 9)؛ ولهذا، إذا أردت مراقبة تفعيل الحدث storage فعليك أن تكتشف ما هي آلية الأحداث التي يدعمها المتصفح أولًا (إذا فعلتَ هذا من قبل مع الأحداث الأخرى، فيمكنك تخطي هذه الفقرة والانتقال إلى آخر القسم. مراقبة الحدث storage مماثلة تمامًا لعملية مراقبة الأحداث الأخرى التي سبقَ وأن راقبتَها؛ وإذا كنتَ تُفضِّل استخدام jQuery أو مكتبة JavaScript أخرى لتسجيل دوال مراقبة الأحداث، فتستطيع فعل ذلك مع الحدث storage أيضًا). if (window.addEventListener) { window.addEventListener("storage", handle_storage, false); } else { window.attachEvent("onstorage", handle_storage); }; ستُستدعى الدالة handle_storage مع تمرير كائن من نوع StorageEvent، عدا في متصفح Internet Explorer حيث يُخزَّن الكائن في window.event. function handle_storage(e) { if (!e) { e = window.event; } } سيكون المتغير e -عند هذه النقطة- كائنًا من نوع StorageEvent، الذي لديه الخاصيات المبيّنة في الجدول الآتي: الخاصية النوع الشرح key سلسلة نصية مفتاح القيمة التي أُضيفَت أو حُذِفَت أو عدِّلَت oldValue أي نوع القيمة (التي كُتِبَ فوقها)، أو null إذا أُضيف عنصرٌ جديد newValue أي نوع القيمة الجديدة، أو null إن حُذِفَ عنصرٌ ما url* سلسلة نصية الصفحة التي تحتوي على الدالة التي أجرت هذا التغيير ملاحظة: كان اسم الخاصية url الأصلي هو uri، وذلك لأنَّ بعض المتصفحات امتلكت هذه الخاصية قبل تغيير مواصفة التخزين المحلي. لأكبر قدر من التوافقية، عليك أن تتحقق من وجود الخاصية url، فإن لم تكن موجودًا فتحقق من قيمة الخاصية uri. لا يمكن إلغاء الأحداث في الحدث storage. فلا توجد طريقة من داخل الدالة handle_storage تستطيع إيقاف تغيير ما من الحدوث. بكل بساطة، هذه طريقة لكي يخبرك المتصفح: "هذا ما حصل لتوِّه، لا يمكنك فعل أي شيء تجاهه؛ كل ما أستطيع فعله هو إخبارك ما الذي حدث". المحدوديات في المتصفحات الحالية في حديثي عن اللمحة التاريخية عن محاولات تخزين البيانات محليًا باستخدام إضافات خارجية، حرصتُ على ذكر محدوديات كل تقنية من تلك التقنيات، مثل محدودية المساحة التخزينية. لكنني لم أذكر شيئًا عن محدوديات التخزين المحلي في HTML5 المعياري. سأعطيك الأجوبة أولًا ثم سأشرحها. الأجوبة هي -بترتيبها حسب الأهمية-: "5 ميغابايت"، و"QUOTA_EXCEEDED_ERR" و "لا". "5 ميغابايت" هي المساحة التي يُسمَح لكل موقع بالحصول عليها افتراضيًا، وهذه القيمة متساوية -على غير العادة- بين المتصفحات، على الرغم من أنَّها مذكورة في مواصفة التخزين المحلي في HTML5 على أنَّها "اقتراح". ابقِ في ذهنك أنَّك تُخزِّن سلاسل نصية، ولا تخزِّن البيانات بصيغتها الأصلية، فلو كنت تُخزِّن الكثير من الأعداد الصحيحة (integers) أو العشرية (floats)، فسيكون الفرق في طريقة تمثيل البيانات مؤثرًا، إذ يُخزَّن كل رقم من عدد عشري كمحرف (character)، وليس بالتمثيل التقليدي لعدد عشري. "QUOTA_EXCEEDED_ERR" هو الاستثناء (exception) الذي سيُرمى (thrown) عندما تتجاوز حد 5 ميغابايت. أما "لا" فهو الجواب على السؤال البدهي الذي سيخطر ببالك: "هل يمكنني طلب المزيد من المساحة التخزينية من المستخدم؟" إلى حد الساعة، لا تدعم أيّة متصفحات أي آلية يتمكن خلالها مطورو الويب من طلب المزيد من المساحة التخزينية. لكن بعض المتصفحات (مثل Opera أو Firefox) تسمح للمستخدم أن يتحكم بالحدّ الأقصى للتخزين المحلي، لكن هذا منوطٌ بالمستخدم تمامًا، ولا يمكنك -كمطوِّر ويب- الاعتماد على ذلك لبناء تطبيقك. مثال عملي عن استخدام التخزين المحلي لنأخذ مثالًا عمليًا عن التخزين المحلي في HTML. هل تتذكر لعبة الضامة التي بنيناها في الدرس الذي يتحدث عن canvas؟ هنالك مشكلة صغيرة مع هذه اللعبة: ستخسر تقدّمك في اللعبة عندما تُغلِق نافذة المتصفح. لكن باستخدام التخزين في HTML5، سنستطيع حفظ التقدّم محليًا داخل المتصفح. هذا مثالٌ حيّ للعبة بعد التعديل. حرِّك بعض القطع، ثم أغلق لسان الصفحة (أو المتصفح)، ثم أعد فتح الصفحة. فإذا كان يدعم متصفحك التخزين المحلي، فيجب أن تتذكر الصفحة السابقة خطواتك التي أجريتها في اللعبة، بما في ذلك عدد الخطوات التي تحركت بها، ومكان كل قطعة على رقعة اللعب، وحتى آخر قطعة قمتَ بتحديدها. ما هي الآلية التي اتبعناها لفعل ذلك؟ سنستدعي الدالة الآتية في كل مرّة يطرأ فيها تغيير داخل اللعبة: function saveGameState() { if (!supportsLocalStorage()) { return false; } localStorage["halma.game.in.progress"] = gGameInProgress; for (var i = 0; i < kNumPieces; i++) { localStorage["halma.piece." + i + ".row"] = gPieces[i].row; localStorage["halma.piece." + i + ".column"] = gPieces[i].column; } localStorage["halma.selectedpiece"] = gSelectedPieceIndex; localStorage["halma.selectedpiecehasmoved"] = gSelectedPieceHasMoved; localStorage["halma.movecount"] = gMoveCount; return true; } كما لاحظت، تستعمل الدالة السابقة الكائن localStorage لتخزين أنَّ المستخدم قد بدأ اللعب (المفتاح gGameInProgress، الذي هو قيمة منطقية [Boolean]). ثم ستدور حلقة for على جميع القطع (المتغير gPieces، الذي هو مصفوفة في لغة JavaScript) ثم يحفظ رقم السطر والعمود لكل قطعة؛ ثم تحفظ الدالة بعض المعلومات الإضافية عن اللعبة، بما في ذلك القطعة التي تم تحديدها (القيمة gSelectedPieceIndex، التي هي رقمٌ صحيح [integer])، وفيما إذا كانت القطعة في منتصف سلسلة من القفزات (القيمة gSelectedPieceHasMoved، التي هي قيمة منطقية)، والعدد الكلي للحركات التي قام بها اللاعب (القيمة gMoveCount، التي هي عدد صحيح). وعند تحميل الصفحة، وبدلًا من الاستدعاء التلقائي للدالة newGame()‎ التي ستُعيد ضبط جميع المتغيرات إلى قيم مُحدَّدة مسبقًا، فسنستدعي الدالة resumeGame()‎. التي تتحقق -باستخدام التخزين المحلي في HTML5- فيما إذا كانت هنالك نسخة محفوظة من اللعبة مُخزَّنةٌ محليًا؛ فإن وُجِدَت، فستُستعاد تلك القيم باستخدام الكائن localStorage. function resumeGame() { if (!supportsLocalStorage()) { return false; } gGameInProgress = (localStorage["halma.game.in.progress"] == "true"); if (!gGameInProgress) { return false; } gPieces = new Array(kNumPieces); for (var i = 0; i < kNumPieces; i++) { var row = parseInt(localStorage["halma.piece." + i + ".row"]); var column = parseInt(localStorage["halma.piece." + i + ".column"]); gPieces[i] = new Cell(row, column); } gNumPieces = kNumPieces; gSelectedPieceIndex = parseInt(localStorage["halma.selectedpiece"]); gSelectedPieceHasMoved = localStorage["halma.selectedpiecehasmoved"] == "true"; gMoveCount = parseInt(localStorage["halma.movecount"]); drawBoard(); return true; } أهم فكرة في هذه الدالة هي تطبيق التحذير الذي ذكرته لك سابقًا في هذا الدرس، وسأكرره هنا: "ستُخزَّن البيانات كسلسلة نصية، وهذا يعني أنَّه لو لم تكن القيمة المُخزَّنة نصيةً فستحتاج إلى تحويل البيانات التي حصلت عليها إلى نوع البيانات الذي تريده". فعلى سبيل المثال، القيمة التي تُحدِّد فيما إذا كانت هنالك لعبة قيد اللعب (gGameInProgress) هي قيمة منطقية، وفي الدالة saveGameState()‎ خزَّنا القيمة دون أن نلقي بالًا لنوعها: localStorage["halma.game.in.progress"] = gGameInProgress; لكن في دالة resumeGame()‎ علينا أن نُعامِل القيمة التي أخذناها من التخزين المحلي كسلسلةٍ نصيةٍ كالآتي: gGameInProgress = (localStorage["halma.game.in.progress"] == "true"); وبشكلٍ مشابه، يُخزَّن عدد الخطوات في gMoveCount كعددٍ صحيحٍ؛ فلقد خزَّناها ببساطة في الدالة saveGameState()‎: localStorage["halma.movecount"] = gMoveCount; لكن في دالة resumeGame()‎ علينا أن نحوِّل القيمة إلى عدد صحيح باستخدام الدالة parseInt()‎ الموجودة في JavaScript: gMoveCount = parseInt(localStorage["halma.movecount"]); مستقبل التخزين المحلي في تطبيقات الويب على الرغم من أنَّ الماضي كان مليئًا بالطرق الالتفافية، لكن الوضع الراهن للتخزين المحلي في HTML5 مشرقٌ، فهنالك واجهة برمجية (API) جديدة قد وُضِعَ لها معيارٌ وطبِّق هذا المعيار في جميع المتصفحات الرئيسية على مختلف المنصات والأجهزة. فهذا أمرٌ لا تراه كل يوم كمطوِّر ويب، أليس كذلك؟ لكن ألا تتطلع إلى أكثر من "5 ميغابايت من الثنائيات على شكل "مفتاح/قيمة"؟ حسنًا، هنالك عدد من الرؤى التنافسية لمستقبل التخزين المحلي. إحدى تلك الرؤى هي اختصارٌ تعرفه بالتأكيد: SQL. أطلقَت Google في عام 2007 إضافة Gears المفتوحة المصدر التي تعمل على مختلف المتصفحات والتي احتوت على قاعدة بيانات مُضمَّنة فيها مبنية على SQLite. أثَّر هذا النموذج الأولي لاحقًا على إنشاء مواصفة Web SQL Database، والتي كانت تعرف رسميًا باسم "WebDB" التي توفر طبقةً للوصول إلى قاعدة بيانات SQL، سامحةً لك بالقيام بأشياء شبيهة بما يلي عبر JavaScript (لاحظ أنَّ الشيفرة الآتية حقيقية وتعمل على أربعة متصفحات): openDatabase('documents', '1.0', 'Local document storage', 5*1024*1024, function (db) { db.changeVersion('', '1.0', function (t) { t.executeSql('CREATE TABLE docids (id, name)'); }, error); }); كما لاحظت، ما يهم في الشيفرة السابقة هو السلسلة النصية التي مررتها إلى الدالة executeSql، ويمكن أن تحتوي تلك السلسلة النصية على أيّة تعليمات SQL مدعومة، بما في ذلك SELECT و UPDATE و INSERT و DELETE. الأمر هنا شبيهٌ ببرمجة قواعد البيانات بلغةٍ مثل PHP، إلا أنَّك تقوم بذلك عبر JavaScript! طُبِّقت مواصفات Web SQL Database من أربعة متصفحات ومنصات. IE Firefox Safari Chrome Opera iPhone Android . . 4.0+ 4.0+ 10.5+ 3.0+ 2.0+ وبكل تأكيد، لو سبق وأن استخدمت أكثر من مُحرِّك لقواعد البيانات في حياتك، فأنت تعلم أنَّ "SQL" هي مصطلح تسويقي أكثر من كونها معيارًا متكاملًا (قد يقول البعض أنَّ HTML5 كذلك، لكن لا تأبه بقولهم). حسنًا، هنالك معيار للغة SQL (يسمى SQL-92) لكن لا يوجد خادوم قواعد بيانات في العالم يتوافق تمامًا مع ذاك المعيار. فهنالك نسخة SQL لقواعد بيانات Oracle، ونسخة أخرى لقواعد MSSQL، ونسخة أخرى لقواعد بيانات MySQL، وأخرى لقواعد بيانات PostgreSQL، ولا ننسَ نسخة SQL لقواعد بيانات SQLite. وحتى كل منتج من تلك المنتجات يُضيف ميزات SQL جديدة على مرّ الزمن، وبهذا يكون قولنا "نسخة SQL لقواعد بيانات SQLite" ليس كافيًا لتحديد ما نتحدث عنه بدقّة. فعليك أن تقول "نسخة SQL التي تأتي مع قواعد بيانات SQLite ذات الإصدار X.Y.Z". كل ما سبق أدى إلى الإعلان الآتي، التي يقبع الآن في أعلى صفحة مواصفة Web SQL Database: وعلى ضوء هذا، سأعرِّفك على رؤية تنافسية أخرى لتخزينٍ محليٍ متقدم وثابت لتطبيقات الويب: Indexed Database API المعروفة رسميًا باسم "WebSimpleDB" التي اشتهرت باسم "IndexedDB". تحتوي IndexedDB على ما يُسمى "مخزن الكائنات" (object store)، الذي يتشارك مع قاعدة بيانات SQL في الكثير من المفاهيم؛ فهنالك "قواعد بيانات" (databases) فيها "سجلات" (records)، ويملك كل سجل عددًا من "الحقول" (fields)، وكل حقل له نوع بيانات معيّن، الذي يُعرَّف عند إنشاء قاعدة البيانات. تستطيع أيضًا تحديد مجموعة فرعية من السجلات، ثم تعرضها عبر "مؤشر" (cursor)، ويتم التعامل مع التغييرات على مخزن الكائنات عبر "التحويلات" (transactions). إذا سبق وأن برمجتَ قليلًا بأي نوع من أنواع قواعد بيانات SQL ، فمن المرجح أن تبدو المصطلحات السابقة مألوفةً لديك. الفرق الرئيسي هو أنَّ "مخزن الكائنات" ليس لديه "لغة استعلام بنيوية"، لا تستطيع كتابة عبارات مثل "SELECT * from USERS where ACTIVE = 'Y'‎" لكنك تستطيع استخدام الدوال التي يوفرها مخزن الكائنات لفتح "مؤشر" (cursor) في قاعدة البيانات "USERS"، ثم تمر عبر السجلات، وتستبعد سجلات المستخدمين غير النشيطين، ثم تستخدم دوالًا للوصول إلى قيم كل حقل في السجلات المتبقية. دعم IndexedDB موجودٌ في Firefox منذ الإصدار 4.0 (صرَّحَت Mozilla أنَّها لن تدعم Web SQL Database في متصفحها)، وChrome منذ الإصدار 11، وحتى Internet Explorer أصبح يدعم IndexedDB منذ الإصدار 10. ترجمة -وبتصرّف- لفصل "Local Storage" من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  16. تحديد الموقع الجغرافي هو آلية معرفة مكان وجودك في هذا العالم ومشاركة تلك المعلومات (اختياريًا) مع الأشخاص الذين تثق بهم؛ وهنالك أكثر من طريقة لمعرفة أين أنت: إما باستخدام عنوان IP الخاص بك، وإما عبر اتصال الشبكة اللاسلكية، أو عبر برج التغطية الخلوية الذي يتصل به هاتفك، أو عبر شريحة GPS التي تحسب مكانك نسبةً إلى خطوط الطول (longitude) والعرض (latitude) من المعلومات التي ترسلها الأقمار الاصطناعية من السماء. س: يبدو لي أنَّ تحديد الموقع الجغرافي مرعبٌ. هل أستطيع تعطيله؟ ج: يقلق المستخدمون من انتهاك الخصوصية عندما نتحدث عن مشاركة موقعك الفيزيائي مع خادوم ويب بعيد. تقول واجهة تحديد الموقع الجغرافي البرمجية (API) "أنَّ على المتصفحات عدم إرسال معلومات الموقع الحالي إلى مواقع الويب دون إذن المستخدم"؛ أي بكلامٍ آخر، السماح بمشاركة الموقع الجغرافي منوطٌ بك، فإن شئتَ سمحتَ بمشاركته، وإلا فلا. واجهة تحديد الموقع الجغرافي البرمجية تسمح واجهة تحديد الموقع الجغرافي البرمجية (geolocation API) لك بمشاركة موقعك الحالي مع مواقع الويب الموثوقة. ستتوفر إحداثيات الطول والعرض للصفحات عبر JavaScript، التي بدورها سترسِل تلك المعلومات إلى خادوم الويب البعيد الذي سيُجري عمليات عجيبة متعلقة بالموقع الجغرافي مثل العثور على شركة محلية أو إظهار موقعك على خريطة. كما سترى في الجدول الآتي، تُدعَم واجهة تحديد الموقع الجغرافي من أغلبية متصفحات الحاسوب والهواتف المحمولة؛ وهنالك دعمٌ لبعض المتصفحات القديمة باستخدام مكتبات خارجية، التي سنأتي على ذكرها لاحقًا في هذا الدرس. IE Firefox Safari Chrome Opera iPhone Android 9.0+ 3.5+ 5.0+ 5.0+ 10.6+ 3.0+ 2.0+ بجانب دعم واجهة تحديد الموقع الجغرافي القياسية، هنالك عدد من الواجهات البرمجية الخاصة بهواتف معيّنة، التي سنغطي شرحها لاحقًا في هذا الدرس. أريني الشيفرة تتمحور واجهة تحديد الموقع الجغرافي البرمجية حول خاصية جديدة في كائن navigator العام: navigator.geolocation. أبسط استخدام لواجهة تحديد الموقع الجغرافي هي كالآتي: function get_location() { navigator.geolocation.getCurrentPosition(show_map); } لكن ليست في الشيفرة السابقة أيّة آليات للتحقق من دعم المتصفح أو التعامل مع الأخطاء أو خياراتٍ أخرى؛ ويجب عادةً أن يتضمن تطبيق الويب اثنين مما سبق. يمكنك استخدام Modernizr للتحقق من دعم واجهة تحديد الموقع الجغرافي البرمجية: function get_location() { if (Modernizr.geolocation) { navigator.geolocation.getCurrentPosition(show_map); } else { //لا يوجد دعم لتحديد الموقع؛ ربما تجرب استخدام Gears؟ } } ما الذي تريد فعله إن لم يكن تحديد المواقع مدعومًا منوطٌ بك؛ وسأشرح كيفية استخدام مكتبة Gears بعد قليل، لكنني سأتحدث أولًا عمّا يحدث أثناء استدعاء الدالة getCurrentPosition()‎. كما ذكرتُ سابقًا في بداية هذا الدرس، لن يُجبِرَك المتصفح على إعطاء موقعك الفيزيائي إلى الخادوم البعيد، ولكن تختلف طريقة فعل ذلك من متصفح إلى آخر؛ فسيؤدي استدعاء الدالةgetCurrentPosition() ‎ في متصفح Firefox إلى إظهار "شريط معلومات" في أعلى نافذة المتصفح، الذي يبدو كالآتي: الشكل 1: شريط المعلومات الذي يُظهِره متصفح Firefox عند محاولة الوصول إلى الموقع الفيزيائي. هنالك الكثير من الأشياء المضمَّنة في ذاك الشريط؛ أنت كمستخدمٍ للمتصفح: سيتم إخبارك أنَّ موقع ويب يحاول معرفة موقعك الفيزيائي سيتم إخبارك ما هو موقع الويب الذي يحاول معرفة موقعك الفيزيائي ستتمكن من الذهاب إلى صفحة المساعدة "Location-Aware Browsing" التي تشرح لك ما الذي يجري (النسخة المختصرة من القصة هي أنَّ Google ستوفر الموقع وستخزن بياناتك بما يتوافق مع اتفاقية الخصوصية لخدمة تحديد المواقع الخاصة بها) ستستطيع أن تسمح بمشاركة موقعك الجغرافي ستتمكن من عدم السماح بمشاركة موقعك الجغرافي ستتمكن من إخبار متصفح أن يتذكر اختيارك (سواءً كنت تريد مشاركة موقعك الجغرافي أم لا) لكي لا تشاهد شريط المعلومات مرةً أخرى لكن هنالك المزيد! هذا الشريط: لا يمنعك من التبديل بين ألسنة (tabs) المتصفح أو بين نوافذه خاص بالصفحة وسيختفي بمجرد تبديلك إلى لسان أو نافذة أخرى ثم سيظهر مرةً ثانية عند عودتك إلى اللسان الأصلي. لا يمكن لمواقع الويب تجاوزه أو الالتفاف عليه يمنع مشاركة الموقع الجغرافي مع خادوم الويب أثناء انتظاره لجوابك (إن كنتَ تريد المشاركة أم لا) لقد رأيتَ شيفرة JavaScript التي تؤدي إلى إظهار شريط المعلومات السابق، وفيها دالة تؤدي إلى استدعاء دالةٍ أخرى (التي سميتُها show_map)، وستُنفَّذ الدالة getCurrentPosition()‎ مباشرةً لكن هذا لا يعني أنَّك تستطيع الوصول إلى بيانات موقع المستخدم؛ فأول مرة تضمن فيها حصولك على تلك البيانات هي داخل الدالة التي ستُستدَعى؛ التي تبدو كالآتي: function show_map(position) { var latitude = position.coords.latitude; var longitude = position.coords.longitude; // لنُظهِر خريطة أو شيئًا آخر مفيدًا } تأتي الدالة السابقة مع معامل (parameter) وحيد، الذي هو كائنٌ له خاصيتان: coords و timestamp. خاصية timestamp بسيطة، فهي الوقت والتاريخ الذي حُسِبَ فيه الموقع (لا يمكنك توقع متى سيُحسَب الموقع لأن ذلك يحدث بشكلٍ غير متزامن. فربما سيأخذ المستخدم بعض الوقت لقراءة شريط المعلومات والموافقة على مشاركة الموقع الجغرافي، وقد تستغرق الأجهزة ذات شريحة GPS بعض الوقت للاتصال بأقمار GPS الاصطناعية، ...إلخ.). أما الكائن coords فلديه خاصيات مثل latitude و longitude الواضح من اسمها أنَّها إحداثيات الموقع الفيزيائي للمستخدم. يوضِّح هذا الجدول خاصيات الكائن position: الخاصية النوع ملاحظات coords.latitude double عدد عشري coords.longitude double عدد عشري coords.altitude double أو null مترًا فوق المجسم المرجعي للأرض coords.accuracy double بواحدة المتر coords.altitudeAccuracy double أو null بواحدة المتر coords.heading double أو null درجات باتجاه عقارب الساعة من الشمال الحقيقي coords.speed double أو null بواحدة متر/ثانية timestamp DOMTimeStamp مثل الكائن Date()‎ من المضمون وجود ثلاث خاصيات من الخاصيات السابقة (coords.latitude و coords.longitude و coords.accuracy) أما البقية فيمكن أن يعيدوا القيمة null اعتمادًا على قدرات جهازك وعلى قدرات خادوم تحديد المواقع الذي تتعامل معه. ستُحسَب الخاصيتان heading و speed اعتمادًا على موقع المستخدم السابق إذا كان متوفرًا. التعامل مع الأخطاء موضوع تحديد الموقع الجغرافي معقدٌ بعض الشيء، ويحتمَل أن تأخذ الأمور منحى خاطئ. ذكرتُ سابقًا ناحية "موافقة المستخدم"؛ فلو أراد تطبيق الويب الحصول على الموقع الفيزيائي للمستخدم لكن المستخدم لم يرغب في إعطائه للتطبيق، فلن تحصل عليه وسيُطبَّق ما يقوله المستخدم دائمًا. كيف يبدو التعامل مع الأخطاء في الشيفرات؟ عليك أن تُمرِّر وسيطًا ثانيًا إلى الدالة ()getCurrentPosition هو الدالة التي ستُستدَعى عند حدوث خطأ. navigator.geolocation.getCurrentPosition(show_map, handle_error) إن حدث أيّ خطأٍ فستُستدعى الدالة المُحدَّدة مع تمرير الكائن PositionError إليها. يوضِّح الجدول الآتي خاصيات الكائن PositionError: الخاصية النوع ملاحظات code short قيمة عددية message DOMString الرسالة الموجودة في هذه الخاصية ليست موجهة للمستخدم النهائي. ستكون قيمة الخاصية code واحدة من القيم الآتية: PERMISSION_DENIED: إذا ضغط المستخدم على زر "Don't Share" أو منع وصول الوصول إلى موقعه بطريقةٍ أو بأخرى. POSITION_UNAVAILABLE: إذا توقفت الشبكة عن العمل أو في حال عدم التمكن من الوصول إلى الأقمار الاصطناعية. TIMEOUT: إذا كانت الشبكة تعمل لكنها تأخذ وقتًا طويلًا لحساب موقع المستخدم الفيزيائي؛ لكن بكم يُقدَّر "الوقت الطويل"؟ سأريك كيفية تعريف تلك القيمة في القسم التالي. function handle_error(err) { if (err.code == 1) { // لم يسمح المستخدم بالحصول على الموقع الجغرافي! } } س: هل تعمل واجهة تحديد الموقع الجغرافي في المحطة الفضائية الدولية، أو على القمر، أو على الكواكب الأخرى؟ ج: تقول مواصفات تحديد المواقع أنَّ "نظام الإحداثيات الجغرافية المستخدم في هذا الصدد هو نظام الإحداثيات الجيوديزية العالمي [WGS84]. بقية أنظمة الإحداثيات غير مدعومة". تدور المحطة الفضائية الدولية حول الأرض، لذلك يمكن وصف موقع رواد الفضاء على المحطة بإحداثيات طول وعرض وارتفاع عن الأرض، لكن يتمحور نظام الإحداثيات الجيوديزية العالمي حول الأرض، ولا يمكن استخدامه لتعيين مواقع على القمر أو بقية الكواكب. الخيارات المتاحة أمامك تدعم بعض الهواتف المحمولة -مثل iPhone وهواتف أندرويد- طريقتين لتحديد مكانك. تحسب أول طريقة موقعك بناءً على قربك من عدِّة أبراج تغطية مملوكة من شركة الاتصالات الخلوية المُشترِك فيها؛ هذه الطريقة سريعة ولا تحتاج إلى شريحة GPS فيزيائية، لكنها تعطي فكرة عامة عن موقعك، ويمكن تعيين الدقة بناءً على عدد أبراج التغطية في موقعك، فقد تكون على مستوى المباني السكنية، أو على نطاق كليو متر من مكانك. تستعمل الطريقة الثانية شريحة GPS في هاتفك لتبادل المعلومات مع أقمار GPS الاصطناعية التي تدور حول الأرض. يمكن تحديد موقعك عبر GPS بدقة كبيرة (عدِّة أمتار)، لكن من سلبيات هذه الطريقة هي الاستهلاك الكبير للطاقة من شريحة GPS، لذا ستُعطِّل الهواتف المحمولة هذه الشريحة إلى أن يتم الاحتياج إليها؛ وهذا يعني أنَّ هنالك تأخير عند تشغيل الشريحة ريثما يُهيَّأ الاتصال مع أقمار GPS الاصطناعية. إذا سبق لك واستخدمت Google Maps على هاتفٍ ذكيٍ مثل iPhone أو هواتف أندرويد، فستشاهد تطبيقًا لكلا الطريقتين السابقتين: ستشاهد أولًا دائرةً كبيرةً تُحدِّد موقعك تقريبيًا (وذلك بالبحث عن أقرب برج تغطية)، ثم دائرة أصغر (بحساب الموقع بناءً على عدِّة أبراج تغطية)، ثم نقطة وحيدة دقيقة (التي هي إحداثيات موقعك الفيزيائي بناءً على المعلومات الآتية من أقمار GPS الاصطناعية). السبب وراء ذكري لهذه المعلومات هي أنَّك لا تحتاج دومًا إلى دقة عالية، فإن كنت تبحث عن قائمة بدور عرض الأفلام التي بالجوار، فلا تلزمك إلا معرفة الموقع العام للمستخدم؛ إذ لا توجد دور عرض سينمائية كثيرة حتى في المدن المزدحمة، وستذكر -على أيّة حال- أكثر من دار عرض. أما على الكفة الأخرى، إذا كان تطبيقك يُعطي توجيهات لسائق السيارة في الوقت الحقيقي، فيجب أن تعرف موقع المستخدم الفيزيائي بدقة لكي تستطيع أن تقول "انعطف نحو اليمين بعد 20 مترًا" (أو ما شابه ذلك). يمكن تمرير وسيط (argument) ثالث اختياري إلى دالة getCurrentPosition()‎ هو كائن PositionOptions؛ وهنالك ثلاث خاصيات يمكنك ضبطها في كائن PositionOptions، وكل تلك الخاصيات اختيارية، إذ تستطيع أن تضبطها جميعًا أو أن لا تضبط أيًّا منها، وهي مبيّنة في الجدول الآتي: الخاصية النوع القيمة الافتراضية ملاحظات enableHighAccuracy Boolean false قد تُسبِّب القيمة true بطئًا timeout long (لا توجد قيمة افتراضية) القيمة بواحدة الميلي ثانية maximumAge long 0 القيمة بواحدة الميلي ثانية وظيفة خاصية enableHighAccuracy مماثلة لاسمها، إن كانت قيمتها true وكان يدعمها الجهاز ووافق المستخدم على مشاركة موقعه الفيزيائي مع التطبيق، فسيحاول الجهاز توفير الموقع الفيزيائي بدقة. هنالك أذونات منفصلة للتحديد الدقيق وغير الدقيق للموقع الجغرافي في هواتف iPhone وأندرويد؛ لذا من الممكن أن يفشل استدعاء الدالة getCurrentPosition()‎ مع ضبط الخاصية enableHighAccuracy:true، لكن قد ينجح استدعاؤها مع ضبط الخاصية enableHighAccuracy:false. تُحدِّد خاصية timeout كم ملي ثانية على تطبيق الويب أن ينتظر الحصول على الموقع الفيزيائي، لكن لا يبدأ المؤقت إلا بعد أن يوافق المستخدم على إعطاء إحداثيات موقعك الفيزيائي؛ فليس الغرض من هذه الخاصية قياس سرعة ردة فعل المستخدم، وإنما قياس سرعة الشبكة. تسمح خاصية maximumAge للجهاز بأن يُجيب مباشرةً بنسخة محفوظة من الإحداثيات. على سبيل المثال، لنقل أنَّك استَدعيتَ getCurrentPosition()‎ لأول مرة، ثم وافق المستخدم على إعطاء موقعه الجغرافي وانتهت عملية حساب الموقع الفيزيائي في الساعة ‎10:00 AM تمامًا؛ وبعد دقيقة واحدة (أي 10:01‎ AM) استدعيتَ الدالة getCurrentPosition()‎ مرةً أخرى مع ضبط خاصية maximumAge إلى 75000. navigator.geolocation.getCurrentPosition( success_callback, error_callback, {maximumAge: 75000}); أنت تقول أنَّه لا يهمك موقع المستخدم في لحظة استدعاء الدالة، وإنما ستقبل بمعرفة أين كان المستخدم منذ 75 ثانية مضت (75000 ميلي ثانية)؛ لكن الجهاز يعرف أين كان المستخدم منذ 60 ثانية (60000 ملي ثانية)، لأنه حسب موقعه في أول مرة استدعيتَ فيها الدالة getCurrentPosition()‎؛ وبالتالي لن يحتاج الجهاز إلى إعادة حساب موقع المستخدم الحالي، إذ سيَستخدِم نفس المعلومات التي أرسلها أول مرة: أي نفس إحداثيات الطول والعرض، ونفس الدقة، ونفس بصمة الوقت (timestamp أي 10:00‎ AM). عليك أن تفكِّر في مدى الدقة المطلوبة قبل أن تسأل المستخدم عن موقعه، وتضبط الخاصية enableHighAccuracy وفقًا لذلك. وإذا كنت تريد معرفة موقع المستخدم أكثر من مرة، فعليك التفكير في العمر الأقصى للمعلومات التي تستطيع الاستفادة منها، وتضبط الخاصية maximumAge وفقًا لذلك. أما إن أردت معرفة موقع المستخدم بشكلٍ دائم، فلن تكون الدالة getCurrentPosition()‎‎ مناسبةً لك، وعليك حينها استخدام watchPosition()‎. تملك دالة watchPosition() نفس بنية الدالة getCurrentPosition()‎، إذ يمكنها استدعاء دالتين، إحداهما ضرورية وتستخدم إن نجحت عملية الحصول على الموقع، وأخرى اختيارية غرضها هو التعامل مع الأخطاء؛ ويمكنها -أي الدالة- أن تقبل تمرير كائن PositionOptions اختياريًا الذي يملك الخاصيات ذاتها التي تعلمتها منذ قليل. الاختلاف في أنَّ الدالة التي ستُستدَعى ستُنفَّذ في كل مرة يتغير فيها موقع المستخدم، ولن تحتاج إلى محاولة الحصول على الموقع يدويًا، فسيُحدِّد جهازك الفاصل الزمني الأمثل لتحديث الموقع وسيستدعي الدالة عند كل تغيّر لموقع المستخدم. يمكنك الاستفادة من هذا لتحديث موضع مؤشر على الخريطة، أو توفير تعليمات عن المكان الذي عليك زيارته لاحقًا، أو أيّ شيءٍ تريده. تُعيد الدالة watchPosition()‎ رقمًا عليك تخزينه في مكانٍ ما، فلو أردت إيقاف عملية مراقبة تغيّر موقع المستخدم، فعليك استدعاء الدالة clearWatch()‎ مُمرِّرًا إليها ذاك الرقم، وسيتوقف الجهاز عن إرسال تحديثات الموقع إلى دالتك. يعمل ما سبق تمامًا كالدالتين setInterval()‎ و clearInterval()‎ في JavaScript إن استخدمتهما من قبل. ماذا عن متصفح IE؟ لم يكن يدعم متصفح Internet Explorer قبل الإصدار التاسع واجهة تحديد المواقع البرمجية من W3C التي شرحتها من قبل، لكن لا تقنط! Gears هي إضافة مفتوحة المصدر للمتصفحات من Google التي تعمل على ويندوز ولينُكس و Mac OS X والهواتف العاملة بنظامَي Windows Phone وأندرويد. حيث مهمتها توفير ميزات للمتصفحات القديمة، وإحدى الميزات التي توفرها Gears هي واجهة برمجية لتحديد المواقع إلا أنَّها ليس مماثلة لواجهة W3C البرمجية، لكنها تخدم نفس الغرض. لمّا كنّا نتحدث هنا عن المنصات القديمة، فمن الجدير بالذكر أنَّ عددًا من أنظمة الهواتف المحمولة القديمة لها واجهات برمجية خاصة بها لتحديد المواقع، حيث توفر هواتف BlackBerry و Nokia وpalm و OMTP BONDI واجهات برمجية خاصة بها؛ التي تختلف بالطبع عن Gears والتي تختلف بدورها عن W3C. مكتبة geo.js geo.js هي مكتبة JavaScript مفتوحة المصدر مرخَّصة برخصة MIT التي تسهِّل التعامل مع واجهة W3C البرمجية وواجهة Gears البرمجية والواجهات البرمجية التي توفرها أنظمة الهواتف القديمة. عليك أن تُضمِّن عنصرَي <script> في أسفل صفحتك لكي تستخدمها (يمكنك أن تضع العنصرين في أي مكان في الصفحة، لكن وضعهما في عنصر <head> سيُبطِّئ من تحميل الصفحة، فلا تفعل ذلك). أول سكربت هو gears_init.js الذي يُهيِّئ إضافة Gears إن وجِدَت، أما السكربت الثاني فهو geo.js. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Dive Into HTML5</title> </head> <body> ... <script src="gears_init.js"></script> <script src="geo.js"></script> </body> </html> ستتمكن من تحديد الموقع الآن بغض النظر عن الواجهة البرمجية المدعومة في المتصفح. if (geo_position_js.init()) { geo_position_js.getCurrentPosition(geo_success, geo_error); } لنُقسِّم ما سبق ونشرح كل سطرٍ على حدة. ستحتاج أولًا إلى استدعاء دالة init()‎، التي تُعيد true إن وجِدَ دعمٌ لإحدى واجهات تحديد المواقع البرمجية. if (geo_position_js.init()) { لن تعثر الدالة init()‎ على الموقع الجغرافي، وإنما تتحقق من أنَّ الوصول إلى الموقع ممكنٌ. عليك أن تستدعي الدالة getCurrentPosition()‎ للحصول على الموقع. geo_position_js.getCurrentPosition(geo_success, geo_error); ستؤدي الدالة getCurrentPosition()‎ إلى جعل المتصفح يطلب من المستخدم إذنه للحصول على موقعه الفيزيائي ومشاركته. إن كان الوصول إلى الموقع الجغرافي موفرًا من إضافة Gears فسيظهر مربع حوار يسألك إن كنت تثق بموقع الويب لكي يحصل على موقعك. أما إذا كان يدعم المتصفح تحديد المواقع داخليًا، فسيظهر مربع حوار ذو شكلٍ مختلف. على سبيل المثال، يدعم Firefox واجهة تحديد الموقع الجغرافي البرمجية داخليًا، فلو حاولت الحصول على الموقع الجغرافي فيه، فسيظهر شريط معلومات في أعلى الصفحة يسأل المستخدم إن كان يريد مشاركة موقعه الجغرافي مع موقع الويب. تأخذ الدالة getCurrentPosition()‎ وسيطين هما الدالتان اللتان ستُستدعيا، فإن نجحت الدالة getCurrentPosition() في الحصول على موقع المستخدم -أي أنَّه أعطى إذنًا للوصول إلى الموقع الجغرافي، واستطاعت واجهة تحديد الموقع الجغرافي البرمجية تعيين الموقع- فستُستدعى الدالة الأولى، التي تكون في هذه المثال geo_success. geo_position_js.getCurrentPosition(geo_success, geo_error); تأخذ تلك الدالة وسيطًا وحيدًا يحتوي على معلومات الموقع الفيزيائي: function geo_success(p) { alert("Found you at latitude " + p.coords.latitude + ", longitude " + p.coords.longitude); } وإن لم تستطع الدالة getCurrentPosition()‎ معرفة موقع المستخدم -إما أن يكون المستخدم قد رفض إعطاء الإذن، أو لفشل تعيين الموقع من الواجهة البرمجية لسببٍ من الأسباب- فستُستدعى الدالة الثانية، التي تكون في مثالنا geo_error. geo_position_js.getCurrentPosition(geo_success, geo_error); لا تأخذ تلك الدالة أيّة وسائط: function geo_error() { alert("Could not find you!"); } لا تدعم مكتبة geo.js الدالة watchPosition()‎، عليك أن تطلب الدالة getCurrentPosition()‎ بشكلٍ متواصل إن أردت الحصول على تحديث فوري لموقع المستخدم. مثال متكامل سأشرح لك مثالًا يستخدم مكتبة geo.js للوصول إلى موقعك وعرض خريطة لما حولك. ستُستدعى الدالة geo_position_js.init()‎ عند تحميل الصفحة لمعرفة فيما إذا كانت تتوفر ميزة تحديد الموقع الجغرافي بأي شكلٍ من الأشكال التي تدعمها geo.js. فإن كانت مدعومةً فسيظهر رابط يمكن للمستخدم النقر عليه لإظهار موقعه الجغرافي؛ يستدعي هذه الرابط الدالة lookup_location()‎ الظاهرة هنا: function lookup_location() { geo_position_js.getCurrentPosition(show_map, show_map_error); } إذا أعطى المستخدم موافقته على تحديد الموقع، وكانت الخدمة الخلفية (backend service) قادرة على تحديد الموقع، فستستدعي مكتبة geo.js أول دالة التي هي show_map()‎ مع وسيط وحيد الذي هو loc حيث يُمثِّل خاصية coords التي تحتوي إحداثيات الطول والعرض ودقة القياس (لا يستخدم هذا المثال معلومات دقة القياس). تستعمل بقية الدالة show_map()‎ واجهة Google Maps البرمجية لإظهار الخريطة. function show_map(loc) { $("#geo-wrapper").css({'width':'320px','height':'350px'}); var map = new GMap2(document.getElementById("geo-wrapper")); var center = new GLatLng(loc.coords.latitude, loc.coords.longitude); map.setCenter(center, 14); map.addControl(new GSmallMapControl()); map.addControl(new GMapTypeControl()); map.addOverlay(new GMarker(center, {draggable: false, title: "You are here (more or less)"})); } أما لو لم تستطع geo.js تحديد موقعك، فستُستدعى الدالة الثانية show_map_error()‎. function show_map_error() { $("#live-geolocation").html('Unable to determine your location.'); } مصادر إضافية W3C geolocation API مكتبة Gears مكتبة geo.js
  17. تعرفنا في الدروس السابقة على صيغ ترميز الفيديو والصوت و كيفية ترميز مقاطع الفيديو باستخدام عدة صيغ. حسنًا، يُفترَض أنَّ هذه السلسلة عن HTML، لذا أين الشيفرات البرمجية؟ تمنحك HTML5 طريقتين لتضمين الفيديو في صفحة الويب، وكلا الطريقتين تستخدمان العنصر <video>. وإذا كان لديك ملف فيديو وحيد، فيمكنك ببساطة إضافة رابط له في خاصية src، وهذا شبيه ٌجدًا بطريقة إدراج صورة بالوسم ‎<img src="...">‎. <video src="pr6.webm"></video> تقنيًا، هذا كل ما تحتاج له؛ ولكن كما في عنصر <img> عليك دومًا أن تضيف الخاصيتين width و height في وسوم <video>. يمكن أن تكون الخاصيتان width و height مطابقتان لعرض وارتفاع الفيديو الذي رمَّزتَه. لا تقلق إن كان أحد بُعدَي الفيديو أصغر من القيمتين المُحدَّدتين، لأن المتصفح سيضع الفيديو في منتصف المربع الناتج عن وسم <video>، ولهذا لن يتشوه المقطع. <video src="pr6.webm" width="320" height="240"></video> افتراضيًا، لن يُظهِر الوسم <video> أي نوع من عناصر التحكم بالتشغيل، يمكنك إنشاء عناصر التحكم الخاصة بك باستخدام HTML و CSS و JavaScript؛ فالعنصر <video> يملك دوال مثل play()‎ و pause()‎ ويمكن القراءة والكتابة إلى خاصيات مثل currentTime، وهنالك أيضًا خاصيات أخرى يمكن القراءة منها والكتابة عليها مثل volume و muted؛ لذا تستطيع أن تبني واجهة عناصر التحكم كيف ما تشاء. إن لم تُرِد بناء واجهة عناصر التحكم يدويًا، فيمكنك أن تخبر المتصفح أن يُظهِر عناصر التحكم المدمجة فيه؛ وذلك بتضمين الخاصية controls في وسم <video>. <video src="pr6.webm" width="320" height="240" controls></video> هنالك خاصيتان اختياريتان إضافيتان أريد أن أذكرهما قبل الإكمال: preload و autoplay؛ دعني أشرح لك فائدتهما. تخبر الخاصية preload المتصفح أنَّك تريد البدء بتنزيل ملف الفيديو في أقرب فرصة بعد انتهاء تحميل الصفحة، وهذا شيءٌ منطقي إذا كان الغرض من الصفحة هو مشاهدة مقطع الفيديو؛ أما على الكفة الأخرى، إذا كان المقطع ثانويًا ولن يشاهده إلا القلة، فيمكنك أن تضبط الخاصية preload إلى none كي تخبر المتصفح بذلك لتقليل استهلاك التراسل الشبكي. هذا مثالٌ عن مقطع فيديو يبدأ بالتنزيل (لكن لن يُشغَّل) بعد انتهاء تنزيل الصفحة: <video src="pr6.webm" width="320" height="240" preload></video> هذا مثالٌ عن مقطع فيديو لن يبدأ بالتنزيل عند انتهاء تحميل الصفحة: <video src="pr6.webm" width="320" height="240" preload="none"></video> وظيفة الخاصية autoplay واضحة من اسمها: تخبر المتصفح أنَّك تحبِّذ تنزيل ملف الفيديو بعد انتهاء تحميل الصفحة، وترغب في أن يبدأ تشغيل المقطع تلقائيًا في أقرب وقتٍ ممكن. بعض الأشخاص يحبون هذا الأمر، وبعضهم يكرهونه؛ لكن دعني أشرح لماذا من المهم وجود هذه الخاصية في HTML5. بعض الأشخاص يريدون بدء تشغيل مقاطع الفيديو في صفحاتهم تلقائيًا حتى لو كان ذلك سيُزعِج زوار موقعهم. إن لم تُعرِّف HTML5 طريقةً معياريةً لبدء تشغيل مقاطع الفيديو فسيلجأ المطورون إلى استخدام JavaScript لفعل ذلك (على سبيل المثال، عبر استدعاء الدالة play()‎ أثناء الحدث window.load)؛ وهذا يُصعِّب مهمة تعطيل هذه الخاصية على الزوار، إذ يمكن استخدام إضافة إلى المتصفح (أو كتابة واحدة إن لزم الأمر) مهمتها هي تجاهل خاصية autoplay، مما يُعطِّل تشغيل الفيديو التلقائي. هذا مثالٌ عن مقطع فيديو سيبدأ تنزيله وتشغيله في أقرب فرصة ممكنة بعد انتهاء تحميل الصفحة: <video src="pr6.webm" width="320" height="240" autoplay></video> هذا هو سكربت Greasemonkey الذي تستطيع تثبيته على متصفحك ليمنع التشغيل التلقائي لفيديو HTML5؛ حيث يستخدم خاصية autoplay في كائن DOM (المكافئة لخاصية autoplay في شيفرة HTML) لتعطيل التشغيل (disable_video_autoplay.user.js): // ==UserScript== // @name Disable video autoplay // @namespace http://diveintomark.org/projects/greasemonkey/ // @description Ensures that HTML5 video elements do not autoplay // @include * // ==/UserScript== var arVideos = document.getElementsByTagName('video'); for (var i = arVideos.length - 1; i >= 0; i--) { var elmVideo = arVideos[i]; elmVideo.autoplay = false; } تمهل قليلًا… إذا كنتَ تتابع معي منذ بداية هذا الفصل، فأنت تعلم أنَّ لدينا ثلاثة ملفات فيديو بدلًا من واحد! هنالك ملف ‎.ogv الذي أنشأته باستخدام Firefogg أو ffmpeg2theora، وهنالك آخر ‎.mp4 أنشأته باستخدام HandBrake، والثالث هو ملف ‎.webm الذي أنشأته عبر ffmpeg. توفر HTML5 طريقةً لإضافة روابط لجميع الملفات السابقة: العنصر <source>. يمكن أن يحتوي كل عنصر <video> على أكثر من عنصر <source>؛ وسيقرأ المتصفحُ قائمةَ عناصر source بالترتيب وسيُشغِّل أول مقطع يستطيع تشغيله. وهذا يطرح سؤالًا آخر: كيف يعلم المتصفح أيَّ مقطعٍ يستطيع تشغيله؟ حسنًا، أسوأ الاحتمالات هي تحميل كل مقطع من تلك المقاطع ومحاولة تشغيله؛ وهذا إهدارٌ لتراسل البيانات. تستطيع أن تسهل الأمر على المتصفح (وتوفر قدرًا لا بأس به من تراسل البيانات) إذا أخبرتَ المتصفح معلوماتٍ عن كل مقطع، وذلك باستخدام الخاصية type في عنصر <source>. <video width="320" height="240" controls> <source src="pr6.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'> <source src="pr6.webm" type='video/webm; codecs="vp8, vorbis"'> <source src="pr6.ogv" type='video/ogg; codecs="theora, vorbis"'> </video> لنُقسِّم الشيفرة السابقة ليسهل فهمها. يُحدِّد العنصر <video> عرض وارتفاع مقطع الفيديو، لكنه لا يُحدِّد رابطًا لملف الفيديو؛ أما داخل عنصر <video> فهنالك ثلاثة عناصر <source>، كل عنصر <source> يُشير إلى ملف فيديو وحيد (باستخدام خاصية src)، ويُعطي معلومات أيضًا حول صيغة الفيديو (في خاصية type). تبدو خاصية type معقدةً، وهي كذلك! فهي مجموعةٌ من ثلاثة أقسام من المعلومات: صيغة الحاوية، ومرماز الفيديو، ومرماز الصوت. لنبدأ من الأسفل إلى الأعلى: لملفات ‎.ogv تكون صيغة الحاوية هي Ogg المُمثَّلة هنا بالعبارة video/ogg (بكلامٍ تقني، هذا هو نوع MIME لملفات Ogg)، ومرماز الفيديو هو Theora، ومرماز الصوت هو Vorbis. تبيّن أنَّ خاصية type بسيطة، عدا كون شكلها مشوه، لأن القيمة نفسها تتضمن علامات اقتباس، وهذا يعني أنَّ عليك استخدام نوع آخر من علامات الاقتباس لإحاطة القيمة كلها. <source src="pr6.ogv" type='video/ogg; codecs="theora, vorbis"'> صيغة WebM مشابهة لما سبق، لكن نوع MIME مختلف (video/webm بدلًا من video/ogg) ومرماز فيديو مختلف (vp8 بدلًا من theora) مذكورٌ ضمن المعامل (parameter) المسمى codecs. <source src="pr6.webm" type='video/webm; codecs="vp8, vorbis"'> أما فيديو H.264 فهو أكثر تعقيدًا؛ تذكر ما قلته عند شرح فيديو H.264 وصوت AAC أنها تأتي بأنماط (profiles) مختلفة؟ لقد رمَّزنا الفيديو بنمط "baseline" والصوت بنمط «low-complexity) ثم وضعناهما في حاوية MPEG-4. كل هذه المعلومات موجودة في خاصية type. <source src="pr6.mp4" type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'> الفائدة التي نجنيها من وراء مشقة تحديد نوع مقطع الفيديو هي أنَّ المتصفح سيتحقق من خاصية type أولًا ليرى إن كان بإمكانه تشغيل ملف فيديو معيّن؛ وإن قرر المتصفح أنَّه لا يستطيع تشغيل فيديو معيّن، فلن ينزِّل الملف، ولا أيَّ جزءٍ منه؛ وبهذا لن تستهلك تراسل البيانات، وسيشاهد زوار موقعك الفيديو الذي يريدونه بشكلٍ أسرع. إذا كنت تتبع التعليمات الواردة سابقًا في هذا الفصل لترميز مقاطع الفيديو، فيمكنك أن تنسخ وتلصق قيم خاصية type من المثال السابق، وإلا فعليك أن تكتبها يدويًا بنفسك. أنواع MIME تكشف عن وجهها القبيح هنالك قطعٌ كثيرةٌ لأحجية تشغيل الفيديو على الويب، ولقد ترددت كثيرًا في طرح هذا الموضوع، لكنه مهم لأنَّ الضبط الخاطئ لخادوم الويب سيؤدي إلى مشاكل لا تنتهي عندما تحاول معرفة سبب تشغيل مقاطع الفيديو على جهازك المحلي بينما لا تستطيع ذلك على الخادم الإنتاجي. إذا واجهتك هذه المشكلة فاعلم أنَّ المُسبِّب الرئيسي لها هو أنواع MIME في أغلبية الحالات. ذكرتُ أنواع MIME في درس لمحة تاريخية، لكن ربما تخطيت تلك الفقرة ولم تعرها اهتمامك، هذه خلاصة الموضوع: يجب أن تُخدَّم ملفات الفيديو بنوع MIME الملائم لها. ما هو "نوع MIME الملائم"؟ لقد رأيته سابقًا: هو جزءٌ من قيمة الخاصية type في العنصر <soruce>، لكن ضبط الخاصية type في شيفرات HTML ليس كافيًا، فعليك أيضًا أن تتأكد أنَّ خادم الويب يُضمِّن نوع MIME المناسب في ترويسة Content-Type في HTTP. إذا كنتَ تستخدم خادم ويب أباتشي (Apache) أو أي خادم آخر مُشتَق منه، فيمكنك استخدام تعليمة AddType في ملف httpd.conf (أو apache2.conf في الإصدارات الحديثة منه) الذي يُعدِّل الضبط لكل الموقع، أو في ملف ‎.htaccess الذي يُعدِّل الضبط للمجلد الذي تُخزِّن فيه مقاطع الفيديو (إذا كنتَ تستخدم خادم ويب آخر فانظر إلى توثيق ذاك الخادم لترى كيف يمكنك ضبط ترويسة Content-Type في HTTP لأنواع ملفات معيّنة). AddType video/ogg .ogv AddType video/mp4 .mp4 AddType video/webm .webm أول سطر مما سبق لمقاطع الفيديو في حاوية Ogg، والسطر الثاني لمقاطع الفيديو في حاوية MPEG-4، والسطر الثالث لمقاطع الفيديو في WebM. اضبطها مرةً ودعها تعمل عملها؛ لكن إن نسيت ضبطها فلن تعمل جميع مقاطع الفيديو المُحدَّدة في بعض المتصفحات، حتى لو وضعتَ نوع MIME في خاصية type في شيفرات HTML. ماذا عن متصفح IE؟ يدعم متصفح Internet Explorer عنصر <video> في HTML5، ويدعم فيديو H.264 وصوت AAC في حاوية MPEG-4، مثل متصفح Safari وهاتف iPhone. لكن ماذا عن الإصدارات القديمة من Internet Explorer؟ مثل IE8 وما دونه؟ أغلبية الأشخاص الذين يستخدمون Internet Explorer لديهم إضافة Adobe Flash مثبتةً على جهازهم. الإصدارات الحديثة من Adobe Flash (بدءًا من الإصدار 9.0.60.184) تدعم فيديو H.264 وصوت AAC في حاوية MPEG-4، مثل متصفح Safari و iPhone. فبمجرد ترميزك لمقطع الفيديو بمرماز H.264 لمتصفح Safari، ستستطيع تشغيله في مشغل فيديو يعتمد على تقنية Flash، وذلك في حال اكتشفت أنَّ أحد زوارك لا يملك متصفحًا متوافقًا مع فيديو HTML5. FlowPlayer هو مشغِّل فيديو مبني على تقنية Flash مفتوح المصدر ومرخَّص برخصة GPL (تتوفر رخص تجارية له). لا يَعرِف مشغِّل FlowPlayer أيّ شيءٍ عن عنصر <video>، ولن يحوِّل وسم <video> بشكلٍ سحري إلى كائن Flash، لكن HTML5 مصممة تصميمًا جيدًا لكي تتعامل مع هذه الحالات، لأنك تستطيع تضمن عنصر <object> ضمن عنصر <video>؛ وستتجاهل المتصفحات التي لا تدعم الفيديو في HTML5 العنصر <video> وستُحمِّل العنصر <object> الموجود ضمنه بدلًا عنه؛ الذي سيُشغِّل مقطع الفيديو باستخدام FlowPlayer؛ أما المتصفحات التي تدعم الفيديو في HTML5 فستعثر على مقطع فيديو (عبر العنصر soruce) تستطيع تشغيله، ثم ستتجاهل العنصر <object> تمامًا. آخر جزء من مفتاح حل الأحجية هو أنَّ HTML5 تقول بوجوب تجاهل جميع العناصر (ما عدا عناصر <source>) التي تكون "أبناءً" (children) للعنصر <video> تمامًا، وهذا يسمح لك باستخدام عنصر video في HTML5 في المتصفحات الحديثة مع توفير حل للمتصفحات القديمة عبر تقنية Flash دون الحاجة إلى كتابة سكربتات JavaScript معقدة. مثال متكامل هذا تطبيقٌ للتقنيات التي تعلمناها سابقًا في الدرس السابق حول ترميز مقاطع الفيديو؛ قمت بتضمين فيديو WebM، ولقد رمَّزتُ الفيديو المصدري إلى ثلاث صيغ باستخدام هذه الأوامر: ## Theora/Vorbis/Ogg you@localhost$ ffmpeg2theora --videobitrate 200 --max_size 320x240 --output pr6.ogv pr6.dv ## H.264/AAC/MP4 you@localhost$ HandBrakeCLI --preset "iPhone & iPod Touch" --vb 200 --width 320 --two-pass --turbo --optimize --input pr6.dv --output pr6.mp4 ## VP8/Vorbis/WebM you@localhost$ ffmpeg -pass 1 -passlogfile pr6.dv -threads 16 -keyint_min 0 -g 250 -skip_threshold 0 -qmin 1 -qmax 51 -i pr6.dv -vcodec libvpx -b 204800 -s 320x240 -aspect 4:3 -an -f webm -y NUL you@localhost$ ffmpeg -pass 2 -passlogfile pr6.dv -threads 16 -keyint_min 0 -g 250 -skip_threshold 0 -qmin 1 -qmax 51 -i pr6.dv -vcodec libvpx -b 204800 -s 320x240 -aspect 4:3 -acodec libvorbis -ac 2 -y pr6.webm سنستخدم العنصر <video> في الشيفرة النهائية، مع وجود عنصر <object> لمشغل Flash إن لم يدعم المتصفح العنصر <video>: <video id="movie" width="320" height="240" preload controls> <source src="pr6.webm" type='video/webm; codecs="vp8, vorbis"' /> <source src="pr6.ogv" type='video/ogg; codecs="theora, vorbis"' /> <source src="pr6.mp4" /> <object width="320" height="240" type="application/x-shockwave-flash" data="flowplayer-3.2.1.swf"> <param name="movie" value="flowplayer-3.2.1.swf" /> <param name="allowfullscreen" value="true" /> <param name="flashvars" value='config={"clip": {"url": "http://wearehugh.com/dih5/pr6.mp4", "autoPlay":false, "autoBuffering":true}}' /> <p>Download video as <a href="pr6.mp4">MP4</a>, <a href="pr6.webm">WebM</a>, or <a href="pr6.ogv">Ogg</a>.</p> </object> </video> ستتمكن من تشغيل مقطع الفيديو السابق على أي متصفح أو جهاز بدمج شيفرات HTML5 مع مشغل Flash كما في المثال السابق. عناصر تحكم بتشغيل الفيديو معدة مسبقا: VideoJS MediaElement.js Kaltura HTML5 Video & Media JavaScript Library ترجمة -وبتصرّف- لفصل "Video" من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim
  18. مدخل إلى html5

    هنالك أدواتٌ عديدةٌ لترميز الفيديو، وهنالك خياراتٌ كثيرةٌ للترميز تؤثر على جودة الفيديو؛ فإن لم تكن تريد فهم كل شيءٍ متعلقٍ بترميز الفيديو فعليك بهذا المقال. ترميز الفيديو باستخدام Miro Video Converter Miro Video Converter هو برنامج مفتوح المصدر مُرخَّص برخصة GPL لترميز الفيديو بعدِّة صيغ، يمكنك تنزيله لنظامَي Mac OS X أو ويندوز. وهو يدعم التحويل إلى جميع الصيغ التي ذكرناها سابقًا في الدرس السابق، لكنه لا يوفر خيارات سوى اختيار ملف الفيديو والصيغة التي سيحوَّل إليها. يمكنه نظريًا قبول أي صيغة من صيغ الفيديو كملف مصدري، بما في ذلك فيديو DV الذي تُنتِجه كاميرات الفيديو الرقمية من فئة المستهلكين. يُنتِج البرنامج مقاطع فيديو ذات جودة مقبولة؛ ولأن هذا البرنامج لا يوفر لك خياراتٍ كثيرة، فليس عندك حل سوى تغيير البرنامج إن لم تعجبك مقاطع الفيديو الناتجة من البرنامج. عليك أولًا تشغيل برنامج Miro Video Converter: الشكل 1: واجهة برنامج Miro Video Converter الرئيسية انقر فوق Choose file واختر ملف الفيديو المصدري الذي تريد ترميزه. الشكل 2: اختيار مقطع فيديو اضغط فوق القائمة المنسدلة Pick a Device or Video Format التي فيها قائمة بـمختلف الأجهزة والصيغ. لكننا مهتمين بثلاثةٍ من تلك العناصر: (WebM (vp8 هو فيديو WebM (أي فيديو VP8 وصوت Vorbis في حاوية WebM). Theora هو فيديو Theora وصوت Vorbis في حاوية Ogg. iPhone هو فيديو H.264 ذو النمط Baseline وصوت AAC ذو النمط low-complexity في حاوية MP4. اختر WebM أولًا. الشكل 3: اختيار صيغة WebM اضغط فوق زر Convert وسيبدأ برنامج Miro Video Converter بترميز الفيديو مباشرةً. سيُسمى الملف الناتج باسم SOURCEFILE.webm وسيُحفَظ في نفس مجلد ملف الفيديو المصدري. الشكل 4: ستُحدِّق في هذه الشاشة لفترةٍ طويلة سترجع إلى الشاشة الرئيسية بعد أن ينتهي الترميز، اختر Theora في هذه المرة من قائمة الأجهزة والصيغ. الشكل 5: حان الوقت لترميز Theora اضغط على زر Convert مرةً أخرى لترميز الفيديو بصيغة Theora؛ وسيُسمَّى الملف الناتج باسم SOURCEFILE.theora.ogv وسيُحفَظ في نفس مجلد المقطع الأصلي. الشكل 6: اذهب واشرب كوبًا من القهوة في النهاية، عليك ترميز المقطع بمرماز H.264 المتوافق مع iPhone وذلك باختيار iPhone من قائمة الأجهزة والصيغ. الشكل 7: اختر iPhone وليس iPhone 4 سيعطيك برنامج Miro Video Converter خيارًا عند ترميز فيديو متوافق مع هواتف iPhone هو إرسال المقطع المُرمَّز إلى مكتبتك في iTunes. القرار عائدٌ إليك هنا، لكنه ليس ضروريًا لنشره على الويب. الشكل 8: لا ترسل الملف إلى iTunes اضغط على زر Convert السحري وانتظر، سيُسمَّى الناتج باسم SOURCENAME.iphone.mp4 وسيُحفَظ بنفس مجلد الملف المصدر. الشكل 9: تصفح موقع فيسبوك قليلًا أو افعل شيئًا مفيدًا يجب أن يكون لديك ثلاثة ملفات فيديو بجانب ملفك الأصلي؛ إن أعجبتك جودة الفيديو فانتقل إلى الدرس القادم لترى كيف تجمِّعها مع بعضها في عنصر <video> وحيد الذي يعمل في جميع المتصفحات. أما إذا كنت تريد تعلم المزيد حول الأدوات الأخرى للترميز، فأكمل القراءة. ترميز الفيديو باستخدام Firefogg ملاحظة: سأستخدم المصطلح "فيديو Ogg" في هذا القسم اختصارًا للعبارة "فيديو Theora مع صوت Vorbis في حاوية Ogg"، وهذه تجميعة من المرمازات + حاوية التي تعمل دون إضافات في متصفحَي Firefox و Google Chrome. Firefogg هي إضافة مفتوحة المصدر مرخَّصة برخصة GPL لمتصفح Firefox لترميز فيديو Ogg. يجب أن يكون لديك إصدار Firefox 3.5 أو ما بعده لتثبيت هذه الإضافة، وذلك بزيادة موقعها الرسمي firefogg.org. الشكل 10: صفحة Firefogg الرئيسية اضغط على زر "Install Firefogg"، وسيسألك Firefox إذا كنت تريد السماح لهذه الموقع بتثبيت إضافة (extension). اضغط على "Allow" للمتابعة. الشكل 11: السماح بتثبيت Firefogg سيُظهِر لك Firefox نافذة تثبيت الإضافات الاعتيادية. اضغط على "Install Now" للمتابعة. الشكل 12: تثبيت Firefogg اضغط على "Restart Firefox" لإكمال التثبيت. الشكل 13: أعد تشغيل Firefox سيؤكد لك موقع firefogg.org أنَّك ثبتت Firefogg تثبيتًا سليمًا بعد إعادة تشغيل Firefox. الشكل 14: نجاح عملية تثبيت Firefogg اضغط على "Make Ogg Video" لبدء عملية الترميز. الشكل 15: البدء في ترميز مقاطع الفيديو اضغط على "Select file" لتحديد ملف الفيديو المصدري. الشكل 16: اختيار ملف الفيديو الذي تريد تحويله هنالك ستة أقسام في Firefogg: Presets (إعدادات الضبط المسبقة): الخيار الافتراضي هو "web video"، وهو جيدٌ لأغراضنا. Encoding range (مجال الترميز): قد يستغرق ترميز مقطع فيديو وقتًا طويلًا. وقد ترغب في البداية أن تُرمِّز جزءًا من الفيديو (لنقل، أول 30 ثانية) حتى تجد تركيبة الإعدادات التي تعجبك. Basic quality and resolution control (التحكم الأساسي بالجودة والدقة): يحتوي هذا القسم على أغلبية الخيارات المهمة. Metadata (البيانات الوصفية): لن أشرحها هنا، لكنك تستطيع إضافة البيانات الوصفية إلى مقاطع الفيديو التي تُرمِّزها مثل العنوان (title) والمؤلف (author). ربما أضفتَ من قبل البيانات الوصفية إلى مجموعتك الموسيقية المُفضَّلة عبر iTunes أو مدير موسيقي آخر. هذا القسم يُجسِّد هذه الفكرة. Advanced video encoding controls (خيارات ترميز الفيديو المتقدمة): لا تعبث بهذه الخيارات إلا إن كنت تعلم ما الذي تفعله (يوفِّر Firefogg مساعدةً تفاعليةً لأغلبية هذه الخيارات؛ اضغط على رمز "i" بجوار كل خيار لكي تتعلم المزيد عنه). Advanced audio encoding controls (خيارات ترميز الصوت المتقدمة): أكرر مرةً أخرى، لا تعبث بهذه الخيارات إلا إذا كنت تعلم ما الذي تفعل. الشكل 17: مختلف أقسم Firefogg الخيارات التي سأشرحها موجودةٌ في قسم "Basic quality and resolution control". وهي تحتوي على كل الخيارات المهمة: Video Quality (جودة الفيديو): تُقاس الجودة هنا بمقياس من 0 (الجودة الأدنى) إلى 10 (أعلى جودة). كلما ازدادت جودة الفيديو كلما كبرت المساحة التخزينية للملف الناتج، لذلك عليك أن تجرب قليلًا لكي تُحدِّد ما هي نسبة المساحة التخزينية/الجودة التي تلائم احتياجاتك. Audio Quality (جودة الصوت): تُقاس بمقياس من -1 (أقل جودة) إلى 10 (أعلى جودة). وكلما ازدادت جودة الفيديو كلما كبرت المساحة التخزينية للملف الناتج مثل خيار جودة الفيديو السابق. Video Codec (مرماز الفيديو): يجب أن تكون قيمة هذا الخيار هي "theora" دائمًا. Audio Codec (مرماز الصوت): يجب أن تكون قيمة هذا الخيار هي "vorbis" دائمًا. Video Width and Video Height (عرض الفيديو وارتفاعه): تكون قيمها الافتراضية هي العرض والارتفاع الحقيقي للمقطع المصدري الذي تُرمِّزه. إذا أردت إعادة تحجيم الفيديو أثناء ترميزه، فتستطيع تعديل قيمة العرض (أو الارتفاع) في هذا الخيار؛ ثم سيُعدِّل Firefogg البُعد الآخر للحفاظ على نسبة البُعدَين الأصلية (لكي لا تتشوه صورة مقطع الفيديو). الشكل 18: التحكم الأساسي بالجودة والدقة سأجرب في هذا المثال إعادة تحجيم مقطع الفيديو إلى نصف عرضه الأصلي، لاحظ كيف يُعدِّل Firefogg الارتفاع تلقائيًا لكي يتناسب مع العرض. الشكل 19: تعديل عرض وارتفاع مقطع الفيديو بعد أن تأخذ وقتك بتعديل الخيارات السابق، فاضغط على "Save Ogg" لبدء عملية الترميز؛ وسيسألك Firefogg عن اسم الملف الناتج. الشكل 20: حفظ الفيديو الناتج سيُظهِر لك Firefogg شريطًا لكي تعرف مقدار تقدم عملية الترميز؛ كل ما عليك فعله هو الانتظار (ثم الانتظار، ثم الانتظار). الشكل 21: شريط يُظهِر تقدم عملية الترميز ترميز عدة مقاطع إلى Ogg دفعة واحدة باستخدام ffmpeg2theora ملاحظة: كما في القسم السابق، سأستخدم المصطلح "فيديو Ogg" في هذا القسم اختصارًا للعبارة "فيديو Theora مع صوت Vorbis في حاوية Ogg"، وهذه تجميعة من المرمازات + حاوية التي تعمل دون إضافات في متصفحَي Firefox و Google Chrome. إذا كنتَ تتطلع نحو ترميز الكثير من ملفات فيديو Ogg وتريد أتمتة العملية، فعليك حكمًا أن تلقي نظرةً إلى ffmpeg2theora. ffmpeg2theora هو برنامج مفتوح المصدر مرخَّص برخصة GPL لترميز الفيديو بصيغة Ogg. هنالك ملفات تنفيذية متوفرة لأنظمة Mac OS X وويندوز وتوزيعات لينكس الحديثة؛ ويقبل ffmpeg2theora ترميز مقاطع الفيديو بأي صيغة تقريبًا، بما في ذلك فيديو DV الذي تُنتِجه كاميرات الفيديو الرقمية من فئة المستهلكين. عليك أن تستدعي ffmpeg2theora من سطر الأوامر لتستعمله (في نظام Mac OS X افتح "Applications > Utilities > Terminal"، أما في ويندوز، فافتح "Start Menu > Programs > Accessories > Command Prompt" أو بالضغط على مفتاح ويندوز+R ثم كتابة "cmd"). يقبل ffmpeg2theora عددًا كبيرًا من خيارات سطر الأوامر (جرب كتابة ffmpeg2theora --help لتعلم المزيد عنها)، لكنني سأركِّز على ثلاثةٍ منها: ‎--video-quality Q: حيث "Q" يمثل رقم من 0 إلى10. ‎--audio-quality Q: حيث "Q" يمثل رقم من -2 إلى 10. ‎--max_size=WxH: حيث "W" و "H" هما العرض والارتفاع الأقصى للمقطع الناتج. (الرمز "x" بينهما هو الحرف "x" العادي وليس إشارة الضرب "×"). سيُعيد ffmpeg2theora تحجيم مقطع الفيديو بحيث يكون بُعداه متناسبين لكي يتسع ضمن الأبعاد المُحدَّدة، لذا قد تكون أبعاد المقطع النهائي أصغر من WxH. على سبيل المثال، ترميز فيديو بأبعاد ‎720×480 مع الخيار ‎--max_size 320x240 سيُنتج مقطعًا أبعاده 320‎×213. إذًا، هذه هي طريقة ترميز مقطع فيديو بنفس الإعدادات التي استخدمناها في القسم السابق: you@localhost$ ffmpeg2theora --videoquality 5 --audioquality 1 --max_size 320x240 pr6.dv سيُحفَظ مقطع الفيديو الناتج في نفس مجلد مقطع الفيديو الأصلي بعد إضافة اللاحقة ‎.ogv له. يمكنك اختيار مسار أو اسم ملف مختلف بتمرير الخيار ‎--output=/path/to/encoded/video إلى ffmpeg2theora. ترميز فيديو H.264 باستخدام HandBrake ملاحظة: سأستخدم المصطلح "فيديو H.264" في هذا القسم اختصارًا للعبارة "فيديو H.264 بنمط (profile) ‏Baseline مع صوت AAC بنمط low-complexity في حاوية MPEG-4"، وهذه تجميعة من المرمازات+حاوية التي تعمل دون إضافات في Safari، و Adobe Flash، وفي هواتف iPhone، والهواتف العاملة بنظام أندرويد. إذا غضضنا النظر عن المشاكل في الترخيص فإن أسهل طريقة لترميز فيديو H.264 هي استخدام HandBrake. ‏HandBrake هو برمجية مفتوحة المصدر مرخصة برخصة GPL لترميز فيديو H.264 (كان تستطيع ترميز صيغ فيديو أخرى فيما سبق، لكن المطورين أسقطوا الدعم عن الصيغ الأخرى لتركيز جهودهم على صيغة H.264). تتوفر ملفات تنفيذية لويندوز و Mac OS X وتوزيعات لينكس الحديثة. يأتي برنامج HandBrake بنسختين: رسومية، وسطرية (أي تعمل من سطر الأوامر). سأشرح لك الواجهة الرسومية أولًا، ثم سأريك كيف يمكن تحويل الإعدادات إلى نسخة سطر الأوامر. أول شيءٍ ستفعله بعد أن تبدأ برنامج HandBrake هو اختيار ملف الفيديو المصدري. اضغط على زر "Source" ثم "Video File" لكي تختار ملفًا. يمكن أن يقبل HandBrake نظريًا أي صيغة من صيغ الفيديو، بما في ذلك فيديو DV الذي تُنتِجه كاميرات الفيديو الرقمية من فئة المستهلكين. الشكل 22: اختر ملف الفيديو الذي تريد ترميزه سيشتكي HandBrake من أنَّك لم تضبط مجلدًا افتراضيًا لتحفظ فيه نواتج ترميز ملف الفيديو. يمكنك أن تتجاهل ذاك التحذير، أو أن تفتح نافذة الخيارات ("options") الواقعة تحت قائمة "Tools" ثم تضبط المجلد الافتراضي. الشكل 23: تجاهل رسالة التحذير الظاهرة هنالك قائمة بأنماط الضبط المسبق (presets) على الجانب الأيمن من البرنامج، اختر منها نمط "iPhone & iPod Touch" الذي سيضبط أغلبية الخيارات التي ستحتاج لها. الشكل 24: اختر نمط iPhone أحد الخيارات المهمة المُعطَّلة افتراضية هو الخيار "Web optimized"، تفعيل هذا الخيار سيؤدي إلى إعادة ترتيب بعض البيانات الوصفية داخل الفيديو المُرمَّز لكي تستطيع مشاهدة بداية الفيديو في أثناء تنزيل الباقي في الخلفية. أنصحك –وبشدة– أن تُفعِّل هذا الخيار دومًا، حيث لا يؤثر على جودة أو حجم الملف الناتج، لذا لن يكون هنالك أي سبب لعدم تفعيله. الشكل 25: فعِّل الخيار "Web optimized" دومًا يمكنك أن تضبط العرض والارتفاع الأقصى لمقطع الفيديو المُرمَّز في لسان "Picture"؛ ويجب عليك دومًا تفعيل الخيار "Keep Aspect Ratio" (أي المحافظة على تناسب الأبعاد) لضمان أنَّ HandBrake لن يشوه مقطع الفيديو عند إعادة تحجيمه. الشكل 26: اضبط العرض والارتفاع يمكنك أن تضبط أربعة خيارات مهمة في لسان "Video": Video Codec: تأكد أنَّ قيمة هذا الخيار هي (H.264 (x264. ‎2-Pass Encoding: إذا فعَّلت هذا الخيار، فسيُشغِّل HandBrake مُرمِّزَ الفيديو مرتين؛ سيحلِّل الفيديو في أول مرة فقط باحثًا عن أشياء مثل تركيبات الألوان، والحركة، ومكان انتهاء المشاهد. وفي المرة الثانية سيُرمِّز الفيديو فعليًا مستخدمًا المعلومات التي جمعها في أول مرة. وكما توقعت، سيستغرق ذلك ضعف المدة الزمنية، لكنه يؤدي إلى دقة أفضل دون زيادة في مساحة الملف التخزينية. أُفعِّل هذا الخيار دومًا عند ترميز H.264. وعليك فعل ذلك ما لم تكن ترمِّز مقاطع الفيديو 24 ساعة في اليوم. Turbo First Pass: بعد أن تُفعِّل "‎2-Pass Encoding" فيمكنك توفير بعض الوقت بتفعيل الخيار "Turbo First Pass"؛ الذي يقلل حجم العمل المُنجَز في أول مرور على الفيديو (أي مرحلة تحليل المقطع)، في حين أنَّه يقلل الجودة بشكل بسيط جدًا. أُفعِّل هذا الخيار عادةً، لكن إن كانت جودة الفيديو مهمةً جدًا لك، فعليك إبقاؤه معطلًا. Quality: هنالك عدِّة طرق لتحديد "جودة" مقاطع الفيديو: يمكنك تحديد المساحة التخزينية التي تتوقعها للملف الناتج، وسيحاول HandBrake جاهدًا أن يضمن أنَّ الملف الناتج ليس أكبر من المساحة المُحدَّدة. أو يمكنك أن تُحدِّد معدل "البث" (bitrate) الوسطي، الذي هو عدد البتات اللازمة لتخزين ثانية واحدة من الفيديو (ولقد سمي معدل البث "الوسطي" لأن بعض الثواني تحتاج إلى بتات أكثر من غيرها). أو يمكنك أن تُحدِّد جودةً ثابتةً على مقياسٍ من 0 إلى 100%. كلما ازدادت النسبة ستزداد الجودة لكن الملفات ستستهلك مساحة تخزينية أكبر. لا يمكن أن أعطيك جوابًا واحدًا عن ضبط الجودة الذي عليك استخدامه. س: هل يمكنني المرور مرتين (two-pass) في فيديو Ogg أيضًا؟ ج: نعم، لكن بسبب الاختلافات الجوهرية في طريقة عمل المُرمِّز، فمن المرجح أنَّك لن تحتاج إلى فعل ذلك. المرور مرتين عند الترميز بمرماز H.264 يضمن لك (في الغالبية العظمى من الحالات) زيادة في جودة مقطع الفيديو. أما المرور مرتين في فيديو Ogg مفيدٌ فقط إذا كنت تحاول أن يكون المقطع الناتج ذو مساحةٍ تخزينيةٍ محدَّدة (ربما سيثير هذا الأمر اهتمامك، لكن ذلك ليس غرض الأمثلة المذكورة هنا، ولا أرى أنَّ ذلك مفيدٌ جدًا لمقاطع الفيديو التي ستُنشَر على الويب). لأفضل جودة لفيديو Ogg، استخدم خيارات جودة الفيديو، ولا تعبأ بتقنية المرور مرتين. لقد اخترت في هذا المثال معدل بث وسطي هو 600 كيلوبت/ثانية، الذي يُعتبَر مرتفعًا بالنسبة إلى فيديو بأبعاد 320x240، واخترت أيضًا ترميز مع المرور مرتين، ويكون أول مرور "سريعًا" (turbo). الشكل 27: خيارات جودة الفيديو لا يفترض عليك تعديل أي شيء في لسان "Audio"؛ أما لو كان لمقطع الفيديو المصدري أكثر من مسار صوتي، فربما تريد اختيار أيُّ المسارات تريدها في الفيديو الناتج. إذا كان محتوى الفيديو في مجمله عن شخصٍ يتحدث (على النقيض من الموسيقى أو الأصوات العامة)، فيمكنك تخفيض معدل البث للصوت إلى 96 كيلوبت/ثانية أو ما شابهه. فيما عدا الحالتين السابقتين، تكون القيم الافتراضية المأخوذة من نمط "iPhone" جيدةً عمومًا. الشكل 28: خيارات جودة الصوت ثم اضغط على زر "Browse" واختر مجلدًا واسم ملف لتحفظ فيه مقطع الفيديو الناتج. الشكل 29: اختر اسم الملف الناتج في النهاية اضغط على "Start" لبدء ترميز الفيديو. الشكل 30: بدء عملية ترميز الفيديو سيعرض HandBrake بعض الإحصائيات عن تقدم عملية ترميز مقطع الفيديو. الشكل 31: "إنَّ غدًا لناظره قريب" ترميز عدة مقاطع إلى H.264 دفعة واحدة باستخدام HandBrake ملاحظة: كما في القسم السابق، سأستخدم المصطلح "فيديو H.264" في هذا القسم اختصارًا للعبارة "فيديو H.264 بنمط (profile) ‏Baseline مع صوت AAC بنمط low-complexity في حاوية MPEG-4"، وهذه تجميعة من المرمازات+حاوية التي تعمل دون إضافات في Safari، و Adobe Flash، وفي هواتف iPhone، والهواتف العاملة بنظام أندرويد. يأتي HandBrake بنسخة سطرية (أي تعمل من سطر الأوامر) كما هو الحال في ffmpeg2theora، نسخة سطر الأوامر من HandBrake توفر عددًا هائلًا من الخيارات (اكتب HandBrakeCLI --help لتقرأ المزيد عنها)، لكنني سأركِّز على بعضها: ‎--preset "X"‎: حيث "X" هو اسم نمط (preset) من أنماط ضبط HandBrake. النمط الذي تريد استخدامه لفيديو H.264 المُخصَّص للويب هو "iPhone & iPod Touch"، ومن المهم أن تضع الاسم بأكمله ضمن علامتَي اقتباس. ‎--width W: حيث "W" هو عرض الفيديو الذي تريد ترميزه، وسيُعدِّل HandBrake الارتفاع تلقائيًا ليُحافِظ على تناسب أبعاد المقطع الأصلي. ‎--vb Q: حيث "Q" هو معدَّل البث الوسطي (مُقاسًا بواحدة الكيلوبت/ثانية). ‎--two-pass: تفعيل المرور مرتين (‎2-pass) أثناء الترميز. ‎--turbo: تسريع المرور الأول عند تفعيل ميزة المرور مرتين أثناء الترميز. ‎--input F: حيث "F" هو مسار ملف الفيديو المصدري. ‎--output E: حيث "E" هو مسار ملف الفيديو الناتج. هذا مثالٌ عن كيفية استدعاء HandBrake من سطر الأوامر، مع استخدام خيارات تُطابِق الإعدادات التي اخترناها في الواجهة الرسومية سابقًا. you@localhost$ HandBrakeCLI --preset "iPhone & iPod Touch" --width 320 --vb 600 --two-pass --turbo --input pr6.dv --output pr6.mp4 شرح الأمر السابق من الأعلى إلى الأسفل: تشغيل HandBrake مع نمط "iPhone & iPod Touch"، وإعادة تحجيم المقطع إلى 320x240، وضبط معدل البث الوسطي إلى 600 كيلوبت/ثانية، وتفعيل خيار المرور مرتين مع تسريع أول مرور، وقراءة الملف المصدري pr6.dv وترميزه وإخراج الملف النهائي إلى pr6.mp4. ترميز فيديو WebM باستخدام FFMPEG دُعِمَت صيغة WebM دعمًا كاملًا في ffmpeg 6.0 وما بعده. نفِّذ الأمر ffmpeg في سطر الأوامر دون وسائط وتحقق أنَّه مبنيٌ مع دعم مرماز VP8: you@localhost$ ffmpeg FFmpeg version SVN-r23197, Copyright (c) 2000-2010 the FFmpeg developers built on May 19 2010 22:32:20 with gcc 4.4.3 configuration: --enable-gpl --enable-version3 --enable-nonfree --enable-postproc --enable-pthreads --enable-libfaac --enable-libfaad --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libtheora --enable-libx264 --enable-libxvid --enable-x11grab --enable-libvorbis --enable-libvpx إن لم تشاهد الكلمتين السحريتين ‎--enable-libvorbis و ‎--enable-libvpx فلا يدعم إصدار ffmpeg المثبت لديك WebM (إذا بَنَيتَ ffmpeg من المصدر بنفسك، فتحقق إذا كانت لديك نسختين منه. لا بأس في ذلك، فلن يتعارضا مع بعضهما، لكن عليك استخدام المسار الكامل لنسخة ffmpeg التي تدعم مرماز VP8). سأستخدم ميزة المرور مرتين، سيمسح (scan) المرور الأول ملف الدخل (‎-i pr6.dv) وسيكتب بعض الإحصائيات إلى السجل (الذي سيُسمَّى تلقائيًا باسم pr6.dv-0.log). وسأُحدِّد مرماز الفيديو باستخدام الخيار ‎-vcodec: you@localhost$ ffmpeg -pass 1 -passlogfile pr6.dv -threads 16 -keyint_min 0 -g 250 -skip_threshold 0 -qmin 1 -qmax 51 -i pr6.dv -vcodec libvpx -b 614400 -s 320x240 -aspect 4:3 -an -y NUL غالبية خيارات الأمر ffmpeg السابق ليس لها علاقة بمرماز VP8 أو صيغة WebM. تدعم مكتبة libvpx عددًا من الخيارات الخاصة بمرماز VP8 التي يمكنك تمريرها إلى ffmpeg، لكنني لم أفعل ذلك في الأمر السابق. وعند المرور مرةً أخرى على الملف، فسيقرأ ffmpeg الإحصائيات التي كتبها أثناء المرور الأول وسيبدأ بترميز الفيديو والصوت، ثم سيكتب ملف ‎.webm. you@localhost$ ffmpeg -pass 2 -passlogfile pr6.dv -threads 16 -keyint_min 0 -g 250 -skip_threshold 0 -qmin 1 -qmax 51 -i pr6.dv -vcodec libvpx -b 614400 -s 320x240 -aspect 4:3 -acodec libvorbis -y pr6.webm هنالك خمسة خيارات مهمة للأمر السابق: ‎-vcodec libvpx: يُحدِّد أننا نريد ترميز المقطع بمرماز VP8. تستخدم صيغة WebM مرماز VP8 للفيديو دومًا. ‎-b 614400: تحديد معدل البث (bitrate). وعلى النقيض من بقية الصيغ، تتوقع مكتبة libvpx أن يُعطى معدل البث بالبتات، وليس بالكيلوبت. فإذا أردت ترميز مقطع فيديو بمعدل بث 600 كيلوبت/ثانية، فعليك ضرب 6000 بالعدد 1024 لتحصل على 614400. ‎-s 320x240: تحديد أبعاد الفيديو الناتج، العرض ثم الارتفاع. ‎-aspect 4:3: تحديد نسبة أبعاد الفيديو. مقاطع الفيديو ذات الجودة العالية تكون نسبة أبعادها 4:3 عادةً، أما أغلبية مقاطع الفيديو عالية الجودة (HD) فتكون نسبة أبعادها 16:9 أو 16:10. أثناء تجاربي واختباراتي، وجدتُ أنَّ عليّ تحديد هذه القيمة بوضوح في سطر الأوامر، بدلًا من الاعتماد على ffmpeg لكي يكتشفها بنفسه. ‎-acodec libvorbis: تحديد أننا نريد ترميز الصوت بمرماز Vorbis. تستخدم صيغة WebM مرماز Vorbis للصوت دومًا. ترجمة -وبتصرّف- لفصل "Video" من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  19. أي شخصٍ زار موقع YouTube في الأعوام الماضية يعلم تمام العلم أنَّ بالإمكان تضمين مقاطع الفيديو في صفحات الويب؛ لكن لم تكن هنالك طريقةٌ معياريةٌ لفعل ذلك قبل وجود HTML5. نظريًا كل مقاطع الفيديو التي سبق وأن شاهدتها على "الويب" تمَّ تشغيلها عبر إضافة خارجية (ربما QuickTime أو RealPlayer أو Flash ‏-YouTube يستخدم تقنية Flash-). تتكامل تلك الإضافات مع متصفحك بشكلٍ ممتاز إلى درجة أنَّك لن تلاحظ استخدامها إلى أن تحاول مشاهدة مقطع فيديو على منصة (أو جهاز) لا تدعم تلك الإضافة. تُعرِّف HTML5 طريقةً معياريةً لتضمين مقاطع الفيديو في صفحة الويب وذلك باستخدام العنصر <video>، ما يزال دعم العنصر <video> قيد التطوير، وهذه طريقةٌ مهذبةٌ للتصريح أنَّه لا يعمل بعد، أو على الأقل لا يعمل في كل مكان؛ لكن لا تقنط ولا تقلق، هنالك بدائل وخيارات أخرى كثيرة نستطيع اللجوء إليها. يبيّن الجدول الآتي إصدارات مختلف المتصفحات التي تدعم العنصر <video>: IE Firefox Safari Chrome Opera iPhone Android 9.0+ 3.5+ 3.0+ 3.0+ 10.5+ 1.0+ 2.0+ لكن الدعم لعنصر <video> نفسه ليس كافيًا بل هو جزءٌ صغيرٌ من الحكاية. لكن قبل أن نتحدث عن دعم الفيديو في HTML5، علينا أن نفهم بعض المعلومات حول مقاطع الفيديو نفسها (إن كنت تعرف تلك المعلومات، فيمكنك تخطي الفقرات الآتية والبدء من فقرة "ما الصيغ التي تعمل في الويب"). حاويات الفيديو قد تتخيل ملفات الفيديو على أنها ملفات "AVI" أو "MP4". لكن "AVI" و "MP4" في الواقع ما هي إلا صيغ حاوية للفيديو. فهي تُشبِه ملفات ZIP التي يمكنها احتواء أي نوع من الملفات ضمنها، فصيغ حاويات الفيديو (video container formats) تُعرِّف طريقة تخزين الأشياء ضمنها فقط، ولا تُحدِّد ما هي أنواع البيانات المُخزَّنة (الأمر أكثر تعقيدًا من هذا، لعدم توافق جميع أنواع مسارات (أو دفق) الفيديو (video streams) مع جميع صيغ الحاويات، لكن اغضض الطرف عنها الآن). يحتوي ملف الفيديو عادةً على عدِّة "مسارات" (tracks)، التي هي مسار الفيديو (دون صوت) بالإضافة إلى مسار صوتي أو أكثر (دون فيديو). تكون المسارات مترابطةً عادةً، فيحتوي مسار الصوت على مؤشرات أو علامات ضمنه للمساعدة في مزامنة الصوت مع الفيديو. يمكن لكل مسار أن يملك بيانات وصفية (metadata)، مثل نسبة العرض إلى الارتفاع في مسار الفيديو، أو لغة مسار الصوت. يمكن للحاويات أيضًا امتلاك بيانات وصفية، مثل عنوان (title) الفيديو نفسه، وغلاف (cover) للفيديو، وأرقام الحلقات (للمسلسلات)، وهلمَّ جرًا. هنالك العديد والعديد من صيغ حاويات الفيديو، هذه أشهرها: MPEG 4، التي تأتي عادةً مع اللاحقة ‎.mp4 أو ‎.m4v، حاوية MPEG 4 مبنية على حاوية QuickTime القديمة (من Apple) ‎.mov، ما تزال تستخدم "الأفلام القصيرة" في موقع Apple حاوية QuickTime القديمة، لكن الأفلام التي تأخذها من iTunes مبنية على حاوية MPEG 4. Flash Video، تأتي عادةً مع اللاحقة ‎.flv، صيغة Flash Video (بدهيًا) تُستعمَل من Adobe Flash؛ وكانت هذه هي صيغة الحاويات الوحيدة التي كان يدعمها Flash قبل الإصدار 9.0.60.184 (أي مُشغِّل Flash الإصدار 9 التحديث 3). الإصدارات الحديثة من Flash تدعم حاوية MPEG 4 أيضًا. Ogg، تأتي عادةً مع اللاحقة ‎.ogv؛ صيغة Ogg معيارها مفتوح المصدر، وغير مرتبطة بأيّة براءات اختراع. متصفحات Firefox 3.5 و Chrome 4 و Opera 10.5 تدعم -داخليًا، دون إضافات خاصة- الحاوية Ogg، ومسار فيديو Ogg (المُسمى "Theora")، ومسار صوت Ogg (المُسمى "Vorbis"). صيغة Ogg مدعومة تلقائيًا في جميع توزيعات لينُكس المشهورة، ويمكنك استخدامها على Mac وويندوز بتثبيت "QuickTime components" أو "DirectShow filters" على التوالي وبالترتيب. من الممكن أيضًا تشغيل هذه الصيغة ببرنامج VLC على جميع المنصات. WebM، هي صيغة حاويات جديدة، وهي -تقنيًا- شبيهة بصيغة أخرى تُسمى Matroska. أُعلِنَ عن WebM في أيار/مايو من عام 2010، وهي مصمَّمة لكي تُستخدم حصريًا مع مرماز الفيديو VP8 ومرماز الصوت Vorbis (سنأتي على ذكرهما بعد قليل)، وهذه الصيغة مدعومةٌ داخليًا -دون إضافات خاصة- في آخر إصدار من Chromium و Google Chrome و Mozilla Firefox و Opera، وأعلنت Adobe أنَّ Flash 10.1 سيدعم صيغة WebM. Audio Video Interleave، التي تأتي عادةً مع اللاحقة ‎.avi تم ابتكار صيغة AVI من مايكروسوفت منذ وقتٍ طويل، حين كانت إمكانية تشغيل الحواسيب لمقاطع الفيديو أمرًا عظيمًا. لا تدعم هذه الصيغة –رسميًا– ميزات صيغ الحاويات الأحدث منها مثل البيانات الوصفية المدمجة، ولا تدعم أيضًا -رسميًا- أغلبية ترميزات الفيديو والصوت المُستعمَلة حاليًا. وقد حاولت الشركات مع مرور الوقت توسعة هذه الصيغة بطرق غير متوافقة مع بعضها بعضًا لدعم بعض الميزات، ولكنها مع ذلك بقيت صيغة الحاوية الافتراضية لبعض المُرمِّزات (encoders) مثل MEncoder. مرمزات الفيديو عندما تتحدث عن "مشاهدة مقطع فيديو" فغالبًا ستقصد مشاهدة تجميعة من مسار للفيديو ومسار للصوت؛ لكن ليس عندك ملفان منفصلان، فكل ما لديك هو ملف "فيديو" واحد، وربما يكون ملف AVI أو MP4؛ تلك هي صيغ الحاويات، مثل ملف ZIP الذي يحتوي على عدِّة أنواع من الملفات داخله. تُعرِّف صيغة الحاوية آلية تخزين مسارات الفيديو والصوت في ملفٍ وحيد. سيقوم مُشغِّل الفيديو بثلاثة أشياء على الأقل عندما "تشاهد مقطع فيديو": محاولة تفسير صيغة الحاوية لمعرفة ما هي مسارات الفيديو والصوت المتوفرة، وطريقة تخزينها ضمن الملف كي يعرف أين سيعثر على البيانات التي يجب عليه فك ترميزها لاحقًا فك ترميز مسار الفيديو وعرض سلسلة من الصور على الشاشة فك ترميز مسار الصوت وإرسال الأصوات إلى مكبرات الصوت في جهازك مرماز الفيديو (video codec) هو الخوارزمية التي يُرمَّز (encode) فيها مقطع الفيديو، أي أنَّه يُحدِّد طريقة القيام بالخطوة رقم 2 مما سبق (الكلمة "codec" آتية من دمج الكلمتين "coder" و "decoder"). يفك مُشغِّل الفيديو ترميز (decode) مسار الفيديو وفقًا لمرماز الفيديو المستخدم، ثم سيعرض سلسلة من الصور أو "الإطارات" على الشاشة، أغلبية مرمزات الفيديو الحديثة تستخدم حيلًا عديدة لتقليل حجم المعلومات اللازمة لعرض الإطار تلو الإطار. على سبيل المثال، بدلًا من تخزين كل إطار على حدة (كأنه لقطة شاشة)، فسيتم تخزين الاختلافات بين الإطارين. لا يحدث في أغلبية مقاطع الفيديو تغيرات كبيرة بين الإطارات المتتالية، مما يسمح بضغطها بدرجة أكبر، مما يؤدي إلى تقليل المساحة التخزينية للملف. هنالك مرمازات فيديو تؤدي إلى ضياع في البيانات (lossy) وأخرى لا تؤدي إلى ضياع البيانات (lossless). المساحة التخزينية لملفات الفيديو التي ليس فيها ضياعٌ في البيانات كبيرةٌ جدًا ولن تكون ذات فائدةٍ في الويب، لهذا سنركِّز على المرمازات التي تؤدي إلى ضياعٍ في البيانات. مرماز يؤدي إلى ضياعٍ في البيانات يعني أنَّ بعض المعلومات ستضيع دون إمكانية استعادتها أثناء عملية الترميز (encoding)، مثل عملية نسخ كاسيت صوتي إلى آخر. ستفقد في هذه العملية بعض المعلومات المتعلقة بالفيديو المصدري، وستُقلِّل الجودة في كل مرة تُعيد ترميز المقطع فيها. فبدلًا من صوت "التشويش" في شريط الكاسيت (إن كنت تستعمله في صغرك)، سيبدو مقطع الفيديو الذي تُعيد ترميزه مراتٍ عدِّة غير واضحٍ، خصوصًا المشاهد التي فيها الكثير من الحركة (في الواقع، يمكن أن يحدث ذلك حتى لو قمتَ بالترميز من المصدر مباشرةً، فربما اخترت مرماز فيديو سيئ أو مررت مجموعة وسائط خاطئة). أما على الكفة الأخرى، تضغط مرمزات الفيديو التي تؤدي إلى ضياعٍ في البيانات مقاطع الفيديو ضغطًا كبيرًا ويصعب في الوقت نفسه ملاحظة فقدان البيانات بالعين المجردة. هنالك الكثير من مرمازات الفيديو، لكن أهم ثلاثة منها هي H.264، و Theora، و VP8. H.264 H.264 معروفٌ أيضًا باسم "MPEG-4 part 10" أو "MPEG-4 AVC" أو "MPEG-4 Advanced Video Coding". طوِّرَ مرماز H.264 من MPEG group وتم جعله معيارًا قياسيًا في 2003، ويهدف إلى توفير مرماز واحد للأجهزة ذات التراسل الشبكي المحدود وقدرة المعالجة المحدودة (أي الهواتف المحمولة)، وللأجهزة التي يتوفر لها تراسلٌ شبكيٌ كبير وقدرة معالجة ممتازة (مثل الحواسيب الحديثة)، وأي شيء يقع بينهما. ولكي يتم تحقيق ذلك، قُسِّم معيار H.264 إلى "أنماط" (profiles) التي يُحدِّد كلٌ منها مجموعة من الميزات الاختيارية التي توازن بين تعقيد ترميز الملف ومساحته التخزينية. الأنماط العليا (Higher profiles) تستخدم ميزات اختيارية أكثر، وتوفر جودةً أكبر بمساحة تخزينية أقل، لكنها تأخذ وقتًا أطول لتُرمَّز، وتستهلك طاقة معالجة أكبر لفك الترميز في الوقت الحقيقي. لإعطائك فكرةً عامةً عن تنوع الأنماط: يدعم iPhone ‏نمط "Baseline"، ويدعم AppleTV نمطي ‏"Baseline" و "Main"، بينما يدعم Adobe Flash على الحواسيب أنماط "Baseline" و "Main" و "High". ويستعمل YouTube الآن مرماز H.264 لترميز مقاطع الفيديو عالية الدقة (HD) التي يمكن تشغليها عبر Adobe Flash؛ ويدعم YouTube أيضًا إرسال مقاطع الفيديو المرمزة بمرماز H.264 إلى الهواتف المحمولة، بما في ذلك هواتف iPhone والهواتف العاملة بنظام أندرويد. إضافةً إلى ما سبق، مرماز H.264 هو المرماز الذي تستعمله مواصفة Blu-Ray، إذ تستخدم أقراص Bul-Ray ‏نمط "High" في العموم. أغلبية الأجهزة التي تُشغِّل فيديو H.264 عدا الحواسيب (بما في ذلك هواتف iPhone وقارئات Blu-Ray) تستعمل شريحة منفصلة لفك ترميز الفيديو، لأن معالجاتها المركزية ضعيفة نسبيًا لفك ترميز الفيديو في الوقت الحقيقة. وأصبحت في هذه الأيام بطاقات العرض الرخيصة للحواسيب المكتبية تدعم فك ترميز فيديو H.264 عتاديًّا. وهنالك أيضًا تنافس بين مرمزات H.264، بما في ذلك المكتبة مفتوحة المصدر x264. معيار H.264 محمي ببراءات اختراع؛ ويتم الترخيص عبر MPEG LA group. يمكن تضمين فيديو H.264 في أغلبية الحاويات الحديثة، بما في ذلك MP4 (تستعملها Apple بشكلٍ أساسي في متجر iTunes) و MKV (التي تُستعمَل بشكلٍ أساسي من هواة الفيديو الذين ليس لهم هدف تجاري). Theora تم تطوير Theora من مرماز VP3 ثم طوِّرَ من مؤسسة Xiph.org.‏ Theora مرماز مجاني الاستخدام (royalty-free) وليس محميًا بأيّة براءات اختراع عدا براءات اختراع مرماز VP3 الأصلي، الذي رُخِّص على أنَّه مجانيُ الاستخدام أيضًا. على الرغم من أنَّ المعيار قد "جُمِّدَ" (frozen) منذ عام 2004، إلا أنَّ Theora project (الذي يتضمن مكتبات ترميز وفك ترميز مفتوحة المصدر) قد أصدر النسخة 1.0 في تشرين الثاني/نوفمبر عام 2008، والإصدار 1.1 في أيلول/سبتمبر عام 2009. يمكن تضمين الفيديو المرمَّز بمرماز Theora في أيّة صيغة من صيغ الحاويات، لكن من الشائع أن نراه في حاوية Ogg. تدعم جميع توزيعات لينكس Theora، ويتضمن متصفح Mozilla Firefox 3.5 دعمًا داخليًا للفيديو المرمز بمرماز Theora؛ وأقصد بكلمة "داخليًا" أنَّه متوفرٌ على جميع الأنظمة دون إضافات خاصة. يمكنك أيضًا تشغيل الفيديو المرمز بمرماز Theora على ويندوز أو على Mac OS X بعد تثبيت برمجية فك الترميز المفتوحة المصدر من Xiph.org. VP8 VP8 هو مرماز فيديو آخر من On2، نفس الشركة التي طوَّرت VP3 (الذي أصبح لاحقًا Theora). بكلامٍ تقني، يُنتِج هذا المرماز فيديو بنفس جودة النمط "High" في H.264، في حين يحاول تقليل تعقيد عملية فك الترميز كما في نمط "Baseline" في H.264. في عام 2010، اشترت Google شركة On2 ونشرت مواصفات مرماز الفيديو وأصدرت برمجية ترميز وفك ترميز مفتوحة المصدر. وكان جزءًا من هذه العملية هو "فتح" Google لجميع براءات الاختراع التي سجلتها شركة On2 لمرماز VP8، وذلك بجعلها مجانية الاستخدام أي royalty-free (وهذا أفضل ما يمكن فعله مع براءات الاختراع، فلا يمكنك حقًا "التخلي" عنها أو حذفها بعد تسجيلها. لكن لجعلها متوافقة مع البرمجيات مفتوحة المصدر فيمكن ترخيص للاستخدام مجانًا، وبهذا يستطيع أي شخصٍ استخدام التقنيات المُغطاة من براءة الاختراع دون دفع أي شيء أو دون محاولة الحصول على ترخيص خاص). وبهذا أصبح VP8 مرماز عصري مجاني الاستخدام ليس محميًا بأيّة براءات اختراع عدا تلك التي سجلتها شركة On2 (وتملكها Google حاليًا) والتي يمكن استعمالها مجانًا. مرمزات الصوت ستحتاج إلى تضمين مسار صوتي في مقاطع الفيديو إلا إذا كنتَ ستنشر الأفلام قبل عام 1927 فقط. ومثل مرمزات الفيديو، مرمازات الصوت هي خوارزميات التي يُرمَّز (encode) بها مسار الصوت. وأيضًا مثل مرمازات الفيديو، هنالك مرمازات صوت ليس فيها ضياعٌ في البيانات (lossless) وأخرى تضيع فيها بعض البيانات (lossy). وكما هو حال الفيديو الذي لا يوجد فيه ضياعٌ في البيانات، ستكون المساحة التخزينية لمسارات الصوت التي ليس فيها ضياعٌ في البيانات كبيرةً جدًا لكي نستفيد منها على الويب؛ لذلك سنركِّز على مرمازات الصوت التي تسبب فقدانًا لبعض البيانات. وسأضيق المجال قليلًا، لوجود تصنيفاتٍ مختلفة لمرمازات الصوت؛ إذ أنَّ الصوت يستعمل في أماكن لا يُستعمَل فيها الفيديو (في الهواتف مثلًا)، وهنالك تصنيفٌ كاملٌ من مرمازات الصوت المُتخصصة بترميز الكلام، فلا تفكر في ترميز الموسيقى بهذه المرمازات، لأن النتيجة النهائية ستبدو وكأن طفلًا ذا أربعة أعوام يغني على الهاتف. لكنك تستطيع استخدامها في خدمة PBX من Asterisk لأن تراسل البيانات ثمين هناك، ولأن هذه المرمازات تضغط الصوت البشري ليأخذ مساحةً تخزينيةً لا تشكِّل إلا جزءًا بسيطًا من المساحة التخزينية التي كانت ستأخذها المرمازات ذات الغرض العام. لكن نتيجةً لعدم دعم تلك المرمازات في المتصفحات داخليًا أو عبر الإضافات الخارجية، فلم يسطع نجم تلك المرمازات في الويب، ولهذا سنركِّز على مرمزات الصوت العامة التي تسبب ضياعًا في بعض البيانات. وكما ذكرنا سابقًا، سيقوم حاسوبك بثلاثة أشياء على الأقل في وقتٍ واحد عندما "تشاهد مقطع فيديو": محاولة تفسير صيغة الحاوية فك ترميز مسار الفيديو فك ترميز مسار الصوت وإرسال الأصوات إلى مكبرات الصوت في جهازك يُحدِّد مرماز الصوت طريقة القيام بالخطوة رقم 3 مما سبق: فك ترميز مسار الصوت وتحويله إلى موجات رقمية يمكن لمكبرات الصوت عندك تحويلها إلى صوت. وكما في مرمازات الفيديو، هنالك حيلٌ عدِّة تُستعمَل لتقليل حجم البيانات المُخزَّنة في مسار الصوت. ولمّا كنّا نتحدث عن مرمازات الصوت التي تُسبِّب ضياعًا في بعض البيانات، فسنفقد بعض المعلومات أثناء عملية "التسجيل ← الترميز ← فك الترميز ← الاستماع". تحذف مختلف مرمزات الصوت أشياءً مختلفة، لكنها كلها تخدم نفس الغرض: ألّا تشعر بضياعِ أيِّ شيءٍ عندما تستمع إلى المسار. هنالك مفهومٌ في مسارات الصوت ليس موجودًا في مسارات الفيديو ألا وهو "القنوات" (channels). نحن نرسل الصوت إلى مكبرات الصوت، صحيح؟ حسنًا، ما عدد مكبرات الصوت عندك؟ إذا كنت تجلس أمام حاسوبٍ محمول فربما يكون عندك مكبران: أحدهما على اليمين والآخر على اليسار. لكن حاسوبي المكتبي يملك ثلاثة مكبرات: واحد على اليمين وآخر على اليسار وثالث على الأرض. أنظمة "الصوت المحيطي" (surround sound) السمعية تملك ستة مكبرات أو أكثر متوزعة توزيعًا مدروسًا في أنحاء الغرفة. مهمة كل مكبر أن يُخرِج قناة (channel) معيّنة من التسجيل الأصلي. الفكرة النظرية وراء ذاك النظام هي أنَّك ستجلس في منتصف المسافة الفاصلة بين تلك المكبرات محاطًا بست قنوات منفصلة من الصوت، وسيدرك مخك الصوت وستشعر كما لو أنك في منتصف الأحداث التي تسمع صوتها. لكن هل تعمل فعلًا كما شرحنا أعلاه؟ تقول الشركات ذوات رؤوس الأموال الكبيرة أنها تعمل. تستطيع أغلبية مرمازات الصوت ذات الغرض العام التعامل مع قناتين من الصوت. يُقسَم الصوت أثناء التسجيل إلى قناتين يمنى ويسرى؛ وستُخزَّن كلا القناتين في نفس المسار الصوتي أثناء الترميز؛ وستُرسَل محتويات كل قناة إلى مكبر الصوت الموافق لها أثناء فك الترميز. يمكن لبعض مرمزات الصوت التعامل مع أكثر من قناتين، إذ يعرفون أي قناة يجب أن تُرسَل لأي مكبر ثم سيتولى مُشغِّل الصوتيات عندك الأمر. هنالك الكثير من مرمازات الصوت، هل ذكرتُ لك سابقًا أنَّ هنالك الكثير من مرمازات الفيديو؟ انسَ ذلك! هنالك الكثير والكثير من مرمازات الصوت، لكن يهمنا في الويب ثلاثةٌ منها: MP3 و ACC و Vorbis. MPEG-1 Audio Layer 3 MPEG-1 Audio Layer 3 معروفة بالعامية على أنها "MP3"؛ لا أعرف من أي كوكبٍ أتيت إن لم تسمع من قبل عن MP3. تبيع شركة Walmart مشغِّلات موسيقى محمولة وتسميها "MP3 players". وهي منتشرة انتشارًا كبيرًا. يمكن أن تحتوي ملفات MP3 على قناتين صوتيتين على الأكثر، ويمكن ترميزها بمختلف معدلات البث (أي bitrates): 64 كيلوبت/ثانية، أو 128 كيلوبت/ثانية، أو192 كيلوبت/ثانية، أو غيرها التي تتراوح قيمها من 32 إلى 320. قيم معدلات البث (bitrates) الأعلى تعني أنَّ مساحة الملفات أكبر وجودة الصوت أدق، لكن العلاقة بين جودة الصوت ومعدل البث ليست خطيّة (linear، أي أنَّ جودة الأصوات المرمزة بمعدل بث 64 كيلوبت/ثانية أفضل بالضعفين من 64 كيلوبت/ثانية، لكن 256 كيلوبت/ثانية ليس أكثر جودة بمقدار الضعفين من 128 كيلوبت/ثانية). تسمح صيغة MP3 بالترميز ذي معدل البث المتغير (variable bitrate encoding، الذي يعني أنَّ أجزاءً من المسار مضغوطةٌ أكثر من غيرها). على سبيل المثال، يمكن ترميز السكت بين النوتات الموسيقية بمعدل بث منخفض، ثم سيرتفع معدل البث عندما يُعزَف لحنٌ معقَّدٌ على عدِّة آلات موسيقية. يمكن أيضًا ترميز MP3 بمعدل بث ثابت، ولا غرابة أن يُسمى ذلك بالترميز ذي معدل البث الثابت (constant bitrate encoding). لا يُعرِّف معيار MP3 كيفية الترميز باستخدام MP3 تمامًا (على الرغم من أنَّه يُعرِّف تمامًا كيفية فك الترميز)؛ تستخدم مختلف المُرمِّزات آلياتٍ مختلفة تُنتِج نتائج متنوعة كثيرًا، لكن يمكن قراءتها جميعًا من نفس المشغِّلات. مشروع LAME المفتوح المصدر هو أفضل مرمِّز حر، ويمكن القول أنَّه أفضل المرمِّزات لجميع معدلات البث إلا المنخفضة منها. صيغة MP3 (التي وُضِعَ معيارها عام 1991) هي صيغة محمية ببراءة اختراع، وهذا هو السبب وراء عدم إمكانية تشغيل نظام لينكس لملفات MP3 مباشرةً. وبشكل عام، تدعم جميع مشغلات الموسيقى المحمولة ملفات MP3، ويمكن تضمين مسارات MP3 ضمن أيّ حاوية فيديو. ويمكن أن يُشغِّل Adobe Flash ملفات MP3 لوحدها أو مسارات MP3 الموجودة ضمن حاوية MP4. Advanced Audio Coding Advanced Audio Coding المعروفة أيضًا باسم "AAC" التي وضِعَ معيارها عام 1997، والتي أنتشر صيتها عندما اختارتها Apple صيغةً افتراضيةً لمتجر iTunes. كانت جميع ملفات AAC التي تم "شراؤها" من متجر iTunes مشفرةً بحقوق رقمية مملوكة لشركة Apple ‏(أي DRM) التي كانت تدعى FairPlay؛ أصبحت بعض الأغنيات متاحةً في متجر iTunes كملفات AAC غير محمية، التي تدعوها Apple ‏"iTunes Plus" لأن ذلك أفضل من تسمية جميع الأشياء الأخرى باسم "iTunes Minus". صيغة AAC محمية ببراءات اختراع؛ وأسعار الترخيص متوفرة على الإنترنت. صُمِّمَت AAC لتوفير صوت بجودة أعلى من MP3 بنفس معدل البث (bitrate)، ويمكن ترميز الصوت بأي معدل بث (صيغة MP3 محدودة بعدد ثابت من معدلات البث، حدّها الأقصى هو 320 كيلوبت/ثانية). يمكن لصيغة AAC ترميز 48 قناة صوتية كحد أقصى، على الرغم من عدم وجود حالة لتطبيق ذلك عمليًا. تختلف صيغة AAC عن MP3 في أنها تُعرِّف أنماطًا (profiles) بطريقة مشابهة لمرمز H.264 ولنفس الأسباب. فهنالك "نمط" بسيط مُصمَّم لكي يُشغَّل في الوقت الحقيقي على الأجهزة ذات قدرة المعالجة المحدودة، بينما توجد "أنماط" توفر صوتًا أكثر جودةً بنفس معدل البث لكن ذلك على حساب البطء في الترميز وفك الترميز. جميع منتجات Apple الحالية بما فيها iPod و AppleTV و QuickTime تدعم بعض "profiles" صيغة AAC في ملفات الصوت المنفصلة وفي مسارات الصوت المدمجة في حاوية MP4. يدعم Adobe Flash جميع "أنماط" صيغة AAC في MP4، وكذلك المشغلات الحرة مثل MPlayer و VLC. أما للترميز، فمكتبة FAAC هي مكتبةٌ مفتوحة المصدر؛ وهنالك خيارٌ في وقت البناء (compile-time) في برمجية mencoder و ffmpeg لدعم ترميز AAC. Vorbis Vorbis تُسمى عادةً "Ogg Vorbis" (على الرغم من أنَّ هذا خاطئ تقنيًا، لأن "Ogg" هي صيغة حاويات ويمكن لمسارات صوت Vorbis أن تُضمَّن في حاويات أخرى). صيغة Vorbis ليست محمية بأيّة براءات اخترع ولهذا ستجدها مدعومةً في توزيعات لينكس مباشرةً وفي جميع الأجهزة المحمولة التي تُشغِّل نظام Rockbox. يدعم Mozilla Firefox 3.5 مسارات صوت Vorbis في حاوية Ogg، أو فيديو Ogg مع مسار صوتي بصيغة Vorbis، ويمكن أيضًا للهواتف العاملة بنظام أندرويد أن تُشغِّل ملفات Vorbis الصوتية المستقلة. عادة ما تُضمَّن مسارات Vorbis الصوتية في حاوية Ogg أو WebM، لكن يمكن أيضًا تضمينها في حاوية MP4 أو MKV (أو بعد تطبيق بعض الحيل: في حاوية AVI). تدعم صيغة Vorbis الصوتية أي عدد من القنوات الصوتية. هنالك عددٌ من برمجيات ترميز وفك ترميز صوت Vorbis المفتوحة المصدر، بما في ذلك OggConvert (للترميز)، و ffmpeg (فك ترميز)، و aoTuV (للترميز)، و libvorbis (لفك الترميز). وهنالك إضافات لبرمجية QuickTime لنظام Mac OS X و DirectShow لنظام ويندوز. ما الصيغ التي تعمل في الويب إن لم يؤلمك رأسك إلى الآن فأنت تبلي بلاءً حسنًا. يمكنك أن تستنتج بسهولة أنَّ الفيديو (والصوت) هو موضوعٌ معقدٌ، وما أوردناه سابقًا هو النسخة المختصرة من القصة! أنا متأكد أنك تتساءل عن ارتباط HTML5 بما سبق. حسنًا، يوجد في HTML5 عنصرٌ اسمه <video> لتضمين مقاطع الفيديو في صفحة ويب، ولا توجد هنالك أيّة قيود على المرماز المستخدم لترميز الفيديو أو الصوت أو حتى صيغة الحاوية التي يمكنك استخدامها لمقاطع الفيديو. يمكن لعنصر <video> وحيد أن يُشير إلى عدِّة ملفات فيديو، وسيختار المتصفح أول ملف فيديو يستطيع تشغيله. كل ما عليك فعله هو معرفة ما هي الحاويات والمرمازات التي تدعمها المتصفحات. هذا هو حال دعم صيغ الفيديو في HTML5 في الوقت الراهن: متصفح Mozilla Firefox ‏(3.5 أو ما بعده) يدعم فيديو Theora وصوت Vorbis في حاوية Ogg. ويدعم Firefox 4 صيغة WebM. متصفح Opera ‏(10.5 وما بعده) يدعم فيديو Theora وصوت Vorbis في حاوية Ogg. يدعم Opera 10.60 صيغة WebM. متصفح Google Chrome ‏(3.0 وما بعده) يدعم فيديو Theora وصوت Vorbis في حاوية Ogg. يدعم Google Chrome 6.0 صيغة WebM. متصفح Safari على نظامي Mac وويندوز (3.0 وما بعده) سيدعم أي شيء يدعمه QuickTime. نظريًا، هذا يعني أنَّك قد تطلب من مستخدميك تثبيت إضافات خارجية إلى مُشغِّل QuickTime؛ لكن عمليًا لن يفعل ذلك إلا القليل. لذلك عليك أن تستعمل الصيغ التي يدعمها QuickTime مباشرةً. وهذه قائمة طويلة إلا أنها لا تحتوي WebM، أو Theora، أو Vorbis، أو حاوية Ogg. لكن QuickTime فيه دعمٌ مدمج لفيديو H.264‏ (نمط main) وصوت AAC في حاوية MP4. الهواتف المحمولة متل iPhone والهواتف التي تعمل بنظام أندرويد تدعم فيديو H.264 ‏(نمط baseline) وصوت AAC ‏(‎نمط ‎"low complexity"‎) في حاوية MP4. يدعم Adobe Flash (الإصدار 9.0.60.184 وما بعده) فيديو H.264 وصوت AAC في حاوية MP4. يدعم Internet Explorer جميع "أنماط" فيديو H.264 وصوت AAC أو MP3 في حاوية MP4. ويمكنه أيضًا تشغيل فيديو WebM إن ثبتتَ مرمازًا خارجي، الذي لا يكون مثبتًا افتراضيًا في أي إصدار من ويندوز. لا يدعم IE9 المرمازات الخارجية الأخرى (على عكس Safari، الذي سيُشغِّل ما يستطيع QuickTime تشغيله). لا يدعم متصفح Internet Explorer 8 عنصر video في HTML5 إطلاقًا، لكن تقريبًا جميع مستخدمي Internet Explorer يملكون إضافة Adobe Flash. وسأريك لاحقًا في هذا الفصل كيف يمكنك استخدام عنصر video في HTML5 لكن مع خيارٍ احتياطي هو استخدام Flash. ربما من الأسهل تلخيص ما سبق في جدول. الترميز/الحاوية IE Firefox Safari Chrome Opera iPhone Android Theora+Vorbis+Ogg . 3.5+ * 3.0+ 10.5+ . 2.3+ H.264+AAC+MP4 9.0+ 21+ 3.2+ 3.0+**** 25+ 3.2+ 2.0+ WebM 9.0+** 4.0+ * 6.0+ 10.6+ . 2.3*** * متصفح Safari سيُشغِّل أي شيء يستطيع QuickTime تشغيله، لكن QuickTime لا يأتي مع دعمٍ مسبق إلا لصيغ H.264/AAC/MP4. ** متصفح Internet Explorer 9 سيدعم WebM "فقط إذا ثبَّت المستخدم مرماز VP8"، الذي يعني ضمنيًا أنَّ مايكروسوفت لن تضيف المرماز بنفسها. *** على الرغم من أنَّ متصفح أندرويد 2.3 يدعم WebM، لكن لا توجد إمكانية لفك الترميز عتاديًا، وهذا يقلل من عمر البطارية. **** قد أعلِنَ أنَّ Google Chrome سيسقط الدعم عن H.264 قريبًا، وقد تم تبرير ذاك القرار؛ لكنه لم يُطبَّق إلى الآن. ولكن هذه هي الطامة الكبرى: لا توجد تجميعة واحدة من الحاويات والمرمازات التي تعمل في جميع متصفحات HTML5، ومن غير المحتمل أن يتغير هذا في المستقبل القريب. لكي تجعل مقاطع الفيديو قابلة للعرض في مختلف الأجهزة والمنصات، فعيلك أن تُرمِّز مقطع الفيديو بأكثر من صيغة. هذا هو مسار عملك إذا كنت تريد توفير مقطع الفيديو بأكبر قدر من التوافقية بين المتصفحات: أنشِئ نسخةً تستعمل WebM ‏(VP8 + Vorbis). أنشِئ نسخةً أخرى تستعمل فيديو H.264 بنمط baseline مع صوت AAC‏ بنمط low complexity‎ في حاوية MP4. أنشِئ نسخةً أخرى تستعمل فيديو Theora وصوت Vorbis في حاوية Ogg. اربط ملفات الفيديو الثلاثة السابقة إلى عنصر <video> وحيد، واترك مجالًا لاستعمال مشغِّل يعتمد على تقنية Flash إن لم يدعم المتصفح العنصر video. مشاكل الترخيص مع فيديو H.264 قبل أن نكمل، علي أن أُشير أنَّ هنالك تكلفة لترميز مقاطع الفيديو مرتين. حسنًا، لا أقصد أنَّ عليك فعلًا ترميزها مرتين، وهذا يعني استهلاك أكبر للوقت ولطاقة المعالجة فيما لو كنت سترمِّزها مرةً واحدة. لكن هنالك تكلفة حقيقة مرتبطة بفيديو H.264: تكلفة الترخيص. هل تتذكر عندما شرحتُ H.264 أول مرة، وذكرت بسرعة أنَّ ذاك الترميز خاضع لبراءات اختراع ويمكن أخذ ترخيص بالاستخدام من MPEG LA؟ قد تبيّن أنَّ ما سبق مهم، ولكن لتفهم لماذا، سأحيلك إلى مقتطف من مقالة "The H.264 Licensing Labyrinth": تُقسِّم MPEG LA رخصة H.264 إلى رخصتين فرعيتين: واحدة لمصنعي برمجيات الترميز وفك الترميز، وأخرى لموزعي المحتوى. الرخصة الفرعية المتعلقة بتوزيع المحتوى تُقسَّم بدورها إلى أربعة تصنيفات أساسية، اثنان منها (الاشتراك subscription و "title-by-title" أي لكل مقطع على حدة) مرتبطان فيما إذا كان المستخدم النهائي سيدفع مباشرةً لقاء خدمة بث الفيديو، واثنان منها (البث "المجاني" للتلفاز وللإنترنت) مرتبطان بالربح القادم من مصادر أخرى بخلاف المشاهد. تكلفة الرخصة للبث "المجاني" للتلفاز مبنيةٌ على خيارَي دفع. أول خيار هو دفع مبلغ 2500$ لكل مُرمِّز AVC، الذي يعني تسليم مُرمِّز AVC واحد فقط "مُستخدَم من قبل أو نيابةً عن المُرخَّص له لبث فيديو AVC للمستخدم النهائي"، الذي سيفك الترميز ويشاهد الفيديو. إذا كنت تتساءل فيما إذا كانت التكلفة مضاعفة هنا، فالجواب هو نعم: دُفِعَت أجرة الرخصة من مُصنِّع المرمِّز، وسيدفع الطرف الذي سيبث الفيديو أحد خيارَي الدفع. الخيار الثاني للترخيص هو دفع اشتراك سنوي للبث. وأجرة البث السنوي مُقسَّمة إلى شرائح تختلف حسب عدد المشاهدين: ‎2500‏‎$‎ في كل سنة لكل سوق من أسواق البث (broadcast markets) لعدد الأسر التي تشاهد البث بين 100,000–499,999 5000‏$‏ في كل سنة لكل سوق من أسواق البث (broadcast markets) لعدد الأسر التي تشاهد البث بين 500,000–999,999 10,000‏$‏ في كل سنة لكل سوق من أسواق البث (broadcast markets) لعدد الأسر التي تشاهد البث الأكبر من 1,000,000 بعد استعراض جميع المشاكل التي تحف البث "المجاني" للتلفاز، لماذا يجب أن يهتم بها شخصٌ لا يبث الفيديو؟ كما ذكرتُ سابقًا، تكلفة الاشتراك تنطبق على أيّ عملية توصيل للمحتوى، لكن تعريف البث "المجاني" للتلفاز يعني أكثر من البث عبر الهوائيات (over-the-air). أضافت MPEG LA أجور اشتراك لبث الفيديو عبر الإنترنت التي لن يدفع المستخدم النهائي أيّة تكاليف لمشاهدة المقطع، أي بكلامٍ آخر: أي بث عام لفيديو سواءً كان عبر الهوائيات أو خدمة "الكيبل" أو الأقمار الاصطناعية أو عبر الإنترنت… سيخضع إلى أجور الاشتراك. لكن MPEG-LA أعلنوا أنَّهم لن يأخذوا أجورًا على البث عبر الإنترنت، لكن هذا لا يعني أنَّ مرماز H.264 مجاني الاستخدام لجميع المستخدمين؛ وبشكل خاص برمجيات الترميز (كالتي تُعالِج الفيديو المرفوع على موقع YouTube) وبرمجيات فك الترميز (مثل البرمجية الموجودة في متصفح Microsoft Internet Explorer 9) التي ما زالت تخضع لأجور الترخيص. ترجمة -وبتصرّف- لفصل "Video" من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  20. مدخل إلى html5

    تُعرِّف HTML العنصر <canvas> على أنه "لوحة نقطية ذات أبعاد معينة يمكن استخدامها لعرض المخططات ورسومات الألعاب وغيرها من الصور المرئية برمجيًا". ويُمثِّل مستطيلًا في صفحتك حيث تستخدم JavaScript لرسم أي شيء تريده فيه. الدعم الأساسي للعنصر canvas IE Firefox Safari Chrome Opera iPhone Android 7.0+* 3.0+ 3.0+ 3.0+ 10.0+ 1.0+ 1.0+ * يتطلب IE ‏7 و 8 مكتبة خارجية هي explorercanvas، أما IE 9 فهو يدعم العنصر <canvas> داخليًا. كيف يبدو عنصر canvas؟ ليس له شكل، حقًا! ليس في عنصر <canvas> أي محتوى وليس له إطارٌ حتى. يُضاف عنصر canvas كالآتي: <canvas width="300" height="225"></canvas> لنضف إطارًا منقطًا لكي نستطيع أن نرى ما الذي نتعامل معه: الشكل 1: عنصر canvas مع إطار يمكن أن يكون لديك أكثر من عنصر <canvas> في نفس الصفحة، وسيظهر كل عنصر على حدة في شجرة DOM، ويحافظ كل عنصر canvas على خاصياته؛ فإن أعطيت كل عنصر canvas خاصية id، فيمكنك الوصول إليه كما تفعل مع أي عنصر آخر. لنوسِّع الشيفرة السابقة لكي تتضمن خاصية id: <canvas id="a" width="300" height="225"></canvas> أصبح بإمكاننا بسهولة العثور على عنصر <canvas> السابق في شجرة DOM. var a_canvas = document.getElementById("a"); الأشكال البسيطة IE Firefox Safari Chrome Opera iPhone Android 7.0+* 3.0+ 3.0+ 3.0+ 10.0+ 1.0+ 1.0+ * يتطلب IE ‏7 و 8 مكتبة خارجية هي explorercanvas، أما IE 9 فهو يدعم رسم الأشكال البسيطة في العنصر <canvas> داخليًا. يبدأ كل عنصر canvas فارغًاـ ثم علينا الرسم فيه. لنبدأ ببعض الأشكال. يمكن استخدام الحدث (action) ‏onclick لاستدعاء الدالة الآتية عندما يضغط المستخدم على زرٍ ما: function draw_b() { var b_canvas = document.getElementById("b"); var b_context = b_canvas.getContext("2d"); b_context.fillRect(50, 25, 150, 100); } لا يوجد شيءٌ مميزٌ في أول سطر من الدالة، إذ أنَّ مهمته هي العثور على عنصر <canvas> الموجود في شجرة DOM. function draw_b() { var b_canvas = document.getElementById("b"); var b_context = b_canvas.getContext("2d"); b_context.fillRect(50, 25, 150, 100); } لدى كل عنصر canvas ما نسميه "إطار الرسم" (drawing context)، الذي يحدث فيه كل الأمور المسلية. فبعد أن تعثر على عنصر <canvas> في شجرة DOM (باستخدام document.getElementById()‎ أو أيّة طريقة أخرى) ستستطيع أن تستدعي الدالة getContext()‎، يجب عليك تمرير السلسلة النصية "2d" دومًا إلى الدالة getContext()‎. س: هل يمكن رسم رسوميات 3D في canvas؟ ج: نعم، عبر تقنية WebGL التي يمكنها رسم الأشكال ثلاثية الأبعاد في المتصفحات دون إضافات. تدعم أغلبية المتصفحات الحديثة هذه التقنية (Firefox الإصدار الرابع وما بعده، و Chrome الإصدار التاسع وما بعده، و Opera 12 وما بعده، و Safari 5.1 وما بعده، و IE 11)؛ يتم العمل على تطوير هذه التقنية في مجموعة عمل WebGL. إذًا، أصبح لديك عنصر <canvas> ولديك إطار الرسم (drawing context) الخاص به. إطار الرسم هو المكان الذي ستُعرَّف فيه جميع دوال وخاصيات الرسم. هنالك مجموعةٌ كاملةٌ من الخاصيات والدوال المُكرَّسة لرسم المستطيلات: يمكن أن تكون الخاصية fillStyle لونًا من ألوان CSS، أو نقشًا (pattern)، أو تدرجًا لونيًا (gradient) (سنذكر مزيدًا من المعلومات عن التدرجات اللونية بعد قليل). القيمة الافتراضية لهذه الخاصية هي اللون الأسود، لكنك تستطيع أن تضبطها لما تشاء. سيتذكر كل إطار رسم (drawing context) خاصياته لطالما بقيت الصفحة مفتوحةً إلا إن فعلت شيئًا لإعادة ضبطه. الدالة (fillRect(x, y, width, height ترسم مستطيلًا مملوءًا باللون أو النقش الموجود في fillStyle. الخاصية strokeStyle شبيهة بخاصية fillStyle، فيمكن أن تكون لون CSS أو نقشًا أو تدرجًا لونيًا. الدالة (strokeRect(x, y, width, height ترسم مستطيلًا دون ملئه، إذ ترسم حوافه وإطاره الخارجي فحسب، وتَستعمل الخاصية strokeStyle لذلك. الدالة (clearRect(x, y, width, height تسمح كل البكسلات الموجودة في المستطيل المُحدَّد. س: هل يمكنني أن أعيد ضبط لوحة الرسم في عنصر canvas؟ ج: نعم، فسيؤدي تحديد عرض أو ارتفاع عنصر <canvas> إلى إعادة ضبط (reset) كل الخاصيات في إطار الرسم إلى قيمها الافتراضية. لاحظ أنَّه ليس من الضروري تغيير العرض، إذ يمكنك أن تضبطه إلى قيمته الحالية كما يلي: var b_canvas = document.getElementById("b"); b_canvas.width = b_canvas.width; بالعودة إلى الشيفرة في المثال السابق: var b_canvas = document.getElementById("b"); var b_context = b_canvas.getContext("2d"); b_context.fillRect(50, 25, 150, 100); سترسم الدالة fillRect()‎ عند استدعائها مستطيلًا وتملؤه بنمط الملء الحالي الذي هو اللون الأسود (إلا إذا غيرتَه). يُعرَّف المستطيل عبر زاويته العليا اليسرى (50، 25)، وعرضه (150)، وارتفاعه (100). لكي نفهم ذلك فهمًا جيدًا، فلنلقِ نظرةً إلى نظام الإحداثيات في canvas. الإحداثيات في عنصر canvas يمكننا تخيل لوحة الرسم في عنصر canvas على أنها شبكة ثنائية البعد، ويكون مبدأ الإحداثيات فيها (‎0, 0) في الزاوية العليا اليسرى من لوحة الرسم. تزداد القيم على المحور X عند الانتقال نحو الحافة اليمنى من لوحة الرسم، وتزداد القيم على المحور Y بالانتقال نحو الحافة السفلية من لوحة الرسم. الشكل 2: توضيح لنظام الإحداثيات في عنصر canvas رُسِمَ الشكل السابق عبر عنصر <canvas> الذي يحتوي على: مجموعة من الخطوط الأفقية مجموعة من الخطوط الشاقولية خطين أفقيين سوداوين خطين صغيرين سوداوين مائلين يشكلان سهمًا خطين شاقوليين سوداوين خطين صغيرين سوداوين مائلين يشكلان السهم الآخر الحرف "x" الحرف "y" النص "(‎(0, 0" قرب الزاوية العليا اليسرى النص "(‎500, 375)" قرب الزاوية السفلى اليمنى نقطة في الزاوية العليا اليسرى، وأخرى في الزاوية السفلى اليمنى علينا أولًا تعريف العنصر <canvas> نفسه، حيث يُحدِّد العنصر <canvas> خاصية العرض width والارتفاع height، والمُعرِّف id كي نستطيع العثور عليه بسهولة لاحقًا. <canvas id="c" width="500" height="375"></canvas> ثم علينا كتابة سكربت لكي نجد عنصر <canvas> في شجرة DOM ونحصل على إطار الرسم الخاص به. var c_canvas = document.getElementById("c"); var context = c_canvas.getContext("2d"); نستطيع الآن البدء في رسم الخطوط. المسارات IE Firefox Safari Chrome Opera iPhone Android 7.0+* 3.0+ 3.0+ 3.0+ 10.0+ 1.0+ 1.0+ * يتطلب IE ‏7 و 8 مكتبة خارجية هي explorercanvas، أما IE 9 فهو يدعم المسارات في العنصر <canvas> داخليًا. تخيل أنك تريد أن ترسم رسمةً بالحبر، من المؤكد أنَّك لن تبدأ الرسم بأقلام الحبر مباشرةً، لأنك قد تُخطئ؛ فعوضًا عن ذلك سترسم المستقيمات والمنحنيات بقلم الرصاص، ثم إن أعجبتك فيمكنك أن "تُحبِّرَها". هنالك ما نسميه "المسارات" (paths) في عناصر canvas، وتعريف المسار يُشبِه تمامًا الرسم بقلم الرصاص؛ يمكنك رسم ما تشاء لكنه لن يكون جزءًا من اللوحة النهائية إلا إن أمسكت أقلام التحبير ومررتها فوق المسار الذي رسمتَه. استعمل الدالتين الآتيتين لرسم المستقيمات "بقلم الرصاص": (moveTo(x, y تحريك قلم الرصاص إلى نقطة البداية المُحدَّدة. (lineTo(x, y رسم خط إلى نقطة النهاية المُحدَّدة. سيزداد حجم المسار كلما استدعيت الدالتين moveTo()‎ و lineTo()‎. ترسم الدالتان السابقتان "بقلم الرصاص" (يمكنك أن تسمي هذه العملية ما تشاء)، أي أنَّك لن ترَ شيئًا على لوحة الرسم إلى أن تستدعي إحدى دوال "التحبير". لنبدأ برسم الشبكة: //رسم المستقيمات الشاقولية for (var x = 0.5; x < 500; x += 10) { context.moveTo(x, 0); context.lineTo(x, 375); } //رسم المستقيمات الأفقية for (var y = 0.5; y < 375; y += 10) { context.moveTo(0, y); context.lineTo(500, y); } تلك الدوال "رصاصية"، أي لن يظهر شيءٌ على لوحة الرسم بعد؛ إذ سنحتاج إلى دالة "تحبير" لإظهار تلك الخطوط. context.strokeStyle = "#eee"; context.stroke(); الدالة storke()‎ هي إحدى دوال "التحبير"، وهي تُحبِّر المسار المعقد الذي عرَّفتَه بدوال moveTo()‎ و lineTo()‎ السابقة. خاصية strokeStyle تتحكم بلون تلك الخطوط. هذه هي النتيجة: الشكل 3: شبكة مرسومة داخل عنصر canvas س: لماذا بدأت x و y من 0.5 وليس من 0؟ ج: تخيل أنَّ كل بكسل هو مربع كبير، وأنَّ الإحداثيات ذات الرقم الكامل (0، 1، 2…) هي حواف تلك المربعات؛ فإذا أردت أن ترسم خطًا عرضُه واحدة الأطوال (one-unit-wide) بين الإحداثيات ذات الرقم الكامل، فسوف يمتد إلى أن يصل إلى طرفَي المربع (انظر الشكل الآتي للإيضاح)، وسيكون الخط الناتج مرسومًا بعرض بكسلين. أما لرسم خط عرضه بكسل واحد، فعليك أن تُزيح الإحداثيات بمقدار 0.5 عموديًا على منحى (أو اتجاه) الخط. على سبيل المثال، إذا كنت تحاول رسم خط من (‎1, 0) إلى (‎1, 3)، فسيرسم المتصفح خطًا يغطي 0.5 بكسل على جانبَيّ x=1، ولكن الشاشة غير قادرة على عرض نصف بكسل، لذلك سيمتد الخط لكي يُغطي بكسلين: أما لو أردت أن ترسم خطًا من (1.5, 0) إلى (1.5, 3)، فسيرسم المتصفح خطأ يغطي 0.5 بكسل على طرفَي x=1.5، الذي يؤدي إلى رسم خط بعرض 1 بكسل: لنرسم الآن السهم الأفقي. جميع الخطوط والمنحنيات الموجودة على نفس المسار ستُرسَم بنفس اللون (أو التدرج اللوني، وسنأتي لذكر ذلك لاحقًا). لكننا نريد أن نرسم السهم بلون مختلف (الأسود)، لذلك نحتاج إلى البدء بمسار (path) جديد. //مسار جديد context.beginPath(); context.moveTo(0, 40); context.lineTo(240, 40); context.moveTo(260, 40); context.lineTo(500, 40); context.moveTo(495, 35); context.lineTo(500, 40); context.lineTo(495, 45); وبنفس الطريقة نرسم السهم الشاقولي؛ ولمّا كان السهم الشاقولي بنفس لون السهم الأفقي، فلا حاجة إلى إنشاء مسار جديد، إذ سيُشكِّل السهمان مسارًا واحدًا. //لا حاجة إلى إنشاء مسار جديد context.moveTo(60, 0); context.lineTo(60, 153); context.moveTo(60, 173); context.lineTo(60, 375); context.moveTo(65, 370); context.lineTo(60, 375); context.lineTo(55, 370); لقد قلتُ أنَّ هذين السهمين يجب أن يُرسما باللون الأسود، لكن لون strokeStyle ما يزال "الفضي الفاتح" (لا تتم عملية إعادة تعيين قيم fillStyle و strokeStyle عندما تبدأ مسارًا جديدًا). لا بأس، لأننا رسمنا إلى الآن "بالرصاص" ولم "نُحبِّر" بعد، علينا الآن أن نضبط قيمة strokeStyle إلى اللون الأسود؛ وإلا سيُرسم هاذين السهمين بالفضي الفاتح وسيصعب علينا رؤيتهما. سيُغيّر السطران الآتيان اللون إلى الأسود ويرسمان الخطوط في لوحة الرسم: context.strokeStyle = "#000"; context.stroke(); هذه هي النتيجة: الشكل 4: المحوران مرسومان دون تسمية على لوحة الرسم النص IE Firefox Safari Chrome Opera iPhone Android 7.0+* 3.0+ 3.0+ 3.0+ 10.0+ 1.0+ 1.0+ * يتطلب IE ‏7 و 8 مكتبة خارجية هي explorercanvas، أما IE 9 فهو يدعم طباعة النصوص في العنصر <canvas> داخليًا. ** يتطلب Firefox 3.0 مكتبة للتوافقية. بالإضافة إلى القدرة على رسم خطوط في لوحة الرسم، يمكنك أيضًا "رسم" الجمل النصية. وعلى عكس النصوص في الصفحة المحيطة بلوحة الرسم، لا يوجد هنالك أيّة خاصيات CSS تتعلق بالتخطيط (layout) مثل floats و margins و padding و word wrapping. تستطيع ضبط بعض خاصيات الخط، ثم اختيار نقطة على لوحة الرسم و"رسم" نصك هناك. تتوفر خاصيات الخطوط الآتية في لوحة الرسم: الخاصية font التي يمكن أن تُضبَط إلى أي شيءٍ تستطيع ضبطه في خاصية font في CSS. وهذا يتضمن نمط الخط، ونوع الخط، وسماكة الخط، وحجم الخط، وارتفاع السطر، واسم "عائلة" الخط. الخاصية textAlign تتحكم بمحاذاة النص، وهي شبيهة (لكن ليس مماثلة تمامًا) بخاصية text-align في CSS. القيم المحتملة هي start و end و left و right و center. الخاصية textBaseline تتحكم في مكان "رسم" النص نسبةً إلى نقطة البداية. القيم المحتملة هي top أو hanging أو middle أو alphabetic أو ideographic أو bottom. الخاصية textBaseline معقدة ودقيقة، لأن طريقة كتابة النصوص معقدة (لا أقصد هنا الإنكليزية، لكنك تستطيع رسم أي محرف يونيكود في عنصر canvas، وكتابة نصوص يونيكود معقدة)، تشرح مواصفات HTML5 مختلف خطوط الأساس (baselines): أعلى مربع em هو تقريبًا على مستوى أعلى المحارف في الخط (font)، أما "hanging baseline" فهو مكان ارتكاز بعض المحارف مثل आ، أما خطmiddle فهو يقع في منتصف المسافة بين أعلى مربع em وأسفل مربع em. أما خط alphabetic فهو مكان ارتكاز الأحرف مثل Á و ÿ و f و Ω، وخط ideographic هو مكان ارتكاز المحارف مثل 私 و 達، وأسفل مربع em هو تقريبًا أسفل المحارف الموجودة في خطٍ (font) ما. قد يكون أعلى وأسفل الصندوق المحيط (bounding box) بعيدًا عن خطوط الأساس السابقة نتيجةً لامتداد بعض المحارف خارج مربع em. الشكل 5: خطوط الأساس تستطيع استخدام top أو middle أو bottom لخاصية textBaseline للأبجديات البسيطة مثل الإنكليزية دون أن تكترث للبقية. لنرسم بعض النصوص! النص المرسوم داخل عنصر canvas يرث حجم الخط ونمطه من عنصر <canvas> نفسه، لكنك تستطيع إعادة تعريف تلك القيم بضبط خاصية font في إطار الرسم. //تغيير نمط الخط context.font = "bold 12px sans-serif"; context.fillText("x", 248, 43); context.fillText("y", 58, 165); ترسم الخاصية fillText()‎ النص. context.font = "bold 12px sans-serif"; //رسم النص context.fillText("x", 248, 43); context.fillText("y", 58, 165); س: هل أستطيع استخدام مقاسات الخطوط النسبية لرسم النصوص في عنصر canvas؟ ج: نعم. مثل أي عنصر HTML آخر في صفحتك، يحسب العنصر <canvas> مقاس الخط بناءً على قواعد CSS في صفحتك. فإذا ضبطت خاصية context.font إلى مقاس خط نسبي مثل 1.5em أو ‎150%، فسيضرب متصفحك هذا الرقم بحجم الخط المحسوب لعنصر <canvas> نفسه. أريد أن يكون أعلى النص الموجود في الزاوية العليا اليسرى على بعد y=5 من الحافة العلوية، لكنني كسول ولا أريد قياس ارتفاع النص وحساب بعد خط الأساس (baseline)، وإنما سأضبط textBaseline إلى top وسأمرر إحداثيات الزاوية العليا اليسرى من مربع النص. context.textBaseline = "top"; context.fillText("( 0 , 0 )", 8, 5); أما للنص في الزاوية السفلى اليمنى، فسأرسم الزاوية السفلى اليمنى للنص في الإحداثيات (492,370)، أي بضعة بكسلات من الزاوية السفلى اليمنى من لوحة الرسم؛ لكنني لا أريد قياس عرض أو ارتفاع النص، لهذا أضبط الخاصية textAlign إلى right والخاصية textBaseline إلى botton، ثم استدعي الدالة fillText()‎ مُمرِّرًا إليها إحداثيات الزاوية السفلى اليمنى من مربع النص. context.textAlign = "right"; context.textBaseline = "bottom"; context.fillText("( 500 , 375 )", 492, 370); وهذه هي النتيجة: الشكل 6: تسمية المحاور لقد نسينا النقاط الموجودة على الزوايا! سنتعلم كيف نرسم الدوائر لاحقًا، أما الآن، فسأغش قليلًا وأرسمها كمستطيلات. context.fillRect(0, 0, 3, 3); context.fillRect(497, 372, 3, 3); هذا كل ما في الأمر! هذه هي النتيجة النهائية: الشكل 7: الرسم النهائي للإحداثيات عبر عنصر canvas التدرجات اللونية IE Firefox Safari Chrome Opera iPhone Android التدرجات اللونية الخطية 7.0+* 3.0+ 3.0+ 3.0+ 10.0+ 1.0+ 1.0+ التدرجات اللونية الشعاعية 9.0+ 3.0+ 3.0+ 3.0+ 10.0+ 1.0+ 1.0+ * يتطلب IE ‏7 و 8 مكتبة خارجية هي explorercanvas، أما IE 9 فهو يدعم التدرجات اللونية في العنصر <canvas> داخليًا. لقد تعلمنا سابقًا في هذا الفصل كيف نرسم مستطيلًا مملوءًا بلونٍ ما، ثم تعلمنا كيف نرسم مستطيلًا ذا إطارٍ ملوَّن بلونٍ معين. لكن الأمر ليس محدودًا للألوان فقط، إذ يمكنك فعل ما تشاء بالتدرجات اللونية، لننظر إلى أحد الأمثلة. الشكل 8: تدرج لوني من اليسار إلى اليمين سيبدو وسم canvas كغيره من الوسوم: <canvas id="d" width="300" height="225"></canvas> علينا أولًا أن نعثر على عنصر <canvas> ثم نحصل على إطار الرسم: var d_canvas = document.getElementById("d"); var context = d_canvas.getContext("2d"); يمكننا أن نبدأ بتعريف التدرج اللوني بعد حصولنا على إطار الرسم. التدرج اللوني هو انتقالٌ ناعمٌ بين لونين أو أكثر. يمكن تعريف نوعين من التدرجات اللونية في إطار الرسم: الدالة (createLinearGradient(x0, y0, x1, y1 ترسم تدرجًا لونيًا عبر قطعة مستقيمة من النقطة (x0, y0) إلى (x1, y1). الدالة (createRadialGradient(x0, y0, r0, x1, y1, r1 ترسم تدرجًا لونيًا عبر مخروط (cone) بين دائرتين. وتُمثِّل أول ثلاثة معاملات (parameters) دائرة البداية ذات المركز (x0, y0) ونصف القطر r0. أما آخر ثلاثة معاملات فهي تمثل دائرة النهاية ذات المركز (x1, y1) ونصف القطر r1. لنُنشِئ تدرجًا لونيًا خطيًّا (linear)؛ يمكن أن تكون التدرجات اللونية بأي قياس، لكننا سنجعل هذا التدرج بعرض 300 بكسل، مثل لوحة الرسم. var my_gradient = context.createLinearGradient(0, 0, 300, 0); لما كانت قيم y (الوسيط الثاني والرابع) تساوي 0، فسيكون اتجاه التدرج اللوني من اليسار إلى اليمين. يمكنك تعريف ألوان التدرج بعد أن تحصل على كائن التدرج اللوني. يمكن أن يكون للتدرج محطتَي توقف لوني أو أكثر. التوقف اللوني (color stop) يمكن أن يكون في أي مكان في التدرج. ولإضافة توقف لوني، عليك تحديد مكانه ضمن التدرج اللوني، يمكن أن تكون تلك القيم بين 0 و 1. لنُعرِّف تدرجًا لونيًا من الأسود إلى الأبيض. my_gradient.addColorStop(0, "black"); my_gradient.addColorStop(1, "white"); لا يُرسَم أيّ شيءٍ في لوحة الرسم عند تعريف التدرج اللوني، إذ سيُخزَّن ذاك الكائن في مكانٍ ما في الذاكرة. أما لرسم التدرج اللوني، فعليك أن تضبط خاصية fillStyle إلى ذاك التدرج ثم ترسم شكلًا ما مثل مستطيل أو مستقيم. context.fillStyle = my_gradient; context.fillRect(0, 0, 300, 225); وهذه هي النتيجة: الشكل 9: تدرج لوني من اليسار إلى اليمين لنفترض أنَّك تريد تدرجًا لونيًا من الأعلى إلى الأسفل؛ فسيكون عليك إنشاء كائن للتدرج اللوني تكون فيه قيمة x ثابتة (الوسيطين الأول والثالث)، وتجعل قيم y (الوسطين الثاني والرابع) تتراوح بين 0 إلى ارتفاع لوحة الرسم. var my_gradient = context.createLinearGradient(0, 0, 0, 225); my_gradient.addColorStop(0, "black"); my_gradient.addColorStop(1, "white"); context.fillStyle = my_gradient; context.fillRect(0, 0, 300, 225); وهذه هي النتيجة: الشكل 10: تدرج لوني من الأعلى إلى الأسفل وتستطيع أيضًا أن تجعل التدرجات اللونية قطرية. //قيم x و y متغيرة var my_gradient = context.createLinearGradient(0, 0, 300, 225); my_gradient.addColorStop(0, "black"); my_gradient.addColorStop(1, "white"); context.fillStyle = my_gradient; context.fillRect(0, 0, 300, 225); وهذه هي النتيجة: الشكل 11: تدرج لوني قطري الصور IE Firefox Safari Chrome Opera iPhone Android 7.0+* 3.0+ 3.0+ 3.0+ 10.0+ 1.0+ 1.0+ * يتطلب IE ‏7 و 8 مكتبة خارجية هي explorercanvas، أما IE 9 فهو يدعم تضمين الصور في العنصر <canvas> داخليًا. يُظهِر الشكل الآتي قطةً معروضةٌ عبر عنصر <img>: الشكل 12: قطة معروضة عبر عنصر img أما هذه فهي نفس القطة لكن مرسومة في عنصر canvas: الشكل 13: قطة معروضة ضمن عنصر canvas هنالك دالة اسمها drawImage()‎ في إطار الرسم تُستعمل لتضمين صورة في عنصر canvas. تأخذ هذه الدالة ثلاثة، أو خمسة، أو تسعة وسائط: (drawImage(image, dx, dy تأخذ صورةً وترسمها في لوحة الرسم، والإحداثيات المعطية (dx, dy) هي إحداثيات الزاوية العليا اليسرى من الصورة. فلو مررنا الإحداثيات (‎(0, 0 فستُرسَم الصورة في الزاوية العليا اليسرى من لوحة الرسم. (drawImage(image, dx, dy, dw, dh تأخذ صورةً وتغير عرضها إلى dw وارتفاعها إلى dh، ثم ترسمها على لوحة الرسم في الإحداثيات (dx, dy). (drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh تأخذ صورةً وتقتطعها لتصبح مساويةً إلى المستطيل (sx, sy, sw, sh) ثم تغير أبعادها إلى (dw, dh) ثم ترسمها على لوحة الرسم في الإحداثيات (dx, dy). توضِّح مواصفات HTML5 معاملات (parameters) الدالة drawImage()‎: المستطيل المصدر هو المستطيل (ضمن حدود الصورة المصدرية) الذي تكون رؤوس أضلاعه هي (sx, sy)، و (sx+sw, sy)، و (sx+sw, sy+sh)، و (sx, sy+sh). أما المستطيل الوجهة، فهو المستطيل (ضمن حدود لوحة الرسم) الذي تكون رؤوس أضلاعه هي النقاط (dx, dy)، و (dx+dw, dy)، و (dx+dw, dy+dh)، و (dx, dy+dh). الشكل 14: تمثيل رسومي لمعاملات الدالة drawImage لرسم صورة داخل لوحة الرسم، فستحتاج إلى صورة! يمكن أن تكون الصورة عنصر <img> موجود مسبقًا، أو بإمكانك إنشاء كائن Image()‎ باستخدام JavaScript. وفي كلا الحالتين عليك أن تتأكد أنَّ الصورة محمَّلة تحميلًا كاملًا قبل أن ترسمها في لوحة الرسم. إذا كنت تستخدم عنصر <img> موجود مسبقًا، فيمكنك رسم الصورة في لوحة الرسم أثناء الحدث window.onload. <img id="cat" src="images/cat.png" alt="sleeping cat" width="177" height="113"> <canvas id="e" width="177" height="113"></canvas> <script> window.onload = function() { var canvas = document.getElementById("e"); var context = canvas.getContext("2d"); var cat = document.getElementById("cat"); context.drawImage(cat, 0, 0); }; </script> أما لو كنتَ تُنشِئ كائن الصورة عبر JavaScript، فيمكنك رسم الصورة داخل لوحة الرسم أثناء الحدث Image.onload. <canvas id="e" width="177" height="113"></canvas> <script> var canvas = document.getElementById("e"); var context = canvas.getContext("2d"); var cat = new Image(); cat.src = "images/cat.png"; cat.onload = function() { context.drawImage(cat, 0, 0); }; </script> المعاملان الاختياريان الثالث والرابع في دالة drawImage()‎ يتحكمان في تغيير أبعاد الصورة. هذه هي نفس الصورة بعد تقليل أبعادها إلى النصف مرسومةً بشكل تكراري في إحداثياتٍ مختلفة ضمن نفس لوحة الرسم. الشكل 15: الكثير من القطط! هذا هو السكربت الذي يؤدي إلى رسم الشكل السابق: cat.onload = function() { for (var x = 0, y = 0; x < 500 && y < 375; x += 50, y += 37) { //تغيير أبعاد الصورة context.drawImage(cat, x, y, 88, 56); } }; ربما تتساءل سؤالًا منطقيًا: لماذا تريد رسم صورة في لوحة الرسم أصلًا؟ بماذا ستستفيد من التعقيد الناتج عن رسم صورة داخل لوحة الرسم عوضًا عن استخدام عنصر <img> وبعض خاصيات CSS؟ حتى التأثير السابق (تكرار صورة القطة) يمكن أن يتم باستخدام 10 عناصر <img> متداخلة. الجواب المبسط هو: لنفس سبب حاجتك إلى رسم النصوص في لوحة الرسم. لاحظ كيف تُضمَّن مخطط إحداثيات canvas نصًا ومستقيماتٍ وأشكالًا. فوضع النص ضمن لوحة الرسم هو جزءٌ من عملٍ أكبر. والمخططات الأكثر تعقيدًا ستستفيد كثيرًا من الدالة drawImage()‎ لتضمين الأيقونات والرسوميات وغيرها. ماذا عن متصفح IE؟ لم تكن الإصدارات الأقدم من الإصدار التاسع من متصفح Internet Explorer تدعم الواجهة البرمجية (API) لعنصر canvas (يدعم IE9 واجهة canvas البرمجية بالكامل). لكن الإصدارات القديمة من Internet Explorer تدعم تقنية مملوكة من مايكروسوفت اسمها VML، التي تفعل العديد من الأشياء التي يفعلها العنصر <canvas>، وبهذا وُلِدَت المكتبة excanvas.js. Explorercanvas ‏(excanvas.js) هي مكتبة JavaScript مفتوحة المصدر مُرخَّصة برخصة Apache التي تجعل من الممكن استعمال واجهة canvas البرمجية في متصفح Internet Explorer. عليك تضمين وسم <script> الآتي في أعلى صفحتك لكي تستخدمها. <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Dive Into HTML5</title> <!--[if lt IE 9]> <script src="excanvas.js"></script> <![endif]--> </head> <body> ... </body> </html> الأسطر ‎<!--[if lt IE 9]>‎ و ‎<![endif]-->‎ تسمى التعليقات الشرطية. حيث يعاملها Internet Explorer كأنها عبارة if الشرطية: "إن كان الإصدار الحالي من متصفح Internet Explorer أقل من الإصدار 9، فنفِّذ الشيفرة الآتية."، وستعامل بقية المتصفحات تلك الشيفرة على أنها تعليقات HTML. فالخلاصة هي أنَّ متصفح Internet Explorer (الإصدار الثامن والسابع) سيُنزِّل السكربت ثم سيُنفِّذه، لكن ستتجاهله بقية المتصفحات تمامًا، وهذا يجعل صفحتك تُحمَّل بشكلٍ أسرع في المتصفحات التي تدعم واجهة canvas البرمجية داخليًا (أي أنها لن تُنزِّل السكربت، ولن تنفذه، ولن تفعل أيّ شيءٍ معه). ليس عليك فعل أي شيءٍ إضافي بعد تضمين excanvas.js في ترويسة صفحتك. ضمِّن عناصر <canvas> في شيفرتك أو أنشِأها ديناميكيًا باستخدام JavaScript، واتبع التعليمات الواردة في هذا الفصل للحصول على لوحة رسم عنصر <canvas>، ثم تستطيع البدء برسم الأشكال والنصوص والنقوش. ليس تمامًا… هنالك بعض المحدوديات: يمكن أن تكون التدرجات اللونية خطيةً فقط. التدرجات اللونية الشعاعية غير مدعومة. يجب تكرار النقوش (patterns) في كلا الاتجاهين. ميزة Clipping regions غير مدعومة. تغيير الأبعاد غير المنتظم لا يستطيع تغيير أبعاد حدود الأشكال (strokes) بشكلٍ صحيح. إنها بطيئة؛ ولا أظن أنَّ ذلك صادمٌ لأي شخص، فمن المعلوم أنَّ مُفسِّر JavaScript في متصفح Internet Explorer أبطأ من غيره من المتصفحات؛ فما بالك برسم أشكال معقدة عبر مكتبة JavaScript ستحول الدوال والأوامر إلى تقنيةٍ مختلفة تمامًا؟ لن تلاحظ بطئًا في الأداء في الأمثلة البسيطة مثل رسم بعض الخطوط وإجراء بعض العمليات على صورة، لكنك سترى ذلك بوضوح بعد أن تحاول إنشاء تأثيرات حركية باستخدام تقنية canvas. هنالك تحذيرٌ آخر حول استخدام excanvas.js، وهذه مشكلة واجهتني أثناء إنشاء أمثلة هذا الفصل. يُهيّئ ExplorerCanvas واجهته البرمجية لعنصر canvas في مكان تضمينك لسكربت excanvas.js في صفحة HTML. لكن ذلك لا يعني أنَّ Internet Explorer جاهزٌ لاستخدامها مباشرةً، وفي بعض الحالات ستواجه حالة خاصة تكون فيها واجهة canvas جاهزة "تقريبًا" (لكن ليس تمامًا) للاستعمال. الأعراض الظاهرة لهذه المشكلة هي أنَّ Internet Explorer سيشتكي من أنَّ "object doesn’t support this property or method" في كل مرة تحاول فيها فعل أي شيء مع عنصر <canvas> مثل الحصول على لوحة الرسم الخاصة به. أسهل حل هذه المشكلة هي تأجيل كل العمليات التي ستجريها على canvas إلى أن يحدث الحدث onload، ولكن هذا قد يستغرق بعضًا من الوقت، خصيصًا إذا كانت في صفحتك الكثير من الصور أو مقاطع الفيديو، التي ستؤخر الحدث onload، لكن ذلك سيعطي المكتبة ExplorerCanvas وقتًا لفعل اللازم. مثال متكامل "الضامة" هي لعبةٌ قديمةٌ جدًا، وهنالك عدِّة نسخ منها؛ أنشأتُ في هذا المثال نسخة السوليتر (أي التي يلعبها شخصٌ واحد فقط) من الضامة ذات تسعِ قطعٍ في رقعةٍ مقاسها 9×9. تتواجد القطع في بداية اللعبة في مربع 3×3 في الركن الأسفل الأيسر من الرقعة. هدف اللعبة هو نقل جميع القطع كي تُشكِّل مربع 3×3 في الركن الأعلى الأيمن من الرقعة بأقل عدد من الحركات. هنالك نوعان من الحركات المسموحة في الضامة: خذ قطعةً وحركها إلى أي مربع فارغ مجاور. المربع "الفارغ" يعني أنَّه لا يحتوي على قطعةٍ فيه حاليًا. أما "مجاور" فهي تعني المربع الذي يقع مباشرةً في شمال أو جنوب أو شرق أو غرب أو الشمال الغربي أو الشمال الشرقي أو الجنوب الغربي أو الجنوب الشرقي من موقع القطعة الحالي (لا يمكن الالتفاف في الرقعة من طرفٍ آخر لآخر، فلو كانت قطعتك في العمود الأيسر من الرقعة، فلا يمكنك أن تحركها نحو الغرب أو الشمال الغربي أو الجنوب الغربي. وإذا كانت قطعتك في السطر الأدنى من الرقعة، فلا تستطيع تحريكها نحو الجنوب أو الجنوب الشرقي أو الجنوب الغربي). يمكنك أخذ قطعة وجعلها "تقفر" فوق قطعة مجاورة، ويمكنك تكرار ذلك. هذا يعني أنَّه إذا قفزت فوق قطعة مجاورة ثم قفزت فوق قطعة أخرى مجاورة لموقعك الجديد، فكل هذا يُحتَسب على أنَّه حركة واحدة. وفي الحقيقة، يتم احتساب أي عدد من القفزات على أنها خطوة واحدة (لأن الهدف من اللعبة هو تقليل العدد الكلي من الحركات؛ ويتطلب اللعب بشكلٍ جيد في الضامة بناءً واستخدامًا لسلاسل طويلة من القطع لكي تستطيع القطع الأخرى القفز قوفها في سلاسل طويل). هذه لقطة شاشة للعبة، يمكنك زيارة الصفحة الآتية لتجربة اللعبة تجربةً حيةً أو لإلقاء نظرة إلى مصدر الصفحة. الشكل 16: لقطة شاشة للعبة الضامة كيف تعمل؟ أنا ممتن لسؤالك. لن أريك كل الشيفرات هنا (يمكنك رؤيتها في halma.js)، وسأتخطى معظم الشيفرات التي تستعمل في تحديد آلية اللعب، لكنني سأركِّز على بعض الأجزاء التي مهتمها هي الرسم في عنصر canvas والاستجابة إلى نقرات الفأرة. سنُهيّئ اللعبة أثناء تحميل الصفحة بضبط أبعاد عنصر <canvas> ثم تخزين مرجع (reference) متعلق بلوحة الرسم. gCanvasElement.width = kPixelWidth; gCanvasElement.height = kPixelHeight; gDrawingContext = gCanvasElement.getContext("2d"); ثم سنفعل شيئًا لم نفعله من قبل قط: سنضيف متتبع أحداث إلى العنصر <canvas> لكي يتتبع حدث النقر على العنصر. gCanvasElement.addEventListener("click", halmaOnClick, false); ستُستدَعى الدالة halmaOnClick()‎ عندما ينقر المستخدم في أي مكان ضمن العنصر canvas، والوسيط الذي يُمرَّر إليها هو كائن MouseEvent الذي يحتوي على معلومات حول مكان نقر المستخدم. function halmaOnClick(e) { var cell = getCursorPosition(e); for (var i = 0; i < gNumPieces; i++) { if ((gPieces.row == cell.row) && (gPieces.column == cell.column)) { clickOnPiece(i); return; } } clickOnEmptyCell(cell); } الخطوة الآتية هي أخذ الكائن MouseEvent وحساب في أي مربع في رقعة الضامة قد تم النقر. تحتل رقعة الضامة كامل عنصر canvas، ولهذا تكون كل نقرة هي نقرة في مكانٍ ما على الرقعة، وكل ما علينا هو معرفة المكان. هذا أمرٌ معقدٌ بعض الشيء، لأن الأحداث المولدة من الفأرة تختلف بشكلٍ أو بآخر بين المتصفحات. function getCursorPosition(e) { var x; var y; if (e.pageX != undefined && e.pageY != undefined) { x = e.pageX; y = e.pageY; } else { x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; } } أصبح لدينا في هذه المرحلة إحداثيات x و y نسبةً إلى المستند (أي صفحة HTML كلها)، أي أنها ليس مفيدةً بعد؛ حيث نريد أن تكون الإحداثيات منسوبةً إلى لوحة الرسم. x -= gCanvasElement.offsetLeft; y -= gCanvasElement.offsetTop; أصبح لدينا إحداثيات x و y منسوبةً إلى لوحة الرسم، هذا يعني أنَّه لو كانت قيمة x مساويةً للصفر و y مساوية للصفر، فهذا يعني أنَّ المستخدم قد نقر على البكسل في أعلى وأيسر لوحة الرسم. من هنا سنستطيع معرفة أي مربع قد نقر المستخدم عليه، ثم نتصرف بناءً على ذلك. var cell = new Cell(Math.floor(y/kPieceHeight), Math.floor(x/kPieceWidth)); return cell; أوه! أمرُ الأحداث المرتبطة بالفأرة معقدٌ. لكنك يمكنك استخدام نفس التسلسل المنطقي (أو بالأحرى، يمكنك استخدام نفس الشيفرة) في جميع البرمجيات التي تعتمد على عنصر canvas. تذكر: نقرة بالفأرة ← إحداثيات منسوبة إلى المستند كله ← إحداثيات منسوبة إلى لوحة الرسم ← شيفرة خاصة بالتطبيق. حسنًا، لنلقِ نظرةً على آلية الرسم الرئيسية. لمّا كانت الرسوميات بسيطةً جدًا، فقررت مسح محتويات لوحة الرسم وإعادة رسم الرقعة في كل مرة يحصل فيها تغييرٌ في اللعبة ولكن هذا ليس ضروريًا. سيحتفظ إطار الرسم في عنصر canvas بمحتواه الذي رسمته عليه حتى لو تخطاه المستخدم بتمريره في الصفحة، أو إذا غيّر إلى لسانٍ (tab) آخر ثم عاد إليه لاحقًا. إذا كنتَ تُطوِّر تطبيقًا مبنيًا على عنصر canvas فيه رسوميات معقدة (مثل ألعاب الورق) فيمكنك تحسين الأداء بتتبع المناطق التي يجب مسحها من لوحة الرسم وإعادة الرسم داخل تلك المناطق فقط. لكن هذا خارجٌ عن نطاق هذا الكتاب. gDrawingContext.clearRect(0, 0, kPixelWidth, kPixelHeight); يجب أن تكون الشيفرات الآتية مألوفةً لديك، لأنها شبيهة بتلك التي استعملناها لرسم مخطط إحداثيات canvas سابقًا. gDrawingContext.beginPath(); // الخطوط الشاقولية for (var x = 0; x <= kPixelWidth; x += kPieceWidth) { gDrawingContext.moveTo(0.5 + x, 0); gDrawingContext.lineTo(0.5 + x, kPixelHeight); } // الخطوط الأفقية for (var y = 0; y <= kPixelHeight; y += kPieceHeight) { gDrawingContext.moveTo(0, 0.5 + y); gDrawingContext.lineTo(kPixelWidth, 0.5 + y); } // لنرسم الرسمة gDrawingContext.strokeStyle = "#ccc"; gDrawingContext.stroke(); المتعة الحقيقة تبدأ عندما نهم برسم القطع. القطعة هي دائرة، لكننا لم نرسم الدوائر من قبل، وعلاوةً على ذلك، لو اختار المستخدم قطعةً لكي يحركها، فسنحتاج إلى رسم تلك القطعة كدائرة مملوءة. يُمثِّل المعامل p هنا قطعة، التي لها خاصيات row (سطر) و column (عمود) التي تُحدِّد المكان الحالي للقطعة. سنستخدم آلية لتحويل (column, row) إلى إحداثيات منسوبة إلى لوحة الرسم (x, y)، ثم سنرسم دائرة، ثم -إن كانت القطعة مُحدَّدةً من المستخدم- سنملأ الدائرة بلونٍ معين. function drawPiece(p, selected) { var column = p.column; var row = p.row; var x = (column * kPieceWidth) + (kPieceWidth/2); var y = (row * kPieceHeight) + (kPieceHeight/2); var radius = (kPieceWidth/2) – (kPieceWidth/10); أصبح لدينا الآن إحداثيات (x, y) منسوبة إلى لوحة الرسم لمركز الدائرة التي نريد رسمها. للأسف لا توجد دالة باسم circle()‎ في واجهة canvas البرمجية، لكن هنالك الدالة arc()‎؛ لكن الدائرة ما هي إلا قوسٌ تتصل بدايته مع نهايته. هل تتذكر أساسيات الهندسة؟ تأخذ الدالة arc()‎ نقطة المركز (x, y)، ونصف قطر، وزاوية البداية والنهاية (بالردايان)، ورايةً (flag) لتحدد اتجاه الدوران (false لاتجاه دوران عقارب الساعة، و true لعكس جهة دوران عقارب الساعة). يمكنك استخدام وحدة Math المبنية في لغة JavaScript لحساب الزوايا بالردايان. gDrawingContext.beginPath(); gDrawingContext.arc(x, y, radius, 0, Math.PI * 2, false); gDrawingContext.closePath(); انتظر برهة! لم يُرسَم شيء. الدالة arc()‎ تشبه عمل moveTo()‎ و lineTo()‎. حيث ترسم "بقلم الرصاص". ولرسم الدائرة فعليًا، علينا ضبط خاصية strokeStyle ثم استدعاء الدالة stroke()‎ "لتحبيرها". gDrawingContext.strokeStyle = "#000"; gDrawingContext.stroke(); ماذا لو كانت القطعة مُحدَّدةً؟ يمكننا إعادة استخدام نفس المسار الذي أنشأناه لرسم حدود القطعة لملئها بلونٍ معيّن. if (selected) { gDrawingContext.fillStyle = "#000"; gDrawingContext.fill(); } هذا كل ما في الأمر. تهتم بقية السكربت بالبنية المنطقية للعبة، مثل التفريق بين الحركات الصحيحة وغير الصحيحة، وإحصاء عدد الحركات، ومعرفة إذا انتهت اللعبة. مرحى! لقد أنشأنا لعبةً عبر عنصر canvas برسم 9 دوائر وبعض المستقيمات ودالة وحيدة مرتبطة بالحدث onclick. لمزيد من المعلومات حول عنصر canvas يمكنك الإطلاع على سلسلة التعامل مع العنصر canvas باستخدام جافاسكربت. ترجمة -وبتصرّف- لفصل "Canvas" من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  21. ربما تتساءل: "كيف أستطيع البدء باستخدام HTML5 إن لم تكن تدعمها المتصفحات القديمة؟" لكن السؤال نفسه مُضلِّل، فليست HTML5 شيئًا واحدًا كبيرًا، وإنما هي مجموعة من الميزات المتفرقة، فلا يمكنك الكشف عن "دعم HTML5" لأن هذا غير منطقي، وإنما يمكنك الكشف عن دعم الميزات المتفرقة مثل canvas أو تشغيل الفيديو أو تحديد الموقع الجغرافي. تقنيات الاكتشاف عندما يُحمِّل متصفحك صفحة ويب، فإنه يُنشِئ ما نسميه "Document Object Model" (اختصارًا DOM)، الذي هو مجموعة من الكائنات التي تُمثِّل عناصر HTML الموجودة في الصفحة، فكل عنصر -كل وسم <p> أو <div> أو <span>- يُمثَّل في DOM بكائن مختلف (هنالك كائنات عامة مثل window و document، التي لا ترتبط بعناصر محددة). تتشارك جميع كائنات DOM بمجموعةٍ من الخاصيات المشتركة، لكن لبعض الكائنات خاصيات أكثر من بعضها الآخر. ويكون لدى بعض الكائنات خاصيات فريدة في المتصفحات التي تدعم ميزات HTML5؛ لذلك يكون من الكافي عادةً إلقاء نظرة على DOM لتعرف ما هي الميزات المدعومة. هنالك أربع تقنيات أساسية لاكتشاف دعم المتصفح لميزة مُحدَّدة، هذه هي بالترتيب من الأبسط إلى الأكثر تعقيدًا: التحقق من وجود خاصية معينة في كائن عام (مثل window أو navigator)، مثلما هو عليه الحال مع دعم تحديد الموقع الجغرافي. إنشاء عنصر، ثم التحقق من وجود خاصية معيّنة في ذاك العنصر، مثلما هو عليه الحال مع تحديد دعم canvas. إنشاء عنصر، ثم التحقق من وجود دالة (method) معينة في ذاك العنصر، ثم استدعاء تلك الدالة والتحقق من القيمة التي تُعيدها. مثلما هو عليه الحال مع معرفة صيغ الفيديو المدعومة. إنشاء عنصر، وضبط خاصية فيه إلى قيمةٍ معيّنة، ثم التحقق من أنَّ تلك الخاصية قد احتفظت بقيمتها. مثل ما هو عليه الحال مع معرفة أنواع حقول <input> المدعومة. Modernizr: مكتبة اكتشاف دعم ميزات HTML5 Modernizr هي مكتبة JavaScript مفتوحة المصدر مُرخَّصة برخصة MIT مهمتها اكتشاف الدعم للعديد من ميزات HTML5 و CSS3، وأنصحك باستعمال آخر إصدار منها دومًا. عليك -لاستعمالها- تضمينها عبر عنصر <script> في ترويسة (header) صفحتك كما يلي: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Dive Into HTML5</title> <script src="modernizr.min.js"></script> </head> <body> ... </body> </html> سيعمل Modernizr تلقائيًا، فلا توجد هنالك دالة modernizr_init()‎ لكي تستدعيها. فعندما يُشغَّل السكربت، فإنه يُنشِئ كائنًا عامًا اسمه Modernizr يحتوي على مجموعة من الخاصيات المنطقية (أي True أو False) لكل خاصية يمكنه الكشف عن دعمها. فعلى سبيل المثال، إن كان متصفحك يدعم استخدام canvas، فإن قيمة الخاصية Modernizr.canvas ستكون True، وإن لم يكن متصفحك يدعمها، فستكون قيمة الخاصية Modernizr.canvas مساويةً إلى False. if (Modernizr.canvas) { // لنرسم بعض الأشكال } else { // لا يوجد دعم لخاصية canvas } الكشف عن دعم Canvas تُعرِّف HTML العنصر <canvas> على أنه "لوحة نقطية ذات أبعاد معينة يمكن استخدامها لعرض المخططات ورسومات الألعاب وغيرها من الصور المرئية برمجيًا". ويُمثِّل مستطيلًا في صفحتك حيث تستخدم JavaScript لرسم أي شيء تريده فيه، وتُعرِّف HTML5 مجموعةً من الدوال (تدعى "canvas API") لرسم الأشكال (shapes) وتعريف المسارات (paths) وإنشاء التدرجات اللونية وتطبيقات التحويلات (transformations) على العناصر. سنستخدم تقنية الكشف عن الدعم الثانية للتحقق من دعم المتصفح للعنصر canvas، فإن دعم متصفحك واجهة canvas البرمجية (API) فهذا يعني أن الكائن في DOM الذي يُمثِّل عنصر <canvas> سيملك الدالة getContext()‎، وإن لم يكن يدعم متصفحك واجهة canvas البرمجية، فسيملك الكائن المُنشَأ لعنصر <canvas> الخاصيات العامة لكل العناصر، وليس من بينها أي شيءٍ متعلقٍ بتقنية canvas. function supports_canvas() { return !!document.createElement('canvas').getContext; } تبدأ هذه الدالة عملها بإنشاء عنصر <canvas> لا حول له ولا قوة، لكن ذاك العنصر لن يرتبط مطلقًا بصفحتك، ولن يراه أحد، وإنما سيبقى عائمًا بالذاكرة دون أن يفعل شيئًا. return !!document.createElement('canvas').getContext; وبعد إنشاء عنصر <canvas>، ستختبر وجود الدالة getContext()‎، فستكون هذه الدالة موجودةً إذا كان المتصفح يدعم واجهة canvas البرمجية. return !!document.createElement('canvas').getContext; وفي النهاية، استعملنا علامة النفي المزدوجة (!!) كي تكون النتيجة قيمةً منطقية (أي true أو false). return !!document.createElement('canvas').getContext; تكتشف هذه الدالة الدعم لأغلبية مكونات واجهة canvas البرمجية، بما في ذلك الأشكال، والمسارات، والتدرجات اللونية والأنماط. لكنها لا تكتشف وجود المكتبة الخارجية explorercanvas التي تسمح باستخدام واجهة canvas البرمجية في متصفح Internet Explorer. وبدلًا من كتابة الدالة السابقة بنفسك، تستطيع استعمال Modernizr للكشف عن دعم واجهة canvas البرمجية. if (Modernizr.canvas) { // لنرسم بعض الأشكال } else { // لا يوجد دعم لخاصية canvas :( } هنالك اختبارٌ منفصلٌ للتحقق من دعم واجهة canvas text البرمجية، وذلك في الفقرة الآتية. الكشف عن دعم النصوص في عنصر Canvas حتى لو كان يدعم متصفحك واجهة canvas البرمجية، فقد لا يدعم واجهة canvas text البرمجية، فنَمَتْ واجهة canvas البرمجية على مر الزمن، وأُضيفت دوال النصوص في فترةٍ لاحقة، لهذا هنالك بعض المتصفحات التي تدعم canvas لكن قبل أن يكتمل العمل على دوال النصوص. سنستخدم تقنية الكشف عن الدعم الثانية للتحقق من دعم المتصفح للعنصر canvas، فإن دعم متصفحك واجهة canvas البرمجية (API) فهذا يعني أن الكائن في DOM الذي يُمثِّل عنصر <canvas> سيملك الدالة getContext()‎، وإن لم يكن يدعم متصفحك واجهة canvas البرمجية، فسيملك الكائن المُنشَأ لعنصر <canvas> الخاصيات العامة لكل العناصر، وليس من بينها أي شيءٍ متعلقٍ بتقنية canvas. function supports_canvas_text() { if (!supports_canvas()) { return false; } var dummy_canvas = document.createElement('canvas'); var context = dummy_canvas.getContext('2d'); return typeof context.fillText == 'function'; } تبدأ الدالة السابقة بالتحقق من دعم canvas باستخدام دالة supports_canvas()‎ التي رأيتها في القسم السابق، فإن لم يكن يدعم متصفحك واجهة canvas البرمجية، فهو بالتأكيد لن يدعم إضافة النصوص إلى عناصر canvas. if (!supports_canvas()) { return false; } ثم ستُنشِئ عنصر <canvas> جديد ثم تحاول الوصول إلى "لوحة الرسم" (drawing context)، ومن المؤكد أن ما سبق سيعمل دون مشاكل لأن الدالة supports_canvas()‎ قد تحققت من وجود الدالة getContext()‎ في جميع عناصر canvas. var dummy_canvas = document.createElement('canvas'); var context = dummy_canvas.getContext('2d'); وفي النهاية، ستتحقق إن كان لدى لوحة الرسم الدالة fillText()‎، فإن كانت تملكها، فهنالك دعمٌ للنصوص في canvas. return typeof context.fillText == 'function'; وبدلًا من كتابة الدالة السابقة بنفسك، تستطيع استعمال Modernizr للكشف عن دعم واجهة canvas text البرمجية. if (Modernizr.canvastext) { // لنضع بعض النصوص } else { // لا يوجد دعم لخاصية canvas text } الكشف عن دعم الفيديو أضافت HTML5 عنصرًا جديدًا هو <video> لتضمين مقاطع الفيديو في صفحات الويب، كان تضمين الفيديو في السابق من المستحيلات دون استخدام إضافات خارجية مثل Apple QuickTime®‎ أو Adobe Flash®‎. صُمِّمَ عنصر <video> ليُستعمَل دون الحاجة إلى أيّة سكربتات للكشف عن الدعم، فيمكنك تحديد عدِّة مسارات لمقاطع الفيديو، وستختار المتصفحات التي تدعم HTML5 video أحدها بناءً على الصيغ التي تدعمها. المتصفحات التي لا تدعم HTML5 video ستتجاهل وسم <video> تمامًا. لكنك تستطيع استخدام ذلك لصالحك بإخبارها أن تُشغِّل المقطع باستخدام إضافة خارجية. برمَج Kroc Camen حلًا اسمه Video for Everybody!‎ الذي يستخدم دعم الفيديو في HTML5 عند توفره، لكنه سيعود إلى استخدام QuickTime أو Flash في المتصفحات القديمة. لا يستعمل هذا الحل JavaScript مطلقًا، ويعمل نظريًا على أي متصفح، بما في ذلك متصفحات الهواتف المحمولة. إذا أردت القيام بأكثر من مجرد وضع الفيديو في صفحتك وتشغيله، فستحتاج إلى استخدام JavaScript، نستخدم التقنية الثانية للتحقق من دعم الفيديو. فإذا كان متصفحك يدعم HTML5 video، فإن كائن DOM الذي سيُنشئه المتصفح لتمثيل عنصر <video> سيملك الخاصية canPlayType()‎. وإن لم يكن يدعم متصفحك HTML5 video، فإن كائن DOM المُنشَأ لعنصر <video> سيملك الخاصيات العامة لأي عنصر. يمكنك التحقق من دعم الفيديو عبر هذه الدالة: function supports_video() { return !!document.createElement('video').canPlayType; } وبدلًا من كتابة الدالة السابقة بنفسك، تستطيع استعمال Modernizr للكشف عن دعم HTML5 video. if (Modernizr.video) { // لنشغل مقاطع الفيديو } else { // لا يوجد دعم للفيديو :( // ربما علينا استخدام QuickTime أو Flash بدلًا منه } سأشرح -في الدرس الخاص بالفيديو- حلًا آخر يستعمل تقنيات الكشف السابقة لتحويل عناصر <video> إلى مشغلات فيديو مبنية على تقنية Flash، وذلك لتشغيل الفيديو على المتصفحات التي لا تدعم HTML5 video. هنالك اختبارٌ منفصلٌ للتحقق من صيغ الفيديو التي يمكن للمتصفح تشغيلها، مشروحٌ في الفقرة الآتية. صيغ الفيديو مَثَلُ صيغ الفيديو كمَثَلِ اللغات المكتوبة، فقد تحتوي صحيفة إنكليزية على نفس المعلومات التي تحتويها صحيفة عربية، لكن إن كنت تقرأ اللغة العربية فقط، فستكون إحدى تلك الصحيفتين مفيدةً لك. ولتشغيل مقطع فيديو، يجب أن يفهم المتصفح "اللغة" التي كُتِبَ فيها هذا المقطع. تسمى "اللغة" التي تكتب فيها مقاطع الفيديو "بالمرماز" (codec) الذي هو الخوارزمية المستخدمة لترميز (encode) مقطع الفيديو إلى سلسلة من البتات، وهنالك عدِّة مرمزات، لكن أيها تستعمل؟ في الواقع، من المؤسف أن المتصفحات لم تتوافق على مرماز معيّن، لكنهم قللوا الخيارات إلى خيارين فقط. أحدهما يكلف مالًا (بسبب براءة الاختراع)، لكنه يعمل في متصفح Safari وفي iPhone (وهو يعمل أيضًا في مشغلات Flash إن كنت ستستعمل حلًا مثل Video for Everybody!‎). أما المزمار الآخر فهو مجاني ويعمل على المتصفحات مفتوحة المصدر مثل Chromium و Firefox. نستخدم التقنية الثالثة لمعرفة صيغ الفيديو المدعومة. وإذا كان متصفحك يدعم HTML5 video، فإن كائن DOM الذي سيُنشئه المتصفح لتمثيل عنصر <video> سيملك الخاصية canPlayType()‎. ستخبرك الطريقة الآتية إن كان متصفحك يدعم صيغة فيديو معينة. تتحقق هذه الدالة من دعم الصيغة المحمية بحقوق براءة الاختراع والمدعومة من أجهزة Mac و iPhone. function supports_h264_baseline_video() { if (!supports_video()) { return false; } var v = document.createElement("video"); return v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'); } تبدأ هذه الدالة بالتحقق من دعم HTML video في المتصفح، وذلك باستخدام الدالة supports_video()‎ التي رأيتها في القسم السابق. فإن لم يكن يدعم متصفحك HTML5 video، فهو بالتأكيد لن يدعم أيّة صيغة من صيغ الفيديو. if (!supports_video()) { return false; } ثم ستُنشِئ الدالة عنصر <video> (لكن دون أن تضيفه إلى الصفحة، أي أنه لن يكون مرئيًا) وتستدعي الدالة canPlayType()‎، من المؤكد وجود هذه الدالة لأن الدالة supports_video()‎ تحققت منها في الخطوة السابقة. var v = document.createElement("video"); في الواقع، "صيغة الفيديو" هي مجموعة من عدِّة أشياء، فبكلامٍ تقني، أنت تسأل المتصفح إن كان يستطيع تشغيل فيديو H.264 بنمط Baseline مع صوت AAC LC في حاوية MPEG-4. return v.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"'); لن تُعيد الدالة canPlayType()‎ القيمة true أو false، وإنما ستعيد سلسلة نصية آخذةً بعين الاعتبار الطبيعة المُعقَّدة لصيغ الفيديو: "probably" إن كان المتصفح واثقًا أنه يستطيع تشغيل هذه الصيغة "maybe" إن ظن المتصفح أنه يستطيع تشغيل هذه الصيغة "" (سلسلة نصية فارغة) إن كان المتصفح متأكدًا أنه لن يستطيع تشغيل هذه الصيغة أما الدالة الآتية فهي تتحقق من دعم صيغة الفيديو المفتوحة المدعومة من متصفح Firefox وغيره من المتصفحات مفتوحة المصدر. العملية مماثلة تمامًا لما سبق، لكن الاختلاف الوحيد هو في السلسلة النصية التي تمررها إلى الدالة canPlayType()‎. تقنيًا، أنت تسأل المتصفح إن كان قادرًا على تشغيل فيديو Theora مع صوت Vorbis في حاوية Ogg. function supports_ogg_theora_video() { if (!supports_video()) { return false; } var v = document.createElement("video"); return v.canPlayType('video/ogg; codecs="theora, vorbis"'); } في النهاية، WebM هو مرماز فيديو جديد ومفتوح المصدر (وغير محمي ببراءة اختراع) تم دعمه في الإصدارات الجديدة من المتصفحات الحديثة، بما في ذلك Chrome، و Firefox، و Opera. ويمكنك استخدام نفس التقنية السابقة لاكتشاف دعم صيغة WebM. function supports_webm_video() { if (!supports_video()) { return false; } var v = document.createElement("video"); return v.canPlayType('video/webm; codecs="vp8, vorbis"'); } بدلًا من كتابة هذه الدالة بنفسك، يمكنك استعمال Modernizr (الإصدار 1.5 أو ما بعده) لاكتشاف الدعم لمختلف صيغ الفيديو في HTML5. if (Modernizr.video) { // لنشغل مقاطع الفيديو! لكن ما صيغتها؟ if (Modernizr.video.webm) { // لنجرب WebM } else if (Modernizr.video.ogg) { // لنجرب Ogg Theora + Vorbis في حاوية Ogg } else if (Modernizr.video.h264){ // لنجرب H.264 video + AAC audio في حاوية MP4 } } التخزين المحلي Local Storage يوفر التخزين المحلي لمواقع الويب طريقةً لتخزين المعلومات على حاسوبك ثم استعادتها لاحقًا، مفهومها مشابه لمفهوم "الكعكات" (cookies)، لكنها مصممة لكمية معلومات أكبر، فالكعكات محدودة الحجم، ويرسلها المتصفح إلى خادوم الويب في كل مرة يطلب فيها صفحة جديدة (مما يأخذ وقتًا أطول ويستهلك بيانات التراسل). أما تخزين HTML5 فهو يبقى في حاسوبك، وتستطيع مواقع الويب الوصول إليه عبر JavaScript بعد أن يتم تحميل الصفحة. س: هل التخزين المحلي هو جزءٌ من HTML5؟ ولماذا وضعوه في معيار منفصل؟ ج: الجواب المختصر هو "نعم"، التخزين المحلي هو جزءٌ من HTML5. أما الجواب الأطول فهو أن التخزين المحلي كان جزءًا من معيار HTML5 الرئيسي، لكنه أصبح معيارًا منفصلًا بسبب شكوى بعض الأشخاص في مجموعة عمل HTML5 أن HTML5 أصبحت كبيرة جدًا. حتى لو كان ذلك يشبه تقسيم شطيرة إلى قطع صغيرة لتقليل كمية الحريرات :-| حسنًا، أهلًا بك في العالم العجيب للمعايير القياسية. سنستخدم تقنية الكشف الأولى للتحقق من دعم المتصفح للتخزين المحلي، فإن دَعَم متصفحك التخزين المحلي، فستكون هنالك خاصية localStorage في كائن window العام. وإن لم يكن يدعم متصفحك التخزين المحلي، فلن تكون الخاصية localStorage معرَّفةً. وبسبب علِّة في الإصدارات القديمة من Firefox، سيسبب هذا الخيار بحدوث استثناء (exception) إن كانت الكعكات (cookies) مُعطَّلةً، لذلك وضعنا الاختبار في عبارة try..catch. function supports_local_storage() { try { return 'localStorage' in window && window['localStorage'] !== null; } catch(e){ return false; } } بدلًا من كتابة هذه الدالة بنفسك، يمكنك استعمال Modernizr (الإصدار 1.1 أو ما بعده) لاكتشاف الدعم للتخزين المحلي. if (Modernizr.localstorage) { // متوفرة! window.localStorage } else { // لا يوجد دعم للتخزين المحلي // ربما تجرب Gears أو مكتبة أخرى } لاحظ أنَّ JavaScript حساسة لحالة الأحرف، إذ تُدعى خاصية Modernizr ‏"localstorage" (جميعها بأحرفٍ صغيرة)، أما خاصية DOM فهي window.localStorage (حرف S كبير). س: ما مدى أمان قاعدة بيانات التخزين المحلي في HTML5؟ هل يستطيع أحدٌ قراءتها؟ ج: أي شخص لديه وصولٌ فيزيائيٌ لحاسوبك قد يستطيع عرض (أو حتى تعديل) البيانات الموجودة في قاعدة بيانات التخزين المحلي في HTML5. ويستطيع أي موقع ويب قراءة وتخزين القيم الخاصة به، لكن لا يستطيع الوصول إلى القيم التي خزنتها المواقع الأخرى، وهذا يسمى same-origin restriction. Web Workers توفر ميزة Web Workers طريقةً قياسيةً لتشغيل JavaScript في الخلفية، إذ تستطيع تشغيل عدِّة "خيوط" (threads) التي تعمل في نفس الوقت تقريبًا (تذكر طريقة تشغيل الحاسوب لعدِّة تطبيقات معًا في آنٍ واحد)، تستطيع تلك الخيوط التي تعمل في الخلفية أن تجري عمليات حسابية معقدة، أو أن تجري طلبيات شبكيّة، أو أن تصل إلى التخزين المحلي في أثناء استجابة صفحة الويب إلى تفاعل المستخدم معها. نستعمل طريقة الاكتشاف الأولى لمعرفة إن كان المتصفح يدعم واجهة Web Worker البرمجية، وذلك إن وُجِدَت الخاصية Worker في الكائن العام window، وإن لم يكن يدعم متصفحك واجهة Web Worker البرمجية، فستكون خاصية Worker غير معرفة. function supports_web_workers() { return !!window.Worker; } بدلًا من كتابة هذه الدالة بنفسك، يمكنك استعمال Modernizr (الإصدار 1.1 أو ما بعده) لاكتشاف الدعم لواجهة Web Workers البرمجية. if (Modernizr.webworkers) { // window.Worker متوفرة! } else { // لا يوجد دعم لواجهة Web Workers :( // ربما تجرب Gears أو مكتبة أخرى } لاحظ أنَّ JavaScript حساسة لحالة الأحرف، إذ تُدعى خاصية Modernizr ‏"webworkers" (جميعها بأحرفٍ صغيرة)، أما خاصية DOM فهي window.Worker (حرف W كبير في Worker). تطبيقات الويب دون اتصال Offline Web Applications يمكن ببساطة قراءة صفحات الويب الثابتة دون اتصال: اتصل إلى الإنترنت، حمِّل صفحة الويب، اقطع اتصالك بالإنترنت، ثم سافر إلى بلدٍ آخر، واقرأ الصفحة في وقت فراغك (يمكنك تخطي خطوة السفر إلى بلدٍ آخر لتوفير الوقت). لكن ماذا عن تطبيقات الويب مثل Gmail أو Google Docs؟ الفضل يعود إلى HTML5، التي تُمكِّن الجميع (وليس فقط Google) من بناء تطبيقات ويب تعمل دون اتصال. تبدأ تطبيقات الويب التي تعمل دون اتصال كتطبيقات تعمل بوجود اتصال بالإنترنت، ففي أول مرة تزور فيها تطبيق ويب يدعم العمل دون اتصل، فسيخبر الخادومُ متصفحَك ما هي الملفات التي يحتاج لها كي يعمل دون اتصال. قد تكون هذه الملفات من أي نوع: صفحات HTML، أو JavaScript، أو الصور أو حتى مقاطع الفيديو. وبعد أن ينزِّل متصفحك كل الملفات الضرورية، ستستطيع إعادة زيادة موقع الويب حتى لو لم تكن متصلًا بالإنترنت، وسيلاحظ متصفحك أنَّك غير متصل وسيستعمل الملفات التي نزلها من قبل. وعندما تتصل مجددًا بالإنترنت، فيمكن رفع أيّة تعديلات أجريتها على خادم الويب البعيد. نستعمل طريقة الاكتشاف الأولى لمعرفة إن كان المتصفح يدعم تشغيل تطبيقات الويب دون اتصال، وذلك إن وُجِدَت الخاصية applicationCache في الكائن العام window، وإن لم يكن يدعم متصفحك تشغيل تطبيقات الويب دون اتصال، فستكون خاصية applicationCache غير معرفة؛ يمكنك التحقق من دعم تشغيل تطبيقات الويب دون اتصال مع هذه الدالة: function supports_offline() { return !!window.applicationCache; } بدلًا من كتابة هذه الدالة بنفسك، يمكنك استعمال Modernizr (الإصدار 1.1 أو ما بعده) لاكتشاف الدعم لتشغيل تطبيقات الويب دون اتصال. if (Modernizr.applicationcache) { // window.applicationCache متوفرة! } else { // لا يوجد دعم للتطبيقات دون اتصال :( // ربما تجرب Gears أو مكتبة أخرى } لاحظ أنَّ JavaScript حساسة لحالة الأحرف، إذ تُدعى خاصية Modernizr ‏"applicationcache" (جميعها بأحرفٍ صغيرة)، أما خاصية DOM فهي window.applicationCache (حرف c كبير في Cache). تحديد الموقع الجغرافي Geolocation يفيد تحديد الموقع الجغرافي في معرفة أين أنت في هذا الكوكب و (اختياريًا) مشاركة تلك المعلومات مع الأشخاص الذين تثق بهم، هنالك أكثر من طريقة لمعرفة أين أنت: عبر عنوان IP، أو عبر اتصال شبكتك اللاسلكية، أو أيّ برج تغطية خلوية تتصل منه، أو عبر عتاد GPS الذي يحسب إحداثيات موقعك الحالي عبر المعلومات التي ترسلها الأقمار الاصطناعية في السماء. س: هل تحديد الموقع الجغرافي جزءٌ من HTML5؟ ولماذا تتحدث عنه إذًا؟ ج: لقد أضيف دعم تحديد الموقع الجغرافي من المتصفحات مع ميزات HTML5 الجديدة. لكن إذا ابتغينا الدقة، يُوفَّر معيار تحديد الموقع الجغرافي من مجموعة عمل Geolocation، التي هي مجموعة عمل منفصلة عن مجموعة عمل HTML5، لكننا سنتحدث عن تحديد الموقع الجغرافي في هذه السلسلة على أيّة حال، لأنه جزءٌ من التطوير الذي يحدث في الويب في الوقت الراهن. نستعمل طريقة الاكتشاف الأولى لمعرفة إن كان المتصفح يدعم واجهة تحديد الموقع الجغرافي البرمجية، وذلك إن وُجِدَت الخاصية geolocation في الكائن العام navigator، وإن لم يكن يدعم متصفحك تحديد الموقع الجغرافي، فستكون خاصية geolocation غير معرفة؛ يمكنك التحقق من دعم تحديد الموقع الجغرافي مع هذه الدالة: function supports_geolocation() { return !!navigator.geolocation; } بدلًا من كتابة هذه الدالة بنفسك، يمكنك استعمال Modernizr لاكتشاف الدعم لتحديد الموقع الجغرافي. if (Modernizr.geolocation) { // لنكتشف أين أنت الآن! } else { // لا يوجد دعم لتحديد الموقع الجغرافي :( // ربما تجرب Gears أو مكتبة أخرى } إن لم يدعم متصفحك واجهة تحديد الموقع الجغرافي البرمجية داخليًا، فلا تيأس. فهنالك Gears التي هي إضافة مفتوحة المصدر للمتصفحات من Google التي تعمل على ويندوز و Mac OS X ولينُكس والهواتف العاملة بنظامَي ويندوز وأندرويد. حيث توفر ميزاتٍ للمتصفحات القديمة التي لا تدعم الأشياء الجديدة التي تحدثنا عنها في هذا الدرس. إحدى الميزات التي توفرها Gears هي تحديد الموقع الجغرافي، لكنها ليست مطابقة لواجهة navigator.geolocation البرمجية، لكنها تخدم نفس الغرض. هنالك واجهات برمجية لتحديد المواقع لأنظمة الهواتف القديمة مثل BlackBerry، و Nokia، و Palm، و OMTP BONDI. سيشرح الدرس الخاص بتحديد الموقع الجغرافي بالتفصيل كيفية استخدام مختلف الواجهات البرمجية السابقة. أنواع الإدخال في النماذج Input Types أنت تعرف الكثير عن نماذج الويب، صحيح؟ أنشِئ عنصر <form> ثم أضف بعض عناصر <input type="text"‎> إليه وربما عنصر <input type="password"‎>، ثم أنهِ النموذج بزر <input type="submit"‎>. حسنًا، ذلك جزءٌ يسيرٌ من النماذج، إذ أضافت HTML5 حوالي ثلاثة عشر نوعًا من أنواع المدخلات التي يمكنك استعمالها في نماذجك. <"input type="search"> لصناديق البحث <"input type="number"> لإدخال الأرقام <"input type="range"> للمزلاج (slider) لتحديد مجال <"input type="color"> لاختيار الألوان <"input type="tel"> لأرقام الهواتف <"input type="url"> لعناوين الويب <"input type="email"> لعناوين البريد الإلكتروني <"input type="date"> التقويم لاختيار التاريخ <"input type="month"> للأشهر <"input type="week"> للأسابيع <"input type="time"> لبصمات الوقت <"input type="datetime"> لبصمات الوقت والتاريخ بدقة <"input type="datetime-locale"> للوقت والتاريخ المحليين سنستخدم التقنية الرابعة لاكتشاف أنواع الحقول المدعومة في النماذج. في البداية سننشِئ عنصر <input> في الذاكرة، وسيكون نوع الحقل الافتراضي لجميع عناصر <input> هو "text"، وسيتضح لك لماذا هذا مهمٌ جدًا. var i = document.createElement("input"); ثم سنضبط خاصية type في عنصر <input> إلى نوع حقل الإدخال الذي تريد معرفة إن كان مدعومًا أم لا. i.setAttribute("type", "color"); إن كان يدعم متصفحك نوع حقل الإدخال المعين، فستحتفظ خاصية type بالقيمة التي ضبطتها، أما إن لم يكن يدعم متصفحك نوع الحقل المعيّن، فسيتجاهل القيمة التي ضبطتها وستبقى قيمة الخاصية type مساويةً إلى "text". return i.type !== "text" بدلًا من كتابة 13 دالة منفصلة يدويًا، تستطيع استخدام Modernizr لاكتشاف الدعم لجميع أنواع حقول الإدخال المُعرَّفة في HTML5. يُعيد Modernizr استخدام عنصر <input> وحيد لكي يكتشف ما هي أنواع حقول الإدخال المدعومة. ثم يبني جدولًا من نوع hash باسم Modernizr.inputtypes يحتوي على 13 مفتاح (خاصيات type في HTML5) و 13 قيمة منطقية (أي true إن كان الحقل مدعومًا، أو false إن لم يكن كذلك). if (!Modernizr.inputtypes.date) { // لا يوجد دعم لحقل <input type="date"‎> :( // ربما تستعمل مكتبة Dojo أو jQueryUI } النص البديل Placeholder بالإضافة إلى أنواع حقول الإدخال الجديدة، تضمنت HTML5 بعض الإضافات الصغيرة للنماذج. أحدها هو إمكانية وضع نص بديل (placeholder) في حقل الإدخال. يُعرَض النص البديل في حقل الإدخال لطالما كان الحقل فارغًا، وبمجرد أن تكتب شيئًا في الحقل، فسيختفي ذاك النص البديل. هنالك لقطات للشاشة في درس نماذج الويب يمكنك النظر إليها إن واجهت صعوبةً في تخيله. سنستخدم تقنية الكشف عن الدعم الثانية للتحقق من دعم المتصفح للنص البديل في حقول الإدخال، فإن دعم متصفحك النص البديل فهذا يعني أن الكائن في DOM الذي يُمثِّل عنصر <input> سيملك الخاصية placeholder (حتى لو لم تضع خاصية placeholder في شيفرة HTML)، وإن لم يكن يدعم متصفحك النص البديل، فلن يملك الكائن المُنشَأ لعنصر <input> الخاصية placeholder. function supports_input_placeholder() { var i = document.createElement('input'); return 'placeholder' in i; } بدلًا من كتابة هذه الدالة بنفسك، يمكنك استعمال Modernizr (الإصدار 1.1 أو ما بعده) لاكتشاف الدعم للنص البديل في حقول الإدخال. if (Modernizr.input.placeholder) { // يمكنك استعمال النص البديل في حقول الإدخال! } else { // لا يوجد دعم للنص البديل :( // استعمل سكربت لفعل ذلك } التركيز التلقائي على النماذج autofocus تستعمل مواقع الويب JavaScript للتركيز (focus) على حقل من حقول الإدخال في نماذج الويب. على سبيل المثال، الصفحة الرئيسية لمحرك البحث Google ستُركِّز تلقائيًا على حقل البحث كي تستطيع كتابة ما الذي تريد البحث عنه مباشرةً دون الحاجة إلى النقر على حقل الإدخال، ربما هذا ملائم للكثيرين، لكنه مزعج للمستخدمين المتقدمين أو لأولي الاحتياجات الخاصة، فإن ضغطت على زر المسافة (space) متوقعًا أن تُمرِّر إلى الأسفل، فلن يتم ذلك، لوجود المؤشر في حقل إدخال (وستُكتب مسافة فارغة في حقل الإدخال بدلًا من التمرير)، وإن ركزت على حقل إدخال مختلف في أثناء تحميل الصفحة، فسيحرك سكربت التركيز التلقائي التركيز إلى الحقل المُحدَّد بعد إكمال تحميل الصفحة، مما يؤدي إلى كتابتك في المكان الخاطئ. ولأن التركيز التلقائي كان يتم عبر JavaScript، فمن الصعب التعامل مع جميع الحالات الغريبة، وليس هنالك ملجأ من التركيز التلقائي للحقول لِمَن لا يريد ذلك! ولحل هذه المشكلة، قدَّمَت HTML5 خاصية autofocus في جميع عناصر نماذج الويب. وهذه الخاصية تفعل تمامًا كما هو واضح من اسمها: تنقل التركيز إلى حقل إدخال معين، ولأنها من شيفرة HTML بدلًا من كونها سكربت، فإن سلوكها سيكون متناغمًا ومتماثلًا في كل مواقع الويب، ويمكن لصانعي المتصفحات (أو مطوري الإضافات) توفير طريقة للمستخدمين لكي يُعطِّلوا إمكانية التركيز التلقائي. سنستخدم تقنية الكشف عن الدعم الثانية للتحقق من دعم المتصفح للتركيز التلقائي في حقول الإدخال، فإن دعم متصفحك التركيز التلقائي فهذا يعني أن الكائن في DOM الذي يُمثِّل عنصر <input> سيملك الخاصية autofocus (حتى لو لم تضع خاصية autofocus في شيفرة HTML)، وإن لم يكن يدعم متصفحك التركيز التلقائي، فلن يملك الكائن المُنشَأ لعنصر <input> الخاصية autofocus. يمكنك اكتشاف دعم التركيز التلقائي عبر هذه الدالة: function supports_input_autofocus() { var i = document.createElement('input'); return 'autofocus' in i; } بدلًا من كتابة هذه الدالة بنفسك، يمكنك استعمال Modernizr (الإصدار 1.1 أو ما بعده) لاكتشاف الدعم للتركيز التلقائي في حقول الإدخال. if (Modernizr.input.autofocus) { // التركيز التلقائي يعمل! } else { // التركيز التلقائي غير مدعوم :( // استعمل سكربت لفعل ذلك } البيانات الوصفية Microdata البيانات الوصفية (Microdata) هي الطريقة القياسية لتوفير هيكليّة معنوية لصفحات الويب. على سبيل المثال، يمكنك استعمال البيانات الوصفية لتصرِّح أن صورةً ما مرخَّصة بإحدى رخص المشاع الإبداعي. وكما سترى لاحقًا في هذه السلسلة، يمكنك استعمال البيانات الوصفية لتوصيف صفحة "معلومات عني"، فيمكن للمتصفحات –أو لإضافات المتصفحات– أو لمحركات البحث تحويل تلك البيانات الوصفية إلى vCard، التي هي صيغة معيارية لمشاركة معلومات الاتصال؛ يمكنك أيضًا تعريف أنواع خاصة بك من البيانات الوصفية. معيار البيانات الوصفية في HTML5 يتضمن شيفرات HTML (تستعملها محركات البحث بشكلٍ أساسي) ومجموعة من دوال DOM (تستعملها المتصفحات بشكلٍ أساسي). لا حرج في تضمين البيانات الوصفية في صفحات الويب، فهي مجرد خاصيات ذات معنى خاص، وستتجاهلها محركات البحث التي لا تستطيع تفسير البيانات الوصفية. لكن إن كنت تريد الوصول إلى أو تعديل البيانات الوصفية عبر DOM، فعليك أن تتحقق أن متصفحك يدعم واجهة البيانات الوصفية البرمجية (API). نستعمل طريقة الاكتشاف الأولى لمعرفة إن كان المتصفح يدعم واجهة البيانات الوصفية البرمجية، وذلك إن وُجِدَت الدالة getItems()‎ في الكائن العام document، وإن لم يكن يدعم متصفحك البيانات الوصفية، فستكون الدالة getItems()‎ غير معرفة. function supports_microdata_api() { return !!document.getItems; } بدلًا من كتابة هذه الدالة بنفسك، يمكنك استعمال Modernizr لاكتشاف الدعم للبيانات الوصفية. if (Modernizr.microdata) { // هنالك دعمٌ للبيانات الوصفية! } else { // البيانات الوصفية غير مدعومة :( } التأريخ History API واجهة التأريخ البرمجية في HTML5 هي طريقة معيارية لتعديل تأريخ (history) المتصفح عبر السكربتات، جزءٌ من هذه الواجهة -التنقل في التأريخ- كان متوفرًا في الإصدارات السابقة من HTML. أما الجزء الجديد في HTML5 هو طريقة إضافة مدخلات جديدة إلى تأريخ المتصفح، والاستجابة عندما تُحذَف تلك المدخلات عبر ضغط المستخدم لزر الرجوع، وهذا يعني أن URL سيبقى يعمل عمله كمُعرِّف فريد للمورد الحالي، حتى في التطبيقات التي تعتمد اعتمادًا كبيرًا على السكربتات التي لا تجري عملية تحديث لكامل الصفحة. نستعمل طريقة الاكتشاف الأولى لمعرفة إن كان المتصفح يدعم واجهة التأريخ البرمجية، وذلك إن وُجِدَت الدالة pushState()‎ في الكائن العام history، وإن لم يكن يدعم متصفحك واجهة التأريخ البرمجية، فستكون الدالة pushState()‎ ‎ غير معرفة. function supports_history_api() { return !!(window.history && history.pushState); } بدلًا من كتابة هذه الدالة بنفسك، يمكنك استعمال Modernizr (الإصدار 1.6 أو ما بعده) لاكتشاف الدعم لواجهة التأريخ البرمجية. if (Modernizr.history) { // يمكنك تعديل تأريخ المتصفح! } else { // لا يوجد دعم لتعديل التأريخ :( // استعمل مكتبة مثل History.js } مراجع إضافية مكتبات JavaScript: Modernizr، مكتبة اكتشاف الدعم لميزات HTML5 geo.js، مكتبة لإضافة الدعم لواجهة تحديد المواقع Video for Everybody!‎ ترجمة -وبتصرّف- للفصل Detecting HTML5 Features من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  22. هل أنت مهتم بتعلم PHP وبناء قوالب وملحقات خاصة بك على ووردبريس؟ أو ربما أنت مُهتمّ بتعلّم القليل الذي يُمكنك من إدخال التّعديلات التي تحتاجها على مواقعك ومدوّناتك التي تعتمد على ووردبريس. أيّا كان وضعك، هناك أمور يجب عليك أن تعرفها قبل أن تغوص في مجال تطوير ووردبريس. سنحاول في هذا المقال إعطاءك فكرة واضحة عليها. كيف يعمل ووردبريس؟ لنفهم ماذا وكيف يمكننا التطوير في ووردبريس، سنلقي أولا نظرة سريعة على آلية عمل ووردبريس. يتكون ووردبريس من ثلاثة عناصر رئيسية: النواة الأساسية والقوالب والمُلحقات. تحتوي النواة على جميع الوظائف الأساسية التي تشكل نظام إدارة المحتوى لووردبريس، وهذه الشيفرات البرمجية تتضمن كل شيء من البنية التحتية (backend) للإدارة إلى دوال جدولة المشاركات والتأكد من قوة كلمة المرور والسماح بإنشاء مستخدمين وغيرها. في حين أن النواة الأساسية (core) مسؤولة عن البنية التحتية للموقع وكيفية التعامل معها، فإن القوالب مسؤولة عن الواجهة الأمامية للموقع وكيفية ظهوره. نستخدم إدارة ووردبريس (WordPress admin) لإنشاء المشاركات والصفحات وبقية محتوى الموقع، وأما بالنسبة لكيفية ظهور هذه الأشياء للزائر فهو أمر متروك للقالب theme، لذلك فإن هذا سيعطيك قوة كبيرة للتحكم في موقعك، فقد ترغب بتثبيت وتفعيل القوالب الموجودة أو حتى تطوير قوالبك المخصصة. أبسط طريقة لوصف المُلحقات plugins هي أنها توفر وظائف إضافية للووردبريس، ولفعل ذلك، قد تعدّل المُلحق الشيفرة البرمجية للواجهة الأمامية أو البنية التحتية للموقع، وأبسط مثال لذلك هي المُلحق الذي يضيف زر لتغريدة تويتر، وقد تُنشئ أيضا صفحة إعدادات جديدة في البنية التحتية لقائمة الإدارة والتي ستمكنك من إعداد بعض الخيارات الافتراضية لتغريدة المستخدم ويمكنها إضافة نفسها إلى الواجهة الأمامية للموقع، والتي سيكون موقعها في الغالب تحت التدوينة (post). مجالات التطوير بقدر تقدمك في تطوير ووردبريس، ستتمكن من تطوير أي واحدة من المكونات الرئيسية المذكورة أعلاه بالإضافة إلى تطبيقات مستقلة بمساعدة REST API الجديدة وسنذكر المزيد عن هذا الموضوع بعد قليل. مهما كان ما تريد فعله مع ووردبريس، أنصحك باتباع منهج في دراستك، هذه القائمة ستساعدك على التعرف على الأنظمة التي تحتاج إلى معرفتها قبل بدء تعلمك المزيد حول المجال الذي اخترته: القوالب المُلحقات REST API النواة القوالب إن تعلم كيفية عمل القوالب وتطويرها سيسمح لك باحتراف إنشاء المواقع بدءا من المواقع الصفحة الواحدة إلى مواقع المحتوي الحيوي الثقيل، وستسمح لك أيضا بالحصول على المزيد من العملاء (إذا كان هذا ما يهمك) أو حتى بيع القوالب تجاريا. يوجد العديد من مطوري القوالب الناجحين في أسواق مثل Themeforest و Mojo Marketplace وغيرها. ففي وقت كتابة هذا المقال، أشهر قالب على موقع Themeforest تم بيع 1377 نسخة منه في أسبوع أي أكثر من $81,000 في أسبوع واحد فقط! يمكنك الحصول على الكثير من الأموال إذا كنت تستطيع تطوير قالب مميزة. على الرغم من أن هذا المبلغ لا يحققه جميع مطوري القوالب، وربما لن تصبح مليونيرًا بين عشيّة وضحاها من إنشاء القوالب، لكن يمكنك الحصول على مبلغ مالي جيد من القوالب إذا كنت تقوم بالأمر بالشكل الصحيح. إذا أردت رؤية بعض الأمثلة عن القوالب الشهيرة، فألق نظرة على الأسواق التي ذكرناها أعلاه، أو على قسم قوالب ووردبربس حيث جميع القوالب مجانية، فالكثير من مطوري القوالب يصدرون أعمالهم الأولى في هذا القسم. يمكنك تعلم كيفية إنشاء قوالب ووردبريس في أكاديمية حسوب من خلال سلسلة الدروس مدخل إلى تطوير قوالب ووردبريس. الملحقات plugins بطريقة ما، تعتبر المُلحقات شريان حياة ووردبريس، فهي تحوله إلى كل شيء من منتدى إلى شبكة اجتماعية أو منصة للتجارة الإلكترونية وغيرها باستخدام ضغطة زر واحدة، فالمُلحقات تعطيك تحكمًا كاملًا بجميع جوانب نظام ووردبريس، وتسمح لك بتعديل ما تشاء، وسيساعدك هذا كثيرا عند إنشاء مواقع ووردبريس للعملاء. كما هي الحال مع القوالب، فالمُلحقات تملك أيضا أسواقا وفي العادة يتم إدارتها من نفس الشركات التي تدير أسواق القوالب. يعتبر سوق CodeCanyon على سبيل المثال أكبر سوق للملحقات حيث يملك أكثر من 4000 ملحق متاحة للشراء. على الرغم من أن المبيعات ستكون أقل من القوالب إلا أنه يمكنك ربح الكثير إذا أنشأت ملحقًا جيدًا. يملك موقع ووردبريس قسمًا للملحقات المجانية ويحتوي على أكثر من 40000 ملحق، وهو بذلك مصدر كبير للأدوات والإلهام والأمثلة على الشيفرات البرمجية المميزة (والشيفرات البرمجية السيئة للأسف). يمكنك تعلم كيفية إنشاء ملحقات وإضافات ووردبريس في أكاديمية حسوب من خلال سلسلة الدروس مدخل إلى برمجة إضافات ووردبريس. REST API ستستخدم REST API لاحقا عندما تحترف تطوير القوالب والمُلحقات، ونظريا ستكون قادرا على استخدامها إذا كانت لديك خبرة في البرمجة بلغة أخرى مثل جافا أو روبي. تعتبر REST API جديدة نسبيا وتسمح لك بإنشاء تطبيقات حقيقية بالاعتماد على ووردبريس، وهذا يتضمن تطبيقات أندرويد وأيفون وغيرها. أفضل طريقة لشرح هذه الميزة هي عن طريق مقارنتها بمواقع مثل تويتر أو انستغرام، لا شك أنك قد رأيت تغذية تويتر معروضة على موقع أحدهم، ولإضافة هذه الميزة إلى موقعك لن تحتاج إلى معرفة كيفية عمل نواة شيفرة البرمجية الخاصة بتويتر، كل ما تحتاجه هو القليل من الشيفرات البرمجية التي تقول: "من فضلك استرجع آخر خمسة من تغريداتي." وسيتعامل تويتر مع الباقي وسيرجع لك بيانات آخر خمسة تغريدات التي طلبتها، وبعد ذلك يمكنك استخدام HTML و CSS لعرضها. وهذا بالضبط ما يفعله REST API لووردبريس، يمكنك الاتصال بأي موقع وطلب منه آخر خمسة مشاركات فيها، ويمكنك أيضا القيام بأكثر من ذلك: يمكنك إنشاء/حذف المستخدمين وتعديل الفئات وغيرها (بالطبع، تحتاج إلى الاستيثاق قبل استخدامها)، هذا يعني أنه يمكنك استخدام ووردبريس كمستودع للمعلومات وبناء واجهة أمامية وبنية خلفية باستخدام نظام مختلف تماما. النواة ساهم في تطوير ووردبريس مجتمع من الآلاف المتطوعين حول العالم، حيث أنه يعمل 471 شخص على الإصدار 4.4 من ووردبريس، والعديد من الأشخاص الآخرين يعملون على جوانب مختلفة من ووردبريس، من الترجمة وتطبيقات الويب إلى الإتاحة (accessibility) والدعم. في الحقيقة، يمكنك الاشتراك معهم الآن، فبينما تحتاج إلى بعض الخبرة للمشاركة في برمجة نواة ووردبريس إلا أنه يمكنك المساهمة في تحديد المشاكل والأخطاء والتأكد منها وتقديم الأفكار في أي وقت باستخدام WordPress Trac، وهو المكان الذي يمكنك من متابعة تطوير شيفرة نواة ووردبريس. إذا وجدت علة (خطأ - bug) يمكنك إرفاقها بالشيفرة المناسبة للتخلص منها، وبعد عدة مراجعات وتدقيقات سيتم استخدام شيفرتك البرمجية إذا كانت مناسبة، وهذه هي أفضل طريقة للمشاركة في تطوير النواة. تطوير ووردبريس: ماذا تحتاج أن تتعلمه يجب أن تعتاد على العمل على مجموعة محددة من الأدوات حتى تتمكن من تطوير ووردبريس. إن عملية التعلم لا تنتهي بالنسبة إلى أي مطور جيد، سوف أعرض عليك هنا حزمة للبداية كما سأريك بعض الأدوات المتقدمة التي قد تحتاج إليها لاحقا عندما تريد أن تتعلم المزيد. الحد الأدنى من المتطلبات لتطوير الووردبريس هي HTML و CSS و PHP، بالإضافة إلى هذه اللغات ستحتاج إلى جافا سكربت و MySQL في مرحلة ما، عندما تتقن هذه اللغات يمكنك الانتقال إلى أشياء أخرى مثل LESS/SASS و Coffeescript و XML و JSON وغيرها، لكن هذه الأشياء لا تعتبر ضرورية لتطوير ووردبريس، لكن إذا عرفتها سيجعل حياتك أسهل كمطور وستوسع آفاقك. HTML و CSS أول لغتين يجب عليك تعلمهما هما HTML و CSS، مهما كانت الشيفرة البرمجية التي تكتبها ومهما كانت اللغة التي تستخدمها فستحتاج حتما إلى HTML عند إرسال صفحات الويب إلى المستخدم وإلى بعض CSS لتصميم وتزيين الصفحة. إن HTML مسؤولة عن إعطاء هيكل المواقع. على المستوى الأساسي، هذا يعني أنك ستقرر ما النص الذي يجب أن يكون عنوانًا وما يجب أن يكون قائمة وما الذي يجب أن يكون في رأس أو أسفل الصفحة. أما في المستوى الأعمق، ستقرر قرارات مهمة حول SEO والتحسين (optimization). يُستخدم CSS لتطبيق أنماط لهيكل HTML، أي أنك ستُعرّف لون النص والروابط والمسافة بين الفقرات ومحاذاة الصورة وألوان الحدود وسمكها وغيرها من العناصر عن طريق استخدام شيفرة CSS. إن CSS تتميز بسهولة تعلمها وبصعوبة إتقانها. PHP تحظى PHP بشعبية كبيرة كأشهر لغة سكربتات من جانب الخادوم server-side-، ووفقا للاستطلاع الذي قام به W3Techs، تستخدم PHP في 81.6% من المواقع وهي اللغة التي كُتب بها ووردبريس (وتقريبا أغلب أنظمة إدارة المحتوى الأخرى أيضا) ولذلك فإن معرفة PHP هو أمر ضروري لتطوير ووردبريس. إن معرفتك بـ PHP وحدها، سيجعلك تحصل على أكثر من معرفة بتطوير ووردبريس فقط. إن أهم فرق بين لغات جانب الخادوم عن لغات جانب العميل - مثل HTML - هو أنه يتم معالجتها في الخادوم أولا، ففي HTML، يمكنك كتابة شيفرة برمجية لـ Good Morning وسيرسلها HTML كما هي وسيعرضها على الزوار. أما في لغات جانب الخادوم، فستكتب شيء مشابه لهذا [good [time_of_day، وقبل أن يتم إرسال ذلك الأمر إلى المستخدم، سيعالج الخادوم هذه الأمر حتى يعرف ما يضعه في مكان [time_of_day] حسب الوقت الذي دخلت فيه إلى الموقع، فيمكنك أن ترى "Good day" أو "Good evening". لاحظ أن البيانات التي يستقبلها متصفحك تبقى HTML لكن تمت معالجتها في الخادوم مسبقا، ولاحظ أيضا أن الشيفرة السابقة لم تكن شيفرة PHP بل هي مثال فقط لأعطيك فكرة عن آلية عمل هذه اللغة. كما هو الحال مع جميع لغات البرمجة، فالممارسة والتمرين هي التي تجعلك تبرمج بشكل أفضل، فيمكنك تعلم PHP في غضون بضعة أيام لكنك ستجد نفسك أنك لا تملك أية فكرة عن ما الذي تقوم به، لذلك تحتاج إلى تجربة الأشياء وتخريبها وليس الوقوف دون معرفة ما يجري، وكما يقولون الممارسة تولد الإتقان. جافا سكربت ازداد دور جافا سكربت أهمية على الإنترنت، خاصة مع ظهور أدوات مبنية على جافا سكربت مثل Node و Angular. جافاسكربت تستخدم عادة لإضافة وظائف حيوية للمواقع وللتحميل غير المتزامن. سأعطيك مثال على كل واحد منها. فمثلا لو أردت أن يتم إخفاء نموذج بشكل كامل وأن يظهر ببطء داخل lightbox عندما يضغط المستخدم على زر معين، فيمكنك فعل ذلك باستخدام جافا سكربت بما أنه يمكنه التعامل مع تحريك الرسوم (animation) وربما الوظائف الأخرى، مثل التأكد من أنه تم ملئ النموذج بشكل صحيح قبل إرساله. أما التحميل غير المتزامن فيتم عمله عبر آلية تسمى بـ AJAX، فيمكنك استخدام AJAX للحصول على معلومات من الخادوم وعرضها دون إعادة تحميل الصفحة، وأبسط مثال لذلك هو التمرير اللانهائي (endless scrolling) فعندما يتم تحميل 10 مشاركات وتصل إلى نهاية الصفحة فسيتم عرض 10 مشاركات أخرى وتستمر هكذا. الكثير من المطورين تعلموا جافا سكربت من خلال إطار jQuery، فهو يُستخدم على نطاق واسع في ووردبريس وفي ملايين المشاريع على الإنترنت، وعلى الرغم من أن هذا الأمر جيد لكن ضع في اعتبارك أن جافا سكربت هو أكثر من مجرد jQuery وإن تعلم جافا سكربت كما هو هي فكرة جيدة. MySQL إن Mysql هي لغة تُستخدم للوصول والعمل مع البيانات المخزنة في قاعدة البيانات، يستخدم ووردبريس هذه اللغة كثيرا للتعامل مع البيانات، لكن كمطور، لا تحتاج إلى معرفة الكثير لأن ووردبريس يملك مجموعة من الدوال المساعدة. لكن في بعض الحالات الخاصة، قد تحتاج إلى كتابة استعلام قاعدة البيانات بنفسك أو ربما قد ترغب بتحسين شيء في موقعك، لذلك سيفيدك تعلم أساسيات MySQL. إن العمل مع قواعد البيانات واضح للغاية، فالجدول في قاعدة البيانات يشبه كثيرا جداول بيانات Microsoft Excel، فكل عمود يملك عنوانًا وبيانات مخزنة في الأسطر، ونستخدم MySQL لإضافة وحذف وتعديل واسترجاع البيانات، ربما ترغب بكتابة استعلام MySQL يرجع لك جميع المشاركات التي تحتوي على كلمة "awesome" ولديها أكثر من 8 تعليقات، أو قد ترغب أيضا بإيجاد جميع المستخدمين الذين يملكون اسم "Daniel". مرة أخرى، إن كتابة شيفرة MySQL خام في ووردبريس هو أمر نادر، لكنه يحدث في بعض الأحيان، لذلك فإن فهم كيفية عمل اللغة سيساعدك على كتابة شيفرات أفضل بشكل عام أدوات متقدمة بمجرد أن تتعود على الأقل على استخدام ثلاثة اللغات الأساسية - HTML و CSS و PHP - يمكنك البدء باكتشاف العديد من الأدوات المساعدة، واحدة من الأشياء الأولى التي يميل الناس إلى تعلمها هي LESS و/أو SASS، كلاهما "مجموعات موسّعة" (Superset) لـ CSS، أي أن أي شيفرة CSS صحيح تعتبر صحيحة في شيفرة LESS و SASS، وتكمن أهمية إضافة هاتين اللغتين هو إمكانية استخدام متغيرات ودوال و غيرها في CSS، والذي هو غير ممكن بشكل افتراضي. إن أدوات البناء (Build tools) هو شيء سترغب في اكتشافه عند مرحلة معينة، أفضلها Gulp و Grunt فهذان يمكنهما مراقبة تعديلات الملفات وتنفيذ مختلف المهام عند استيفاء المتطلبات، فعلى سبيل المثال، في أي وقت تعدل فيه ملف SASS يمكنك ترجمته تلقائيا إلى CSS وحفظه إلى ملف معين، أو يمكنك تحسين الصور أو دمج الملفات أو تحميل الحزم الخارجية فجميع المهام الأخرى يمكنك أن تفعلها باستخدام هذه الأدوات. سطر الأوامر أو الطرفية (terminal) هو شيء يجب أن تلقي نظرة عليه. نعم إن استخدام سطر الأوامر قد يبدو مخيفا لكنه ليس كذلك، فبمجرد كتابة بضعة أوامر يمكنك حفظ الكثير من الوقت، فسكربت/أداة مثل WP-CLI يمكنه تثبيت ووردبريس في بضعة ثواني، بما في ذلك تثبيت القوالب والمُلحقات والمحتويات للتجربة والتي لو قمت بها بشكل يدوي ستتطلب الكثير من الوقت والجهد. إن أنظمة االتّحكّم في الإصدارات (Version control) هي من الأدوات المفيدة للغاية فلن تعرف حاجتك إليها إلا لو استخدمتها، فعلى الرغم من أنها وُضعت أصلا للعمل على نفس الشيفرة البرمجية في مجموعات إلا أنه يمكنك استخدامها كحل لإدارة المشاريع والمشاكل والنسخ الاحتياطي في نفس الوقت. ويعتبر كل من SVN و Git الأشهر على الإطلاق، لكن بالنسبة لي، أعتبر أن Git أفضل نظرا لأنه يعمل بشكل أفضل وبسبب Github الذي يوفر خدمة استضافة مستودعات Git على الإنترنت. ووردبريس: بوابتك إلى عالم البرمجة إن أفضل شيء بالنسبة إلى ووردبريس أنه يمكن أن يكون مدخلا لتعلم مهارات تطوير ويب ولغات أخرى. ولقد خضت هذه التجربة شخصيا، فلقد تعلمت أولا البرمجة الموجهة في PHP ثم تعلمت إطار عمل Laravel وأدوات البناء ثم LESS/SASS وأدوات سطر الأوامر وغيرها الكثير. ولقد خضت أيضا في غمار native app باستخدام #C وغيرها من اللغات. بمجرد معرفتك بكيفية عمل كل شيء، ستتعلم المزيد عند إنشاء مشاريع جديدة وكبيرة وكل شيء سيصبح أسهل، فبعد البرمجة كائنية التّوجّه في PHP لن تبذل الكثير من الجهد في تعلم #C لأن دماغك سيتعود على منطقه بطريقة مشابه لتعلم قيادة الشاحنات بعد تعلمك قيادة السيارات، فعلى الرغم من وجود الكثير من الاختلافات لكن بمجرد أن يكون لديك بعض الأساسيات سيسهل عليك التّحكمّ فيه. خاتمة ها قد عرفت الآن ما الذي تحتاجه للبدء في تطوير موقع ووردبريس، وقد تشعر بالإرباك، لكن لا تقلق، فالجميع سيشعر بذلك في هذه المرحلة. تذكّر أمرًا: من المهم أن لا تثبط عزيمتك، فجميع المبرمجين بدؤوا من مكان ما، فالكثير منهم كان سيئا للغاية عندما بدأ، يصارع لفهم المفاهيم الجديدة وشعر بالإحباط، لكنهم لم يستسلموا وكانوا دائما يدفعون أنفسهم للتّعلّم وهذا ما يُميّز المبرمج الجيد من السيئ. ترجمة -وبتصرف- للمقال: WordPress Development for Beginners: Getting Started لصاحبه Daniel Pataki. حقوق الصورة البارزة: Designed by Freepik.
  23. مدخل إلى html5

    وقعتُ أخيرًا بالصدفة على اقتباس من مطور في مشروع Mozilla عن التوتر الكامن في إنشاء المعايير القياسية: ابقِ هذه المقولة في عقلك، ولنشرح كيف أصبحت HTML5 على ما هي عليه. أنواع MIME هذه السّلسلة تتحدث حول HTML5، وليس عن إحدى إصدارات HTML السابقة أو أي إصدارٍ من XHTML، لكن لفهم تاريخ HTML5 والدوافع خلف إنشائها تمامًا، فعليك أن تستوعب بعض التفاصيل التقنية أولًا، تحديدًا أنواع MIME. في كل مرة يطلب فيها متصفح الويب صفحةً، فسيُرسِل الخادوم "ترويسات" (headers) قبل أن يرسل شيفرة الصفحة نفسها، وهذه الترويسات غير ظاهرة عمومًا على الرغم من توفر أدوات تطويرية تمكنك من رؤيتها إن كنت مهتمًا بها. لكن الترويسات مهمة لأنها تُخبر المتصفح كيف يُفسِّر الشيفرات الموجودة في الصفحة، أحد أهم تلك الترويسات هو Content-Type وهو يبدو كما يلي: Content-Type: text/html "text/html" يدعى "Content Type" أو نوع المحتوى أو نوع MIME، هذه الترويسة هي الشيء الوحيد الذي يُحدِّد طبيعة المورد المُحدَّد وكيف يجب عرضه. فالصورة لها أنواع MIME خاصة بها (image/jpeg لصور JPEG، و image/png لصور PNG، وهلمَّ جرًا)؛ ولملفات JavaScript نوع MIME خاص بها، وكذلك الأمر لصفحات الأنماط CSS، فلكل شيء في الويب له نوع MIME خاص به. وبالطبع الواقع معقَّد أكثر من هذا، فالجيل الأول من خواديم الويب (وهنا أتكلم عن خواديم الويب في 1993) لم تكن تُرسِل ترويسة Content-Type لأنها لم تكن موجودةً في ذاك الوقت (إذ لم تُستعمَل إلا في 1994)، وكانت بعض المتصفحات الشهيرة تتجاهل ترويسة Content-Type في بعض الظروف لأسبابٍ لها علاقة بالتوافقية (وهذا يسمى "content sniffing")، لكن كقاعدة عامة، كل شيء يمكنك الحصول عليه على الويب (كصفحات HTML، والصور، والسكربتات، والفيديو، وملفات PDF، وكل شيء له رابط URL) يجب أن يُخدَّم بإضافة نوع MIME المناسب في ترويسة Content-Type. خزِّن المعلومات السابقة في عقلك، وسنعود إليها لاحقًا. شرح مسهب لكيفية إنشاء المعايير لماذا لدينا عنصر <img>؟ لن تسمع مثل هذا السؤال كل يوم. بكل تأكيد أنشأه أحدهم، إذ لم تظهر تلك الأشياء من العدم، فكل عنصر وكل خاصية (attribute) وكل ميزة من ميزات HTML التي استخدمتها من قبل قد أنشأها أحدهم وقرر كيف يجب أن تعمل وكتب كل ذلك، هؤلاء الأشخاص الذين كتبوا المعايير ليسوا خارقين وإنما مجرد بَشَر ولكنهم أذكياء. أحد الأشياء الرائعة عن المعايير هي أنها طُوِّرَت على الملأ، أي أنَّك تستطيع العودة في الزمن والإجابة عن ذاك النوع من الأسئلة؛ إذ أنَّ النقاشات كانت تُجرى في القوائم البريدية، التي أُرشِفَت وأصبحت متاحةً للعموم كي يبحثوا فيها؛ لذلك قررت أن "أنقِّب" في رسائل البريد الإلكتروني كي أحاول الإجابة عن السؤال السابق: "لماذا لدينا عنصر <img>؟"، كان علي البدء في حقبةِ قبل إنشاء منظمة باسم "رابطة الشبكة العالمية" (World Wide Web Consortium اختصارًا W3C)، ذهبت إلى الأيام الأولى للويب، حيث كنتَ تستطيع عدّ صفحات الويب على أصابع اليدين. في 25 شباط/فبراير من عام 1993، كتب Marc Andreessen: كانت صيغتا Xbm و Xpm صيغتَا رسوميات شهيرتان في أنظمة يونكس. "Mosaic" كان من أوائل متصفحات الويب ("X Mosaic" كان الإصدار الذي يعمل على أنظمة يونكس)، عندما كتب Marc Andreessen رسالته في بدايات 1993، لم يكن قد أسس الشركة التي جعلته مشهورًا (شركة Mosaic Communications Corporation)، ولم يكن بدأ العمل على المنتج الرائد في تلك الشركة: "Mosaic Netscape" (ربما تعرفها بأسمائها الحديثة "Netscape Corporation" و "Netscape Navigator"). عبارته "ربما أنواع MIME" كانت تُشير إلى ميزة Content negotiation في HTTP، حيث يُخبر العميل (أي متصفح الويب) الخادوم (أي خادوم الويب) ما هي أنواع الوسائط التي يدعمها (مثل image/jpeg) ومن ثم سيُرسِل الخادوم الوسائط إلى العميل بالصيغة التي يُفضلِّها. عُرِّفَت الصيغة الأصلية من HTTP في 1991 (وهي النسخة الوحيدة التي كانت موجودةً في شباط/فبراير 1993) ولم تكن توفِّر طريقةً للعملاء لأن يخبروا الخواديم ما هي صيغ الصور التي يدعموها، وهذه هي المعضلة التي واجهها Marc. بعد عدِّة ساعات، ردِّ Tony Johnson: Midas هو متصفح آخر من فترة بدايات الويب، وكان منافسًا لمتصفح X Mosaic، وكان متعدد المنصات، إذ كان يعمل على أنظمة يونكس و VMS. أما "SLAC" فهي تُشير إلى Stanford Linear Accelerator Center التي أصبح اسمها الآن "SLAC National Accelerator Laboratory" التي استضافت أول خادوم ويب في الولايات المتحدة (وفي الواقع، أول خادوم ويب خارج أوروبا). عندما كتب Tony هذه الرسالة، كانت SLAC من المحاربين القدامى في الشبكة العالمية، التي استضافت خمس صفحات على خادوم الويب الخاص بها لمدة 441 يومًا. أكمل Tony قائلًا: قصد ببروتوكول "HTTP2" بروتوكول HTTP الأساسي المُعرِّف في 1992، الذي لم يُطبَّق تطبيقًا كاملًا في بدايات 1993، وعُرِفَت المسودة باسم HTTP2 ثم أصبحت معيارًا قياسيًا باسم "HTTP 1.0" (بالرّغم من أن الأمر قد تم ذلك بعد ثلاثة أعوام)، تضمَّن بروتوكول HTTP 1.0 ترويسات طلبات لتحديد صيغة المحتوى (request headers for content negotiation)، أي أنواع MIME. أكمل Tony: لم يُطبَّق اقتراحه أبدًا، إلا أنَّ فكرة توفير نص إن لم تتوفر الصورة هي تقنية مهمة لتسهيل الوصول التي كانت ناقصة من اقتراح Marc الأولي لوسم <IMG>. استعملت هذه الميزة في خاصية <img alt>، التي أساء متصفح Netscape معاملتها باعتبارها تلميحًا (tooltip). بعد عدِّة ساعات من إرسال Tony لرسالته، ردَّ Tim Berners-Lee عليها: لم يُطبَّق هذا الاقتراح مطلقًا، لكن خاصية rel ما زالت موجودةً. أضاف Jim Davis: لم يُطبَّق هذا الاقتراح قط، لكن أضاف متصفح Netscape لاحقًا دعمًا لتضمين عناصر الوسائط المتعددة باستعمال عنصر <embed>. سأل Jay C. Weber: ردَّ Marc Andreessen: أجاب Jay C. Weber له قائلًا: أدركنا بعد فوات الأوان أنَّ مخاوف Jay كان لها أساسٌ من الصحة لكن ذلك استغرق فترةً طويلةً، فقد أضافت HTML5 أخيرًا عنصرَيّ <video> و <audio>. ردًا على رسالة Jay الأصلية، قال Dave Raggett: لاحقًا في 1993، اقترح Dave Raggett معيار HTML+‎ الذي كان تطويرًا لمعيار HTML. لكن لم يُطبَّق اقتراحه مطلقًا، ثم حلَّت HTML 2.0 محله التي أعطت طابعًا رسميًا للميزات شائعة الاستعمال: "هذا المعيار يضفي توضيحًا وطابعًا رسميًا لمجموعة من ميزات HTML التي كانت شائعة الاستعمال قبل حزيران/يونيو عام 1994". كتب Dave لاحقًا معيار HTML 3.0 المبني على مسودة HTML+‎ التي كتبها سابقًا، وذلك خارج إطار W3C للمعايير (المسمى Arena)؛ لكن لم تُطبَّق HTML 3.0 أبدًا، وحلَّت محلها HTML 3.2، التي رسَّمَت الميزات كالإصدار السابق HTML 2.0: "أضافت HTML 3.2 الميزات شائعة الاستخدام مثل الجداول، و applets والتفاف النص حول الصور، بالإضافة إلى توافقيتها مع معيار HTML 2.0". شارك Dave لاحقًا بوضع معيار HTML 4.0، وطوَّر HTML Tidy، وشارك في المساعدة بتطوير XHTML، و XForms، و MathML، وغيرها من معايير W3C الحديثة. بالعودة إلى 1993، رد Marc على Dave: Intermedia هو مشروعٌ من جامعة Brown، طوِّر من عام 1985 إلى 1991 وكان يعمل على نظام A/UX، الذي هو نظام شبيه بِيونكس (Unix-like) لحواسيب ماكنتوش في ذاك الوقت. راجت فكرة "لغة لتوصيفِ الرسومات ذاتُ غرضٍ عام"، وذلك بدعم المتصفحات الحديثة لصيغة SVG (لغة توصيفية يمكن دمج السكربتات معها)، و <canvas> (واجهة برمجية [API] إجرائية [procedural] مباشرة)، على الرغم من أن الأخيرة بدأت كإضافة مملوكة (proprietary extension) قبل أن يتم ترسيمُها من WHATGW. ردBill Janssen: "Andrew" هو إشارة إلى Andrew User Interface System (إلا أنه كان يسمى في ذاك الوقت Andrew Project). في ذاك الحين، وجد Thomas Fine فكرةً مختلفةً: "Display Postscript" هي تقنية لعرض Postscript على الشاشة طوِّرَتها كل من Adobe و NeXT. لم يُطبَّق هذا الاقتراح أبدًا، لكن الفكرة أنَّ أفضل طريقة لحل مشاكل HTML هي استبدال شيءٍ ما آخر بها ما زالت تظهر من وقتٍ لآخر. قال Tim Berners-Lee في الثاني من آذار/مارس عام 1993: HyTime كان نظام مستندات قديم مبني على SGML، وقد أثَّرَ كثيرًا في النقاشات الأولية للغة HTML، ولاحقًا لغة XML. لم يُطبَّق اقتراح Tim لوسم <INCLUDE> مطلقًا، لكنك تجده صداه واضحًا في عناصر <object> و <embed> و <iframe>. في النهاية، قال Marc Andreessen في الثاني عشر من آذار/مارس عام 1993: ترجمة -وبتصرّف- لفصل A Quite Biased History of HTML5 من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  24. أنا منبهر كثيرًا بجميع نواحي المحادثة ذات 23 عامًا، التي أدت إلى إنشاء عنصر HTML الذي استعمل في كل صفحة ويب تقريبًا. خذ بعين الاعتبار أنَّ: HTTP ما زال موجودًا، ونجح بالتطور من الإصدار 0.9 إلى 1.0 ثم إلى 1.1، وما يزال يتطور. HTML ما زالت موجودةً، صيغة البيانات البدائية تلك التي لم تكن تدعم تضمين الصور! تطورت إلى الإصدار 2.0 ثم إلى 3.2 ثم إلى 4.0، أي أنَّ خط تطويرها لم ينقطع، لكنه بالتأكيد خطٌ ملتوٍ ومتعرج، وفيه العديد من النهايات المغلقة. لكن ها نحن ذا في عام 2016، وما زالت صفحات الويب من عام 1990 تُعرَض بشكلٍ جيد في المتصفحات الحديثة. ولقد فتحتُ إحداها في متصفح هاتفي الحديث العامل بنظام أندرويد، ولم أحصل على رسالة تقول "الرجاء الانتظار إلى أن يتم استيراد الصيغة القديمة". نتجت HTML من النقاشات بين صانعي المتصفحات والمطورين وواضعي المعايير والأشخاص الذين يحبون التحدث عنها. أغلبية الإصدارات الناجحة من HTML كانت عبارة عن إضفاءِ صفةٍ رسميةٍ لما هو موجودٌ مُحاوِلَةً توجيهه نحو الطريق الصحيح. أي شخص يخبرك أنه يجب أن تكون HTML «صرفة» (أي على الأرجح بتجاهل صانعي المتصفحات أو المطورين أو كليهما) هو شخصٌ لديه معلوماتٌ خاطئة. لم تكن HTML صرفةً أبدًا، وأيةّ محاولات لجعلها كذلك قد باءت بالفشل. لم يبق أي متصفح منذ عام 1993 موجودًا كما هو. فتم التخلي عن متصفح Netscape Navigator في 1998، ثم أعيدت كتابته من الصفر لإنشاء Mozilla Suite، ثم تم الاشتقاق لإنشاء Firefox. وكانت لمتصفح Internet Explorer بداياتٌ متواضعة في «Microsoft Plus!‎» لويندوز 95، حيث حُزِّمَ مع بعض سمات سطح المكتب ولعبة pinball (لكن بالطبع يمكن تتبع تاريخ المتصفح إلى أبعد من تلك النقطة). بعض أنظمة التشغيل من عام 1993 ما زالت موجودةٌ، لكن ليس لها صلة بالويب الحديث الذي نعرفه. فأغلبية مستخدمي الويب الآن بدؤوا باستعماله على حاسوب مكتبي يعمل بنظام ويندوز 2000 أو أحدث منه، أو على جهاز Mac يعمل بنظام OS X، أو على حاسوب مكتبي يعمل بتوزيعة من توزيعات لينُكس، أو على هاتف محمول مثل هواتف أندرويد أو iPhone. لكن كان إصدار ويندوز 3.1 في عام 1993، وكان لينُكس يوزَّع عبر Usernet. بقي بعض الأشخاص موجودين وما يزالون منهمكين في العمل على ما ندعون «معايير الويب»، حيث بقي هؤلاء الأشخاص يعملون لأكثر من 20 عامًا، وعمل بعضهم على اللغات التي تسبق HTML التي يمكن تتبع تاريخها إلى ثمانينيات القرن الماضي. بالحديث عن اللغات التي سبقت HTML، أصبح من السهل نسيان الصيغ والأنظمة التي كانت موجودةٌ عند إنشاء HTML بعد الانتشار الواسع والكبير للغة HTML والويب. ماذا عن Andrew؟ أو Intermedia؟ أو HyTime؟ لم تكن HyTime مشروعًا بحثيًا أكاديميًا لا شأن لها، وإنما كانت من معايير ISO، ولقد كانت تُستعمل لأغراضٍ عسكرية. لم يُجِب كل ما سبق عن تساؤلنا الأصلي: لماذا لدينا عنصر <img>؟ لماذا ليس عنصر <icon>؟ أو عنصر <include>؟ لماذا ليس رابطًا مع خاصية include، أو مجموعةٌ من قيم الخاصية rel؟ لماذا عنصر <img>؟ الجواب بسيطٌ للغاية، لأن Marc Andreessen قام بتنفيذ (بناء) وتوفير العنصر <img> في مُتصفّحه والذي يقوم ببناء العنصر وتوفيره في متصفّحه سيربح. لكن هذا لا يعني أنَّ مَن يبني العُنصر وينشره سيربح دائمًا، فلا تنسَ أنَّ Adnrew و Intermedia و HyTime قامت بذلك أيضًا، فبناء العنصر وتوفيره هو شرطٌ لازمٌ لكنه غير كافٍ للنجاح؛ وأنا هنا لا أقصد أنَّ بناء العُنصر قبل المعيار هو الحل الأمثل، ووسم <img> لم يكن يستعمل صيغة صور شائعة، ولم يُعرِّف كيف يلتف النص حول الصورة، ولم يكن يدعم النص البديل أو محتوى احتياطي للمتصفحات القديمة، وبعد 23 عامًا، ما زلنا نعاني مع مشكلة Content Sniffing، وما تزال هذه المشكلة مصدرًا للثغرات الأمنية. ويمكنك تتبع كل هذا إلى 23 عامًا مضت، مرورًا بحرب المتصفحات، إلى 23 شباط/فبراير عام 1993، عندما قال Marc Andreessen بشكلٍ عابر «ربما في يوم ما سنعتمد الـ MIME» ومع ذلك نشر العنصر الذي قام ببنائه وتضمينه في مُتصفّحه. التسلسل الزمني لتطوير HTML من 1997 إلى 2004 في كانون الأول/ديسمبر من عام 1997، نشرت رابطة الشبكة العالمية (W3C) نسخة HTML 4.0 ثم أغلقت مجموعة عمل HTML‏ (HTML Working Group) في الحال. وبعد أقل من شهرين، نشرت مجموعة عمل أخرى من W3C معيار XML 1.0، وبعد ذلك بثلاثة أشهر، أنشَأ القائمون على W3C ورشة عمل (workshop) اسمها "بلورة مستقبل HTML"‏ (Shaping the Future of HTML) للإجابة عن هذا السؤال: "هل تخلت W3C عن HTML؟" وكان جوابهم: منحت W3C مجموعة عمل HTML هذا التكليف لإنشاء "مجموعة من وسوم XML"، كانت أول خطوة –في كانون الأول/ديسمبر 1998– هي كتابة مسودة لمعيار مؤقت (انتقالي) الذي كانت مهمته إعادة قولبة HTML في XML دون إضافة أيّة عناصر أو خاصيات جديدة، عُرِفَ هذا المعيار لاحقًا باسم "XHTML 1.0" وعرَّف نوع MIME جديد لمستندات XHTML ‏"application/xhtml+xml"، لكن لتسهيل انتقال صفحات HTML 4 الموجودة من قبل، فقد تضمن المعيار "الملحق C" الذي "يلخص إرشادات التصميم للمطورين الذين يريدون تشغيل مستندات XHTML في متصفحات HTML الموجودة من قبل"، إذ قال الملحق C أنك تستطيع كتابة ما يسمى "صفحات XHTML" لكن تستطيع تخديمها عبر نوع MIME القديم text/html. الهدف الجديد هو النماذج (forms)، ففي آب/أغسطس 1999، نشرت مجموعة عمل HTML نفسها أول مسودةٍ لنماذج XHTML الموسعة ولقد وضعوا التوقعات التي يرمون إلى تنفيذها في أول فقرة: وبعد عدِّة أشهر أُعيدت تسمية "XHTML Extended Forms" إلى "XForms" وانتقلت إلى مجموعة العمل الخاصة بها، وعَمِلَت هذه المجموعة على التوازي مع مجموعة عمل HTML ونَشرت في النهاية الإصدار الأول من XForms 1.0 في تشرين الأول/أكتوبر 2003. وفي تلك الأثناء -وبعد اكتمال التحويل إلى XML- حطَّت مجموعة عمل HTML أنظارها إلى إنشاء "الجيل الجديد من HTML"، ففي أيار/مايو 2001، نشروا الإصدار الأول من XHTML 1.1، الذي أضاف بعض الميزات الصغيرة على XHTML 1.0، لكنه أزال الملحق C، فيجب -بدءًا من الإصدار 1.1- تخديم مستندات XHTML بنوع MIME الخاص بها application/xhtml+xml. كل شيء تعرفه عن XHTML خاطئ لماذا أنواع MIME مهمة جدًا؟ لماذا أذكرها بين الحين والآخر؟ لسبب واحد: draconian error handling (أي عدم التساهل في معالجة الأخطاء). فكانت المتصفحات "متسامحة" مع HTML، فلو أنشأت صفحة HTML ولكن نسيت وسم الإغلاق ‎</head>‎، فستُظهِرها المتصفحات على أيّة حال (لأن هنالك وسومًا معيّنة تشير إلى نهاية قسم <head> وبداية <body>)، فيجب عليك أن تضع هيكلية معيّنة عند تداخل الوسوم (أي إغلاق الوسوم المفتوحة بنمط "last-in-first-out" أي أن آخر وسم مستعمل سيُغلق أولًا) لكن إن كتبتَ <b><i></b></i>، فستتعامل المتصفحات معها (بطريقةٍ ما) وستكمل عرض الصفحة دون إظهار رسالة خطأ. وكما قد تتوقع، أدت عدم كتابة شيفرات HTML بطريقة سليمة تمامًا إلى جعل المطورين يكتبون شيفرات غير سليمة. ففي بعض التقديرات، أكثر من 99% من صفحات HTML على الويب فيها خطأ واحد على الأقل، لكن لما كانت هذه الأخطاء لا تسبب عرض رسالة خطأ مرئية من المتصفحات، ولهذا لن يصلحها أحد. رأت W3C هذه المشكلة الأصولية في الويب، ثم همّوا لتصحيحها. لغة XML، المنشورة في 1997، تخلصت من العملاء المتساهلين وقالت بأن جميع البرامج التي تستعمل XML يجب أن تعامل الأخطاء المتعلقة "بطريقة الكتابة السليمة" على أنها أخطاء (Fetal Errors). اشتهر مفهوم توقف المعالجة عند أول خطأ بالاسم "draconian error handling" وذلك نسبةً إلى القائد الإغريقي Draco الذي شرَّع عقوبة الموت لأيّة تجاوزات (ولو صغيرة نسبيًا) لقوانينه. فعندما أعادت W3C صياغة HTML بمفردات XML، اعتبرت أنَّ جميع المستندات التي تُخدَّم بنوع MIME الجديد application/xhtml+xml ستخضع إلى سياسة draconian error handling، فلو كان هنالك خطأٌ ما في أسلوب صياغة صفحة XHTML -نسيان وسم الإغلاق ‎</head>‎ أو تداخل غير صحيح للوسوم على سبيل المثال- فلن يكون لمتصفح الويب خيارٌ إلا إيقاف معالجة الصفحة وإظهار رسالة خطأ إلى المستخدم النهائي. لم تكن هذه الفكرة شائعةً على نطاقٍ واسع، لأن النسبة المُقدَّرة للأخطاء في صفحات الويب هي 99%، هذا يعني أن رسائل الخطأ ستُعرَض على المستخدم طوال الوقت، ولقلة الميزات التي توفرها XHTML 1.0 و 1.1، فقرر مطورو الويب تجاهل application/xhtml+xml، لكن هذا لا يعني أنهم تجاهلوا XHTML بالكلية، لأن الملحق C من معيار XHTML 1.0 أعطى مطوري الويب ثغرةً يمكنهم استعمالها: "اكتب شيئًا يشبه بنية XHTML، لكن خدِّمه بنوع MIME القديم text/html"، وهذا ما فعله الآلاف من مطوري الويب، إذ «حدَّثوا» صفحاتهم إلى بنية XHTML لكن بقيت تلك الصفحات مُخدَّمة بنوع MIME القديم text/html. ولليوم، ما تزال تعلن ملايين الصفحات أنها XHTML، فيبدؤون بنوع المستند doctype الخاص بلغة XHTML في أول سطر، ويستعملون أحرفًا صغيرةً لأسماء الوسوم، ويستعملون علامات الاقتباس حول قيم الخاصيات، ويضيفون خطًا مائلًا بعد العناصر الفارغة مثل <br /‎> و <hr /‎> لكن قلّة قليلة من تلك الصفحات تُخدَّم بنوع MIME الجديد application/xhtml+xml، الذي سيُفعِّل عدم التساهل في الأخطاء الخاص بلغة XML. فأيّة صفحة تُخدَّم بنوع MIME القديم text/html -بغض النظر عن doctype أو بنية الوسوم- ستُفسَّر باستعمال مُفسِّر HTML المُتساهل، وسيتم تجاهل الأخطاء دون إظهار أيّة رسالة، ودون تحذير المستخدم (أو أي شخص آخر) حتى لو كانت الصفحة فيها أخطاء تقنية. فتحت XHTML 1.0 هذه النافذة، ولكن XHTML 1.1 أغلقتها، والنسخة التي لن تُصدَر XHTML 2.0 استمرت في منهج عدم التساهل في الأخطاء، وهذا هو السبب وراء ادعاء ملايين الصفحات على أنها XHTML 1.0 وأنَّ حفنة قليلة منهم هي XHTML 1.1 (أو XHTML 2.0)، فهل أنت تستعمل XHTML حقًا؟ تحقق من نوع MIME (في الواقع، إن لم تكن تعرف ما هو نوع MIME الذي تستعمله، فأنا أضمن لك أنك ما زلتَ تستعمل text/html)، فما لم تُخدِّم صفحاتك بنوع MIME الجديد application/xhtml+xml، فإن «XHTML» هي XML بالاسم فقط. رؤية تنافسية في حزيران/يونيو 2004، أقامت W3C ورشة عمل حول تطبيقات الويب والمستندات المجمّعة، حضر هذا الاجتماع ممثلون عن مصنعي ثلاثة متصفحات، وشركات تطوير الويب، وأعضاء آخرون من W3C، ومجموعة من الجهات المهتمة بما في ذلك منظمة Mozilla وشركة Opera Software، وأعطوا رؤيتهم لمستقبل الويب: تطوير معيار HTML 4 لتضمين ميزات جديدة تساعد مطوري تطبيقات الويب الحديثة. تمثِّل المبادئ السبعة الآتية ما نظن أنه أهم المتطلبات اللازمة لهذا العمل: التوافقية مع الإصدارات القديمة، ومسار واضح للهجرة يجب أن تكون تقنيات تطبيقات الويب مبنية على تقنيات مألوفة للمطورين، بما في ذلك HTML و CSS و DOM و JavaScript. يجب أن تُطبَّق الميزات الأساسية لتطبيقات الويب باستعمال السكربتات وصفحات الأنماط في IE6، لذا يكون هنالك مسارٌ واضحٌ للهجرة نصب عيني المطورين. أي حل لا يمكن أن يستعمل مع المتصفحات ذات الشعبية الكبيرة حاليًا دون حاجة إلى إضافات لن يكون ناجحًا. نظام معالجة أخطاء مُحدَّد بدقة يجب تفصيل كيفية معالجة الأخطاء إلى درجة لا تحتاج المتصفحات فيها إلى اختراع نظام معالجة أخطاء خاص بهم، أو أن يبنوا واحدًا مشتقًا من المتصفحات الأخرى. لا يجب عرض الأخطاء على المستخدمين يجب أن تُحدِّد المعايير السلوك اللازم اتباعه عند حدوث أي نوع من الأخطاء، ويجب أن يكون نظام معالجة الأخطاء متساهلًا (كما في CSS) بدلًا من أن يكون واضحًا للمستخدم وكارثيًا (كما في XML). الاستعمال العملي يجب أن تُبرَّر إضافة كل ميزة إلى معايير تطبيقات الويب بحالات الاستعمال العملي لها، لكن العكس ليس صحيحًا بالضرورة: ليست كل حالة استخدام تتطلب إنشاء ميزة جديدة. يُفضَّل أن تكون حالات الاستخدام ذات أساس في مواقع حقيقة استخدم فيها المطورون حلًا ليس مثاليًا للالتفاف على القصور في التقنية. سيبقى استخدام السكربتات قائمًا لكن يجب الابتعاد عنه إذا توفرت وسوم مناسبة. ويجب أن لا تتدخل السكربتات في طريقة العرض، وأن تكون مستقلة عن الجهاز المشغل لها. يجب تجنب التفريق بين الأجهزة يجب أن يتمكن المطورون من استخدام نفس الميزات الموجودة في نسخة سطح المكتب ونسخة الهواتف المحمولة لنفس المتصفح. عملية مفتوحة استفاد الويب من كونه مطوَّرًا في بيئة مفتوحة. وستصبح تطبيقات الويب أساسية في المستقبل، ويجب أن يكون تطويرها في بيئةٍ مفتوحةٍ أيضًا، ويجب إتاحة القوائم البريدية والأرشيفات ومسودات المعايير للجميع. سُئِلَ المشاركون في ورشة العمل في استطلاعٍ للرأي: «هل يجب على W3C تطوير إضافات لوظائف جاهزة في HTML و CSS، وإضافات لأساس DOM لكي تتحقق متطلبات تطوير تطبيقات الويب متوسطة التعقيد. أم عليها تطوير واجهة برمجية (API) كاملة على مستوى النظام (OS-level)؟ (هذا الاقتراح من Ian Hickson، من Opera Software)» كان التصويت 11 إلى 8 ضد هذا الاقتراح، وكتبت W3C في ملخص الورشة: وبعد هذا القرار، كان لدى الأشخاص الذين اقترحوا تطوير HTML ونماذج HTML خياران فقط: الاستسلام، أو إكمال عملهم خارج إطار W3C، وقرروا المضي قدمًا في الخيار الثاني، وسجلوا النطاق whatwg.org، وفي حزيران/يونيو 2004، وُلِدَت مجموعة عمل WHAT. مجموعة عمل WHAT ما هي مجموعة عمل WHAT؟ سأقتبس من كلامهم: الجملة الأساسية في الفقرة السابقة هي "دون التسبب بمشاكل تتعلق بالتوافقية"، ليست XHTML (دون الثغرة التي وفرها الملحق C) متوافقةً مع HTML، وتتطلب نوع MIME خاص بها، وXForms ليست متوافقة مع نماذج HTML لعدم القدرة على استعمالها إلا مع الصفحات المُخدَّمة بنوع MIME الخاص بصفحات XHTML، هذا يعني أن XForms تتطلب عدم التساهل في التعامل مع الأخطاء. فبدلًا من أن نرمي ما يقارب عقدًا من الزمن من العمل على HTML ونجعل 99% من صفحات الويب الموجودة حاليًا غير قابلة للاستخدام، قررت مجموعة عمل WHAT أن تنتهج منهجًا آخر: توثيق خوارزميات التعامل مع الأخطاء «المتسامحة» التي تستعملها المتصفحات. كانت وما زالت المتصفحات متساهلةً مع أخطاء HTML، لكن لم يُتعِب أحدٌ نفسه ويوثق آلية التساهل بالضبط. لدى متصفح NCSA Mosaic خوارزميات خاصة به للتعامل مع الأخطاء، وحاول Netscape أن يقلده، ثم حاول Internet Explorer تقليد Netscape، ثم حاول Opera و Firefox تقليد Internet Explorer، ثم حاول Safari تقليد Firefox، وهلّم جرًا حتى يومنا هذا. إذ ضيع المطورون آلاف الساعات محاولين جعل منتجاتهم متوافقة مع منتجات منافسيهم. استغرقت مجموعة عمل WHAT خمسة أعوامٍ من العمل للنجاح في توثيق كيفية تفسير HTML (ما عدا بعض الحالات الخاصة جدًا والغامضة) بطريقة متوافقة مع جميع المحتوى الموجود على الويب. فلا يوجد في الخوارزمية النهائية أيّة حالة يتوقف فيها مفسر HTML عن إكمال تفسير صفحة HTML ويعرض رسالة خطأ للمستخدم النهائي. وفي الفترة التي كانت تجرى فيها الهندسة العكسية، كانت تعمل مجموعة WHAT بصمت على أشياءٍ أخرى أيضًا، منها معيارٌ سُمي في بادئ الأمر "Web Forms 2.0" الذي أضاف بعض عناصر التحكم إلى نماذج HTML (ستتعلم المزيد عن نماذج HTML في درسٍ لاحق). ومسودة معيار أخرى باسم "Web Applications 1.0" التي حَوَت ميزات أخرى رئيسية مثل canvas والدعم المُضمَّن لتشغيل الصوت والفيديو دون إضافات. العودة إلى W3C لمدة تقارب السنتين ونصف، تجاهلت W3C ومجموعة عمل WHAT بعضهما البعض، على الرغم من أن مجموعة عمل WHAT كانت تركز على نماذج الويب وميزات HTML الجديدة، ومجموعة عمل W3C مشغولة بإصدار 2.0 من XHTML، لكن بحلول تشرين الأول/أكتوبر، كان جليًا أن مجموعة عمل WHAT قد أحدثت زخمًا كبيرًا، بينما كانت XHTML 2.0 تقبع على شكل مسودة لم يتم تطبيقها من أيّ متصفح رئيسي. في تشرين الأول/أكتوبر، أعلن Tim Berners-Lee -مؤسس W3C- أن W3C ستعمل مع مجموعة عمل WHAT لتطوير HTML. واحد من أول الأشياء التي قررت مجموعة عمل W3C الجديدة فعلها هي إعادة تسمية "Web Applications 1.0" إلى "HTML5"، ومن هنا بدأت رحلة هذا السلسلة. حاشية في تشرين الأول/أكتوبر 2009، أغلقت W3C مجموعة عمل XHTML 2 وأصدرت هذه الإفادة لشرح قرارها: مراجع إضافية The History of the Web، مسودة قديمة كتبها Ian Hickson HTML/History كتبها Michael Smith، و Henri Sivonen، وآخرون A Brief History of HTML لكاتبها Scott Reynen ترجمة -وبتصرّف- لفصل A Quite Biased History of HTML5 من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.
  25. مدخل إلى html5

    هذه السلسلة عبارة عن ترجمة لكتاب Dive Into HTML5 لمؤلفه Mark Pilgrim والتي سنتعلم من خلالها أساسيات HTML5 وكيفية الإنتقال إليها من نسخ HTML أقدم مع مراعاة دعم المتصفحات المختلفة. قبل البدء باستخدام HTML5 سنتطرق في هذا الدرس الأول إلى خمسة أشياء عليك معرفتها حول HTML5. 1. HTML5 ليست شيئا واحدا كبيرا ربما تتساءل: "كيف يمكنني البدء باستعمال HTML5 إن لم تكن تدعمها المتصفحات القديمة؟" لكن السؤال نفسه سيُضلِّلُكَ، HTML5 ليست شيئًا واحدًا كبيرًا، وإنما مجموعة من الميزات المنفصلة عن بعضها، أي أنَّك لن تحاول اكتشاف "دعم HTML5" في المتصفح، لأن ذلك غير منطقي؛ وإنما يمكنك اكتشاف الدعم للمزايا المختلفة مثل التخزين المحلي، أو عرض الفيديو، أو الحصول على الموقع الجغرافي. ربما تظن أنَّ HTML هي مجموعة من الوسوم وتلك الأقواس التي تشبه الزاوية… إن هذا جزءٌ مهمٌ منها، لكنه لا يمثلها كلها. إذ تُعرِّف مواصفات HTML5 كيف تتفاعل تلك الوسوم مع لغة JavaScript وذلك عبر ما يُعرَف بالمصطلح "DOM" (اختصار للعبارة Document Object Model). فلا تُعرِّف HTML وسمًا باسم <video> فقط، وإنما هنالك واجهة برمجية للتعامل مع كائنات الفيديو عبر DOM. يمكنك استعمال تلك الواجهة البرمجية (أي API) لكي تكتشف الدعم لمختلف صيغ الفيديو، ولكي تبدأ المقطع أو توقفه مؤقتًا، أو أن تكتم صوته، أو أن تعرف ما هو المقدار الذي نُزِّل (downloaded) من الفيديو، وكل شيءٍ آخر يلزمك لبناء تجربة مستخدم رائعة عند استعمال وسم <video> لعرض المقاطع. 2. ليس عليك التخلي عن كل شيء شئت أم أبيت، لا تستطيع أن تنكر أنَّ HTML 4 هي أنجح لغة توصيف (markup) على الإطلاق. بُنيَت HTML5 على هذا النجاح، وليس عليك أن تتخلى عن الشيفرات التي كتبتها، وليس عليك إعادة تعلم أشياء تعرفها من قبل، فإن كان تطبيقك يعمل البارحة باستخدام HTML 4، فسيبقى يعمل اليوم في عصر HTML5. لكن إن أتيت لتحسين تطبيق الويب الخاص بك، فقد أتيت إلى المكان الصحيح. هذا مثالٌ واقعي: تدعم HTML5 كل عناصر النماذج (forms) في HTML 4، لكنها تتضمن عناصر جديدة أخرى. كنا ننتظر إضافة بعض تلك العناصر بفارغ الصبر، مثل المزلاج (slider) ومنتقي التاريخ (date picker)؛ بعضها الآخر ذو ميزاتٍ خفية. فحقل email مثَلَهُ كمَثَلِ حقل الإدخال النصي العادي، إلا أنَّ متصفحات الهواتف الذكية ستخصص لوحة المفاتيح الظاهرة على الشاشة لتسهيل كتابة عناوين البريد الإلكتروني. بعض المتصفحات القديمة لا تدعم حقل email وستعامله على أنَّه حقل نصي عادي، وسيبقى النموذج يعمل دون تعديلات في الشيفرة أو استخدام أساليب ملتوية عبر JavaScript. هذا يعني أنك تستطيع تحسين النماذج في صفحاتك اليوم، حتى لو كان زوارك يستعملون IE 6. 3. من السهل البدء باستعمالها يمكن أن يكون "التحديث" إلى HTML5 بسيطًا لدرجة أنَّ كل ما عليك فعله هو تعديل doctype، الذي يجب أن يكون أول سطر من كل صفحة HTML. تُعرِّف الإصدارات السابقة من HTML الكثير من أنواع doctype، وكان من الصعب اختيار النوع المناسب؛ لكن هنالك نوع doctype وحيد في HTML5: <!DOCTYPE html> لن يضر التحديث إلى نمط doctype في HTML5 شيفراتك المكتوبة، لأنَّ جميع الوسوم (tags) المُعرَّفة في HTML 4 ما تزال مدعومةً في HTML5، لكنها ستسمح لك باستعمال –والتحقق من صحة صياغة– العناصر التنظيمية الجديدة مثل <article> و <section> و <header> و <footer>، سنتحدّث عن هذه العناصر الجديدة في مقال قادم. 4. إنها تعمل بالفعل سواءً كنت تريد الرسم عبر canvas، أو تشغيل مقطع فيديو، أو تصميم نماذج أفضل، أو بناء تطبيقات ويب تعمل دون اتصال؛ فستجد أنَّ HTML5 مدعومةً دعمًا جيدًا، حيث يوجد دعمٌ لخاصية canvas في Firefox و Safari و Chrome و Opera ومتصفحات الهواتف الذكية وتشغيل الفيديو وتحديد المواقع والتخزين المحلي والمزيد. تدعم غوغل (في متصفحها) البيانات الوصفية الخاصة (microdata)، وحتى مايكروسوفت –المشهورة بتأخرها عن اللحاق بركب دعم المعايير القياسية– تدعم أغلبية ميزات HTML5 في متصفح "Internet Explorer 9". يتضمن كل درس من هذه السلسلة جداول لتوافقية المتصفحات الشهيرة للميزة المشروحة، ولكن الأهم من ذلك أنَّ كل درس يتضمن نقاشًا عن خياراتك إن كنت تحتاج إلى دعم المتصفحات القديمة. تم توفير ميزات في HTML5 مثل تحديد الموقع الجغرافي وتشغيل الفيديو في السابق عبر إضافات للمتصفح مثل Gears أو Flash. الميزات الأخرى، مثل canvas، تستطيع محاكاتها بشكلٍ تام باستعمال JavaScript. ستتعلم من خلال هذه السّلسلة (التي تقرأ الآن درسها الأول) كيف تستهدف المتصفحات ذات الدعم المدمج لتلك الميزات، دون أن تترك خلفك المتصفحات القديمة. 5. HTML5 ستبقى وستتطور اخترع "Tim Berners-Lee" الشبكة العنكبوتية في بدايات التسعينات من القرن الماضي، ثم أنشَأ جمعية W3C لكي تكون المرجع في معايير الويب، وهذا ما فعلته تلك الجمعية لأكثر من 20 عامًا. هذا ما قالته W3C عن مستقبل معايير الويب في تموز/يوليو عام 2009: ستبقى HTML5 في المستقبل، لنبدأ بتعلمها. ترجمة -وبتصرّف- لفصل Introduction من كتاب Dive Into HTML5 لمؤلفه Mark Pilgrim.