http://www.vbforums.com/showthread.p...31#post2745431
************************************************** ******
VB6 OR VBA & Webbrowser DOM Tiny $50 Mini Project Programmer help
wanted
************************************************** ******
For this teeny job, please refer to:
http://feeds.reddit.com/feed/8fu/?o=25
"bookmarklet" code
Job is to convert that JAVASCRIPT code into SIMPLE VBA code
(I'm uising MS ACCESS) that uses DOM/Webbrowser skills
After the conversion if successful, I can expand the job and pay more
if we phone chat first.
Since this and future jobs like it require a small amount of phone
contact, I prefer a phone number, and Yahoo Messenger ID
I can pay you using Paypal.
My contact info:
Yahoo Messenger ID:
stevesworde
Email:
st*********@hotmail.com
************************************************** ******
Here's a bookmarklet that will tell you whether or not the browser
renders your page in Standards Complaiance Mode or Quirks Mode. The
bookmarklet will figure this out for the page as well as for all the
frames (and their frames) recursively. Enjoy!
Install
Drag this to your bookmarklets/favorites or right click and add to
favorites:
0){response+='\n\n';for(var i=0;iquirks or !quirks
Sample output
Here's a sample output, produced when used in my Wordpress backend when
writing this post:
As you can see the page has two frames (probably iframes, doesn't
matter), one of them is rendered in Standards Compliant Mode
(CSS1Compat) the other one is in Quirks Mode (BackCompat). The overall
document is CSS1Compat as well. For the frames, if they were named, you
would see the name of the frame before the URL brackets.
And this is GMail, wow, lotsa frames, none compliant
The code
The code is pretty simple, just accessing the compatMode of the the
document object. Here it is in more human readable form (not one long
line like bookmarket code).
var response = 'Document mode: ' + document.compatMode;
function checkFrames(w) {
if(w.frames && w.frames.length>0){
response+='\n\n';
for(var i=0;i<w.frames.length;i++){
var fr=w.frames[i];
try {
response+=fr.name +
' ('+fr.document.location+') - '+
fr.document.compatMode+'\n';
} catch (e) {
response+='Could not access this frame\n';
}
checkFrames(fr);
}
}
}
checkFrames(window);
alert(response);0 points September 21, 2006 by Stoyan at phpied.com
post #
[-]
ABAP code syntax highlighting
Just finished and eager to share - I added a new syntax definition to
the Text_Highlighter PEAR package (see also here). It's for
highlighting code written in the SAP's own ABAP programming language.
A live demo is available at the hiliteme.com site, just pick ABAP from
the drop-down of programming languages. Any feedback is appreciated,
because it's a brand new thing and may have bugs or incompletenesses
(is that a word?). So feel free to highlight your ABAP code and post it
to blogs or forums.
The implementation wasn't hard, the Text_Highlighter package is made to
be extended and even provides the tools for that. All you need to do is
create an XML file that contains keywords and other syntax rules, such
as formats of the comments and so on. Then there is a command line tool
that takes the XML file and generates a class out of it. The class is
later on used when highlighting. Here's the XML file in case you want
to improve on it and generate your own ABAP.php class:
abap.xml
ABAP.phps source file generated
0 points September 20, 2006 by Stoyan at phpied.com post #
[-]
AJAX MVC (so to speak)
This is sort of a framework thing to create AJAX applications, based on
the MVC design pattern. Yep, I have a lot of buzzwords here, I admit,
but this shouldn't be taken too seriously. I was doing a bunch of small
projects lately and I found myself using something like this little
framework, without even thinking about it. Then I thought about it and
I found that the scripts and the organization of them may resamble MVC
a bit. So how does MVC fit when you mix things like thin and fatter
client, HTML, JavaScript, XMLHttpRequest, PHP and CSS?
Usual AJAX app flow
What usually happens in an AJAX application is:
you have an HTML page, styled with CSS
you click on something
JS sends request to the server (to a PHP script)
JS updates the original HTML page
Mapping to the MVC pattern
OK, so what part of this process can be associated with a View, or a
Model or a Controller? The Model is easy, it's the business logic,
writing to a database and so on. This is the PHP script. The View?
Obviously this is the HTML page and the CSS. But I'd like to think also
about the JS that updates the page as part of the View. I mean it makes
sense, it's updating the presentation part. Sometimes you even use
innerHTML in the JS, but even if you use DOM, it becomes part of the
HTML anyway. How about the Controller? Well, we have two controllers
here. One that is on the server side, a PHP script that receives
requests and "asks" the Model for the response. The other controller is
on the client side, this is the JavaScript that decides what happens on
a click of a button and sends an appropriate AJAX request to the PHP
controller. Therefore I would consider any behavioural JS as part of
the Controller, including attaching events as well as sending HTTP
requests.
Here's an illustration:
In action (example)
I went ahead and implemented a very simple application to prove the
concept. It's just a blank styled HTML page with a button. The HTML
page includes two JavaScripts responsible for behaviours (Controller)
and page updates (View). The page also includes a few unrelated helper
javascripts, in my case I'm using the YUI library. The JS Controller
attaches an event to the button. Then when you click the button, the JS
Controller sends a request to the PHP controller. The PHP controller
(just a simple switch) figures out what was requested and calls the
appropriate object of the business model. In my simplistic case, the
abovementioned "model object" is just a simple function, but this can
be easily built upon. The Model returns (JSON-encoded) response, in
this case it's a list of installed PHP extensions. Now the response is
received by the View JS and it updates the page. After that the View
calls another function from the JS controller that attaches new events
to the new content. (Yep, a little glitch here, maybe it would have
been better if the Model's response is handled by the JS controller
which in turn calls the JS view updater, but anyway this is easy to
fix)
Directory layout
Here's the directory structure:
One might argue that it's better if you don't mix .js, .css and .php
files in the same directory but the whole idea is open to
interpretations anyway, it's just an illustration of the idea.
The code for the example
We get to the fun part, the actual implementation. So we start with a
simple .html page, the initial part of the view.
This is index.html
<?xml version="1.1" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Welcome</title>
<link rel="stylesheet" href="../view/styles.css" type="text/css"
media="all" title="Default styles" />
<script language="javascript" type="text/javascript"
src="../_extras/yui/build/yahoo/yahoo-min.js"></script>
<script language="javascript" type="text/javascript"
src="../_extras/yui/build/event/event-min.js"></script>
<script language="javascript" type="text/javascript"
src="../_extras/yui/build/connection/connection-min.js"></script>
<script language="javascript" type="text/javascript"
src="../view/updates.js"></script>
<script language="javascript" type="text/javascript"
src="../controller/behaviours.js"></script>
</head>
<body>
Welcome to my app!
<br />
<form action="" method="post">
<input type="button" name="b" id="thebutton" value="I'm a button,
click me!" />
</form>
<div id="content"> </div>
</body>
</html>As you can see, nothing special, simply including the CSS
styles, the YUI "extras" and two other javascripts - one part of the
View and one that is part of the Controller.
The Controller JS is responsible for attaching an event listener to the
button.
This is an excerpt from the behaviours.js
// the behaviour class
var behaviours = {
phpcontroller: "../controller/switch.php?request=",
// more behaviour.methods....
}
// initial page load, attach onload event(s)
YAHOO.util.Event.addListener(
'thebutton', 'click', behaviours.theButtonClick);Now when the user
clicks the button, the method behaviours.theButtonClick() is executed.
It fires a request to the PHP controller switch and says that the
request type is "loadSomething":
theButtonClick: function(e) {
alert('Ouch! nnOK, I'll make a request for ya, buddy!');
YAHOO.util.Connect.asyncRequest (
'GET',
behaviours.phpcontroller + 'loadSomething',
{success: updates.writeContent}
);
},The PHP controller (controller/switch.php) receives the request, does
a simple switch to validate the request type and then calls the
appropriate (in my case just a simple) function from the business
model. Here's the full switch.php code:
<?php
// is this a request?
if (empty($_GET['request'])) {
die();
}
// get the business logic
include_once '../model/business.php';
// figure out the request
// and call the business logic object
switch ($_GET['request'])
{
case 'loadSomething':
echo loadSomething();
break;
case 'loadSomeMore': // not used, example
echo loadSomeMore();
break;
}
?>The function loadSomething() from the PHP model gets a list of
installed PHP extensions, encodes them into JSON and sends them back.
This is a full listing of the ../model/business.php
<?php
function loadSomething() {
$extensions = get_loaded_extensions();
return '["'. implode('","', $extensions) . '"]';
}
?>If you go back and look at the AJAX request, you'll see that on
success, I call the updates.writeContent() method. The
.../view/updates.js script contains stuff that updates the HTML of the
original page, so its place is in the View part of the app.
writeContent simply creates an HTML table with the results (the list of
PHP extensions). Then I wanted to attach event listeners to this table
just to change color, but it can be more than that. Attaching events is
a job for the JS Controller, therefore a method of its class is called.
Here's a full listing of updates.js:
var updates = {
writeContent: function (xmlhttp) {
if (!xmlhttp.responseText) {
alert("I got nothing from the server");
}
var data = eval(xmlhttp.responseText);
var write_to = document.getElementById('content');
write_to.innerHTML = ''; // yeah, I know
var html2dom_root = write_to;
var table = document.createElement("table");
var table_1_tbody = document.createElement("tbody");
for (var i in data) {
table_1_tbody_2_tr = document.createElement("tr");
table_1_tbody_2_tr_1_td = document.createElement("td");
num = 1 + parseInt(i);
table_1_tbody_2_tr_1_td_1_text = document.createTextNode(num);
table_1_tbody_2_tr_1_td.appendChild(table_1_tbody_ 2_tr_1_td_1_text);
table_1_tbody_2_tr.appendChild(table_1_tbody_2_tr_ 1_td);
table_1_tbody_2_tr_2_td = document.createElement("td");
table_1_tbody_2_tr_2_td_1_text =
document.createTextNode(data[i]);
table_1_tbody_2_tr_2_td.appendChild(table_1_tbody_ 2_tr_2_td_1_text);
table_1_tbody_2_tr.appendChild(table_1_tbody_2_tr_ 2_td);
table_1_tbody.appendChild(table_1_tbody_2_tr);
}
table.appendChild(table_1_tbody);
html2dom_root.appendChild(table);
behaviours.updateTableBehaviour();
}
}(BTW, for the DOM part I'm used the help from my little tool html2dom
to make my life a bit easier)
And finally here's the rest of the JS controller (behaviours.js), the
method behaviours.updateTableBehaviour() that adds an event listener to
the new table and the trClick() that handles clicks on this table. On
click, it justs changes the color of the underlying row.
trClick: function (e) {
var target = (e.srcElement) ?
e.srcElement.parentNode : e.target.parentNode;
if (target.tagName == 'TR') {
if (target.className == 'tr-on') {
target.className = '';
} else {
target.className = 'tr-on';
}
}
},
updateTableBehaviour: function () {
var el = document.getElementById('content').firstChild;
YAHOO.util.Event.addListener(
el, 'click', behaviours.trClick);
}Demo and downloads
Demo - the live example
Zipped demo - all the source code for the example
Template - the source code for the example but with the example part
commented, so you can use it as a template for your next AJAX project.
The only thing you need to do is to drop the YUI in the _extras/yui
folder.
Thank you for reading, any comments welcome!
0 points September 20, 2006 by Stoyan at phpied.com post #
[-]
HTML2DOM
Here's this HTML-2-DOM service - http://www.html2dom.com What it does
is pretty simple - you paste some HTML code and the output is JS script
code that uses DOM functions to produce the same result. Could be
useful when you're working on an AJAX-style app that generates new
content using JavaScript.
I build this simple script, inspired by this great book I was reading -
"Build Your Own AJAX Web Applications". In the book, the author
sometimes starts with writing up what is the HTML code for the result
you want to achieve, and then goes ahead with giving the DOM code.
Because, you know, DOM code can be quite verbose and sometimes a bit
hard to follow. So I thought, why not write up a simple tool to
automate this HTML to DOM transition.
The code is not complicated at all, it just takes the HTML, treats it
as an XML document, then loops through all the elements of the XML doc
and all the attributes for each element. The script is here, hopefully
reusable and you can grab it for your own projects if you wish. You can
check the source of html2dom.com's index page for an example how to use
the html-2-dom class.
Some limitations of the script (that I know of) are result of the fact
that I'm treating the HTML as XML document. So you might get some
errors if the HTML you paste is not well-formed, with all closed tags
and so on. Also you cannot use and other entities, because XML
doesn't know about them. What XML knows is only the pre-defined 5. And
last, out of the different node types, my script understands only three
- element, attribute and comment. I think it's enough for the practical
purposes I was aiming at, even the comment type is a bit of a stretch.
So enjoy and as always, any feedback is welcome!
0 points September 15, 2006 by Stoyan at phpied.com post #
[-]
The PEAR book is on it's way
Here's the link to publisher's page dedicated to the PHP Programming
with PEAR. Guess who wrote the chapter for MDB2?
It's an honour to me to be in the company of the other authors, people
who have done a lot for the PEAR community:
Stephan Schmidt
Aaron Wormus
Carsten Lucke
Here's what the book is about:
Accessing databases with MDB2
Displaying data in a range of formats (HTML, Excel spreadsheet, PDF)
Creating and parsing XML documents
Serializing PHP objects into XML, and unserializing XML documents to
PHP objects
Consuming and offering web services
Accessing Web APIs including Google, Yahoo, Amazon, and Technorati
250 pages of good stuff Get your copy or just spread the word!
Update: In the rush to share the news I forgot to say a big "thank
you!" to Lukas Smith, the man behind MDB2, who was responsive as always
and was kind enough to review my chapter.
0 points September 12, 2006 by Stoyan at phpied.com post #
[-]
HiLiteMe.com updated
Following from here, I'm proud to announce an update to HiLiteMe.com.
With two custom renderers for Text_Highlighter, the service now offers
you:
- BB code for your source, so you can post beautiful code to bulletin
boards and others that use BB code for formatting
- simple HTML code - the formatting is using only the tags b, i and u,
this is for devices such as iPod that can understand only tiny amount
of HTML code. So you highlight some code and take it on the road,
sweet.
In addition to that there is Simple HTML preview, CSS styles that are
used for the "rich" HTML highlighting and also the code for the rich
HTML formatting, so no need to view->source->copy
Other additions - tabsize setting (how many spaces for a tab, if you
paste code with tabs in it) and line numbers setting (yes/no) to
specify whether you want lines to be numbered in your code.
Test drive?
Note on the BB code highlighting - I tested on:
- vBulletin - works beautifully and
- phpBB - the current out-of-the-box version will not work, becuase
it's not allowing [color] BB tags inside [code] tags. Buuut, there is a
MOD to enable this
0 points September 9, 2006 by Stoyan at phpied.com post #
[-]
Greasemonkey - execute custom javascripts on any page
If you want to try executing custom local scripts on any page you
visit, try Greasemonkey. Here's a 10 seconds tut.
The task is to create a custom script and to make sure it's executed
every time you leave a page on phpied.com
(prerequisite) Get Firefox!
Install Gresemonkey from here
Create a file phpied.user.js (all your custom scripts must end in
..user.js) with the following
var start = new Date();
window.addEventListener("unload", function(e) {
var end = new Date();
var diff = Math.floor((end - start) / 1000);
alert("Man, I spent " + diff +
" of my precious seconds on this guy's page!" +
" Now that's called investment!"
);
}, false);Open phpied.user.js in the browser. You'll see a message from
Greasemonkey inviting you to install.
Click Install... and you're done. Now this script will execute on every
single page you hit. To change it so that it executes only when you
visit phpied.com do:
Right-click the monkey icon in the bottom-right of the browser screen.
Select Manage User Scripts.
Click the * in the Included Pages list, then hit Edit. Type
"http://www.phpied.com/*" This means "execute this script on every page
on phpied.com". Click OK.
Reload this page to see the script in action.
N.B. To modify a user script, do not modify the original file where you
had it initially on your file system, won't work, I tried it Instead,
modify the copy that GM has stored. Right-click the monkey icon ->
Manage -select your script in the listing on the left and click Edit.
I've read before about the Greasemonkey Firefox extension, but never
tried it before yesterday. Never tried probably because of a comment on
the sitepoint.com's article about Greasemokey. The comment I found so
funny, yet true, was "I just don't like the idea of having to spend
time on another person's website when I barely get enough time to spend
on my own." Well, sometimes one might like to try custom scripts on
his/her own site, for example to test some stuff on the production
server without the risk of breaking something.
More resources:
- Home page
- Tutorial @ sitepoint
- Free ebook
- User scripts
0 points September 7, 2006 by Stoyan at phpied.com post #
[-]
HiLiteMe.com
In case someone is wondering how do I highlight the code I post on this
blog ... well, the lazy way. I don't. Some time ago I setup a free
service, hiliteme.com to do it for me, then I just copy/paste the
generated code. It's far from the best solution, but it's definitelly
the laziest, without any spend-time-to-save-time effort on my end
So if you ever need to highlight source code - for a blog, word doc or
whatever, you can use this free service, I'll be happy. HiLiteMe.com is
using the PEAR Text_Highlighter package (I talk about it here)
BTW, I think in general it's a good idea is to use JavaScript to do the
code highlighting. After all, it's just presentation and if it can be
done on the client, why loading you server with this task. I know at
least two free scripts that do that, there's probably more. The one
that looks very good is this one.
0 points September 4, 2006 by Stoyan at phpied.com post #
[-]
Parent's styles in an iframe
Here's a JavaScript that let's you style an iframe just like its top
parent. The script is basically just a proof of concept I did after
talking to a friend about similar problem he has had in the past, so
feel free to modify and use if you like it.
So I have a page, called big.html and a stylesheet for this page,
called big.css. On the page there is an iframe that loads small.html.
small.html has its own style, called small.css. What my little
Javascript function does is:
Getting all top parent's <linktags
Looping through the tags and checking if the rel attribute has value
stylesheet
For all stylesheets found, makes a copy of the link tag and all its
attributes and adds it to the head of the iframed page
Here's the code:
function styleMe() {
if(window.top && window.top.location.href != document.location.href)
{
// I'm small but I'm not alone
// all parent's <link>s
var linkrels = window.top.document.getElementsByTagName('link');
// my head
var small_head = document.getElementsByTagName('head').item(0);
// loop through parent's links
for (var i = 0, max = linkrels.length; i < max; i++) {
// are they stylesheets
if (linkrels[i].rel && linkrels[i].rel == 'stylesheet') {
// create new element and copy all attributes
var thestyle = document.createElement('link');
var attrib = linkrels[i].attributes;
for (var j = 0, attribmax = attrib.length; j < attribmax; j++)
{
thestyle.setAttribute(attrib[j].nodeName,
attrib[j].nodeValue);
}
// add the newly created element to the head
small_head.appendChild(thestyle);
}
}
// maybe, only maybe, here we should remove the kid's own styles...
} else {
alert('I hate to tell you that, but you are an orphant ');
}
}To see the code in action, see big.html.
0 points September 4, 2006 by Stoyan at phpied.com post #
[-]
SAP container for PEAR::Auth
PEAR::Auth is a package that allows you to abstract the user
authentication from the main part of your application and not worry
about it. What is good about the package is that it comes with
different "containers" that allows you to authenticate users against
different storages, I mean you can store users data in a database and
use the PEAR::DB or PEAR::MDB2 containers, or you can use flat files,
IMAP servers, SOAP and what not. And the package is easily extensible.
So I played around with creating an SAP container that allows you to
check users against your company's SAP system and for example build a
section of your Internet (or Extranet) page that is only accessible for
people and partners that exist as users in the SAP system.
In order to connect to an SAP system with PHP you need the SAPRFC PHP
extension. Get it here. Then you use the function saprfc_open() (more
docs here) to establish a connection. You provide some info about the
SAP system as well as your username/password. Once connected, a
so-called "SSO ticket" is generated for you. This is just a long
string, like a session ID. For consecutive connections you can use this
SSO ticket instead of providing username/password every time. BTW, SSO
stands for Single Sign-On.
Now, with my little SAP container you can benefit from the PEAR and
PEAR::Auth infrastructure to do the logins. The way to do an
authentication is simple (example stolen in parts from this PEAR manual
entry). You pass the connection options (such as hostname). Then, once
the user is authenticated, the container retrieves the SSO session ID
and sticks into the Auth session data, so that it's reusable for
consecutive connections within the same session. If you need to do more
with the SAP system, apart from authenticating users, you can get back
the updated connection options and just pass them to saprfc_open().
Here's an example:
<?php
// get Auth lib
require_once "Auth.php";
// SAP connection options
$options = array (
'ASHOST' ='hostname'
);
// create Auth object using the SAP container
$a = new Auth("SAP", $options);
$a->start();
// check
if ($a->checkAuth()) {
// authorised! You can do the protected stuff here
// For example open a connection to the SAP system
// using the stored authentication data
$rfc = saprfc_open($a->getAuthData('sap'));
// show sapinfo if you will
echo '<pre>';
print_r(saprfc_attributes($rfc));
echo '</pre>';
}
?>And here's the actual Auth_Contaner_SAP class, should be placed in a
file called SAP.php in your_pear_dir/Auth/Container/
<?php
require_once 'PEAR.php';
require_once 'Auth/Container.php';
/**
* Performs authentication against an SAP system
* using the SAPRFC PHP extension.
*
* When the option GETSSO2 is TRUE (default)
* the Single Sign-On (SSO) ticket is retrieved
* and stored as an Auth attribute called 'sap'
* in order to be reused for consecutive connections.
*
* @author Stoyan Stefanov <ss****@gmail.com>
* @package Auth
* @see http://saprfc.sourceforge.net/
*/
class Auth_Container_SAP extends Auth_Container {
/**
* @var array Default options
*/
var $options = array(
'CLIENT' ='000',
'LANG' ='EN',
'GETSSO2' =true,
);
/**
* Class constructor. Checks that required options
* are present and that the SAPRFC extension is loaded
*
* Options that can be passed and their defaults:
* <pre>
* array(
* 'ASHOST' ="",
* 'SYSNR' ="",
* 'CLIENT' ="000",
* 'GWHOST' =>"",
* 'GWSERV' =>"",
* 'MSHOST' =>"",
* 'R3NAME' =>"",
* 'GROUP' =>"",
* 'LANG' =>"EN",
* 'TRACE' =>"",
* 'GETSSO2'=true
* )
* </pre>
*
* @var array array of options.
*/
function Auth_Container_SAP($options)
{
$saprfc_loaded = PEAR::loadExtension('saprfc');
if (!$saprfc_loaded) {
return PEAR::raiseError('Cannot use SAP authentication, '
.'SAPRFC extension not loaded!');
}
if (empty($options['R3NAME']) && empty($options['ASHOST'])) {
return PEAR::raiseError('R3NAME or ASHOST required for
authentication');
}
$this->options = array_merge($this->options, $options);
}
/**
* Performs username and password check
*
* @var string Username
* @var string Password
* @return boolean TRUE on success (valid user), FALSE otherwise
*/
function fetchData($username, $password)
{
$connection_options = $this->options;
$connection_options['USER'] = $username;
$connection_options['PASSWD'] = $password;
$rfc = saprfc_open($connection_options);
if (!$rfc) {
$message = "Couldn't connect to the SAP system.";
$error = $this->getError();
if ($error['message']) {
$message .= ': ' . $error['message'];
}
PEAR::raiseError($message, null, null, null,
@$erorr['all']);
return false;
} else {
if (!empty($this->options['GETSSO2'])) {
if ($ticket = @saprfc_get_ticket($rfc)) {
$this->options['MYSAPSSO2'] = $ticket;
unset($this->options['GETSSO2']);
$this->_auth_obj->setAuthData('sap',
$this->options);
} else {
PEAR::raiseError("SSO ticket retrieval failed");
}
}
@saprfc_close($rfc);
return true;
}
}
/**
* Retrieves the last error from the SAP connection
* and returns it as an array.
*
* @return array Array of error information
*/
function getError()
{
$error = array();
$sap_error = saprfc_error();
if (empty($err)) {
return $error;
}
$err = explode("n", $sap_error);
foreach ($err AS $line) {
$item = split(':', $line);
$error[strtolower(trim($item[0]))] = trim($item[1]);
}
$error['all'] = $sap_error;
return $error;
}
}
?>0 points September 2, 2006 by Stoyan at phpied.com post #
[-]
Hello POV-Ray
End-of-summer draft postings cleanup time! I found this piece of code I
wanted to share but never had the time to. The code is in the POV-Ray
programming language. "POV-what?" you might think. Well, from the
hourse's mouth:
http://www.povray.org:
-
The Persistence of Vision Raytracer is a high-quality, totally free
tool for creating stunning three-dimensional graphics. It is available
in official versions for Windows, Mac OS/Mac OS X and i86 Linux. The
source code is available for those wanting to do their own ports.
Maybe a year or so ago, I played with it, and here's the result.
Funny, isn't it? But don't judge me too harsh, I'm the first to admit
my design and color matching abilities as trully very limited.
Anyway, here's the code, I hope you can make sense from the limitted
amount of comments.
#include "shapes.inc"
#include "colors.inc"
#include "textures.inc"
#include "shapesq.inc"
#include "metals.inc"
#include "skies.inc"
// drop definition
#declare drop = object {
Piriform
sturm
rotate -90*z
translate 0.5*y
scale <0.5, 2, 1>
texture {
pigment { P_Cloud1 }
finish { F_MetalB }
}
}
// add 5 drops
object {
drop
//pigment { Col_Fluorite_01 }
translate -1.5*x
}
object {
drop
//pigment { Col_Fluorite_02 }
translate 0.8*y
translate 15.5*z
translate 0.5*x
}
object {
drop
translate .5*z
translate 1.9*x
}
object {
drop
translate 2.5*z
translate 1.5*y
translate -0.8*x
}
object {
drop
translate -1.5*z
translate 1.8*y
translate 2.5*x
}
// some clouds
O_Cloud1
// this is the sky
sky_sphere {
pigment {
gradient y
color_map {
[0 color Blue]
[1 color Brown]
}
scale 1.5
translate -11
}
}
// the ground
plane {
y, -1.5
pigment { checker pigment{Jade}, pigment{Yellow} }
//finish { reflection {1.0} ambient 0 diffuse 0 }
finish { reflection { 0.03, 0.81 }}
}
// camera, light ... shoot!
camera {
location <0, 1, -8>
look_at 0
angle 43
}
light_source {
<200, 150, 100White
//projected_through {O_Cloud2}
}
Warning before anyone dives into POV-Ray: It's a time killer! Once you
get hooked you might suffer from sudden lapse of sleep. BTW, in the
pov-ray programming manual there are some clever bits that start with
"You know you have been raytracing too long when ...". I can totally
relate to this one:
You know you have been raytracing too long when ...
.... You want to cheat and look at nature's source code.
- Mark Stock
In case you thought JavaScript could be tough...
0 points September 1, 2006 by Stoyan at phpied.com post #
[-]
What's $this?
Simple question, what's $this - a or b?
<?php
class a {
function aa(){
var_dump($this);
}
}
class b {
function bb(){
a::aa();
}
}
$b_obj = new b;
$b_obj->bb();
?>Answer: object(b)#1 (0) { }
0 points September 1, 2006 by Stoyan at phpied.com post #
[-]
phpbb and security
Man, do I have a lot of todos! I was going through my posting drafts on
this blog and found this 1 year old piece (from Aug 10th, 2005). I
remember I was responding to this blog posting, but at some point I
desided that I don't have the time to finish it, so I saved it as a
draft. Well, one year was not enough to finish it, so here is the a
draft as part of my postings cleanup.
-
Nice posting, thanks! I just want to add something on the phpBB part of
it. OK, PHP is the most popular, compared to the other web languages,
hence the most security issues with it. Well, I can apply the same
logic to phpBB and phpMyAdmin. Everybody uses phpMyAdmin and phpBB is
probably the most popular BB package out there. phpBB is an open
(therefore exposed) source package and being also a bulletin board
package makes it a nice target. Any BB site out there has has its kids
that hate it and want it hacked, defaced, DB-emptied, userbase-exposed
or otherwise dead. So they start digging every single regexp looking
for a "door". And they find it, one after the other. It's normal, we
all know that there's no such thing as a secure or bug-free software.
I don't say that phpBB's code is perfect (is there a perfect code?!),
but I don't think phpBB should pay for all the sins of all PHP devs out
there. We all make mistakes, that's nature. And it's not nice to call
each other names in such situations. Two examples - PEAR's recent
XML_RPC expliot (you cannot say that Stig Bakken can't hack in PHP) and
a blog posting about some omissions in this PHP security guide!
-
Update from Aug 31st, 2006:
I really like this piece Harry Fuecks wrote ovet at SitePont. Hopefully
the "war" is over and people no longer point fingers at each other, but
learn from each other's mistakes instead.
Being able to see many shades of grey rather than black and white could
be another point to add to the ideal profile. PHP (and security) is a
good case in point-what strikes you as a smarter response: screaming
PHP sucks or understanding that it's popular and doing something to
improve the situation?
0 points September 1, 2006 by Stoyan at phpied.com post #
[-]
phpDoc clip library for TextPad
Here's a little something I did to make it a bit easier to write API
docs in TextPad, it's a clip library to save some typing when writing
comments in the phpDoc format. I submitted it to the TextPad team, so
at some point it will probably appear in the downloads section, but
meanwhile you can get from here.
Installation
Download the file phpdoc.tcl
Copy to your Samples directory in the TextPad folder
Make sure the Clip Library panel is on (menu option View -Clip
Library)
Choose phpDoc clip from the dropdown in the Clip Library panel
Some screenshots
Type in some class description, highlight and then select "Class
comment"
Example class property comment
0 points August 31, 2006 by Stoyan at phpied.com post #
[-]
News update
Hi all, after a long time of silence, I wanted to drop a line here to
say I'm still alive. I'm back from beautiful Bulgaria, had a great time
there, friends, family, trailer trip. Now I'm back in Montreal, moved
to and working on the new house we bought at the end of May; it's
coming along pretty nice, but all this renovation work really does take
time, especially if you're doing it yourself ("with a little help from
my friends") and you don't have too much experience. But I'm actually
enjoying being away from the monitor all day, it was a surprising
discovery
Meanwhile other things are happening, but they are mainly result of
work I've done previously. The second part of my MDB2 tutorial was
published in the IPM (TOC). In the same issue, the magazine published a
book review of mine, actually it's the first book review I've ever
written. It's about this excellent book - AJAX and PHP. I have a few
other book (and one software) reviews piped, I'll probably publish them
here on this blog. Meanwhile I also became the lead dev for the
Image_Text PEAR package, expect a quick intro soon. The last news is
that I submitted the first draft of one chapter I'm contributing to
what is going to be a great book. Details later.
I thinks that's it for now. Probably there won't be too much postings
in the nearest future (I started a new job on top of everything), but
I'll be back!
0 points July 25, 2006 by Stoyan at phpied.com post #
[-]
Bulgaria, IPM, quick update
I'm currently on a vacation in my native land Bulgaria, the party's on
(and so the soccer World Cup finals) so It wuould be quiet around here
for a while. Meanwhile I've disabled comments, trackbacks and
pingbacks, I appologize, it's just that I'm receiving a lot of spam and
since I don't have the time to clean it, the spam will look as an
insult to my readers.I cannot find the WordPress option to disable
comments retroactively, so all comments will be held for moderation. I
appologize once again.
Meanwhile my article on DB and MDB2 was published in the International
PHP Magazine, the TOC is here. This article is an extended and improved
version of the original DB-to-MDB2 blog posting you can find here, plus
I've added an intro part in case you've never used DB or MDB2.
BTW, I'm enjoying writing this post on a dial-up and using IE5.5., this
is an experience that is pretty ... interesting
l8r!
0 points June 12, 2006 by Stoyan at phpied.com post #
[-]
Amazon Connect blog
Just finished setting up my Amazon Connect profile and posted one
message to the blog Amazon create for you when you set your profile up.
Here's the blog.
Amazon Connect is an initiative where book authors can post messages
directly to their books pages. So my smiling face is exposed on Amazon
now when you browse one of the two phpBB books. Pretty nice. I mean the
Connect idea, not so much the face
BTW, Amazon really have to work on those URLs to make them friendlier.
Every time I post an Amazon link anywhere, it's just waaay too long and
usually messes things up.
0 points May 23, 2006 by Stoyan at phpied.com post #
[>]
Form auto-fill bookmarklet
Intro
So here's the thing, we all know we hate forms, the only thing we hate
more than forms themselves is actually filling out forms. But the forms
are the interface to our web apps, so we cannot do without them. What
we would love to do without, but can't, is skip the application testing
part where you fill out forms like there's no tomorrow, just to make
sure your app is rock solid.
And filling out forms is a pain. So for some time I wanted to get my
hands on a little something that can fill out a form, any form, with a
click of a button. JavaScript is ideal for such a task and the best
sort of a "little something" is probably a bookmarklet. That is how
this bookmark was born.
What is it, what it does?
This is a bookmarklet. You go to page that has one or more forms and
you click the bookmarklet. It completes the form for you with some
random data. The whole thinking was to have a form ready to be
submitted and generating as less validation errors as possible. Here
are some details:
All defaults are kept as they are
All passwords fields are completed with the same password, in case
there is a password/password confirmation combo. The default password
is "secret"
If a text field has the string "email" in its name, the auto-generated
value would be a random string @ example.org
If a text field has the string "name" in its name, a name-looking value
will be generated.
All checkboxes will be checked (who knows which one of them might be
"Accept terms" or anything else that is required)
Multi-selects will have a random number of random options selected
Install
Right-click and bookmark or drag to your personal bookmarks toolbar.
max; i++) {var sel = all_selects[i]; if (sel.selectedIndex != -1&&
sel.options[sel.selectedIndex].value) {continue; }var howmany = 1; if
(sel.type == 'select-multiple') { var howmany = 1 +
this.getRand(sel.options.length - 1);}for (var j = 0; j howmany; j++)
{var index = this.getRand(sel.options.length -
1);sel.options[index].selected = 'selected';}}for (var i = 0, max =
all_textareas.length; i max; i++) {var ta = all_textareas[i];if
(!ta.value) {ta.value = this.getRandomString(10)+ '\n\n'+
this.getRandomString(10);}}for (var i = 0, max = all_inputs.length; i
max; i++) {var inp = all_inputs[i];var type =
inp.getAttribute('type');if (!type) {type = 'text'; }if (type ==
'checkbox') {inp.setAttribute('checked', 'checked');}if (type ==
'radio') {var to_update = true;var name = inp.name;var input_array =
inp.form.elements[inp.name];for (var j = 0; j input_array.length; j++)
{if (input_array[j].checked) {to_update = false;continue;}}if
(to_update) {var index = this.getRand(input_array.length -
1);input_array[index].setAttribute('checked', 'checked');}}if (type ==
'password') {if (!inp.value) {inp.value = this.getPassword();}}if (type
== 'text') {if (!inp.value) {if (inp.name.indexOf('name') != -1)
{inp.value = this.getRandomName() + ' ' + this.getRandomName();} else
if (inp.name.indexOf('email') != -1) {inp.value =
this.getRandomString(1) + '@example.org';} else {inp.value =
this.getRandomString(1);}}}}},getRandomString: function
(how_many_words) {if (!how_many_words) {how_many_words = 5;}if
(!this.words) {this.words = this.blurb.split(' ');}var retval = '';for
(var i = 0; i how_many_words; i++) {retval +=
this.words[this.getRand(this.words.length) - 1];retval += (i
how_many_words - 1) ? ' ' : '';}return retval;},getRandomName: function
() {if (!this.split_names) {this.split_names = this.names.split('
');}return this.split_names[this.getRand(this.split_names.length) -
1];},getPassword: function () {if (!this.password) {this.password =
'secret';}return this.password;},getRand: function (count) {return
Math.round(count * Math.random());}}; auto.fillerup()">Form auto-fill
Demo
Here's the demo.
The code
The demo and the code below are "normal" code, with proper indentation
and all. The actual bookmark though has to be on one line and as small
as possible, so it's pretty much unreadable. Ah, and while the demo
will work in IE, the bookmarklet won't, because it's too big for IE. IE
allows up to about 500 characters in the URL (or a bookmarklet) while
mine is about 2000 "compressed" or 3000 cleaner. So unless I do
something heroic in compressing the script, it won't work on IE. No
biggie I'd say, since you'll be testing your application and most
likely you use Firefox anyway.
The big picture
Using JSON, the class/object is called auto and it has the following
interface:
var auto ={
// a list of names that will be used to generate
// normal looking names
names: "Steve Buscemi Catherine Keener ...",
// this is where all the random words will come from
blurb: "phpBB is a free...",
// default password to be used in all password fields
password: "secret",
// the main function that does all
fillerup: function() {},
// helper function, returns randomly selected words
// coming from this.blurb
getRandomString: function (how_many_words) {},
// helper function, returns randomly selected names
// coming from this.names
getRandomName: function () {},
// returns this.password
getPassword: function () {},
// returns a random int from 0 to count
getRand: function (count) {}
}The actual form fill-out is initiated by calling auto.fillerup()
As you can probably guess, the only interesting function is fillerup(),
so let me show you what it does.
fillerup()
In case you're wondering, the name of the function comes from a Sting
song:
Fill'er up, son, unleaded.
I need a full tank of gas where I'm headed ...
The function starts by identifying all the elements candidate to be
completed:
var all_inputs = document.getElementsByTagName('input');
var all_selects = document.getElementsByTagName('select');
var all_textareas = document.getElementsByTagName('textarea');OK, we
have our work cut out for us, let's start by looping through the
selects:
// selects
for (var i = 0, max = all_selects.length; i < max; i++) {
var sel = all_selects[i]; // current element
if (sel.selectedIndex != -1
&& sel.options[sel.selectedIndex].value) {
continue; // has a default value, skip it
}
var howmany = 1; // how many options we'll select
if (sel.type == 'select-multiple') { // multiple selects
// random number of options will be selected
var howmany = 1 + this.getRand(sel.options.length - 1);
}
for (var j = 0; j < howmany; j++) {
var index = this.getRand(sel.options.length - 1);
sel.options[index].selected = 'selected';
// @todo - Check if the selected index actually
// has a value otherwise try again
}
}Then - textareas, they cannot be simpler. We only check if there isn't
already a value and if there's none, we get two "paragraphs" of 10
words each.
// textareas
for (var i = 0, max = all_textareas.length; i < max; i++) {
var ta = all_textareas[i];
if (!ta.value) {
ta.value = this.getRandomString(10)
+ '\n\n'
+ this.getRandomString(10);
}
}Next (and last), come the inputs. They are a bit more complicated as
there are too many of them. Here's the overall code with the skipped
details for each input type.
// inputs
for (var i = 0, max = all_inputs.length; i < max; i++) {
var inp = all_inputs[i];
var type = inp.getAttribute('type');
if (!type) {
type = 'text'; // default is 'text''
}
if (type == 'checkbox') {...}
if (type == 'radio') {...}
if (type == 'password') {...}
if (type == 'text') {...}
}We're absolutely unforgiving when it comes to checkboxes - just check
them all, no questions asked, take no prisoners.
if (type == 'checkbox') {
// check'em all
// who knows which ones are required
inp.setAttribute('checked', 'checked');
/* ... ooor random check-off
if (!inp.getAttribute('checked')) {
if (Math.round(Math.random())) { // 0 or 1
inp.setAttribute('checked', 'checked');
}
}
*/
}Next, do the radios. They are a bit more complicated, because once we
have an element, before checking it, we need to verify that there are
no other radios with the same name (and in the same form) are already
selected and checked.
if (type == 'radio') {
var to_update = true;
// we assume this radio needs to be checked
// but in any event we'll check first
var name = inp.name;
var input_array = inp.form.elements[inp.name];
for (var j = 0; j < input_array.length; j++) {
if (input_array[j].checked) {
// match! already has a value
to_update = false;
continue;
}
}
if (to_update) {
// ok, ok, checking the radio
// only ... randomly
var index = this.getRand(input_array.length - 1);
input_array[index].setAttribute('checked', 'checked');
}
}Passwords - trivial, just make sure you always set the same password.
if (type == 'password') {
if (!inp.value) {
inp.value = this.getPassword();
}
}And finally - the text inputs. We try to guess the nature of the text
field by its name. Here there's plenty of room for improvement and more
guesses.
if (type == 'text') {
if (!inp.value) {
// try to be smart about some stuff
// like email and name
if (inp.name.indexOf('name') != -1) { // name
inp.value = this.getRandomName() + ' ' +
this.getRandomName();
} else if (inp.name.indexOf('email') != -1) { // email address
inp.value = this.getRandomString(1) + '@example.org';
} else {
inp.value = this.getRandomString(1);
}
}
}C'est tout
That's it, hope you liked it and start using it Any comment or
suggestions - let me know.
1 points May 16, 2006 by Stoyan at phpied.com post #
[-]
Enumerating with MySQL
Here's how I got MySQL to do some enumerating for me, meaning increment
a counter and add it to a select statement as in a numbered list. It is
questionable if this type of functionality should be part of the
"business" logic, as opposed to the display logic, but still, you never
know.
What you need to do in this case is first to define the variable @inc
using SET and you assign the default value of 0.
Then you include @inc as part of your SELECT statement. You can even
use AS to nickname the variable expression.
Also as part of the SELECT you take care of incrementing the value in
@inc.
Here's the thing:
SET @inc :=0;
SELECT
@inc := @inc + 1 AS a,
`some_field`
FROM
`some_table`;Tested in MySQL versions (oldest to latest) 4.0.26,
4.1.10, 4.1.15 and 5.0.20
If anyone has an idea how to this in one shot, without executing the
SET first, I'll be very interested. I played with IFNULL and IF in
order to check if @inc was defined, and if not to define it, but hit a
brick wall.
1 points May 16, 2006 by Stoyan at phpied.com post #
[-]
PHP fun
I remember reading a discussion over at Sitepoint - has the fun gone
out of PHP? No it isn't! No way, the fun is here... or should I say
PHun
Check this out - videos from a beer drinking contest at a PHP
conference. I love how serious the gyus are, I mean, at the end, a
contest is a contest, you're serious or you're out, it is a serious
matter and should be taken with the necessary amount of responsibility
And how about this T-shirt on sale at php|arch?
While it may be amusing in a sorta geeky kinda way to wear your
programming inclinations (or any other inclinations, for that matter)
on a t-shirt, I believe I stopped doing it long time ago, my last fan
t-shirt was probably the one displaying a nasty scene from a Canibal
Corpse's album cover (man, am I that old!)
The nice thing about the shopping experience at php|arch,
usability-wise, is that they really make sure you're aware of the
shipping details. Did you see, they ship only to North America and the
Rest of the World. No way you can get that t-shirt delivered on Mars or
Venus, sorry, not at this time, working on it...
0 points May 6, 2006 by Stoyan at phpied.com post #
[-]
Opacity for the thumbs
I've been just toying with the CSS opacity to make fancier image
thumbnail rollovers, it's actually quite easy. The idea is when you
have a thumbnail photo gallery to make the thumbs semi-transparent and,
on mouse over, to remove the transparency and show the real image as
is.
All it takes is this little piece of CSS:
a img {
opacity: 0.55;
filter:alpha(opacity=55);
}
a:hover img {
opacity: 1;
filter:alpha(opacity=100);
}Here's a demo. CSS file is here.
The demo uses Y!API to get a few images (I never seem to have good
images laying aroud when I need them). The API call gets JSON output
from the Yahoo! search API to make it easy (and server-side free) to
display the results. You may peek into the JS source if you're curious
(more Y! JSON search API here), but doesn't have anything to do with
the main purpose - the thumbs rollovers.
0 points May 5, 2006 by Stoyan at phpied.com post #
[-]
JavaScript to find your Yahoo! ranking
Motivation
Inspired by this article on SitePoint that shows how to find the Google
ranking for a specific page and a search query, I decided to do the
same, but for the Yahoo! ranking. The fun part is that my script is a
JavaScript and requires nothing but a browser in order to run.
How mine is different
In the article above you need to use Google's SOAP service, so if
you're not lucky enough to be running PHP5, you'll probably need
something along the lines of PEAR SOAP or NuSOAP. That implies you also
need a web server, running PHP. Then you need a Google API key and you
need to download stuff and upload it to your server.
Nothing even close to that in terms of requirements if you opt in for
the Yahoo! web service. All you need is a browser and JavaScript
enabled, which shouldn't be a big deal, I don't think
About the Yahoo! JSON web service
Yahoo's web service can return XML as everybody else, but it can also
return serialized PHP and also JSON. Using the JSON option you can make
a simple XMLHTTPRequest and get all the content JavaScript-ready,
without the headaches of getELementsByTagName() or other DOMmy methods
to wrestle that XML tree. The problem here is that you're requesting a
file from a different domain, so the browser won't allow it. Workaround
- a simple PHP script to serve as a proxy. Oooor (as we said we don't
need no stinkin' server) you can use the dynamic JavaScript includes
(discussed here) to do the request. As a result you get a working
solution with JS only.
By the way, if you're wondering about the beauty of JSON, try this
eye-opener.
Demo
Ah, yes, the demo is here.
Enter a/ your URL, or part of it, and b/ a search query. Then the
script will tell you where in the first 1000 results is your URL to be
found. If it is found.
How it works
Check the source for the details, it's reasonably well commented, but
the big picture:
You make a request (in yjsonrank.makeRequest()) by appending a new
SCRIPT element to the HEAD of your HTML. The URL of the script element
(the SRC attribute) points to the Y! web service and also passes the
search query and a function to be called once the script is included.
This function happens to be yjsonrank.process()
The yjsonrank.process() function receives JSON data returned by the
service, assigned to the resp variable.
We loop through resp.ResultSet, checking every resp.ResultSet.Result if
its Url property contains our URL. If yes - we're done! If not, we make
another request this time for the next 50 results. (50 is randomly
chosen, feel free to modify). We continue until we reach 1000th result,
which is the max that Y! will be willing to give.
And that's pretty much it, the rest is just fluff and beautifications
More Y! info
The JSON description
The Web Search API page, listing all the additional parameters you can
add to your search request and what format would have the result
Thanks for reading!
1 points April 19, 2006 by Stoyan at phpied.com post #
[-]
Suddenly structured articles
This post talks about a JavaScript that can be used on any web/blog
page to auto-generate a Table of Contents.
Motivation
Here's the idea I've playing with: say you have a relatively long web
page, where the content uses H1-H6 tags to structure the copy. How
about running a JavaScript when the page is loaded and getting a Table
of Contents (TOC) generated for you, based on the heading tags that
you've used? Wikipedia does this kind of TOCs, but on the server side,
using not H tags but the wiki tags.
Anyway, I decided it's a cool idea and rolled up the JS sleaves. Then
once I had the TOC sorted out, I added a list of external references,
meaning a list of all external links contained within the content.
Demo and files
Here's the demo
Here's the "bare" page - before running the script
The JS - the actual JavaScript
The CSS used to contain a few styles, this is irrelevant to the script
Integration
If you like the idea, you're free to get the files and experiment. All
you need to do is:
include the JS
create two divs in your document - one for the TOC (with an ID
"suddenly-structured-toc") and one for the list of external links (with
ID suddenly-structured-references)
call suddenly_structured.parse();
The code
The script is not 100% finished yet, I was thinking of adding some more
features, such as the ability to create TOCs only starting from H3 for
example. But if you want to play with the code, you're more than
welcome to.
What's happening in the code? There is an object/class called
suddenly_structured, its main "controller" method is parse() that calls
the other methods as needed. You can peek at the code for more but the
basically the work is done by the methods:
init() - initializes the "environment" where (in which element ID) is
the content, where to print the TOC and links.
traverse() - this goes through the document and if it finds a heading,
it adds it to a list, but first makes sure that this heading has an ID.
If there's no ID, a random one is generated.
generateTOC() - once we have a list of all the headings, we can
generate a TOC tree.
appendReferences() goes through all the links, checks the URL protocol
and host to make sure they are external links and adds to the list of
external references. When generating the list, I'm using the title
attribute of the A tag to make the list nicer.
/**
* Script flow controller
*
* @param {string} start_id The ID of the element that contains
the content.
* Default is the BODY element
* @param {string} output_id ID of the element that will contain
* the result TOC
* @param {string} output_ref_id ID of the element that will contain
the result
* list of external links
* @param {int} heading_level From which heading level to start (1
to 6),
* not yet implemented
*/
parse: function (start_id, output_id, output_ref_id, heading_level)
{
// initialize the environment pass all parameters
this.init(start_id, output_id, output_ref_id, heading_level);
// if the content is found, run through it to extract the headings
if (this.the_element) {
this.traverse();
}
// run through the extracted headings and generate TOC
this.generateTOC();
// add the TOC to the element specified
if (this.toc_div) {
this.toc_div.appendChild(this.stack[0].list);
}
// run through all the links and list them
if (this.ref_div) {
this.appendReferences();
}
}For the rest of the high-quality (*cough-cough*) JavaScript, check the
source.
Misc
At some point I figured out that quirksmore.org also has an
auto-generated TOC script. He grabs only the h2-h4 tags. His TOC is
links with different styles, and not a semantic HTML list. Here's his
post about how he coded the script. He also has a show/hide TOC which
is a very slick idea.
I also did my TOC and references lists to show/hide and left the
references hidden by default.
After I did the script (of course!) I decided to google other similar
scripts. It turned out, quite a few exist. But I didn't see any one
that uses UL or OL for the actual TOC. They all use DIVs and As with a
different style to do the indentation. My script uses a
semantically-correct list tags UL|OL (can be changed on the fly by
calling suddenly_structured.list_type = 'ul' for example) and LIs. But
that I guess because until recently no one was really losing any sleep
over semantic markup. The web was young ...
Thanks for reading!
That's it. Enjoy the script! Of course, any feedback is welcome.
I personally would like to integrate the script into this blog. I like
using heading tags and this way my articles will become ... suddenly
structured, TOC-ed and beautiful
0 points April 8, 2006 by Stoyan at phpied.com post #
collapse all | expand all
view more: « prev | next »
log in/register
username
password
remember me?
log in
username
password
verify password
register
options
show oldest first
group by feed
faq | store | feedback
a reddit site