כל עמוד של האתר הציבורי נטען עם ארבעה קבצי JS מתיקיית system/js/ (בריפו: api/js/). הם נותנים לקוד ה-JS שלכם שכבת בסיס: טעינת קבצים דינמית, תור לקוד שמחכה ל-jQuery, פונקציות עזר, קוקיז ומדידה. בעמוד הזה: מי נטען ואיפה, מה כל קובץ חושף, ואילו מלכודות כדאי להכיר.
הפאנל של הניהול משתמש בנכסים אחרים (assets/, רכיבי Vue). העמוד הזה מתאר רק את מה ש-PAGE::load מוסיף לאתר הציבורי.
מה נטען ואיפה#
PAGE::load מוסיף כל קובץ system/js/*.js ב-add_asset, ולכן הם עוברים את אותו צינור כמו כל נכס (גרסה ב-?ver=, קבוצות minify, הסוואה):
| קובץ | אזור | תפקיד |
|---|---|---|
system.js | head | טעינת קבצים דינמית, תור jQuery, applyTags, apply_after_interaction |
jquery.min.js | body_end | jQuery 3.7.1. בסופו נשלח האירוע jqueryloaded על document |
MISC.js | body_end | פונקציות עזר, מחלקת wz_cookies, קיצור המקשים לניקוי מטמון |
analytics.js | body_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
תהליך השליחה:
init()קורא את הקוקי. אם אין,get_token()מבקש טוקן חדש.add(...)שולחPOST /system/analyticsעם גוף JSON (action: "add",item_type,item_id,event,url) ועם הכותרתtoken.- אם השרת מחזיר
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). אל תוסיפו מתודות ציבוריות רגישות למחלקה הזו.
ראו גם#
- צינור הנכסים: איך הקבצים האלה נרשמים, מקבלים גרסה ונאגדים
- PAGE API:
add_asset_advanced,after_user_intraction,include_system_js - מצב הסוואה:
system/js/מוחלף ב-lib/js/ב-HTML - קוקיז וסשן: הצד של ה-PHP
- ניקוי מטמון