javascript設計モード-プロキシモード

31250 ワード

<!DOCTYPE html>

<html>

<head>

    <title>    </title>

    <meta charset="utf-8">

</head>

<body>

<script>

/**

 *     

 *

 *   :

 *                       。

 *

 *   :

 *       

 *

 *   (proxy)     ,               。                ,                 。            (real subject)。             ,         。                     ,            ,                        。     。                         ,       。

 */



/*

      



                   。          (  )         。           。                    。                  。   ,                          (       ),            (        )。             ,                   。

 */



(function(){

    //     

    

    //     ,         

    function Subject(){}

    Subject.prototype.request = function(){};



    /**

     *     

     * @param {Object} realSubject [             ]

     */

    function Proxy(realSubject){

        this.realSubject = readSubject;

    }

    Proxy.prototype.request = function(){

        this.realSubject.request();

    };

}());





/*

             



                    。                  。        ,               。

        ,              。       Book  

 */

var Publication = new Interface('Publication', ['getIsbn', 'setIsbn', 'getTitle', 'setTitle', 'getAuthor', 'display']);

var Book = function (isbn, title, author) {

    // implements Publication

};

// Library interface

var Library = new Interface('Library', ['findBooks', 'checkoutBook', 'returnBook']);

// PublicLibrary class

var PublicLibrary = function (books) {

    // implements Library

    this.catalog = {};

    for (var i = 0, len = books.length; i < len; i++) {

        this.catalog[books[i].getIsbn()] = {

            book: books[i],

            available: true

        };

    }

};

PublicLibrary.prototype = {

    findBooks: function (searchString) {

        var results = [];

        for (var isbn in this.catalog) {

            if (!this.catalog.hasOwnProperty(isbn)) {

                continue;

            }

            if (searchString.match(this.catalog[isbn].getTitle()) || searchString.match(this.catalog[isbn].getAnchor())) {

                results.push(this.catalog[isbn]);

            }

        }

        return results;

    },

    checkoutBook: function (book) {

        var isbn = book.getIsbn();

        if (this.catalog[isbn]) {

            if (this.catalog[isbn].available) {

                this.catalog[isbn].available = false;

                return this.catalog[isbn];

            } else {

                throw new Error('PublicLibrary:book ' + book.getTitle() + ' is not currently available.');

            }

        } else {

            throw new Error('PublicLibrary:book ' + book.getTitle() + ' not found');

        }

    },

    returnBook: function (book) {

        var isbn = book.getIsbn();

        if (this.catalog[isbn]) {

            this.catalog[isbn].available = true;

        } else {

            throw new Error('PublicLibrary:book ' + book.getTitle() + ' nout found');

        }

    }

};



/*

        。       ,     。                PublicLibrary    :

 */

// PublicLibraryProxy class, a useless proxy

var PublicLibraryProxy = function (catalog) {

    // implements Library

    this.library = new PublicLibrary(catalog);

};

PublicLibraryProxy.prototype = {

    findBooks: function (searchString) {

        return this.library.findBooks(searchString);

    },

    checkoutBook: function (book) {

        return this.library.checkoutBook(book);

    },

    returnBook: function (book) {

        return this.library.returnBook(book);

    }

};



/*

              。         ,    (virtual proxy)         。                       。                     。                。                    。      ,  PublicLibrary      。              。              ,   PublicLibrary            。

 */

// PublicLibarryVirtualProxy class

var PublicLibraryVirtualProxy = function (catalog) {

    this.library = null;

    this.catalog = catalog;

};

PublicLibraryVirtualProxy.prototype = {

    _initializeLibrary: function () {

        if (this.library === null) {

            this.library = new PublicLibrary(this.catalog);

        }

    },

    findBooksL: function (searchString) {

        this._initializeLibrary();

        return this.library.findBooks(searchString);

    },

    checkoutBook: function (book) {

        this._initializeLibrary();

        return this.library.checkoutBook(book);

    },

    returnBook: function (book) {

        this._initializeLibrary();

        return this.library.returnBook(book);

    }

};



/*

 PublicLibraryProxy PublicLibraryVirtualProxy                 PublicLibrary   。PublicLibraryVirtualProxy             ,                    。    ,             ,           。                      。    ,          。

 */



/*

     ,         



   JS  ,               。

     (remote proxy)               。 Java ,              ,                  。           ,                   。            JS 。  JS            。 JS                            ,        。          JSON          ,   Ajax            。

                              。         Web    ,      PHP  。     ,            。          ,          。



            JS 。      ,                      。    PublicLibrary                    。 Java                     ,           (       )      ,                。  JS ,               。

 */



/*

              



              。                   ,               ,                 。

                            ,              。                 ,                    。              。                   。       ,                 。          ,                 。       ,                      。           ,           ,          。  ,              ,         。

 */



/*

          



          ,                   。           。                     。                      ,  ,                                             。                   “    。。。”     ,                ,                    。

               。              ,                ,             XMLHttpRequest  。                      ?         。                  ,          。               ,         。           (       )   ,        。            ,                     JS API。

     ,               ,                   ,          。          ,                     ,          。

 */



var SimpleHandler = function () {

};     // implements AjaxHandler

SimpleHandler.prototype = {

    request: function (method, url, callback, postVars) {

        var xhr = this.createXhrObject();

        xhr.onreadystatechange = function () {

            if (xhr.readyState !== 4) {

                return;

            }

            ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) ?

                    callback.success(xhr.responseText, xhr.responseXML) :

                    callback.failure(xhr.status);

        };

        xhr.open(method, url, true);

        if (method !== 'POST') {

            postVars = null;

        }

        xhr.send(postVars);

    },

    createXhrObject: function () {      // Factory method

        var methods = [

            function () {

                return new XMLHttpRequest();

            },

            function () {

                return new ActiveXObject('Msxml2.XMLHTTP');

            },

            function () {

                return new ActiveXObject('Microsoft.XMLHTTP');

            }

        ];



        for (var i = 0, len = methods.length; i < len; i++) {

            try {

                methods[i]();

            } catch (e) {

                continue;

            }

            // if we reach this point,method[i] worked

            this.createXhrObject = methods[i]();    // Memoize the method

            return methods[i]();

        }



        // if we reach this point,none of the methods worked

        throw new Error('SimpleHandler:Could not create an XHR object.');

    }

};



// Manually making the calls

var xhrHandler = new SimpleHandler();



// Get he pageview statistics

var callback = {

    success: function (responseText) {

        // Parse the JSON data

        var stats = eval('(' + responseText + ')');

        // Display the stats on the page

        displayPageviews(stats);

    },

    failure: function (statusCode) {

        throw new Error('Asynchronous request for stats failed.');

    }

};

xhrHandler.request('GET', '/stats/getPageviews/?page=index.html', callback);



// Get the browser statistics

var callback2 = {

    success: function (responseText) {

        var stats = eval('(' + responseText + ')');

        displayBrowserShare('Asynchronous request for stats failed');

    }

};

xhrHandler.request('GET', '/stats/getBrowserShare/?page=index.html', callback);





//     :

// PgeStats interface

var PageStats = new Interface('PageStats', ['getPageviews', 'getUniques', 'getBrowserShare', 'getTopSearchTerms', 'getMostVisitedPages']);



//       StatsProxy:

// StatsProxy singleton

var StatsProxy = (function () {

    // Private attributes

    var xhrHandler = new SimpleHandler();

    var urls = {

        pageviews: '/stats/getPageviews/',

        uniques: '/stats/getUniques/',

        browserShare: '/stats/getBrowserShare/',

        topSearchTerms: '/stats/getTopSearchTerms/',

        mostVisitedPages: '/stats/getMostVisitedPages/'

    };



    // Private methods

    function xhrFailure() {

        throw new Error('StatsProxy: Asynchronous request for stats failed.');

    }



    function fetchData(url, dataCallback, startDate, endDate, page) {

        var callback = {

            success: function (responseText) {

                var stats = eval('(' + responseText + ')');

                dataCallback(stats);

            },

            failure: xhrFailure

        };



        var getVars = [];

        if (startDate !== undefined) {

            getVars.push('startDate=' + encodeURIComponent(startDate));

        }

        if (endDate !== undefined) {

            getVars.push('endDate=' + encodeURIComponent(endDate));

        }

        if (page !== undefined) {

            getVars.push('page=' + page);

        }



        if (getVars.length > 0) {

            url = url + '?' + getVars.join('&');

        }



        xhrHandler.request('GET', url, callback);

    }



    // Public methods

    return {

        getPageviews: function (callback, startDate, endDate, page) {

            fetchData(urls.pageviews, callback, startDate, endDate, page);

        },

        getUniques: function (callback, startDate, endDate, page) {

            fetchData(urls.uniques, callback, startDate, endDate, page);

        },

        getBrowserShare: function (callback, startDate, endDate, page) {

            fetchData(urls.browserShare, callback, startDate, endDate, page);

        },

        getTopSearchTerms: function (callback, startDate, endDate, page) {

            fetchData(urls.topSearchTerms, callback, startDate, endDate, page);

        },

        getMostVisitedPages: function (callback, startDate, endDate, page) {

            fetchData(urls.mostVisitedPages, callback, startDate, endDate, page);

        }

    };

}());

/*

        Web          ,            。  StatsProxy       JS       ,           。  ,               。    ,     ,            ,             ,               ,         ,              ,    。        ,              。                            ,                       。                       ,                         。

 */





/*

   Web       



                      Web      。  JS      ,Web                           。              ,        。

 */

// WebserviceProxy class

var WebserviceProxy = function () {

    this.xhrHandler = new SimpleHandler();

};

WebserviceProxy.prototype = {

    _xhrFailure: function (statusCode) {

        throw new Error('StatsProxy:Asynchronous request for stats failed.');

    },

    _fetchData: function (url, dataCallback, getVars) {

        var that = this;

        var callback = {

            success: function (responseText) {

                var obj = eval('(' + responseText + ')');

                dataCallback(obj);

            },

            failure: that._xhrFailure

        };



        var getVarArray = [];

        for (var varName in getVars) {

            getVarArray.push(varName + '=' + getVars[varName]);

        }

        if (getVarArray.length > 0) {

            url = url + '?' + getVarArray.join('&');

        }



        this.xhrHandler.request('GET', url, callback);

    }

};

/*

          ,   WebserviceProxy      ,    _fetchData           :

 */

// StatsProxy class

// StatsProxy class

var StatsProxy = function () {

    StatsProxy.superclass.constructor.call(this);

};

function extend(subClass, superClass) {

    var F = function () {

    };

    F.prototype = superClass.prototype;

    subClass.prototype = new F();

    subClass.prototype.constructor = subClass;



    subClass.superclass = superClass.prototype;

    if (superClass.prototype.constructor === Object.prototype.constructor) {

        superClass.prototype.constructor = superClass;

    }

}

extend(StatsProxy, WebserviceProxy);



StatsProxy.prototype.getPageviews = function (callback, startDate, endDate, page) {

    this._fetchData('test.json', callback, {

        'startDate': startDate,

        'endDate': endDate,

        'page': page

    });

};

StatsProxy.prototype.getUniques = function (callback, startDate, endDate, page) {

    this._fetchData('/stats/getsUniques/', callback, {

        'startDate': startDate,

        'endDate': endDate,

        'page': page

    });

};





/*

                           。                 。 A  ,                 。            ,                。

                 ,                           。    ,                      。               ,                            。                           ,            。

 */

/*

                 。                         HTML  ,             :

 */

// Directory interface

var Directory = new Interface('Directory', ['showPage']);



// PersonnelDirectory class, the Real Subject

var PersonnelDirectory = function (parent) {

    this.xhrHandler = new SimpleHandler();

    this.parent = parent;

    this.data = null;

    this.currentPage = null;



    var that = this;

    var callback = {

        success: that._configure,

        failure: function () {

            throw new Error('PersonnelDirectory: failure in data retrieval.');

        }

    };

    xhrHandler.request('GET', 'directoryData.php', callback);

};

PersonnelDirectory.prototype = {

    _configure: function (responseText) {

        this.data = eval('(' + responseText + ')');

        //...

        this.currentPage = 'a';

    },

    showPage: function (page) {

        $('page-' + this.currentPage).style.display = 'none';

        $('page-' + page).style.display = 'block';

        this.currentPage = page;

    }

};



/*

             XHR         。 ——configure              ,    HTML          。                。                 、                。

 */



//                 ,             。

//           showPage       

// DirectoryProxy class, just the outline

var DirectoryProxy = function (parent) {

};

DirectoryProxy.prototype = {

    showPage: function (page) {

    }

};



//                    ,         

//            :

// DirectoryProxy class, as a useless proxy

var DirectoryProxy = function (parent) {

    this.directory = new PersonnelDirectory(parent);

};

DirectoryProxy.prototype = {

    showPage: function (page) {

        return this.directory.showPage(page);

    }

};



/*

           PersonnelDirectory     。         。  ,                    。           ,                ,                      

 */

// DirectoryProxy class, as a virtual proxy

var DirectoryProxy = function (parent) {

    this.parent = parent;

    this.directory = null;

    var that = this;

    addEvent(parent, 'mouseover', function () {

        that._initialize();

    });

};

DirectoryProxy.prototype = {

    _initialize: function () {

        this.directory = new PersonnelDirectory(this.parent);

    },

    showPage: function (page) {

        return this.directory.showPage(page);

    }

};

/*

     ,                   ,        。          ,                 ,             ,                。

            。         ,                 ,                   :

 */

// DirectoryProxy class, with loading message

var DirectoryProxy = function (parent) {

    this.parent = parent;

    this.directory = null;

    this.warning = null;

    this.interval = null;

    this.initialized = false;

    var that = this;

    addEvent(parent, 'mouseover', function () {

        that._initialize();

    });

};

DirectoryProxy.prototype = {

    _initialize: function () {

        this.warning = document.createElement('div');

        this.parent.appendChild(this.warning);

        this.warning.innerHTML = 'The company directory is loading...';



        this.directory = new PersonnelDirectory(this.parent);

        var that = this;

        this.interval = setInterval(function () {

            that._checkInitialization();

        }, 100);

    },

    _checkInitialization: function () {

        if (this.directory.currentPage !== null) {

            clearInterval(this.interval);

            this.initialized = true;

            this.parent.removeChild(this.warning);

        }

    },

    showPage: function (page) {

        if (!this.initialized) {

            return;

        }

        return this.directory.showPage(page);

    }

};

/*

    showPage      ,       initialized  ,     true               。    ,                       。                  ?                  ,           ,                    。  PersonnelDirectory   currentPage                  ,      100          ,             。                     。

 */





/**

 *            

 *

 *    JS    ,             ,             ,         ,                         。     ,                 _initialize _checkInitialization     。       ,                   。

 */

// DynamicProxy abstract class, incomplete

var DynamicProxy = function () {

    this.args = arguments;

    this.initialized = false;

};

DynamicProxy.prototype = {

    _initialize: function () {

        // Instantiate the class

        /*

         this.subject = {};

         this.class1.apply(this.subject, this.args);

         this.subject.__proto__ = this.class1.prototype;

         */

        this.subject = new this.class1(this.args);



        var that = this;

        this.interval = setInterval(function () {

            that._checkInitialization();

        }, 100);

    },

    _checkInitialization: function () {

        if (this._isInitialized()) {

            clearInterval(this.interval);

            this.initialized = true;

        }

    },

    _isInitialized: function () {

        throw new Error('Unsupported operation on an abstract class.');

    }

};



//    

// DynamicProxy abstract class, complete

var DynamicProxy = function () {

    this.args = arguments;

    this.initialized = false;



    if (typeof this.class1 !== 'function') {

        throw new Error('DynamicProxy: tha class attribute must be set before calling the super-class constructor.');

    }



    // Create the methods needed to implement the same interface.

    for (var key in this.class1.prototype) {

        // Ensure that the property is a function

        if (this.class1.prototype.hasOwnProperty(key)) {

            if (typeof this.class1.prototype[key] !== 'function') {

                continue;

            }



            // Add the method

            var that = this;

            (function (methodName) {

                that[methodName] = function () {

                    if (!that.initialized) {

                        return;

                    }

                    return that.subject[methodName].apply(that.subject, arguments);

                };

            }(key));

        }

    }

};

DynamicProxy.prototype = {

    _initialize: function () {

        // Instantiate the class

        /*

         this.subject = {};

         this.class1.apply(this.subject, this.args);

         this.subject.__proto__ = this.class1.prototype;

         */

        this.subject = new this.class1(this.args);



        var that = this;

        this.interval = setInterval(function () {

            that._checkInitialization();

        }, 100);

    },

    _checkInitialization: function () {

        if (this._isInitialized()) {

            clearInterval(this.interval);

            this.initialized = true;

        }

    },

    _isInitialized: function () {

        throw new Error('Unsupported operation on an abstract class.');

    }

};

/*

        ,        prototype          ,              。              ,          ,                              。                      ,         ,              ,              。



      ,         :

 */



// TestProxy class

var TestClass = function () {

    console.log('start');

};

TestClass.prototype = {

    f1: function () {

        console.log(1);

    },

    f2: function () {

        console.log(2);

    }

};

var TestProxy = function () {

    this.class1 = TestClass;

    var that = this;

    addEvent($('test-link'), 'click', function () {

        that._initialize();

        that.f1();

    });

    TestProxy.superclass.constructor.apply(this, arguments);

};

extend(TestProxy, DynamicProxy);

TestProxy.prototype._isInitialized = function () {

    //...

    // Initialization condition goes here

    if (!this.initialized) {

        for (var i = 0, s = ''; i < 10; i++) {

            s += '<li>' + i + '</li>';

        }

        document.getElementById('content').innerHTML += '<ul>' + s + '</ul>';

        return true;

    }

    return false;

};



var p = new TestProxy();



/*

          4 ; this.class      ;          (                   );         ;  _isInitialized  (               true false)

 */



/**

 *      

 *

 *                              。                ,           。            ,          ,             。

 *

 *       

 *

 *              ,              。              。              ,              。              ,             

 */



/* Title: Proxy

 Description: provides a placeholder for another object to control access, reduce cost, and reduce complexity

 */



var $ = function (id) {

    return document.getElementById(id);

};



var http = {

    makeRequest:function (ids, callback) {

        var url = 'http://query.yahooapis.com/v1/public/yql?q=',

                sql = 'select * from music.video.id where ids IN ("%ID%")',

                format = "format=json",

                handler = "callback=" + callback,

                script = document.createElement('script');



        sql = sql.replace('%ID%', ids.join('","'));

        sql = encodeURIComponent(sql);



        url += sql + '&' + format + '&' + handler;

        script.src = url;



        document.body.appendChild(script);

    }

};



var proxy = {

    ids:[],

    delay:50,

    timeout:null,

    callback:null,

    context:null,

    makeRequest:function (id, callback, context) {



        // add to the queue

        this.ids.push(id);



        this.callback = callback;

        this.context = context;



        // set up timeout

        if (!this.timeout) {

            this.timeout = setTimeout(function () {

                proxy.flush();

            }, this.delay);

        }

    },

    flush:function () {

        http.makeRequest(this.ids, 'proxy.handler');

        // clear timeout and queue

        this.timeout = null;

        this.ids = [];



    },

    handler:function (data) {

        var i, max;



        // single video

        if (parseInt(data.query.count, 10) === 1) {

            proxy.callback.call(proxy.context, data.query.results.Video);

            return;

        }



        // multiple videos

        for (i = 0, max = data.query.results.Video.length; i < max; i += 1) {

            proxy.callback.call(proxy.context, data.query.results.Video[i]);

        }

    }

};





var videos = {

    getPlayer:function (id) {

        return '' +

                '<object width="400" height="255" id="uvp_fop" allowFullScreen="true">' +

                '<param name="movie" value="http://d.yimg.com/m/up/fop/embedflv/swf/fop.swf"\/>' +

                '<param name="flashVars" value="id=v' + id + '&eID=1301797&lang=us&enableFullScreen=0&shareEnable=1"\/>' +

                '<param name="wmode" value="transparent"\/>' +

                '<embed ' +

                'height="255" ' +

                'width="400" ' +

                'id="uvp_fop" ' +

                'allowFullScreen="true" ' +

                'src="http://d.yimg.com/m/up/fop/embedflv/swf/fop.swf" ' +

                'type="application/x-shockwave-flash" ' +

                'flashvars="id=v' + id + '&eID=1301797&lang=us&ympsc=4195329&enableFullScreen=1&shareEnable=1"' +

                '\/>' +

                '<\/object>';

    },



    updateList:function (data) {

        var id,

                html = '',

                info;



        if (data.query) {

            data = data.query.results.Video;

        }

        id = data.id;

        html += '<img src="' + data.Image[0].url + '" width="50" \/>';

        html += '<h2>' + data.title + '<\/h2>';

        html += '<p>' + data.copyrightYear + ', ' + data.label + '<\/p>';

        if (data.Album) {

            html += '<p>Album: ' + data.Album.Release.title + ', ' + data.Album.Release.releaseYear + '<br \/>';

        }

        html += '<p><a class="play" href="http://new.music.yahoo.com/videos/--' + id + '">» play<\/a><\/p>';

        info = document.createElement('div');

        info.id = "info" + id;

        info.innerHTML = html;

        $('v' + id).appendChild(info);

    },



    getInfo:function (id) {

        var info = $('info' + id);



        if (!info) {

            proxy.makeRequest(id, videos.updateList, videos);

            return;

        }



        if (info.style.display === "none") {

            info.style.display = '';

        } else {

            info.style.display = 'none';

        }

    }

};





$('vids').onclick = function (e) {

    var src, id;



    e = e || window.event;

    src = e.target || e.srcElement;



    if (src.nodeName.toUpperCase() !== "A") {

        return;

    }



    if (typeof e.preventDefault === "function") {

        e.preventDefault();

    }

    e.returnValue = false;



    id = src.href.split('--')[1];



    if (src.className === "play") {

        src.parentNode.innerHTML = videos.getPlayer(id);

        return;

    }



    src.parentNode.id = "v" + id;

    videos.getInfo(id);

};



$('toggle-all').onclick = function (e) {



    var hrefs,

            i,

            max,

            id;



    hrefs = $('vids').getElementsByTagName('a');

    for (i = 0, max = hrefs.length; i < max; i += 1) {

        // skip play links

        if (hrefs[i].className === "play") {

            continue;

        }

        // skip unchecked

        if (!hrefs[i].parentNode.firstChild.checked) {

            continue;

        }



        id = hrefs[i].href.split('--')[1];

        hrefs[i].parentNode.id = "v" + id;

        videos.getInfo(id);

    }

};





// http://www.dofactory.com/javascript-proxy-pattern.aspx



function GeoCoder() {

    this.getLatLng = function(address) {

        

        if (address === "Amsterdam") {

            return "52.3700° N, 4.8900° E";

        } else if (address === "London") {

            return "51.5171° N, 0.1062° W";

        } else if (address === "Paris") {

            return "48.8742° N, 2.3470° E";

        } else if (address === "Berlin") {

            return "52.5233° N, 13.4127° E";

        } else {

            return "";

        }

    };

}



function GeoProxy() {

    var geocoder = new GeoCoder();

    var geocache = {};



    return {

        getLatLng: function(address) {

            if (!geocache[address]) {

                geocache[address] = geocoder.getLatLng(address);

            }



            log.add(address + ": " + geocache[address]);

            return geocache[address];

        },

        getCount: function() {

            var count = 0;

            for (var code in geocache) { count++; }

            return count;

        }

    };

};



// log helper

var log = (function() {

    var log = "";

    return {

        add: function(msg) { log += msg + "
"; }, show: function() { alert(log); log = ""; } } })(); function run() { var geo = new GeoProxy(); // geolocation requests geo.getLatLng("Paris"); geo.getLatLng("London"); geo.getLatLng("London"); geo.getLatLng("London"); geo.getLatLng("London"); geo.getLatLng("Amsterdam"); geo.getLatLng("Amsterdam"); geo.getLatLng("Amsterdam"); geo.getLatLng("Amsterdam"); geo.getLatLng("London"); geo.getLatLng("London"); log.add("
Cache size: " + geo.getCount()); log.show(); } </script> </body> </html>