27/7/2008
כיתת אוּמן: איך כותבים סקריפט לגריזמונקי
לפני כמה ימים פנה אלי ”טובטוב“ מפורום מק-איט בבקשה לבדוק אם אוכל לכתוב סקריפט גריזמונקי לטיפול באתר בית הספר שלו שלא עובד בפיירפוקס או בספארי. בדרך כלל, אני לא עושה ”עבודות פרטיות“ כאלה, מה גם שהאתר נראה כאילו בנה אותו אחד התלמידים בתיכון. בדיקה קצת יותר מדודקת גילתה שהאתר מופעל ע”י חברת schooly שלטענתה אחראית על מיחשוב 350 בתי ספר, ובכלל זה הקמת האתרים שלהם. זה כבר נשמע יותר מעניין, וחוץ מזה חיפשתי אתר שאוכל לתעד את כתיבת הסקריפט עבורו כדי לכתוב עליו את הפוסט הזה.
עוד משהו קטן לפני התכל’ס על סדרת הפוסטים: ”כיתת אוּמן“. לא מזמן קראתי פוסט שכתב Gustavo Duarte. המשפט הזה גרם לי לחשוב על המונח ”כיתת אוּמן“ לסדרה.
programming is an intense creative pleasure, a perfect mixture of puzzles, writing, and craftsmanship
אז לעבודה…
המצרכים הדרושים
ידע ב JavaScript ,HTML, ו-CSS.
דפדפן פיירפוקס.
תוסף פיירפוקס: Firebug.
תוסף פיירפוקס: Greasemonkey.
עורך קוד, רצוי עם אפשרות לצביעה ע”פ תחביר. אני משתמש ב TextMate על מק, אבל יש גם חלופות חינמיות: Smultron למק, Notepad++ לחלונות, ו Kate או SciTE ללינוקס.
אתר שלא עובד טוב בפיירפוקס: במקרה הזה, אתר תיכון בית-ירח.
שלב ראשון: סקריפט בסיסי שעושה את העבודה
נתחיל פשוט בגלישה לאתר. במבט ראשון נראה שהבעיה העיקרית שלו היא הקישורים הגולשים מצד ימין. מעבר עם העכבר על הקישורים יראה שגם מערכת התפריטים מכילה באגים וויזואליים רבים. בכל זאת, לא בגלל זה פנה אלי ”טובטוב“, אלא כדי לפתור את הבעיה הפונקציונלית: לחיצה על הפעולה הסופית בקצה כל תפריט לא מגיבה.

השלב הבא הוא להפעיל את Firebug. בדרך כלל, פיירבאג עובד רק על אתרים בהם בחרנו להפעיל אותו, ולכן כרגע הוא מנוטרל. לחיצה על האיקון שלו (שנראה כמו תיקן מצוי) תפתח חלון עם מספר אפשרויות להפעלתו על האתר הנוכחי. נסמן את האפשרויות: Console ו- Script ונלחץ על הכפתור: ”Enable Selected panels for www.schooly.co.il“ להפעלת פיירבאג באתר שלנו.

אחרי ההפעלה האיקון של פיירבאג יהפוך לצבעוני והאתר ייטען מחדש. כבר בשלב הזה נוכל לראות שתי הודעות שגיאה שמופיעות ב console של פיירבאג, אבל בנתיים נתעלם מהן. נחזור על הפעולה של בחירת פעולה מהתפריט באתר, ואז נקבל הודעת שגיאה שלישית, שהיא מעניינת אותנו כרגע. נשים לב לסיבה לשגיאה, ונלחץ על הלינק הכחול שנמצא בצד ימין של ה console.

אחרי הלחיצה תתחלף הלשונית של פיירבאג, ותעבור למצב Script. שורה 154 שצויינה במסך ה Console תופיע במרכז המסך. אם נתבונן בשורה הזאת יחד עם הודעת השגיאה: document.frames is undefined, נבין שמקור הבעיה הוא בשימוש שנעשה בסקריפט בהרחבה של מיקרוסופט שמאפשרת לגשת לכל המסגרות בדף דרך document.frames. מה שנותר לעשות כעת הוא להחליף את הפונקציה GetPage1 בפונקציה משלנו. לשם כך, נלחץ לחיצה ימנית מעל אחת השורות בגוף הפונקציה (לא לבחור בשורת הערה) ונבחר באפשרות Copy Function.

כעת יכיל ה paste-buffer שלכם את הקוד הבא:
function GetPage1(val) {
document.frames.center_frame1.location.href = val + "&SchoolId=95";
window.location.hash = "#";
}
זה הזמן להתחיל לכתוב את סקריפט התיקון לאתר בית הספר. נתחיל בלחיצה ימנית על האיקון של גריזמונקי (קוף מחייך), ונבחר המתפריט את הפעולה: ”סקריפט חדש“

יפתח דיאלוג שבו נתבקש למלא מספר שדות. לחלק מהשדות יש כבר ערכי ברירת מחדל, אבל לא תמיד נרצה להשאיר אותם כך. השדה הבעייתי ביותר למילוי בסקריפט הראשון שלכם הוא ”מרחב שמות“. אם יש לכם שם דומיין, תוכלו להשתמש בו. אם לא, תוכלו ליצור מרחב שמות מכתובת ה email שלכם. לדוגמה, אם כתובת ה email היא myname@mail.com מרחב השמות שתוכלו לבחור הוא: tag:myname@mail.com,2008-07-26:myscripts. עוד על יצירת מרחב שמות מתוך email אפשר לקרוא באתר Tag URI.
שדה נוסף שנתעקב עליו הוא ”כלול“. ברירת המחדל שתוצג בשדה הזה תהיה ה url של הדף. הבעיה היא שבדרך כלל נרצה שהסקריפט שאנחנו כותבים יפעל על יותר מדף אחד. בעיה נוספת היא שהדף הראשי מכיל פעמים רבות מספר מסגרות (frame או iframe). לכל מסגרת כתובת משלה, ואם נשאיר את ערך ברירת המחדל, הסקריפט שלנו לא יפעל על המסגרות הפנימיות. במקרה שלנו יש מספר די גדול של מסגרות, ולכן נשנה את השורה הראשונה בשדה זה ל: http://www.schooly.co.il/*. את השם והתאור נמלא כרצוננו (אבל רצוי באנגלית), ונלחץ על הכפתור ”אישור“.

אם זהו הסקריפט הראשון שלכם, יפתח בשלב הזה דיאלוג שמבקש לבחור עורך טקסט. נתעלם מההצעה שמופיעה בראש הדיאלוג (לבחור את notepad) ונבחר את עורך הקוד שהתקנו בשלב ה”חומרים הדרושים“.

בשלב הבא, יפתח עורך הקוד עם 4 שורות של טקסט שמולאו אוטומטית לפי ההגדרות בדיאלוג הקודם. השורות מופיעות בצורת הערה, כך שלא יפגעו בתחביר של JavaScript, אבל בכל זאת יש להן תחביר משלהן שאותו מפענח גריזמונקי, כך שבנתיים נשאיר את השורות האלה כמות שהן, ונתחיל להוסיף שורות חדשות אחריהן.
אם עדיין שמרתם את קוד הפונקציה GetPage1 ב paste buffer שלכם, תוכלו להדביק אותו כעת. אם לא, אפשר להעתיק מחדש. כך אמור להראות הסקריפט שלכם:
// ==UserScript==
// @name schooly fixer
// @namespace http://yehudab.com
// @description Fix menu operations in schooly web site
// @include http://www.schooly.co.il/*
// ==/UserScript==
function GetPage1(val) {
document.frames.center_frame1.location.href = val + "&SchoolId=95";
window.location.hash = "#";
}
עוד קצת סבלנות — אנחנו רחוקים מרחק של שני שינויים קלים מסקריפט בסיסי עובד.
מכוון שאוביקט ה window של גריזמונקי שונה מזה של אוביקט ה window ה”אמיתי“, הקוד שלנו עדיין לא מחליף את הפונקציה המקורית. לשם כך, יש להשתמש באוביקט שנקרא unsafeWindow, שמאפשר לגשת מהקוד שלנו לאוביקט window המקורי. את השורה הראשונה, נחליף לכן לשורה הבאה:
unsafeWindow.GetPage1 = function(val) {
עכשיו הפונקציה שלנו באמת מחליפה את הפונקציה GetPage1 המקורית, אבל עדיין לא מתקנת את הבעיה. התיקון במקרה הזה פשוט יחסית: במקום להשתמש ב document.frames הפרטי של מיקרוסופט, נשתמש ב document.getElementById התיקני (למזלנו, schooly העניקו גם id וגם name למסגרת center_frame1). עוד בעיה הדורשת תיקון בשורה זו היא השימוש ב location.href. מכוון ש document.frames מכיל אוביקטים מסוג window, ואילו document.getElementById יחזיר לנו במקרה הזה אוביקט מסוג iframe, נצטרך להחליף את location.href ב src. התוצאה הסופית של שתי ההחלפות בשורה זו היא:
document.getElementById("center_frame1").src = val + "&SchoolId=95";
נשמור את הקובץ, נטען מחדש את הדף הראשי של האתר (אין צורך לאתחל את פיירפוקס), והפלא ופלא: לחיצה על התפריט עובדת!
שלב שני: תוספות ושיפורים
בשלב הבא ננסה לראות איך אפשר לשכלל את הסקריפט שלנו. קודם כל, נסתכל בפרמטר SchoolId=95 שמופיע בקוד שהעתקנו. מכוון שהקוד הזה מיוצר ע”י השרת (דף asp במקרה הזה), ככל הנראה המספר 95 הוא מספר שנשתל ע”י השרת לפני שהקוד הגיע לדפדפן. כנראה שבאתרים של בתי ספר אחרים נקבל מספר אחר עבור הערך SchoolId. כדי שהקוד שלנו יעבוד גם באתרים האחרים, נצטרך להחליף את הפרמטר בערך שיחושב בזמן ריצה. במקרה שלנו, נוכל לחשב את ערך הפרמטר לפי ה url הראשוני של המסגרת הפנימית. נעשה את זה ע”י קטע הקוד הבא:
var SchoolId = "";
function getSchoolId()
{
if (SchoolId != "")
return SchoolId;
var s = document.getElementById("center_frame1").src;
if (s.indexOf("SchoolId") >= 0)
SchoolId = s.replace(/^.*SchoolId=([0-9]+).*$/, "$1");
return SchoolId;
}
למאותגרי ביטויים רגולריים, הנה הסבר קצר על השורה:
SchoolId = s.replace(/^.*SchoolId=([0-9]+).*$/, "$1");
^ – מתאים לתחילת המחרוזת
$ – מתאים לסוף המחרוזת
. – מתאימה לכל תו שהוא
* – מתאימה ל 0 או יותר תוים שקדמו לכוכבית
+ – מתאים ל 1 או יותר תוים שקדמו לפלוס
[0-9] – מתאים לכל ספרה בין 0 ל 9
() – משמשים ל”שמירה בשם“ של הביטוי בתוכם, כדי שניתן יהיה להתייחס אליהם אחר-כך
$1 – ”קורא בשם“ הביטוי הראשון שנשמר קודם
בשפה חופשית, השורה מבצעת את ההוראות הבאות:
עבור מתחילת ה url ועד סופו. דלג על כל תו שהוא עד שתגיע לרצף: SchoolId=. אחריו, חפש רצף של ספרות ושמור אותן תחת השם $1. דלג שוב עד סוף ה url. בסופו של דבר, החלף את כל ה url ברצף הספרות שנשמר תחת $1.
בפונקציה שלנו, נחליף כעת את השורה:
document.getElementById("center_frame1").src = val + "&SchoolId=95";
בשורה:
document.getElementById("center_frame1").src = val + "&SchoolId=" + getSchoolId();
קיבלנו סקריפט שתומך כעת לא רק בתיכון בית ירח, אלא בכל בתי הספר המשתמשי בשרותי schooly.
התוספת האחרונה שנבצע בקוד עצמו היא השלמת התיקון של פונקציות נוספות המופיעות בקוד האתר ושעושות שימוש בהרחבות לא תקניות של מיקרוסופט. מי שיתבונן שוב בצילום המסך שבו הדגמנו את פעולת העתקת הפונקציה GetPage1, יראה שממש מעליה יש פונקציה דומה שנקראת GetPage. גם הפונקציה GetPage עושה שימוש דומה ב document.frames, ולכן גם אותה כדאי לתקן. גלילה של הקוד באתר מעט למטה תגלה 3 פונקציות נוספות עם אותו הקוד: GetPage2, getaccount, getaccount1. נעתיק את 4 הפונקציות האלה לעורך הקוד שלנו, ונבצע לכל אחת את הטיפול המשולש: החלפת שורת ההגדרה כך שתעשה שימוש ב unsafeWindow; החלפת הקוד הלא תיקני בקוד תיקני; החלפת הקבוע 95 בקריאה לפונקציה שמחשבת את מזהה בית הספר. לפונקציות getaccount, getaccount1 נידרש לעשות תיקון נוסף: השורה הראשונה בכל אחת מהפונקציות האלה כוללת קריאה לפונקציה חיצונית. כזכור, מרחב השמות של גריזמונקי שונה מזה של הסקריפט הרץ בחלון הרגיל, ולכן השמות sethalfwindow ו-setfullwindow לא יזוהו כאשר הם נקראים מהקוד החדש שלנו. כדי שהקריאה אליהן תצליח, נוסיף את הקידומת unsafeWindow לפני הקריאה. הפונקציה getaccount תיראה בקוד שלנו כך:
unsafeWindow.getaccount = function(val) {
unsafeWindow.sethalfwindow()
if (val!=0)
document.getElementById("center_frame1").src=val + '.asp?SchoolId=' + getSchoolId();
}
שלב אחרון: הרחבת התיעוד ופרסום ברשת
כזכור, גריזמונקי הכין לנו פתיח לסקריפט שכולל שם, הסבר קצר ומידע טכני נוסף. אני אוהב להרחיב קצת את המידע המופיע בראש הסקריפט, כך שניתן יהיה לדעת מה התאריך והגרסה שלו, וכן הגנה על זכויות היוצרים. גם אם מבחינתכם הקוד חינמי, כדאי להגדיר מה מותר לעשות איתו (למשל להפיץ מחדש), ומה אסור (למשל לעשות שימוש מסחרי). כמו כן, כדאי לציין שאתם לא אחראים לבעיות שיצוצו כתוצאה משימוש בסקריפט. אני בחרתי ברשיון השימוש של GNU, וזה הפתיח שאני הוספתי לסקריפט שלנו:
// schooly fixer user script // version 0.1 // 2008-07-27 // Copyright (c) 2008, Yehuda B. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // http://www.gnu.org/licenses/gpl.html //
כל שנותר לעשות הוא להירשם באתר userscripts.org (חינמי) ולהעלות את הסקריפט. שם גם תוכלו למצוא את הגרסה הסופית שלי לסקריפט.
תודה רבה!!!!
כל הכבוד! בעצם מגיע לך עוד אחד: כל הכבוד!!!
הראשון היה על יצירת הסקריפט, והשני על כתיבת המדריך.
מסקרן אותי אם ניסית פעם לשלוח את אחד הסקריפטים שלך ליוצרים של האתר שתיקנת כדי שהם יתקנו אותו מהמקור. אם כן, אולי תספר לנו קצת על התגובה שקיבלת או לא.
חזי,
לא בדיוק, אבל בערך:
לשני אתרים שלחתי מכתב עם הוראות מדויקות כיצד לתקן את קוד המקור באתר. עד כה (מזה מספר חודשים), לא בוצע כל תיקון.
אתר שלישי פנה אלי ביוזמתו והתעניין אצלי בקשר לאפשרות שכירת שרותי יעוץ לתיקון המקור. בגלל שאני עסוק עד מעל הראש, סרבתי. למיטב ידיעתי (עם או בלי קשר לסירוב שלי) הם נטשו את רעיון ההתאמה של האתר לפיירפוקס.
אני מניח שבהמשך הבלוג אני אפרסם את המכתבים האלה (ואולי גם אחרים) כסדרת פוסטים המקבילה לסדרה הזאת.
תודה רבה! גם בית הספר שלי משתמש במערכת סקולי, וממש חיפשתי דרך להציג אותו בלינוקס, כני אני חייב.
הבעייה היא שהסקריפט מתקן רק חצי מהאתר, התפריט העליון (שמאפשר כניסה לפורומים) לא עובד. הם השתמשו בקישורי JS לא תקניים. זאת מערכת גרועה, אמרתי את זה לסקולי, אבל הם לא מקשיבים.
האם תוכל לשפר את הסקריפט כך שיתן גישה לשאר חלקי האתר?
אופס, טעות שלי, זה עובד גם בפורומים, פשוט לא הפעלת את הסקריפט גם ל schooly.co.il/*
רק לזה עם WWW. זה עובד יופי.
el.il,
תודה על ההערה. הוספתי את הכתובת המקוצרת לגרסה 0.2 של הסקריפט.
תודה רבה, מדריך מצוין.
כמה שאלות:
- בעצם מה התועלת בפיירבאג, את הודעות השגיאה אפשר לקבל ממסוף השגיאות של פיירפוקס.
- מה העניין של מרחב השמות, אף פעם לא הצלחתי להבין למה הוא טוב.
חתול,
- בקשר לפיירבאג, אתה צודק במקרה הזה, אבל בהמשך אני אלמד איך להשתמש ביכולות נוספות שלו (למשל כדי לנסות תיקוני CSS ולראות מיד את השפעתם), כך שרציתי כבר עכשיו להכניס אותו לארגז הכלים של הקוראים.
- מרחב השמות עוזר לגריזמונקי להבחין האם שני סקריפטים עם אותו שם הם באמת סקריפטים שונים, שיכולים לתפקד זה לצד זה, או שהאחרון שהותקן הוא גרסה מתקדמת יותר של הראשון ולכן יחליף אותו אחרי ההתקנה. למשל, אם אתה תכתוב סקריפט לאתר schooly שיתקן את הבעיה הוויזואלית שהראתי בתמונת המסך הראשונה, עדיין תוכל לקרוא לו schooly fixer. אבל מכוון שהוא יהיה במרחב השמות שלך (נניח hatul.com), גריזמונקי יתקין אותו לצד הסקריפט שלי, ולא במקומו.
[...] לבלוג של יהודה, הייתי ממליץ לכם לקרא את המאמר המעולה: “כיתת אומן: איך לכתוב סקריפט ל- Greasemonkey” (גם אם אינכם תכנתים [...]
שוב פעם יצאתה כוכב !
לתקן את סקולי !!!
(מה דעתך על אתרנט של חברת אדיוסיסטמס? האם זה אפשרי ? http://www.edusystems.co.il/)
ובנוסף, אני משכפל את התגובה ששלחתי מהפוסט של היילרן
“תזכה למצוות, אריכות ימים, ברכה, שלווה, בריאות ונחת כמו שאתה מסב לי כל פעם שאני נפגש עם מקופפ שאתה כותב.”
בברכה,
נדב
תודה, נדב.
בעניין אדיוסיסטמס הכל אפשרי, רק שאני כרגע ב overbooking רציני. נראה לי שזה יחכה בתור.
סידרת גם את אתר הבית ספר שלי
תודה!