* @author Sascha Leib */ class helper_plugin_adhoctags extends DokuWiki_Plugin { /* list of languages which normally use RTL scripts */ static protected $rtllangs = array('ar','dv','fa','ha','he','ks','ku','ps','ur','yi','arc'); /* list of right-to-left scripts (may override the language defaults to rtl) */ static protected $rtlscripts = array('arab','thaa','hebr','deva','shrd'); /* selection of left-to-right scripts (may override the language defaults to ltr) */ static protected $ltrscripts = array('latn','cyrl','grek','cyrs','armn'); /* Helper plugins should return info about the methods supported. */ public function getMethods() { $result = array(); $result[] = array( 'name' => 'buildAttributes', 'desc' => 'writes out element attributes', 'params' => array( 'data' => 'string', 'custom' => 'array', 'addClass (optional)' => 'string', 'mode (optional)' => 'string' ), 'return' => array('attributes' => 'array') ); return $result; } /** * build attributes */ function buildAttributes($data, $myObj, $addClass='', $mode='xhtml') { $attList = $this->getAttributes($data); $out = ''; //dbg('attList=' . print_r($attList, true)); if ($mode=='xhtml') { foreach($attList as $key => $val) { switch ($key) { /* common HTML attributes (always enabled) */ case 'id': /* id */ case 'class': /* custom classes */ case 'title': /* title */ case 'lang': /* language */ case 'tabindex': /* tabindex */ case 'is': /* is */ /* Microformat attributes */ case 'itemprop': /* item property */ case 'itemscope': /* item scope */ case 'itemref': /* item reference */ case 'itemid': /* item id (microformat) */ $out .= ' '.$key. (is_null($val) ? '' : '="'.$val.'"'); break; case 'dir': /* custom attribute: direction */ if (in_array(strtolower(trim($val)), array('ltr','rtl','auto'))) { $out .= ' dir="'. hsc($val).'"'; } break; case 'hidden': /* custom attribute: hidden */ if (in_array(strtolower(trim($val)), array('hidden','until-found'))) { $out .= ' hidden="'. hsc($val).'"'; } else { $out .= ' hidden'; } break; case 'style': /* style can be disabled */ if ($this->getConf('allowStyle') == '1') { $out .= ' '.$key.'="'.hsc($val).'"'; } break; default: /* special case 1: data-* attributes: */ if (preg_match('/^data-[a-z][a-z0-9_-]*$/', $key)) { $out .= ' '.$key.'="'.hsc($val).'"'; } /* special case 2: aria-* attributes: */ if (preg_match('/^aria-[a-z]+$/', $key)) { $out .= ' '.$key.'="'.hsc($val).'"'; } /* any other attribute: ask the class if it is allowed: */ if ($myObj->allowAttribute($key, $val)) { $out .= ' '.$key. (is_null($val) ? '' : '="'.$val.'"'); } } } // special case: no class name specified, but there is one passed down from a plugin: if($addClass !== '' && !isset($attr['class'])) { $out .= ' class="'.$addClass.'"'; } } return $out; } /** * get attributes (pull apart the string between '') * and identify classes, width, lang and dir * * @author Sascha Leib * @author Anika Henke * @author Christopher Smith * (parts taken from http://www.dokuwiki.org/plugin:box) */ function getAttributes($data) { //dbg('getAttributes("$data="' . $data . '"'); // store the attributes here: $attr = array(); // split up the attributes string (keep quoted and square brackets intact): $tokens = $this->tokenizeAttr($data); foreach ($tokens as $token) { //get language attribute if (preg_match('/^:([a-z\-]+)/', $token)) { $attr['lang'] = strtolower(trim($token,':')); continue; } //get id (IDs can not start with a number!) if (preg_match('/^#([A-Za-z]\w+)/', $token)) { $attr['id'] = trim($token,'#'); continue; } // get title (any quoted string) if (preg_match('/^\"(.*)\"$/', $token)) { $attr['title'] = trim($token,'"'); continue; } /* custom attributes */ if (preg_match('/^\[([^\]]+)\]$/', $token)) { $cAttr = explode('=', trim($token,'[]'), 2); //dbg('$token = ' . $token . ', $cAttr = ' . print_r($cAttr, true)); if ($cAttr) { $attr[$cAttr[0]] = ( isset($cAttr[1]) ? $cAttr[1] : null ); } continue; } //add to list of classes if it matches the pattern for class names: if (preg_match('/^[\w\d\-\\_]*$/',$token)) { $attr['class'] = (isset($attr['class']) ? $attr['class'].' ' : '') . $token; } } /* improved RTL detection to make sure it covers more cases: */ if(!array_key_exists('dir', $attr) && array_key_exists('lang', $attr) && $attr['lang'] !== '') { // turn the language code into an array of components: $arr = explode('-', $attr['lang']); // is the language iso code (first field) in the list of RTL languages? $rtl = in_array($arr[0], self::$rtllangs); // is there a Script specified somewhere which overrides the text direction? $rtl = ($rtl xor (bool) array_intersect( $rtl ? self::$ltrscripts : self::$rtlscripts, $arr)); $attr['dir'] = ( $rtl ? 'rtl' : 'ltr' ); } return $attr; } /** * Split the input data into suitable tokens * * @author Sascha Leib */ function tokenizeAttr($data) { $result = array(); $token = ''; // temporary storage of each item $escaped = false; // should the next character be treated "as is"? $state = 0; // parser state (0 = default, 1 = in quotation, 2 = in square backets) // loop over all characters: forEach(str_split($data) as $c) { switch($c) { case ' ': // Space case '\t': // Horizontal tabulation case '\n': // Newline case '\r': // Carriage return if (!$escaped && $state == 0) { if (trim($token)!==''){array_push($result, $token);} $token = ''; } else { $token .= ' '; $escaped = false; } break; case '"': // Quote if (!$escaped) { switch ($state) { case 0: if (trim($token)!==''){array_push($result, $token);} $state = 1; $token = $c; break; case 1: $token .= $c; array_push($result, $token); $state = 0; $token = ''; break; case 2: $token .= $c; break; default: // should never happen! } } else { $token .= $c; $escaped = false; } break; case '[': // Opening Square Brackets if (!$escaped && $state == 0) { if (trim($token)!==''){array_push($result, $token);} $token = $c; $state = 2; } else { $token .= $c; } break; case ']': // Opening Square Brackets if (!$escaped && $state == 2) { $token .= $c; array_push($result, $token); $token = ''; $state = 0; } else { $token .= $c; $escaped = false; } break; case '\\': // Escape character if (!$escaped) { // next character is escaped: $escaped = true; } else { $token .= $c; $escaped = false; } break; default: $token .= $c; $escaped = false; } } if (trim($token)!=='') {array_push($result, $token);} return $result; } /* Does anyone miss ODT support? */ }