1. 程式人生 > >【mxGraph】原始碼學習:(2)mxClient

【mxGraph】原始碼學習:(2)mxClient

1. mxClient.js檔案

mxClient.js是客戶端的引導機制,此檔案include了執行mxGraph所需的所有原始檔,並載入了其依賴的資原始檔,以及配置了客戶端的語言。

意思就是隻要在需要使用mxGraph的地方用<script>標籤載入mxClient.js即可使用該庫。這是一種非常好的做法,不僅能方便的進行開發,還能提供mxClient的壓縮版本以提升載入速度。

mxClient.js的作用如下:

  1. 設定載入相關檔案的全域性變數
  2. 設定相關路徑
  3. 設定客戶端語言
  4. 載入css檔案和js檔案

1.1 控制載入相關檔案

mxClient.js定義了一些用於控制載入相關檔案的全域性變數:

// 用於切換<mxGraph>和<mxEditor>中兩個資原始檔的載入
// 這兩個資原始檔在resource資料夾中,如果應用已經提供則置false,參照GraphEditor
if (typeof(mxLoadResources) == 'undefined') {
    mxLoadResources = true;
}

// 用於指定資原始檔的字尾。預設為.txt
if (typeof(mxResourceExtension) == 'undefined') {
    mxResourceExtension = '.txt';
}

// 用於強制在開發模式下載入JavaScript檔案。預設為undefined
if (typeof(mxForceIncludes) == 'undefined') { mxForceIncludes = false; } // 用於在初始化庫時切換載入CSS檔案 if (typeof(mxLoadStylesheets) == 'undefined') { mxLoadStylesheets = true; }

1.2 相關路徑

還需要設定一些路徑,如果沒有指定則使用預設值:

// 所有檔案的基本路徑,不帶斜槓。預設為'.'
// 在載入mxClient庫之前覆蓋此設定,比如mxBasePath='/path/to/core/directory'
if (typeof
(mxBasePath) != 'undefined' && mxBasePath.length > 0) { // 去掉斜槓 if (mxBasePath.substring(mxBasePath.length - 1) === '/') { mxBasePath = mxBasePath.substring(0, mxBasePath.length - 1); } mxClient.basePath = mxBasePath; } else { mxClient.basePath = '.'; } // 所有圖片的基本路徑,不帶斜槓。預設為<mxClient.basePath> + '/images' // 在載入mxClient庫之前覆蓋此設定,比如mxImageBasePath='/path/to/image/directory' if (typeof(mxImageBasePath) != 'undefined' && mxImageBasePath.length > 0) { // 去掉斜槓 if (mxImageBasePath.substring(mxImageBasePath.length - 1) === '/') { mxImageBasePath = mxImageBasePath.substring(0, mxImageBasePath.length - 1); } mxClient.imageBasePath = mxImageBasePath; } else { mxClient.imageBasePath = mxClient.basePath + '/images'; }

1.3 配置語言

還支援主動設定客戶端的語言,或者檢測瀏覽器的語言以配置其語言:

// 設定客戶端的語言,比如en表示英語,de表示德語。預設是en
// 資原始檔的格式是使用下劃線表示語言,比如graph_zh.txt
// 在載入mxClient庫之前覆蓋此設定,比如mxLanguage='en'
if (typeof(mxLanguage) != 'undefined' && mxLanguage != null) {
    mxClient.language = mxLanguage;
} else {
    // 在GraphEditor中必須設定mxLanguage,介面的語言才更改
    mxLanguage = (mxClient.IS_IE) ? navigator.userLanguage : navigator.language;
    // 中文分簡體和繁體,這裡只設置簡體
    mxLanguage = (mxLanguage === 'zh-CN') ? 'zh' : mxLanguage;
    
    mxClient.language = mxLanguage;
}

// 設定預設語言載入不帶下劃線的資原始檔。預設為en
// 在載入mxClient庫之前覆蓋此設定,比如mxDefaultLanguage='zh'
if (typeof(mxDefaultLanguage) != 'undefined' && mxDefaultLanguage != null) {
    mxClient.defaultLanguage = mxDefaultLanguage;
} else {
    mxClient.defaultLanguage = 'en';
}

// 設定支援的所有語言。預設語言不用新增到列表中
// 在載入mxClient庫之前覆蓋此設定,比如mxLanguages=['de','it','fr']
if (typeof(mxLanguages) != 'undefined' && mxLanguages != null) {
    mxClient.languages = mxLanguages;
}

1.4 載入cs和js檔案

開發環境中,將所有程式碼分成了不同的包便於開發,還需要將它們集中在同一個檔案即mxClient.js中,這樣可以在HTML通過匯入一個檔案而匯入整個庫:

// 匯入所有樣式和名稱空間
if (mxLoadStylesheets) {
    mxClient.link('stylesheet', mxClient.basePath + '/css/common.css');
}

// 為舊的IE瀏覽器新增所需的名稱空間,樣式表和記憶體處理
if (mxClient.IS_VML) {
	// 如果支援SVG則使用SVG
    if (mxClient.IS_SVG) {
        mxClient.IS_VML = false;
    } else {
        // 允許支援IE8的standards模式,這需要所有VML屬性可以直接賦值,比如node.attr=value
        // 不能使用setAttribute
        if (document.documentMode == 8) {
            document.namespaces.add(mxClient.VML_PREFIX, 'urn:schemas-microsoft-com:vml', '#default#VML');
            document.namespaces.add(mxClient.OFFICE_PREFIX, 'urn:schemas-microsoft-com:office:office', '#default#VML');
        } else {
            document.namespaces.add(mxClient.VML_PREFIX, 'urn:schemas-microsoft-com:vml');
            document.namespaces.add(mxClient.OFFICE_PREFIX, 'urn:schemas-microsoft-com:office:office');
        }

        // IE中有限數量樣式表的解決方法(在標準模式下不起作用)
        if (mxClient.IS_QUIRKS && document.styleSheets.length >= 30) {
            (function () {
                var node = document.createElement('style');
                node.type = 'text/css';
                node.styleSheet.cssText = mxClient.VML_PREFIX + '\\:*{behavior:url(#default#VML)}' +
                    mxClient.OFFICE_PREFIX + '\\:*{behavior:url(#default#VML)}';
                document.getElementsByTagName('head')[0].appendChild(node);
            })();
        } else {
            document.createStyleSheet().cssText = mxClient.VML_PREFIX + '\\:*{behavior:url(#default#VML)}' +
                mxClient.OFFICE_PREFIX + '\\:*{behavior:url(#default#VML)}';
        }

        if (mxLoadStylesheets) {
            mxClient.link('stylesheet', mxClient.basePath + '/css/explorer.css');
        }
    }
}

// 如果通過CommonJS載入指令碼,不要將<script>標記寫入頁面以獲取依賴項。這些已經包含在編譯版本中
if (mxForceIncludes || !(typeof module === 'object' && module.exports != null)) {
// PREPROCESSOR-REMOVE-END
    mxClient.include(mxClient.basePath + '/js/util/mxLog.js');
    mxClient.include(mxClient.basePath + '/js/util/mxObjectIdentity.js');
    mxClient.include(mxClient.basePath + '/js/util/mxDictionary.js');
    mxClient.include(mxClient.basePath + '/js/util/mxResources.js');
    ...
    mxClient.include(mxClient.basePath + '/js/io/mxDefaultKeyHandlerCodec.js');
    mxClient.include(mxClient.basePath + '/js/io/mxDefaultToolbarCodec.js');
    mxClient.include(mxClient.basePath + '/js/io/mxDefaultPopupMenuCodec.js');
    mxClient.include(mxClient.basePath + '/js/io/mxEditorCodec.js');
// PREPROCESSOR-REMOVE-START
}

2. mxClient類

mxClient在mxClient.js檔案中是作為一個類定義的,主要的作用如下:

  1. 說明當前mxGraph的版本號
  2. 標識客戶端的瀏覽器和作業系統
  3. 定義檢查瀏覽器是否支援的函式
  4. 定義載入css檔案、js檔案和依賴資源的函式
  5. 儲存一些配置相關的變數,比如語言、路徑等

2.1 版本號

版本號說明了mxGraph庫的當前版本,格式是主版本.次版本.編譯號,如下所示:

VERSION: '3.9.9'

2.2 標識瀏覽器

標識瀏覽器的變數如下表所示:

// IE瀏覽器
IS_IE: navigator.userAgent.indexOf('MSIE') >= 0,
IS_IE6: navigator.userAgent.indexOf('MSIE 6') >= 0,
IS_IE11: !!navigator.userAgent.match(/Trident\/7\./),
// 如果是IE瀏覽器,標識是否是Quirks模式
IS_QUIRKS: navigator.userAgent.indexOf('MSIE') >= 0 && 
	(document.documentMode == null || document.documentMode == 5),
// IE11的enterprise模式(IE8的standards模式)
IS_EM: 'spellcheck' in document.createElement('textarea') && 
	document.documentMode == 8,
// Edge瀏覽器
IS_EDGE: !!navigator.userAgent.match(/Edge\//),
// VML名稱空間中結點的字首,預設為v
VML_PREFIX: 'v',
// VML office命令空間中結點的字首,預設為o
OFFICE_PREFIX: 'o',

// Netscape瀏覽器(包括Firefox瀏覽器)
IS_NS: navigator.userAgent.indexOf('Mozilla/') >= 0 &&
    navigator.userAgent.indexOf('MSIE') < 0 &&
    navigator.userAgent.indexOf('Edge/') < 0,

// Opera瀏覽器
IS_OP: navigator.userAgent.indexOf('Opera/') >= 0 ||
    navigator.userAgent.indexOf('OPR/') >= 0,
// 如果-o-transform可用作CSS樣式,則為True,即基於具有2.5或更高版本的Presto引擎的Opera瀏覽器。
IS_OT: navigator.userAgent.indexOf('Presto/') >= 0 &&
    navigator.userAgent.indexOf('Presto/2.4.') < 0 &&
    navigator.userAgent.indexOf('Presto/2.3.') < 0 &&
    navigator.userAgent.indexOf('Presto/2.2.') < 0 &&
    navigator.userAgent.indexOf('Presto/2.1.') < 0 &&
    navigator.userAgent.indexOf('Presto/2.0.') < 0 &&
    navigator.userAgent.indexOf('Presto/1.') < 0,

// Safari瀏覽器
IS_SF: navigator.userAgent.indexOf('AppleWebKit/') >= 0 &&
    navigator.userAgent.indexOf('Chrome/') < 0 &&
    navigator.userAgent.indexOf('Edge/') < 0,

// Google Chrome瀏覽器
IS_GC: navigator.userAgent.indexOf('Chrome/') >= 0 &&
    navigator.userAgent.indexOf('Edge/') < 0,

// Chrome App
IS_CHROMEAPP: window.chrome != null && 
	chrome.app != null && 
	chrome.app.runtime != null,

// Firefox瀏覽器
IS_FF: navigator.userAgent.indexOf('Firefox/') >= 0,
// 如果-moz-transform可用作CSS樣式,則為True。
// 所有基於Firefox的瀏覽器都是3或者等於3的情況,例如Camino,Iceweasel,Seamonkey和Iceape。
IS_MT: (navigator.userAgent.indexOf('Firefox/') >= 0 &&
        navigator.userAgent.indexOf('Firefox/1.') < 0 &&
        navigator.userAgent.indexOf('Firefox/2.') < 0) ||
    (navigator.userAgent.indexOf('Iceweasel/') >= 0 &&
        navigator.userAgent.indexOf('Iceweasel/1.') < 0 &&
        navigator.userAgent.indexOf('Iceweasel/2.') < 0) ||
    (navigator.userAgent.indexOf('SeaMonkey/') >= 0 &&
        navigator.userAgent.indexOf('SeaMonkey/1.') < 0) ||
    (navigator.userAgent.indexOf('Iceape/') >= 0 &&
        navigator.userAgent.indexOf('Iceape/1.') < 0),

// 瀏覽器是否支援SVG
IS_SVG: navigator.userAgent.indexOf('Firefox/') >= 0 || // FF and Camino
    navigator.userAgent.indexOf('Iceweasel/') >= 0 || // Firefox on Debian
    navigator.userAgent.indexOf('Seamonkey/') >= 0 || // Firefox-based
    navigator.userAgent.indexOf('Iceape/') >= 0 || // Seamonkey on Debian
    navigator.userAgent.indexOf('Galeon/') >= 0 || // Gnome Browser (old)
    navigator.userAgent.indexOf('Epiphany/') >= 0 || // Gnome Browser (new)
    navigator.userAgent.indexOf('AppleWebKit/') >= 0 || // Safari/Google Chrome
    navigator.userAgent.indexOf('Gecko/') >= 0 || // Netscape/Gecko
    navigator.userAgent.indexOf('Opera/') >= 0 || // Opera
    (document.documentMode != null && document.documentMode >= 9), // IE9+

// 如果foreignObject支援不可用,則為True。 這是Opera,較舊的基於SVG的瀏覽器和所有版本的IE的情況。
NO_FO: !document.createElementNS || 
	document.createElementNS('http://www.w3.org/2000/svg','foreignObject') != 
	'[object SVGForeignObjectElement]' || 
    navigator.userAgent.indexOf('Opera/') >= 0,

// 瀏覽器是否支援VML
IS_VML: navigator.appName.toUpperCase() == 'MICROSOFT INTERNET EXPLORER',

2.3 標識系統

標識系統的變數如下所示:

// iPad、iPhone和iPod平臺
IS_IOS: (navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false),

// Windows系統
IS_WIN: navigator.appVersion.indexOf('Win') > 0,

// Mac系統
IS_MAC: navigator.appVersion.indexOf('Mac') > 0,

// 如果此裝置支援touchstart/-move/-end事件,
// 啟用觸控的裝置上的Apple iOS,Android,Chromebook和Chrome瀏覽器,則為True。
IS_TOUCH: 'ontouchstart' in document.documentElement,

// 如果此裝置支援Microsoft指標事件,則為True(在Mac上始終為false)。
IS_POINTER: window.PointerEvent != null && !(navigator.appVersion.indexOf('Mac') > 0),

// 是否是本地執行(如果文件位置不以http://或https://開頭,則為True)。
IS_LOCAL: document.location.href.indexOf('http://') < 0 &&
    document.location.href.indexOf('https://') < 0,

2.4 瀏覽器支援

檢查瀏覽器是否支援VML或者SVG,因為mxGraph的cell可以用VML或者SVG在畫出來,所以必須要瀏覽器支援其中一種語言:

isBrowserSupported: function () {
    return mxClient.IS_VML || mxClient.IS_SVG;
},

2.5 載入檔案

mxClient類中定義了三個函式用於載入css檔案、js檔案和資原始檔。

載入css檔案到HTML的head中,link標籤的charset固定為UTF-8,type固定為text/css

/**
 * rel:link標籤的rel屬性
 * href:link標籤的href屬性,相對路徑
 * doc:可選的link標籤所屬的document
 */
link: function (rel, href, doc) {
	// 使用指定的document。如果未指定,則使用當前頁面的document
    doc = doc || document;

    // 如果是IE6則直接在head中新增link標籤
    if (mxClient.IS_IE6) {
        doc.write('<link rel="' + rel + '" href="' + href + '" charset="UTF-8" type="text/css"/>');
    } else {
        var link = doc.createElement('link');

        link.setAttribute('rel', rel);
        link.setAttribute('href', href);
        link.setAttribute('charset', 'UTF-8');
        link.setAttribute('type', 'text/css');

        var head = doc.getElementsByTagName('head')[0];
        head.appendChild(link);
    }
}

載入js檔案到HTML的head中,這個函式只在開發環境中使用,因為在生成環境中使用的是mxClient.min.js,如下所示:

/**
 * src:script標籤的src屬性,相對路徑
 */
include: function (src) {
    document.write('<script src="' + src + '"></script>');
}

如果mxLoadResources全域性變數為false,則使用該函式載入mxClient的依賴資源:

/**
 * fn:在所有資原始檔載入之後呼叫的函式
 * lan:可選的傳遞給<mxResources.add>的引數
 */
loadResources: function (fn, lan) {
	// 待載入的資原始檔數
    var pending = mxClient.defaultBundles.length;

	// 如果沒有需要載入的資原始檔,則直接呼叫fn
    function callback() {
        if (--pending === 0) {
            fn();
        }
    }

	// 使用<mxResources.add>載入資原始檔
    for (var i = 0; i < mxClient.defaultBundles.length; i++) {
        mxResources.add(mxClient.defaultBundles[i], lan, callback);
    }
}