Sunday, June 28, 2015

資產從一萬到一百億的經營智慧 (自助,人助 的最佳典範,看完 我真的懂了!)

臺北有一位建築商,

年輕時就以精明著稱於業內。

那時的他,雖然頗具商業頭腦,

做事也成熟幹練,但摸爬滾打許多 年,

事業不僅沒有起色,最後還以破產告終。

在那段失落而迷茫的日子裡,

他不斷地反思自己失敗的原因,

想破腦殼也找尋不到答案。

論才智,論勤奮,論計謀,

他都不遜於別人,為什麼有人成功了,

而他離成功越來越遠呢...?

百無聊賴的時候,

他來到街頭漫無目的地閑轉,

路過一家書報亭,就買了一份報紙隨便翻看。

看著看著,他的眼前豁然一亮,

報紙上的一段話如電光石火般擊中他的心靈。

後來,他以一萬元為本金,再戰商場。

這次,他的生意好像被施加了魔法,

從雜質鋪到水泥廠,從包工頭到建築商,

一路順風順順水,合夥夥伴趨之若鶩。

短短幾年內,他的資產就突飛猛進到一億元,

創造了一個商業神話。

有很多記者追問他東山再起的秘訣...

他只透露四個字:只拿六分

又過了幾年,他的資產如滾雪般越來越大,

達到一百億元。

有一次,他來到大學演講,期間不斷有學生提問,

問他從一萬元變成一百億元到底有何秘訣。

他笑著回答,因為我一直堅持少拿兩分。

學生們聽得如墜雲裡霧裡。

望著學生們渴望成功的眼神,

他終於說出一段往事。

他說,當年在街頭看見一篇採訪李澤楷的文章,

讀後很有感觸。

記者問李澤楷:

“你的父親李嘉誠究竟教會了你怎樣的賺錢秘訣?”

李澤楷說:“父親從沒告訴我賺錢的方法,

只教了我一些做人處事的道理。”

記者大驚,不信。

李澤楷又說:“父親叮囑過,你和別人合作,

假如你拿七分合理,八分也可以,

那我們李家拿六分就可以了。”

說到這裡,他動情地說,

這段採訪我看了不下一百遍,

終於弄明白一個道理:

做人最高的境界是 "厚道",

所以精明的最高境界也是 "厚道"

細想一下就知道,李嘉誠總是讓別人多賺兩分,

所以,每個人都知道和他合作會佔便宜,

就有更多 的人願意和他合作。

如此一來,雖然他只拿六分,

生意卻多了一百個,假如拿八分的話,

一百個會變成五個。

到底哪個更賺呢?奧秘就在其中。

我最初犯下的最大錯誤就是....

我最初犯下的最大錯誤就是 過於精明,

總是千方百計地從對方身上多賺錢,

以為賺得越多,就越成功,

結果是,多賺了眼前,輸掉了未來。

演講結束後,他從包裡掏出一張泛黃的報紙,

正是報導李澤楷的那張,多年來,他一直珍藏著。

報紙的空白處,有一 行毛筆書寫的小楷:

七分合理,八分也可以,那我只拿六分。

這位建築商就是臺北全盛房地產開發公司董事長林正家。

他說,這就是一百億的起點。

個人發展的可持續觀就是合作共贏。

小勝靠智,大勝靠德,厚積薄發,氣勢如虹。

只懂追逐利潤,是常人所為;

更懂分享利潤,是超人所作。

人生百年,不可享盡世間所有榮華;

惠及百人,能夠得到人間更多真愛。

當我讀完這篇房產人物傳記時我深刻反省自己,

也警醒自己要做一個厚道而非精明的人。

給別人借過時實際是在給自己修路,厚道的人,

人生路總是很寬很長…厚德載物啊!

Reference:

http://tw.anyelse.com/article/17631.html#006

Symfony web debug toolbar does not show up

The toolbar inserts itself in pages by looking for a terminating tag on your generated page.

If you don't have a tag in your page the toolbar will not appear.

Add the body block to your template, so that the web debug toolbar would show:

# vim src/Acme/DemoBundle/Resources/views/Default/index.html.twig

{% extends '::base.html.twig' %}

{% block body %}
  Hello {{ name }}
{% endblock %}

Reference:

http://stackoverflow.com/questions/19147502/symfony-web-debug-tool-bar
http://blog.ijun.org/2015/06/symfony-toolbar-error-occurred-while.html

Saturday, June 27, 2015

Symfony toolbar an error occurred while loading the web debug toolbar 403 forbidden

an error occurred while loading the web debug toolbar 403 forbidden

http://symfony.cent-dev.local/app_dev.php/_wdt/7eadee

[Sat Jun 27 22:06:03.426819 2015] [proxy_fcgi:error] [pid 42941:tid 140100818134784] [client 192.168.6.112:52086] AH01071: Got error 'Access to the script '/var/www/html/test_symfony/myproject/web/app_dev.php/_wdt/d13d9a' has been denied (see security.limit_extensions)\n', referer: http://symfony.cent-dev.local/app_dev.php

Solution:

# vim /etc/php.ini

cgi.fix_pathinfo = 1

or just comment it out to use the default.

; cgi.fix_pathinfo = 1

How to install SSHFS on CentOS 7

# yum install sshfs

# mkdir /var/mnt

# sshfs root@192.168.0.10:/ /var/mnt/

# umount /var/mnt/

Internal Dummy Connection

Requests From the Server to Itself

When the Apache HTTP Server manages its child processes, it needs a way to wake up processes that are listening for new connections. To do this, it sends a simple HTTP request back to itself. This request will appear in the access_log file with the remote address set to the loop-back interface (typically 127.0.0.1 or ::1 if IPv6 is configured). If you log the User-Agent string (as in the combined log format), you will see the server signature followed by "(internal dummy connection)" on non-SSL servers. During certain periods you may see up to one such request for each httpd child process.
These requests are perfectly normal and you do not, in general, need to worry about them. They can simply be ignored.
If you wish to exclude them from your log, you can use normal conditional-logging techniques. For example, to omit all requests from the loopback interface from your logs, you can use
SetEnvIf Remote_Addr "127\.0\.0\.1" loopback
and then add env=!loopback to the end of your CustomLog directive.
In 2.2.6 and earlier, in certain configurations, these requests may hit a heavy-weight dynamic web page and cause unnecessary load on the server. You can avoid this by using mod_rewrite to respond with a redirect when accessed with that specific User-Agent or IP address.

SSL Considerations

The internal dummy connections are not capable of speaking SSL. Thus, on servers with SSL enabled, these requests may generate noise in the server error log similar to the following:
[info] [client ::1] Connection to child 6 established (server localhost:443)
[info] Seeding PRNG with 656 bytes of entropy
[info] [client ::1] SSL library error 1 in handshake (server localhost:443)
[info] SSL Library Error: 336027900 error:140760FC:SSL routines:SSL23_GET_CLIENT_HELLO:unknown protocol speaking not SSL to HTTPS port!?
[info] [client ::1] Connection closed to child 6 with abortive shutdown (server localhost:443)
You can work around this by ensuring that the last Listen directive in your server configuration is not using SSL. In a typical setup, this would mean that "Listen 443" would come before "Listen 80".
This workaround should cause the internal dummy connections to be made to the non-SSL port, where you can optionally filter them out using the suggestions above.

In a future release, the server will attempt to connect to a non-SSL port by default. This change has been committed to svn, but not yet released (as of this writing).

Reference:

http://wiki.apache.org/httpd/InternalDummyConnection

Monday, June 22, 2015

FAILED ERROR: Magento is already installed

We get this error when re-trying to install magento. This happens because magento stores the log of the previous installation date and before carrying out installation process again it checks for previous installations and it does so by checking the date which is stored inside the xml file.

Solution 1:

delete the app/etc/local.xml file entirely.

Solution 2:

Edit the app/etc/local.xml:

Change:

<date><![CDATA[Tue, 15 Mar 2011 15:14:24 +0000]]></date>

To:

<date><![CDATA[]]></date>

Reference:

http://www.samundra.com.np/solved-failed-error-magento-is-already-installed/439

Friday, June 19, 2015

To prevent the right click menu show up

Method 1:

<div id="dialogDiv" oncontextmenu="return false;"></div>

Method 2:

$('#dialogDiv').on('contextmenu', function(e){
  e.preventDefault();
});

The difference between return false; and e.preventDefault();

function() {
  return false;
}

// IS EQUAL TO

function(e) {
  e.preventDefault();
  e.stopPropagation();
}

Wednesday, June 10, 2015

How to prevent events from being bound multiple times

How to prevent events from being bound multiple times

If your Single Page Application is slow or have large DOMs, removing a previously-attached event handler from the elements may help to prevent it being bound twice or few times.

.off( events [, selector ] [, handler ] )

Remove an event handler.

.empty()

Remove all child nodes of the set of matched elements from the DOM.

.remove( [selector ] )

Remove the set of matched elements from the DOM.

Similar to .empty(), the .remove() method takes elements out of the DOM. Use .remove() when you want to remove the element itself, as well as everything inside it. In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed. To remove the elements without removing data and events, use .detach() instead.

.detach( [selector ] )

Remove the set of matched elements from the DOM.

Looping through the direct children:

$("#div1").children().off();
$("#div1").children().empty();

or

$("#div1").children().remove();

Looping through all descendants:

$("#div1").find('*').off();
$("#div1").find('*').empty();

or

$("#div1").remove();

this example .off() is called immediately to ensure previous events (of any kind) with the namespace are un-bound:

$("form")
    .off(".validator")
    .on("keypress.validator", "input[type='text']", validate);

It's quite a flexible API so you can be very specific if you need to be:

$("form")
    .off("keypress.validator", "input[type='text']", validate)
    .on("keypress.validator", "input[type='text']", validate);

Reference:

http://stackoverflow.com/questions/23043234/jquery-off-remove-or-remove-off-performance
http://stackoverflow.com/questions/968949/how-to-prevent-events-from-being-bound-multiple-times

Google Maps inside of jQuery Dialog Window

<html>
<head>
  <link rel="stylesheet" type="text/css" href="/javascript/jquery-ui/jquery-ui-1.11.4/jquery-ui.min.css" />
  <script src="https://maps.googleapis.com/maps/api/js"></script>
  <script src="/javascript/google_map/markerclusterer_compiled.js"></script>
  <script src="/javascript/jquery-ui/jquery-ui-1.11.4/external/jquery/jquery.js"></script>
  <script src="/javascript/jquery-ui/jquery-ui-1.11.4/jquery-ui.min.js"></script>
  <script>
    var myMap = {
      map: null,
      mapCenter: null,
      markerArr: [],
      markerCluster: null,
      markerClusterOptions: {
        gridSize: 50,
        maxZoom: 21, // to make sure the marker will not show up.
        minimumClusterSize: 1, // to make sure the marker will not show up.
        styles: [],
      },
      infoWindow: null,
    };

    // Initializing Google Map
    myMap.init = function(optObj) {
      if (!myMap.map) {
        myMap.map = new google.maps.Map(document.getElementById(optObj.mapDiv), {
          zoom: optObj.mapZoom,
          center: optObj.mapCenter,
          mapTypeId: google.maps.MapTypeId.ROADMAP,
        });
        myMap.mapCenter = optObj.mapCenter;
        myMap.infoWindow = new google.maps.InfoWindow();

        // zoom changed listener.
        google.maps.event.addListener(myMap.map, 'zoom_changed', function() {
          var zoomLevel = myMap.map.getZoom();
          //console.log(zoomLevel);
        });

        // Setting Cluster Style.
        // The number of styles to add depends on how many the icon (index) would have in the myMap.markerClusterCalculator().
        var imgSize = [53, 56, 66, 78, 90];

        for (var i = 0; i < imgSize.length; i++) {
          myMap.markerClusterOptions.styles.push({
            url: '/javascript/google_map/images/m' + (i + 1) + '.png?time=1',
            height: imgSize[i],
            width: imgSize[i],
            //anchor: [26, 0],
            textColor: '#ffffff',
            //textSize: 12,
          });
        }
      }
    };

    // A custom calculator for Marker Cluster.
    // It will show the sales difference instead of markers count.
    myMap.markerClusterCalculator = function(markers, numStyles) {
      var index = 0;
      var count = 0;

      for (var i = 0; i < markers.length; i++) {
        count += markers[i].salesDiff;
      }

      if (count > 0) {
        index = 1; // Green
      }
      else if (count > -10) {
        index = 2; // Yellow
      }
      else if (count > -20) {
        index = 3; // Orange
      }
      else if (count > -30) {
        index = 4; // Purple
      }
      else {
        index = 5; // Red
      }

      return {
        text: count,
        index: index
      };
    };

    // Add a marker to the map and push to the array.
    myMap.addMarker = function(optObj) {
      var marker = new google.maps.Marker({
        map: myMap.map,
        position: new google.maps.LatLng(optObj.lat, optObj.lng),
        icon: myMap.getIcon(optObj.salesDiff),
        dealerName: optObj.dealerName,
        salesDiff: optObj.salesDiff,
        lastInvoiceDate: optObj.lastInvoiceDate,
      });

      // mouseover
      google.maps.event.addListener(marker, 'mouseover', myMap.setInfoWindowContent_v2(myMap.infoWindow));

      // mouseout
      //google.maps.event.addListener(marker, 'mouseout', function(){
      //  myMap.infoWindow.close();
      //});

      // click
      google.maps.event.addListener(marker, 'click', function(){
        console.log($(this).attr('dealerName'));
      });

      myMap.markerArr.push(marker);

      // marker list div.
      var item = document.createElement('DIV');
      var title = document.createElement('A');
      title.href = '#';
      title.className = 'title';
      title.innerHTML = optObj.dealerName;

      item.appendChild(title);
      $('#markerListDiv').append(item);

      google.maps.event.addDomListener(title, 'click', myMap.setAllMarkerListItems({
        dealerName: optObj.dealerName,
        lat: optObj.lat,
        lng: optObj.lng,
      }));
    };

    // Add all markers.
    myMap.addAllMarkers = function(dataArr) {
      myMap.deleteAllMarkers();

      for (var i = 0; i < dataArr.length; i++) {
        // I initialize the lat and lng to zero for the data points. So, omit it.
        if (dataArr[i].lat === 0 && dataArr[i].lng === 0) {
          continue;
        }

        myMap.addMarker({
          lat: dataArr[i].lat,
          lng: dataArr[i].lng,
          dealerName: dataArr[i].dealerName,
          salesDiff: dataArr[i].salesDiff,
          lastInvoiceDate: dataArr[i].lastInvoiceDate,
        });
      }

      myMap.markerCluster = new MarkerClusterer(myMap.map, myMap.markerArr, myMap.markerClusterOptions);

      myMap.markerCluster.setCalculator(myMap.markerClusterCalculator);
    };

    // Removes the markers from the map, but keeps them in the array.
    myMap.clearAllMarkers = function() {
      for (var i = 0; i < myMap.markerArr.length; i++) {
        myMap.markerArr[i].setMap(null);
      }
    };

    // Shows any markers currently in the array.
    myMap.showAllMarkers = function() {
      myMap.setAllMarkers();
    };

    // Sets the map on all markers in the array.
    myMap.setAllMarkers = function() {
      for (var i = 0; i < myMap.markerArr.length; i++) {
        myMap.markerArr[i].setMap(myMap.map);
      }
    };

    // Deletes all markers in the array by removing references to them.
    myMap.deleteAllMarkers = function() {
      if (myMap.markerCluster) {
        myMap.markerCluster.clearMarkers();
        myMap.markerCluster = null;
      }

      myMap.clearAllMarkers();
      myMap.markerArr = [];
    };

    myMap.setInfoWindowContent_v2 = function(infowindow) {
      return function() {
        var color = myMap.diffColor(this.salesDiff);

        myMap.infoWindow.setContent(''
          + '<b>' + this.dealerName + '</b>'
          + '<br>'
          + '<span style="color: ' + myMap.diffColor(this.salesDiff) + ';">'
            + '<b>Last 1M vs Last 2M Diff: ' + this.salesDiff + '</b>'
          + '</span>'
          + '<br>Last Invoice: ' + this.lastInvoiceDate
        );
        myMap.infoWindow.open(this.map, this);
      }
    };

    // Add listener to marker list items.
    myMap.setAllMarkerListItems = function(optObj) {
      return function(e) {
        e.cancelBubble = true;
        e.returnValue = false;
        if (e.stopPropagation) {
          e.stopPropagation();
          e.preventDefault();
        }

        var latLngObj = new google.maps.LatLng(optObj.lat + 0.004, optObj.lng);

        myMap.map.setCenter(latLngObj);
        myMap.map.setZoom(13);
        myMap.infoWindow.setContent(optObj.dealerName);
        myMap.infoWindow.setPosition(latLngObj);
        myMap.infoWindow.open(myMap.map);
      };
    };

    // icon
    myMap.getIcon = function(num) {
      var iconURL = 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=';
      var iconTxt = 'D';

      if (num > 0) {
        iconTxt = 'U';
      }

      return iconURL + iconTxt + '|' + myMap.diffColor(num) + '|000000';
    };

    // Get color by sales number.
    myMap.diffColor = function(num) {
      if (num > 0) {
        return '33CC00'; // green
      }
      else if (num == 0) {
        return '000000'; // black
      }
      else if (num >= -20) {
        return 'FF9933'; // orange
      }
      else {
        return 'FF0000'; // red
      }
    };

    $(document).ready(function(){
      $('#myDialogDiv').dialog({
        title: 'test Google Map API',
        autoOpen: false,
        resizable: true,
        draggable: true,
        show: {effect: 'drop', direction: "up"},
        hide: {effect: 'slide', direction: 'up'},
        width: 800,
        height: 500,
        resizeStop: function (event, ui) {
          // this line helps to prevent showing the incomplete map.
          google.maps.event.trigger(myMap.map, 'resize');
        },
        open: function (event, ui) {
          // this line helps to prevent showing the incomplete map.
          google.maps.event.trigger(myMap.map, 'resize');
        },
        close: function (event, ui) {
          $('#markerListDiv').html('');
        },
      });

      $('#showMapBtn').on('click', function(e){
        e.preventDefault();

        $.getJSON('/javascript/google_map/example/jquery_dialog/jquery_dialog_data.php', [], function(response) {
          myMap.addAllMarkers(response);

          $('#myDialogDiv').dialog('open');
        });
      });

      myMap.init({
        mapDiv: 'myMapDiv',
        mapZoom: 3,
        mapCenter: new google.maps.LatLng(57.4519, -162.1419),
      });
    });
  </script>
</head>
<body>
  <p>This example shows Google Maps JavaScript API inside of jQuery Dialog Window. If the map was not showing up incompletely, there are two important considerations here:</p>
  <ul>
    <li>1. Ensure that all the javascript/jQuery is included on the parent page. Don't try to deliver the js via AJAX.</li>
    <li>2. Ensure that the map is initialized only when the canvas is visible. Initializing an invisible canvas is only ever partially successful.</li>
  </ul>

  <p>A marker clustering library for the Google Maps JavaScript API v3:</p>
  <a href="https://github.com/googlemaps/js-marker-clusterer">https://github.com/googlemaps/js-marker-clusterer</a>

  <p>Some functions in markerclusterer.jsyou may be interested in:</p>
  <ul>
    <li>Cluster.prototype.updateIcon</li>
    <li>ClusterIcon.prototype.setSums</li>
    <li>ClusterIcon.prototype.useStyle</li>
    <li>MarkerClusterer.prototype.calculator_</li>
    <li>MarkerClusterer.prototype.setupStyles_</li>
  </ul>

  <p><b>Reference:</b></p>
  <a href="http://stackoverflow.com/questions/16547779/google-maps-javascript-api-inside-of-jquery-dialog-window">http://stackoverflow.com/questions/16547779/google-maps-javascript-api-inside-of-jquery-dialog-window</a>


  <div id="panel" style="margin-top: 30px;">
    <input id="showMapBtn" type=button value="Show Map">
    <input onclick="myMap.clearAllMarkers();" type=button value="Hide Markers">
    <input onclick="myMap.showAllMarkers();" type=button value="Show Markers">
    <input onclick="myMap.deleteAllMarkers();" type=button value="Delete Markers">
  </div>

  <div id="markerListDiv"></div>

  <div id="myDialogDiv" style="display: none;">
    <div id="myMapDiv" style="width: 700px; height: 400px;"></div> 
  </div>
</body>
</html>

Single Page Application - Large DOM - SLOW

Ok so I have reworked my code so that when a tab is closed, all widgets are destroyed, all handlers created by .on() are removed with .off() and the containing DIV is emptied with .empty(). This has DRAMATICALLY improved the performance alone.

jQuery will do the looping for you for just the direct children:

$("#div1").children().off();
$("#div1").children().empty();
$("#div1").off();
$("#div1").empty();

Or if you want all descendants:

$("#div1").find('*').off();
$("#div1").find('*').empty();
$("#div1").off();
$("#div1").empty();

Or simply:

$('#div1').remove();

Note: similar to .empty(), the .remove() method takes elements out of the DOM. Use .remove() when you want to remove the element itself, as well as everything inside it. In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed. To remove the elements without removing data and events, use .detach() instead.

Reference:

http://stackoverflow.com/questions/18907584/single-page-application-large-dom-slow
http://www.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/
http://stackoverflow.com/questions/13945025/jquery-remove-all-event-handlers-inside-element
https://api.jquery.com/remove/

Tuesday, June 9, 2015

List all bindings of an element (with jQuery)

<html>
  <head>
    <script src="jquery-1.11.3.min.js"></script>
    <script>
      $(document).ready(function() {
        $('.MyElement').on('click', function(e){
          e.preventDefault();
          console.log('ok 1');
        });

        $('.MyElement').on('click', function(e){
          e.preventDefault();
          console.log('ok 2');
        });

        console.log('Number of element in this selector: ' + $('.MyElement').get().length);

        $.each($._data($('.MyElement').get(0), 'events'), function(i, e) {
          console.log(i, e);
        });
      });
    </script>
  </head>
  <body>
    <a href="#" class="MyElement">test 1</a><br>
    <a href="#" class="MyElement">test 2</a><br>
    <a href="#" class="MyElement">test 3</a><br>
  </body>
</html>

Reference:

http://stackoverflow.com/questions/4138543/list-all-bindings-of-an-element-with-jquery
http://stackoverflow.com/questions/2518421/jquery-find-events-handlers-registered-with-an-object
http://blog.ijun.org/2014/11/quickly-finding-and-debugging-jquery.html