ה-runtime בדפדפן

קבצי ה-JS שהליבה טוענת בכל עמוד באתר הציבורי - jQuery, system.js, MISC.js ו-analytics.js - הפונקציות שהם חושפים, וכללי הטעינה והתלות ביניהם.

⏱ 6 דק' קריאה 1112 מילים ערוך דף זה ב-GitHub

כל עמוד של האתר הציבורי נטען עם ארבעה קבצי JS מתיקיית system/js/ (בריפו: api/js/). הם נותנים לקוד ה-JS שלכם שכבת בסיס: טעינת קבצים דינמית, תור לקוד שמחכה ל-jQuery, פונקציות עזר, קוקיז ומדידה. בעמוד הזה: מי נטען ואיפה, מה כל קובץ חושף, ואילו מלכודות כדאי להכיר.

הערה

הפאנל של הניהול משתמש בנכסים אחרים (assets/, רכיבי Vue). העמוד הזה מתאר רק את מה ש-PAGE::load מוסיף לאתר הציבורי.

מה נטען ואיפה#

PAGE::load מוסיף כל קובץ system/js/*.js ב-add_asset, ולכן הם עוברים את אותו צינור כמו כל נכס (גרסה ב-?ver=, קבוצות minify, הסוואה):

קובץאזורתפקיד
system.jsheadטעינת קבצים דינמית, תור jQuery, applyTags, apply_after_interaction
jquery.min.jsbody_endjQuery 3.7.1. בסופו נשלח האירוע jqueryloaded על document
MISC.jsbody_endפונקציות עזר, מחלקת wz_cookies, קיצור המקשים לניקוי מטמון
analytics.jsbody_endהאובייקט wz_analytics ושליחת אירועים ל-/system/analytics

הרשימה נבנית עם glob("system/js/*.js"), ולכן סדר הקבצים הוא סדר ה-glob: MISC.js, analytics.js, jquery.min.js (אותיות גדולות קודם), ו-system.js ב-head. הם נרשמים אחרי נכסים שנרשמו ב-application/includes ולפני נכסים שנרשמו בבקר וב-theme. המשמעות: MISC.js ו-analytics.js רצים לפני jquery.min.js, ואסור להניח ש-$ קיים בזמן הטעינה שלהם. כל השימוש ב-$ בתוכם נעשה בתוך פונקציות שנקראות מאוחר יותר.

כדי לא לטעון אותם באתר או בעמוד מסוים, קבעו PAGE::$include_system_js = false בקובץ ב-application/includes (הקבצים האלה נטענים אחרי PAGE::reset ולפני רישום קבצי המערכת). הערך מאופס ל-true בכל reset, ולכן קביעה ב-application/includes/init.php או בבקר (שרץ אחרי הרישום) לא תשפיע. בלי system.js הפונקציות wz_jquery, loadobjs ו-applyTags לא קיימות, וגם קוד שה-theme או ה-tag manager מניחים עליהן ייכשל.

system.js#

פונקציהמה היא עושה
wz_jquery(fn)מריץ fn(jQuery) מיד אם jQuery כבר נטען, אחרת מכניס אותה לתור שמורץ באירוע jqueryloaded
loadobjs(m_file, ver)טוענת קובץ CSS או JS פעם אחת (לפי הרשימה loadedobjects). מוסיפה ?ver=<ver> לכתובת. מחזירה Promise (גם ל-CSS)
loadPage(url, scripts)טוענת רשימת קבצים במקביל עם loadobjs(script, 'version') ומחכה לכולם. שימו לב: הגרסה מקודדת כמחרוזת 'version'
writeLoadedFile(...files)מסמנת קבצים כטעונים, כדי ש-loadobjs לא יטען אותם שוב
buildUrl(base, query_string)בונה כתובת מוחלטת ומחליפה פרמטר query. לכתובת יחסית היא משתמשת ב-$('base').attr('href'), כלומר דורשת jQuery וגם <base href>
applyTags(tags)מזריקה תגים שהגיעו מ-/tag_manager.js (ראו צינור הנכסים). מחכה לסקריפטים חיצוניים לפני תגים עם is_after_done
apply_after_interaction(arr)מריצה את נכסי after_user_intraction אחרי הלחיצה או המקש הראשונים

PAGE::writeLoadedFile() מדפיס בסוף העמוד קריאה ל-writeLoadedFile('..','..') עם כל הנכסים שכבר נטענו, כך ש-loadobjs שלכם לא יכפיל קובץ שה-PHP כבר רשם.

// טעינת ספרייה בעת הצורך, פעם אחת
loadobjs('themes/default/chart.js', window.ASSET_VER).then(function () {
    renderChart();
});

// קוד שתלוי ב-jQuery ויכול לרוץ לפני שנטען
wz_jquery(function ($) {
    $('.menu-toggle').on('click', function () {
        $('body').toggleClass('menu-open');
    });
});
שימו לב
apply_after_interaction מריץ נכסי after_user_intraction שהם קוד HTML (למשל תג <script>) באמצעות eval על התוכן, ומשמיט את תגי ה-script. נכס שהוא כתובת עובר ל-loadobjs(url) בלי ארגומנט גרסה, ולכן הכתובת שנטענת נגמרת ב-ver=undefined. אל תשלבו בנכס כזה ערכים שמקורם בקלט משתמש, ואל תסתמכו על ver כדי לעקוף מטמון.

הערה
loadobjs מזהה CSS לפי הסיומת .css בכתובת (indexOf('.css')), ואת כל השאר מתייחס כ-JS. כתובת SCSS כמו style.scss.css נחשבת CSS, אבל כתובת עם .css בתוך נתיב של סקריפט תיחשב בטעות כ-CSS.

MISC.js#

פונקציות גלובליות, חלקן תלויות ב-jQuery (מסומן):

פונקציהפירוט
isInt(x)true אם x מספר שלם (גם כמחרוזת, בלי אפסים מובילים)
IsValidURL(str)בדיקה בביטוי רגולרי. פרוטוקול http(s) אופציונלי
IsValidEmail(str)בדיקה בביטוי רגולרי. מקבל סיומת דומיין של 2 עד 4 תווים בלבד
FORM_AlertMsg(el, msg)(jQuery) מפוקס על האלמנט, מוסיף לו errorInp ומציג alert. הסימון נמחק בהקשה הבאה
is_mobile()זיהוי טלפון לפי navigator.userAgent (Android, webOS, iPhone, iPod, BlackBerry, לא iPad)
is_iphone()לפי navigator.platform
isNull(str)true עבור מחרוזת ריקה, null או undefined
number_format(num)מעגל ל-2 ספרות, ומחזיר מספר שלם כשאין שבר
getQueryParam(param)ערך מה-query string של העמוד, או null
removeURLParameter(url, parameter)מחזירה את הכתובת בלי הפרמטר
get_banner(type, func)(jQuery) ראו אזהרה למטה
get_all_banners()(jQuery) אוסף את כל האלמנטים .bannerCls, שולח ל-system/ad_manager/get_all_banners ומציב את ה-HTML שחוזר. כשל הבקשה מפעיל את האירוע adblock_active על #page

בנוסף, קבוצת ה-keydown על body חוסמת הקלדה של תווים שאינם ספרות בשדה עם המחלקה numeric (מאפשרת Backspace, Delete, Tab, חצים ו-Ctrl+A).

קוקיז: wz_cookies#

wz_cookies.set('seen_banner', '1', 7);       // 7 ימים, path=/
wz_cookies.isset('seen_banner');             // true
wz_cookies.get('seen_banner');               // "1", או null

set(name, value, days = 0, path = '/'): עם days = 0 נוצר session cookie. הערך עובר encodeURIComponent והקריאה עושה decodeURIComponent. הקריאה אינה מתמודדת עם ערך שמכיל = (היא מפצלת לפי = ולוקחת את החלק השני בלבד).

ניקוי מטמון מהדפדפן#

הקצר Ctrl+Shift+ (המקש מעל Tab) עושה, רק כשקיים קוקי של אדמין מחובר (CRM_admin_loggedאוCRM_admin_token): localStorage.clear(), ואז POST system/Tools/delete_cacheעם כתובת העמוד, וכשהתשובהtrue` טוען את העמוד מחדש. השרת בודק הרשאת אדמין בעצמו (הקוקי בדפדפן הוא רק תנאי נוחות). פירוט הפעולה ב-ניקוי מטמון.

שימו לב
get_banner שולח POST ל-system/ad_manager/get_by_type, אבל בקר ad_manager לא מגדיר מתודה בשם הזה (יש בו gotourl, get_all_banners ו-get_types). הפונקציה נראית כשריד, ולא נבדקה מול אתר חי. השתמשו ב-get_all_banners.

הערה
onkeydown מוגדר על document בשיוך ישיר (document.onkeydown = ...), ולכן הוא דורס handler קודם מאותו סוג ויידרס אם קוד אחר ישייך אחריו. השתמשו ב-addEventListener בקוד שלכם.

analytics.js#

wz_analytics שולח אירועים (צפייה, לחיצה) על פריטים לבקר analytics ושומר טוקן בקוקי wzanalytics_token ל-2 ימים.

wz_analytics.add('article', 1234, 'view');   // item_type, item_id, event

תהליך השליחה:

  1. init() קורא את הקוקי. אם אין, get_token() מבקש טוקן חדש.
  2. add(...) שולח POST /system/analytics עם גוף JSON (action: "add", item_type, item_id, event, url) ועם הכותרת token.
  3. אם השרת מחזיר data === false (טוקן שפג), renew_token() מקבל טוקן חדש והאירוע נשלח שוב בלי השדה url.

כל הבקשות הן POST ל-/system/analytics, והבקר מעביר את הערך של action אל מתודה באותו שם במחלקה m_analytics (הבדיקה היא method_exists). תשובה היא תמיד JSON בצורה {"data": ...}.

שימו לב

בקר analytics עונה עם Access-Control-Allow-Origin: * ועם Access-Control-Allow-Credentials: true, כלומר כל אתר יכול לשלוח אליו אירועים. ובנוסף, כל שם מתודה ציבורית של m_analytics נגיש דרך action (לא רק add, get_token ו-renew_token). אל תוסיפו מתודות ציבוריות רגישות למחלקה הזו.

ראו גם#

מצאתם טעות או חוסר? תקנו את הדף או פתחו Issue בריפו. התיעוד נכתב מתוך הקוד של ליבה 5.0.115.