tag:blogger.com,1999:blog-3353151914192513902024-03-13T10:46:31.863+08:00Hew's dev notesWeb development, and other nerdary.Edward Hewhttp://www.blogger.com/profile/17330787150106524106noreply@blogger.comBlogger7125tag:blogger.com,1999:blog-335315191419251390.post-34530639250122509792012-12-30T12:59:00.001+08:002013-08-08T22:57:44.109+08:00Convert RGB values, Hex color codes to percentConvert 8-bit RGB channel values (0-255) or Hex color code to percentage values (0-1.0f) and back again.<br />
<br />
<br />
<div id="color-swatch" style="width:320px; height:20px; border:1px solid #ccc"></div><form method="get" action="" id="converter"><dl> <dt><label for="rgbSrc">From R G B</label></dt>
<dd><input type="text" size="15" id="rgbSrc" /> <input type="button" value="Convert" id="convertFromRgbInt" />
<br /><small>ex: 255 105 51</small></dd>
<dt><label for="hexSrc">Or Hex color</label></dt>
<dd><input type="text" size="15" id="hexSrc" /> <input type="button" value="Convert" id="convertFromHex" />
<br /><small>ex: 3eccff</small></dd>
<dt><label for="result">Result</label></dt><dd>
<input type="checkbox" id="type-float" value="1" /> <label for="type-float">type float</label><br />
<input type="text" id="digits" size="3" /> (number of digits) <br />
<textarea col="30" rows="4" id="result"></textarea> <input type="button" value="Convert from Percentage" id="convertPercToRgbAndHex" /><br/>
<small>ex: (each value in a new line)<br/>
0.905882<br/>
0.129412<br/>
0.623529</small><br />
</dd>
</dl></form><br />
<script type="text/javascript">
var digitsLast = 0;
//alert((16).toString(16));
function start() {
// If convert button was clicked
document.getElementById('convertFromRgbInt').onclick = function(e) {
convert(1, 0);
};
// If enter key was pressed
document.getElementById('rgbSrc').onkeypress = function(e) {
var unicode = e.charCode? e.charCode : e.keyCode;
if (unicode == 13)
convert(1, 0);
};
document.getElementById('convertFromHex').onclick = function(e) {
convert(0, 1);
};
document.getElementById('hexSrc').onkeypress = function(e) {
var unicode = e.charCode? e.charCode : e.keyCode;
if (unicode == 13)
convert(0, 1);
};
document.getElementById('convertPercToRgbAndHex').onclick = function(e) {
convertPercToRgbAndHex();
};
document.getElementById('convertPercToRgbAndHex').onkeypress = function(e) {
var unicode = e.charCode? e.charCode : e.keyCode;
if (unicode == 13)
convertPercToRgbAndHex();
};
document.getElementById('type-float').onclick = function(e) {
if (this.checked) {
document.getElementById('digits').value = 7;
// Also recalculate, update result to 7 digit
convert(0, 1);
} else {
repopulateDigitsField();
}
};
document.getElementById('digits').onchange = function(e) {
digitsLast = document.getElementById('digits').value;
if (digitsLast > 7) {
document.getElementById('type-float').checked = false;
}
convert(0, 1);
};
}
window.onload = start;
function repopulateDigitsField() {
if (digitsLast === 0)
document.getElementById('digits').value = '';
else
document.getElementById('digits').value = digitsLast;
}
function numberOfDigits(n, digits)
{
if (!digits) {
if (n - Math.floor(n) == 0)
return n.toFixed(1);
else
return n;
}
if ((n + "").length > digits) {
return n.toFixed(digits - 1);
} else {
if (n - Math.floor(n) == 0)
return n.toFixed(1);
else
return n;
}
return NaN;
}
function rgbToPerc(rgbArray, precision, hexField)
{
var rgb = new Array();
var len = rgbArray.length;
var hex = '';
for(var i = 0; i < len; i++) {
rgb[i] = rgbArray[i] / 255;
hex += toHex(rgbArray[i] - 0);
}
hexField.value = hex;
return rgb;
}
function hexToPerc(src, precision, rgbIntField)
{
src = src.toLowerCase();
// Break hex code into parts
var rgb = new Array();
rgb[0] = src.slice(0,2);
rgb[1] = src.slice(2,4);
rgb[2] = src.slice(4,6);
// var range = "0123456789abcdef";
var rgbInt = 0;
var rgbPerc = 0.0;
var rgbIntParts = new Array();
for ( i = 0; i < 3; ++i ) {
rgbInt = parseInt(rgb[i], 16); // (range.indexOf(rgb[i].charAt(0)) * 16) + range.indexOf(rgb[i].charAt(1));
rgbIntParts[i] = rgbInt;
rgb[i] = rgbInt / 255;
}
rgbIntField.value = rgbIntParts.join(' ');
return rgb;
}
function fillColorSwatch(rgb) {
document.getElementById("color-swatch").style.backgroundColor = "#"+rgb;//"rgb("+rgb[0]+","+rgb[1]+","+rgb[2]+")";
}
function toHex(value)
{
var hex = '';
if ((value+"").length <= 2) {
if (value < 16) {
hex = '0' + (value).toString(16);
} else {
hex = (value).toString(16);
}
} else {
hex = (value).toString(16);
}
return hex;
}
function convert(fromRgbInt, fromHex) {
var numOfDigits = document.getElementById("digits").value;
var rgbSrcField = document.getElementById("rgbSrc");
var hexSrcField = document.getElementById("hexSrc");
var result = document.getElementById("result");
var typeFloat = document.getElementById("type-float");
var digits = document.getElementById("digits");
var appendChar = '', rgb = new Array();
if (fromRgbInt) {
var rgbParts = rgbSrcField.value.split(" ");
rgb = rgbToPerc(rgbParts, numOfDigits, hexSrcField);
} else if (fromHex) {
rgb = hexToPerc(hexSrcField.value, numOfDigits, rgbSrcField);
}
if (typeFloat.checked) {
appendChar = "f";
//digits.value = 7;
} else {
repopulateDigitsField();
}
fillColorSwatch(hexSrcField.value);
result.value = numberOfDigits(rgb[0], numOfDigits) + appendChar +"\n" + numberOfDigits(rgb[1], numOfDigits) + appendChar +"\n" + numberOfDigits(rgb[2], numOfDigits) + appendChar;
}
function convertPercToRgbAndHex () {
var percField = document.getElementById("result"); // its the "result" textarea
var percValue = percField.value;//.replace('f', '');
var asArray = percValue.split("\n");
var len = asArray.length;
var rgb = new Array();
var hex = '';
for (var i = 0; i < len; ++i) {
if (asArray[i]) {
rgb[i] = asArray[i].replace('f', '');
rgb[i] = Math.round(rgb[i] * 255);
hex += toHex(rgb[i]);
} else {
rgb[i] = 0;
hex += '??';
}
}
var rgbSrcField = document.getElementById("rgbSrc");
var hexSrcField = document.getElementById("hexSrc");
rgbSrcField.value = rgb.join(' ');
hexSrcField.value = hex;
fillColorSwatch(hex);
}
</script>Edward Hewhttp://www.blogger.com/profile/17330787150106524106noreply@blogger.com4tag:blogger.com,1999:blog-335315191419251390.post-29696323633726889302012-09-21T11:31:00.002+08:002013-11-10T12:18:25.096+08:00Make Siri use Celsius instead of Fahrenheit for her weather report on the iPad[UPDATE: Sept 2013] Since iOS7 came out this post became pretty much obsolete, because Siri will use your Region Format setting to determine which unit of measurement to use, pretty much means it'll just work. If you need a little reminder, its here: Settings > General > International > Region Format.<br />
<br />
On iOS6, to have Siri report temperature in Celcius, the setup is in a strange place, its actually in the iPad clock app.<br />
<ol>
<li>Launch the new Clock App on your iPad running iOS6.</li>
<li>Tap that Edit button on the top right.</li>
<li>On the pop-over you'll see Fahrenheit is selected, simply tap on Celsius.</li>
</ol>
Ask Siri again about the weather, she will now report the temperature in Celsius; excuse the Chinese words, those are simply city names.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-rHdsW7y8B0o/UFvbfGWAOkI/AAAAAAAAAHc/o1P727yvgjQ/s1600/clock_app_changing_temp_unit.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="http://3.bp.blogspot.com/-rHdsW7y8B0o/UFvbfGWAOkI/AAAAAAAAAHc/o1P727yvgjQ/s400/clock_app_changing_temp_unit.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">iOS6</td></tr>
</tbody></table>
Edward Hewhttp://www.blogger.com/profile/17330787150106524106noreply@blogger.com0tag:blogger.com,1999:blog-335315191419251390.post-31556484271944771262012-08-01T23:10:00.002+08:002013-12-25T14:13:07.914+08:00Chinese fonts included in OSX Mountain LionThere are a few nice additional Chinese typefaces in Mountain Lion, considering the cost of buying these fonts separately, upgrading to Mountain Lion is a tremendous bargain.<br />
<br />
Most of these new fonts are not enabled by default and will only start downloading when you enable them in Font Book. Speaking of which, Font Book is updated in Mountain Lion, the typeface sample layout is improved.<br />
<br />
I didn't include Japanese typefaces in OSX which are <a href="http://en.wikipedia.org/wiki/CJK_font" target="_blank">CJK fonts</a>, they include extensive Kanji characters or Traditional Chinese, but without Simplified Chinese characters.<br />
<br />
I'm not able to show the original size here, but you can <a href="https://picasaweb.google.com/105245843010468322282/HewSDevNotes?authkey=Gv1sRgCJ6_nbr2z7aZCw#5771718533587647810" target="_blank"><b>download the original size image here</b> (336 KB)</a>, choose "Actions" > "Download photo".<br />
<br />
<b>UPDATE</b>: Apple list of all the <a href="http://support.apple.com/kb/HT5484" target="_blank">fonts in iOS 6</a>, and interestingly what additional typefaces you can optionally install with your App, a lot of the fonts listed here is in the optional table.<br />
<br />
<b>UPDATE 2</b>: Mavericks added 2 additional Chinese typefaces, both hand written style and very good looking ones for both TC and SC versions.<br />
<br />
<span lang="zh-CN">苹果 OSX Mountain Lion 所具有的中文字型。不过大多需用者重 OSX Font Book App 里下载,绝对不难。以下图表<a href="https://picasaweb.google.com/105245843010468322282/HewSDevNotes?authkey=Gv1sRgCJ6_nbr2z7aZCw#5771718533587647810" target="_blank">最大版本下载</a>,请选 "Actions" 然后选单里单击"Download photo".</span><br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-7dqVdxD-zR0/UBlDbSoHvUI/AAAAAAAAAG8/63NbdzA_dWE/s1600/chinese-fonts-in-osx-ml-v1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="http://4.bp.blogspot.com/-7dqVdxD-zR0/UBlDbSoHvUI/AAAAAAAAAG8/63NbdzA_dWE/s640/chinese-fonts-in-osx-ml-v1.png" width="104" /></a></div>
Edward Hewhttp://www.blogger.com/profile/17330787150106524106noreply@blogger.com0tag:blogger.com,1999:blog-335315191419251390.post-25463749488913556392012-06-09T10:11:00.000+08:002012-11-29T12:45:13.643+08:00Kingston ValueRAM KVR1333D3S9/4G works on iMac 2011<img border="0" src="http://1.bp.blogspot.com/-t7gdCmyPHEc/T9VZWPdDpvI/AAAAAAAAAEo/yZw22ygtsoc/s1600/ram_casing_and_result.jpg" /><br />
<br />
<br />
Wasn't sure if these would work on the iMac, specs meets Apple's but I tend to worry. Totally unwarranted, installed in less than 5 minutes, iMac accepts it. So if you were wondering about this particular model of RAM, yeah they work!<br />
<div>
<br /></div>
<div>
<b>Ingredients:</b></div>
<div>
1x iMac 2011</div>
<div>
2x Kingston ValueRAM KVR1333D3S9/4G (These are 4GB modules)</div>
<div>
5 Minutes to install with prior research and preparation</div>
<div>
<br /></div>
<div>
Added 2 modules of these 4GB Kingston RAM to the existing factory 4GB on the iMac for a total of 12GB, yep, works with the existing modules.</div>
<div>
<br /></div>
<div>
<b>Technical skills required</b></div>
<div>
Basic skills, you'll have to discharge static from your hands, and make sure the RAM is installed correctly, the OWC video at the end of this post shows how its done, its pretty easy.</div>
<div>
<br /></div>
<div>
<b>Links:</b></div>
<div>
You can find details of this model of RAM on <a href="http://www.kingston.com/en/memory/valueram/notebook">this page on the Kingston website</a>.</div>
<div>
<br /></div>
<div>
<b>Official Memory Specs:</b></div>
<div>
<a href="http://support.apple.com/kb/HT3011">Apple's iMac 2011 Memory specs</a> (See User Installable Slot Column):</div>
<div>
<br /></div>
<div>
<b>Installation instructions:</b></div>
<div>
<a href="http://www.youtube.com/watch?v=k8Sj10wYXAI">OWC instructional video</a> (YouTube)</div>
<div>
<a href="http://support.apple.com/kb/HT1423?viewlocale=en_US#1">Apple memory removal and installation instructions</a></div>
<div>
<br /></div>
<div>
<br /></div>
Edward Hewhttp://www.blogger.com/profile/17330787150106524106noreply@blogger.com3tag:blogger.com,1999:blog-335315191419251390.post-76737761528088631282011-09-20T17:10:00.000+08:002014-12-11T22:26:33.141+08:00Using D-Link DSL-2750U's Storage Service feature<b>UPDATE</b>: My 2750U has kicked the bucket due lightning striking my house, quite a shame, while it wasn't much to look at, it performed very well, R.I.P little modem-router. And so dear reader, I am not able to try out any new firmware versions. That USB port on this model isn't a swift one, so if you can't see it or can't get it to work, my opinion, not something I'd lose sleep over. Have a great day. Love you, fellow human!<br />
<hr />
<br />
The manual doesn't explain this feature in detail, so here is a quick guide on using the <b>Storage Service</b> feature on D-Link DSL-2750U.<br />
<br />
<h3>
For firmware SE_1.01</h3>
You can see firmware version on the top right corner of the router control panel. On this version, Storage Service is much more direct, I like this a lot more. Here is how it can be accessed.<br />
<br />
<h3>
Windows</h3>
<br />
Open up any Windows Explorer window like My Computer for example and type the address of your D-Link router, by default its:<b> "</b><b>\\192.168.1.1</b>"<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-npegzDInrWo/UI4_tq3fGhI/AAAAAAAAAHw/DZ_jXgFMtHM/s1600/windows_access_dlink_dsl2750u.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-npegzDInrWo/UI4_tq3fGhI/AAAAAAAAAHw/DZ_jXgFMtHM/s200/windows_access_dlink_dsl2750u.png" height="193" width="200" /></a></div>
<br />
Use "admin" for both username and password (without quotes). I did not see anyway to change that, might as well because it gives the impression the drive is secured by user accounts, which is not the case.<br />
<br />
<h3>
Mac</h3>
<br />
Go to Finder > Go > Connect to Server ...<br />
<br />
And enter "<b>SMB:\\192.168.1.1</b>"<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-gTndCQbd540/UI5DP6cEpyI/AAAAAAAAAIA/bJEtyXJFcD8/s1600/SMB_to_router_usb.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-gTndCQbd540/UI5DP6cEpyI/AAAAAAAAAIA/bJEtyXJFcD8/s320/SMB_to_router_usb.png" height="149" width="320" /></a></div>
<br />
Use "admin" for both username and password (without quotes). I did not see anyway to change that, might as well because it gives the impression the drive is secured by user accounts, which is not the case. <br />
<br />
I got both read and write access now, where as on firmware 1.0 it was only read and delete access, I'm not entirely sure if its because I have Fuse and NTFS-3G installed, which are for native writing to NTFS drives. So I guess you'll have to try and see, apologies.<br />
<br />
(Everything below is for firmware SE_1.0)<br />
<br />
<br />
<h3>
For firmware SE_1.0</h3>
On firmware SE_1.0, Storage device is read and delete but not write access if accessed from a Macintosh; on Windows, you can both read and write to the drive. <br />
<br />
<h3>
Step 1</h3>
Plug in your USB device into the USB port at the back of the DSL-2750U, you should see the USB indicator light at the front panel light up when it's recognized.<br />
<br />
<h3>
Step 2</h3>
Go to the router's administration interface on your computer, access this on your web browser by typing in <b>http://192.168.1.1/</b><br />
<br />
<span class="Apple-style-span" style="color: #999999;">If you didn't update the admin password at all, then the login username and password is the default <b>admin</b> and <b>admin</b>. You can update this under: Maintenance > Access Controls.</span><br />
<br />
<h3>
Step 3</h3>
Now head to <b>Advanced</b> > <b>Storage Service</b>.<br />
<br />
There are two links here, click on <b>Storage Device Info</b> first, you'll see your USB device listed, make note of the <b>Volumename</b> field, like for example: <b>usb1_1</b>.<br />
<br />
<h3>
Step 4</h3>
This is the main step, head to <b>Advanced</b> > <b>Storage Service</b> > <b>User Accounts</b>, the screenshot below shows this page.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-o1z8m5vMtRw/TnhOz2ygQHI/AAAAAAAAAEM/6a0-ENCtUUQ/s1600/router_adv_sto_srv.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-o1z8m5vMtRw/TnhOz2ygQHI/AAAAAAAAAEM/6a0-ENCtUUQ/s640/router_adv_sto_srv.png" height="507" width="640" /></a></div>
<br />
Create a new user, this will be your account credential when you access your USB storage, create your username and password and fill in the <b>VolumeName</b> as noted earlier in<b> Step 3</b>. My Username is <i>sundaymorning</i> and the volumeName is <i>usb1_1</i>. Hit the Apply button and we are done with the admin interface.<br />
<br />
Accessing your storage device on both Windows and Mac is pretty easy too, here are the steps.<br />
<br />
<br />
<h2>
On Windows XP</h2>
Launch <b>Windows Explorer</b>, just any window basically and type in:<br />
<b>\\192.168.1.1\sundaymorning\usb1_1</b><br />
<br />
Note the pattern is: \\ Server Address \ Username \ Volume Name.<br />
<br />
You'll be prompted for your login credentials you created earlier in Step 4, click OK and that's it!<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://3.bp.blogspot.com/-IyFjZodcKIU/TnhOsQTt6YI/AAAAAAAAAD4/L7j83blMWNo/s1600/pc_connecting.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://3.bp.blogspot.com/-IyFjZodcKIU/TnhOsQTt6YI/AAAAAAAAAD4/L7j83blMWNo/s400/pc_connecting.png" height="400" width="367" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Type in the address any window's <b>address bar</b> (top), the login panel will appear. </td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-yhXewS6lnZw/TnhOyv771FI/AAAAAAAAAEA/SwoAyPFhAqU/s1600/browsing_folder_xp.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-yhXewS6lnZw/TnhOyv771FI/AAAAAAAAAEA/SwoAyPFhAqU/s320/browsing_folder_xp.png" height="192" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Once logged in successfully, you can start browsing the volume!</td></tr>
</tbody></table>
You can add this address to <i>Favorites</i> or you can go the more advanced route of Mapping the Network Drive, I'm not familiar with Mapping, so for convenience here is a <a href="http://www.google.com/search?q=windows+xp+mapping+a+network+drive">Google search result link</a> if that is what you want to do.<br />
<br />
<br />
<br />
<h2>
On OS X</h2>
<b>This doesn't work well with Macs. </b>On all media and formats I tried, I can read and copy files, even delete files, but could not write to it, the Finder error I get is along the lines of "no space available ..." on the device. Those media I tried were empty, and I can write to them via Windows without problem. Especially the FAT32 formatted USB thumb drive where I can write to it on the Mac if connected directly.<br />
<br />
<span class="Apple-style-span" style="color: #999999;">Snow Leopard was my first OS X, but I'm guessing its the same with earlier versions since it is using SMB, I could be wrong. Make sure <b>Finder</b> is selected then choose <b>Go </b>> <b>Connect to Server ... </b>(or Cmd+K), then type in:</span><br />
<b><span class="Apple-style-span" style="color: #999999;">smb://192.168.1.1/sundaymorning/usb1_1</span></b><br />
<span class="Apple-style-span" style="color: #999999;"><br />
</span><br />
<span class="Apple-style-span" style="color: #999999;">Note the pattern is: smb:// Server Address / Username / Volume Name.</span><br />
<span class="Apple-style-span" style="color: #999999;"><br />
</span><br />
<span class="Apple-style-span" style="color: #999999;">Enter the username and password you created in Step 4, click Connect and done!</span><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://1.bp.blogspot.com/-6G1imvhXrRs/TnhOzB-0rDI/AAAAAAAAAEE/5z3iFEPYAlM/s1600/mac_connecting.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://1.bp.blogspot.com/-6G1imvhXrRs/TnhOzB-0rDI/AAAAAAAAAEE/5z3iFEPYAlM/s320/mac_connecting.png" height="152" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Type the address here at, Finder menu > Go > Connect to Server ... <br />
then hit the Connect button</td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://4.bp.blogspot.com/-vCk9kgi6DkY/TnhOzZX40xI/AAAAAAAAAEI/j3KSxCw25Kw/s1600/mac_login.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://4.bp.blogspot.com/-vCk9kgi6DkY/TnhOzZX40xI/AAAAAAAAAEI/j3KSxCw25Kw/s320/mac_login.png" height="293" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Finally, type in your login credentials and hit the Connect button, <br />
if everything is set correctly, the file browser will appear, however it seems to be read only for Macs.</td></tr>
</tbody></table>
<br />
[Updated on Oct 29, 2012 added firmware 1.01 changes]Edward Hewhttp://www.blogger.com/profile/17330787150106524106noreply@blogger.com71tag:blogger.com,1999:blog-335315191419251390.post-61222728099425772022010-09-12T00:46:00.003+08:002014-02-22T15:28:18.348+08:00A dynamic sidebar implementation in Zend FrameworkThis is for displaying a dynamic set of small boxes or modules of content on a side column of a website, like maybe a box that displays a shopping cart summary, or a box that shows advertisement.<br />
<br />
The outline of this method is:<br />
- Declare an array of classes to run, each class represents a boxed module on the page.<br />
- Each module's data is collected as an associative array and sent to the view.<br />
- In the layout, loop through this data and then call each partial passing along the data set when there is one.<br />
<br />
Here is a picture of the outcome:<br />
<img border="0" src="http://2.bp.blogspot.com/_FWF3ThvVOnE/TIukmGzgnkI/AAAAAAAAADI/tuBI9GscqWs/s640/dynamic-side-column.png" height="231" width="520" /> <br />
<a name='more'></a><br />
<br />
Below is an example of 3 sidebar items to be loaded. <br />
<pre class="brush:php" name="code">$s = new App_Sidebar_Loader();
$this->view->sidebar = $s->load(array(
array('SomeAlert', 'hideEmpty'=> true),
array('FavsList'),
array('StaticContent', 'viewOnly'=>'true'),
));
</pre>
<br />
File locations:<br />
Library / App / Sidebar / Loader.php<br />
Library / App / Sidebar / Modules / SomeAlert.php<br />
Library / App / Sidebar / Modules / FavsList.php<br />
Library / App / Sidebar / Modules / StaticContent.php<br />
<br />
Partials (<a href="http://framework.zend.com/manual/1.11/en/zend.view.helpers.html#zend.view.helpers.initial.partial">Zend framework view helper</a>):<br />
View / script / Sidebar / SomeAlert.phtml<br />
View / script / Sidebar / FavsList.phtml<br />
View / script / Sidebar / StaticContent.phtml<br />
<br />
I didn't want to pre-process the view file names, like from "SomeAlert" to "some-alert.phtml", so I'll just stick to the camel casing for the partial file names.<br />
<br />
The Loader class currently supports 2 types of options:<br />
- "viewOnly": Load the view only, no class to execute.<br />
- "hideEmpty": Show module or not when there is no data for it.<br />
<br />
<pre class="brush:php" name="code">/**
* @author 2010 Edward Hew
* @license http://creativecommons.org/licenses/MIT/ Creative Commons MIT License
*
* Options accepted are:
* - viewOnly: boolean, when true grab only view script, no class to exec
* - hideEmpty: boolean, default false, don't show sidebar module id 'data' is empty
*/
class App_Sidebar_Loader
{
public function load($loadList){
$len = count($loadList);
for($i = 0; $i < $len; ++$i){
$sidebarHandle = $loadList[$i][0];
// viewOnly check
if(empty($loadList[$i]['viewOnly'])) {
$class = 'App_Sidebar_Modules_'.$sidebarHandle;
$c = new $class;
$runData = $c->run();
if (sizeof($runData) > 0)
$data[$sidebarHandle]['data'] = $runData;
else if (empty($loadList[$i]['hideEmpty'])) {
$data[$sidebarHandle]['data'] = null;
}
} else {
$data[$sidebarHandle]['data'] = null;
}
}
return $data;
}
}
</pre>
<br />
A module class looks like this:<br />
<br />
<pre class="brush:php" name="code">class App_Sidebar_Modules_FavsList
{
public function run() {
// Call model normally, but I'll just show a sample resulting array
$fav[] = array( 'id'=>1 , 'name'=> 'Blade Runner' );
$fav[] = array( 'id'=>2 , 'name'=> 'Alien' );
return $fav;
}
}
</pre>
<br />
For this module, the return value is an empty array on purpose<br />
<br />
<pre class="brush:php" name="code">/*
* To test loader option hideEmpty
*/
class App_Sidebar_Modules_SomeAlert
{
public function run() {
return array();
}
}
</pre>
<br />
Finally the view helper partial template is called in the layout like this:<br />
Location: layout / script / layout.phtml <br />
<pre class="brush:php" name="code">if($this->sidebar) {
foreach($this->sidebar as $key => $data){
echo $this->partial( ('sidebar/'.$key.'.phtml'), $data);
}
}
</pre>
<br />
That's all of it, uploaded a working ZF project to <a href="http://github.com/edwardhew/code-snippets/tree/master/Dynamic_Sidebar/">Github here</a> (apologies, its in the Branches link on that page). If you have any comments or criticism, please drop a comment. And thanks for visiting!Edward Hewhttp://www.blogger.com/profile/17330787150106524106noreply@blogger.com1tag:blogger.com,1999:blog-335315191419251390.post-80480691078117830512010-09-07T11:43:00.009+08:002013-04-01T13:45:09.481+08:00Select All Checkboxes With jQuery<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript">
</script> There are other ways to do this, but this way is pretty easy and it works nicely. As simple as it is though, I still tend to forget the syntax, so at least I'll know where to look when I forget. <br />
<br />
<form action="" id="formFooBar" method="post"><input id="select-all-jq" type="checkbox" /> <label for="select-all-jq">Select All with jQuery</label><br />
<input id="select-all-js" type="checkbox" /> <label for="select-all-js">Select All with JavaScript</label><br />
<hr /><input class="foo-list" name="bar[1]" type="checkbox" /> Some<br />
<input class="foo-list" name="bar[2]" type="checkbox" /> Checkboxes<br />
<input class="foo-list" name="bar[3]" type="checkbox" /> for testing<br />
<input class="foo-list" name="bar[4]" type="checkbox" /> script </form><script type="text/javascript">
$("#select-all-jq").click(function(){
if(this.checked) {
$(".foo-list").attr('checked','checked');
} else {
$(".foo-list").removeAttr('checked');
}
});
function SelectAll(formName, toggler, classNameOfList)
{
this.toggler = document.getElementById(toggler);
this.form = document.getElementById(formName);
this.classNameOfList = classNameOfList;
this.toggle = function (e) {
var i = 0, n = this.form.elements.length, targetChecked = null;
if(document.all) { // IE check
targetChecked = e.srcElement.checked;
} else {
targetChecked = e.target.checked;
}
for (i; i < n; i = i + 1) {
if (this.form.elements[i].className.indexOf(classNameOfList) !== -1) {
this.form.elements[i].checked = targetChecked;
}
}
};
}
var selectAllFooList = new SelectAll('formFooBar', 'select-all-js', 'foo-list');
if(document.all) { // IE Check
selectAllFooList.toggler.attachEvent('onclick', function(e) { selectAllFooList.toggle(e); });
} else {
selectAllFooList.toggler.addEventListener('click', selectAllFooList.toggle, false);
}
</script>
(Please note that blogger removes my closing forward slashes.)
<pre class="brush:javascript" name="code"><form action="" id="formFooBar" method="post">
<input id="select-all-jq" type="checkbox" /> <label for="select-all-jq">Select all with jQuery</label>
<input id="select-all-js" type="checkbox" /> <label for="select-all-js">Select all with JavaScript</label>
<hr />
<input class="foo-list" name="bar[1]" type="checkbox" /> Some
<input class="foo-list" name="bar[2]" type="checkbox" /> Checkboxes
<input class="foo-list" name="bar[3]" type="checkbox" /> for testing
<input class="foo-list" name="bar[4]" type="checkbox" /> script
</form>
</pre>
Select all using jQuery
<pre class="brush:javascript" name="code"> $("#select-all-jq").click(function(){
if(this.checked) {
$(".foo-list").attr('checked','checked');
} else {
$(".foo-list").removeAttr('checked');
}
});
</pre>
<a name='more'></a>
Select all using only JavaScript (no library), the extra length isn't so bad because this is the whole iceberg so to speak, no extra code elsewhere. The problem really is redoing the browser detection 2 times and the declaration of event listener is different IE/W3C for my code here. I either have to rewrite it, abstract away the browser differences, or live with my ad-hoc browser detection, which will probably lead to confusion and pain down the road.
<pre class="brush:javascript" name="code"> "use strict";
function SelectAll(formName, toggler, classNameOfList)
{
this.toggler = document.getElementById(toggler);
this.form = document.getElementById(formName);
this.classNameOfList = classNameOfList;
this.toggle = function (e) {
var i = 0, n = this.form.elements.length, targetChecked = null;
if(document.all) { // IE check
targetChecked = e.srcElement.checked;
} else {
targetChecked = e.target.checked;
}
for (i; i < n; i = i + 1) {
if (this.form.elements[i].className.indexOf(classNameOfList) !== -1) {
this.form.elements[i].checked = targetChecked;
}
}
};
}
var selectAllFooList = new SelectAll('formFooBar', 'select-all-js', 'foo-list');
if(document.all) { // IE Check
selectAllFooList.toggler.attachEvent('onclick', function(e) { selectAllFooList.toggle(e); });
} else {
selectAllFooList.toggler.addEventListener('click', selectAllFooList.toggle, false);
}
</pre><br />
Edward Hewhttp://www.blogger.com/profile/17330787150106524106noreply@blogger.com0