PageRenderTime 181ms CodeModel.GetById 61ms app.highlight 63ms RepoModel.GetById 37ms app.codeStats 1ms

/lib/layout/naviforms.class.php

https://bitbucket.org/navigatecms/navigatecms
PHP | 1269 lines | 1130 code | 127 blank | 12 comment | 62 complexity | f449a4efefda17e8b1ac014f4a51cfd5 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1<?php
   2require_once(NAVIGATE_PATH.'/lib/packages/files/file.class.php');
   3
   4class naviforms
   5{
   6	public function select_from_object_array($id, $data, $value_field, $title_field, $selected_value="", $style="", $remove_keys=array(), $control_replacement=true)
   7	{
   8		$out = array();
   9
  10        $class = '';
  11        if($control_replacement)
  12        {
  13            $class = 'select2';
  14        }
  15
  16		$out[] = '<select class="'.$class.'" name="'.$id.'" id="'.$id.'" style="'.$style.'">';
  17				
  18		if(!is_array($data)) $data = array();
  19		
  20		foreach($data as $row)
  21        {
  22			if(in_array($row->{$value_field}, $remove_keys)) continue;
  23			if($row->{$value_field}==$selected_value)
  24				$out[] = '<option value="'.$row->{$value_field}.'" selected="selected">'.$row->{$title_field}.'</option>';
  25			else
  26				$out[] = '<option value="'.$row->{$value_field}.'">'.$row->{$title_field}.'</option>';
  27		}
  28		
  29		$out[] = '</select>';		
  30		
  31		return implode("\n", $out);	
  32	}
  33	
  34	public function selectfield($id, $values, $texts, $selected_value="", $onChange="", $multiple=false, $titles=array(), $style="", $control_replacement=true, $allow_custom_value=false, $extra_classes="")
  35	{
  36        $class = '';
  37        if($control_replacement)
  38            $class = 'select2';
  39
  40        $class.= ' '.$extra_classes;
  41
  42		$out = array();
  43		if($multiple)
  44			$out[] = '<select name="'.$id.'[]" id="'.$id.'" onchange="'.$onChange.'" multiple="multiple" style=" height: 100px; '.$style.' ">';
  45		else
  46			$out[] = '<select class="'.$class.'" name="'.$id.'" id="'.$id.'" onchange="'.$onChange.'" style="'.$style.'">';
  47
  48		for($i=0; $i < count($values); $i++)
  49		{
  50            if(!isset($titles[$i]))
  51                $titles[$i] = "";
  52
  53			if( (is_array($selected_value) && in_array($values[$i], $selected_value)) ||
  54				($values[$i]==$selected_value))
  55				$out[] = '<option value="'.$values[$i].'" selected="selected" title="'.$titles[$i].'">'.$texts[$i].'</option>';
  56			else
  57				$out[] = '<option value="'.$values[$i].'" title="'.$titles[$i].'">'.$texts[$i].'</option>';			
  58		}
  59		
  60		$out[] = '</select>';
  61
  62        if($allow_custom_value)
  63        {
  64            $out[] = '<a href="#" class="uibutton" data-action="create_custom_value"><i class="fa fa-plus"></i></a>';
  65        }
  66		
  67		return implode("\n", $out);	
  68	}
  69	
  70	public function buttonset($name, $options, $default, $onclick="", $jqueryui_icons=array())
  71	{
  72		$buttonset = array();
  73		$buttonset[] = '<div class="buttonset">';
  74
  75		foreach($options as $key => $val)
  76		{
  77			$buttonset[] = '<input type="radio" id="'.$name.'_'.$key.'" name="'.$name.'[]" value="'.$key.'" '.((!is_null($default) && ($default==$key))? ' checked="checked" ' : '').' />';
  78            //    $buttonset[] = '<label for="'.$name.'_'.$key.'"  onclick="'.$onclick.'"><span class="ui-button-icon-primary ui-icon '.$icon.'" style=" float: left; "></span> '.$val.'</label>';
  79			$buttonset[] = '<label class="unselectable" for="'.$name.'_'.$key.'"  onclick="'.$onclick.'">'.$val.'</label>';
  80		}
  81		
  82		$buttonset[] = '</div>';
  83		
  84		return implode("\n", $buttonset);		
  85	}
  86
  87    public function splitbutton($id, $title, $links, $texts)
  88	{
  89        global $layout;
  90
  91        $out = array();
  92        $out[] = '<div id="'.$id.'" class="nv-splitbutton" style="float: left;">';
  93        $out[] =    '<a class="'.$id.'_splitbutton_main" href="'.$links[0].'">'.$title.'</a><a href="#">'.t(200, 'Options').'</a>';
  94        $out[] = '</div>';
  95        $out[] = '<ul id="'.$id.'_splitbutton_menu" class="nv_splitbutton_menu" style="display: none; position: absolute; ">';
  96        for($i=0; $i < count($texts); $i++)
  97            $out[] = '<li><a href="'.$links[$i].'">'.$texts[$i].'</a></li>';
  98        $out[] = '</ul>';
  99
 100        $layout->add_script('
 101            $(".'.$id.'_splitbutton_main").splitButton();
 102        ');
 103
 104		return implode("\n", $out);
 105	}
 106	
 107	public function hidden($name, $value)
 108	{
 109		return '<input type="hidden" id="'.$name.'" name="'.$name.'" value="'.$value.'" />';
 110	}
 111	
 112	public function checkbox($name, $checked=false)
 113	{
 114		if($checked)
 115			$out = '<input id="'.$name.'" name="'.$name.'" type="checkbox" value="1" checked="checked" /><label for="'.$name.'"></label>';
 116		else
 117			$out = '<input id="'.$name.'" name="'.$name.'" type="checkbox" value="1" /><label for="'.$name.'"></label>';
 118			
 119		return $out;
 120	}
 121	
 122	public function textarea($name, $value="", $rows=4, $cols=48, $style="")
 123	{
 124        $value = htmlspecialchars($value);
 125		$out = 	'<textarea name="'.$name.'" id="'.$name.'" rows="'.$rows.'" cols="'.$cols.'" style="'.$style.'">'.$value.'</textarea>';
 126		return $out;
 127	}
 128	
 129	public function textfield($name, $value="", $width="400px", $action="", $extra="")
 130	{
 131        // may happen when converting a property type from (multilanguage) text to a (single) value
 132        if(is_array($value))
 133            $value = array_pop($value);
 134		$value = htmlspecialchars($value);
 135
 136        if(!empty($width))
 137            $extra .= ' style=" width: '.$width.';"';
 138
 139        if(!empty($action))
 140            $extra .= ' onkeyup="'.$action.'"';
 141
 142		$out = '<input type="text" name="'.$name.'" id="'.$name.'" value="'.$value.'" '.$extra.' />';
 143		return $out;	
 144	}
 145
 146	public function decimalfield($name, $value="", $precision=2, $decimal_separator=".", $thousands_separator="", $prefix="", $suffix="", $width="400px", $action="", $extra="")
 147	{
 148	    global $layout;
 149
 150        // may happen when converting a property type from (multilanguage) text to a (single) value
 151        if(is_array($value))
 152            $value = array_pop($value);
 153		$value = htmlspecialchars($value);
 154
 155        if(!empty($width))
 156            $extra .= ' style=" width: '.$width.';"';
 157
 158        if(!empty($action))
 159            $extra .= ' onkeyup="'.$action.'"';
 160
 161        if(intval($value) == $value)
 162            $value = intval($value);
 163
 164		$out = '<input type="text" name="'.$name.'" id="'.$name.'" value="'.$value.'" '.$extra.' />';
 165
 166		if(!empty($prefix)) $prefix.= " ";
 167		if(!empty($suffix)) $suffix = " " . $suffix;
 168
 169        $layout->add_script('
 170            $("#'.$name.'").inputmask(
 171                { 
 172                    alias: "decimal",
 173                    rightAlign: false,
 174                    digitsOptional: true,
 175                    autoGroup: true,
 176                    allowMinus: true,
 177                    digits: '.$precision.',
 178                    radixPoint: "'.$decimal_separator.'",
 179                    groupSeparator: "'.$thousands_separator.'",                    
 180                    suffix: "'.$suffix.'",
 181                    prefix: "'.$prefix.'"
 182                }
 183            );
 184        ');
 185
 186		return $out;
 187	}
 188	
 189	public function autocomplete($name, $value="", $source, $callback='""', $width="400px", $add_custom_value=false)
 190	{
 191		global $layout;
 192		
 193		$value = htmlspecialchars($value);
 194		
 195		$out = '<input type="text" name="'.$name.'" id="'.$name.'" value="'.$value.'" style=" width: '.$width.';" />';
 196
 197        if(is_array($source))
 198            $source = '["'.implode('","', $source).'"]';
 199        else
 200            $source = '"'.$source.'"';
 201
 202		$layout->add_script('
 203			$("#'.$name.'").autocomplete(
 204			{
 205				source: '.$source.',
 206				minLength: 1,
 207				select: '.$callback.'				
 208			});
 209		');
 210
 211        if($add_custom_value)
 212        {
 213            $uid = uniqid();
 214            $out .= ' <a href="#" class="uibutton" data-action="create_custom_value" data-uid="'.$uid.'"><i class="fa fa-plus"></i></a>';
 215            $layout->add_script('
 216                var naviforms_autocomplete_'.$uid.' = [];
 217                
 218                $("a[data-action=create_custom_value][data-uid='.$uid.']").on("click", function()
 219                {
 220                    var text = prompt(navigate_t(159, "Name"));
 221                    text = text.trim();
 222                    if(text != "")
 223                    {
 224                        naviforms_autocomplete_'.$uid.'.unshift(text);
 225                        $("#'.$name.'").val(text);
 226                        $("#'.$name.'").trigger("navigate-added-custom-value");                         
 227                    }
 228                });
 229                
 230                $("#'.$name.'").on( "autocompleteresponse", function( event, ui ) 
 231                {
 232                    for(i in naviforms_autocomplete_'.$uid.')
 233                        ui.content.unshift({ "id": "custom-" + new Date().getTime(), "label": naviforms_autocomplete_'.$uid.'[i], "value": naviforms_autocomplete_'.$uid.'[i]});
 234                });
 235            ');
 236        }
 237
 238		return $out;	
 239	}	
 240	
 241	public function datefield($name, $value="", $hour=false, $style="")
 242	{
 243		global $layout;
 244		global $user;
 245
 246		if(!empty($value))
 247			$value = core_ts2date($value, $hour);
 248
 249		$out = '<input type="text" class="datepicker" name="'.$name.'" id="'.$name.'" value="'.$value.'" style="'.$style.'" />
 250				<img src="img/icons/silk/calendar_delete.png" width="16" height="16" align="absmiddle" 
 251					 style=" cursor: pointer; " onclick=" $(this).parent().find(\'input\').val(\'\'); $(this).parent().find(\'input\').trigger(\'change\'); " />';
 252		
 253		$format = $user->date_format;   // custom user date format
 254
 255        // format to jquery ui datepicker
 256        // http://docs.jquery.com/UI/Datepicker/formatDate
 257        $format = php_date_to_jquery_ui_datepicker_format($format);
 258
 259        $translations = '
 260                monthNames: [
 261                    "'.t(101, "January").'",
 262                    "'.t(102, "February").'",
 263                    "'.t(103, "March").'",
 264                    "'.t(104, "April").'",
 265                    "'.t(105, "May").'",
 266                    "'.t(106, "June").'",
 267                    "'.t(107, "July").'",
 268                    "'.t(108, "August").'",
 269                    "'.t(109, "September").'",
 270                    "'.t(110, "October").'",
 271                    "'.t(111, "November").'",
 272                    "'.t(112, "December").'"
 273                ],
 274                monthNamesShort: [
 275                    "'.t(113, "Jan").'",
 276                    "'.t(114, "Feb").'",
 277                    "'.t(115, "Mar").'",
 278                    "'.t(116, "Apr").'",
 279                    "'.t(117, "May").'",
 280                    "'.t(118, "Jun").'",
 281                    "'.t(119, "Jul").'",
 282                    "'.t(120, "Aug").'",
 283                    "'.t(121, "Sept").'",
 284                    "'.t(122, "Oct").'",
 285                    "'.t(123, "Nov").'",
 286                    "'.t(124, "Dec").'"
 287                ],
 288                dayNames: [
 289                    "'.t(131, "Sunday").'",
 290                    "'.t(125, "Monday").'",
 291                    "'.t(126, "Tuesday").'",
 292                    "'.t(127, "Wednesday").'",
 293                    "'.t(128, "Thursday").'",
 294                    "'.t(129, "Friday").'",
 295                    "'.t(130, "Saturday").'"
 296                ],
 297                dayNamesShort: [
 298                    "'.t(138, "Sun").'",
 299                    "'.t(132, "Mon").'",
 300                    "'.t(133, "Tue").'",
 301                    "'.t(134, "Wed").'",
 302                    "'.t(135, "Thu").'",
 303                    "'.t(136, "Fri").'",
 304                    "'.t(137, "Sat").'"
 305                ],
 306                dayNamesMin: [
 307                    "'.t(138, "Sun").'",
 308                    "'.t(132, "Mon").'",
 309                    "'.t(133, "Tue").'",
 310                    "'.t(134, "Wed").'",
 311                    "'.t(135, "Thu").'",
 312                    "'.t(136, "Fri").'",
 313                    "'.t(137, "Sat").'"
 314                ],
 315                prevText: "'.t(501, "Previous").'",
 316                nextText: "'.t(502, "Next").'",
 317                closeText: "'.t(92, "Close").'",
 318                currentText: "'.t(503, "Now").'",
 319                timeText: "'.t(504, "Time").'",
 320                hourText: "'.t(93, "Hour").'",
 321                minuteText: "'.t(94, "Minute").'",
 322                secondText: "'.t(96, "Second").'",
 323        ';
 324
 325		if(!$hour)
 326        {
 327            $format = str_replace('H:i', '', $format);
 328            $layout->add_script('
 329                $("#'.$name.'").datepicker(
 330                {
 331                    '.$translations.'
 332                    dateFormat: "'.trim($format).'",
 333                    changeMonth: true,
 334                    changeYear: true
 335                });
 336            ');
 337        }
 338        else
 339        {
 340            $format = str_replace('H:i', '', $format);
 341            $layout->add_script('
 342                navigatecms.forms.datepicker["'.$name.'"] = $("#'.$name.'").datetimepicker(
 343                {
 344                    '.$translations.'
 345                    dateFormat: "'.trim($format).'",
 346                    timeFormat: "HH:mm",
 347                    changeMonth: true,
 348                    changeYear: true,
 349                    timezone: null,
 350                    onClose: function()
 351                    {
 352                        if(navigatecms.forms.datepicker["'.$name.'"].qtip_obj)
 353						{
 354							navigatecms.forms.datepicker["'.$name.'"].qtip_obj.qtip("hide");
 355							navigatecms.forms.datepicker["'.$name.'"].qtip_obj.qtip("disable");
 356						}
 357                    },
 358                    onChangeMonthYear: function(year, month, instance)
 359                    {
 360						setTimeout(function()
 361						{
 362							if(navigatecms.forms.datepicker["'.$name.'"].qtip_obj)
 363							{
 364								navigatecms.forms.datepicker["'.$name.'"].qtip_obj.qtip("hide");
 365								navigatecms.forms.datepicker["'.$name.'"].qtip_obj.qtip("disable");
 366							}
 367							else
 368							{
 369	                            navigatecms.forms.datepicker["'.$name.'"].qtip_obj = $("table.ui-datepicker-calendar").qtip(
 370								{
 371								    content: "'.t(609, "Click a day of the month selected to update the value", null, true).'",
 372									overwrite: true,
 373									show: true,
 374							        hide:
 375							        {
 376								        event: "unfocus"
 377						            },
 378							        style:
 379							        {
 380								        tip: true,
 381								        width: 200,
 382								        classes: "qtip-cream"
 383							        },
 384							        position:
 385							        {
 386								        at: "center right",
 387								        my: "bottom left"
 388							        }
 389						        });
 390					        }
 391                        }, 100);
 392                    }
 393                });
 394            ');
 395        }
 396
 397		return $out;
 398	}
 399
 400    public function colorfield($name, $value="#ffffff", $swatches=array(), $onchange='')
 401    {
 402        global $layout;
 403        global $user;
 404
 405        $out = '<input type="text" class="naviforms-colorpicker-text" name="'.$name.'" id="'.$name.'" value="'.$value.'" data-previous="'.$value.'" />
 406                <div id="'.$name.'-selector" class="naviforms-colorpicker-selector ui-corner-all"><div style="background: '.$value.'; "></div></div>';
 407
 408        if(!is_array($swatches) || empty($swatches))
 409            $swatches = array();
 410
 411        $swatches = array_map(function($c) { return hex2rgb($c); }, $swatches);
 412
 413        $swatches_js = "{";
 414        for($s=0; $s < count($swatches); $s++)
 415        {
 416            $swatches_js .= '"'.$s.'": {r:'.($swatches[$s]['r']/255).', g:'.($swatches[$s]['g']/255).', b:'.($swatches[$s]['b']/255).'},';
 417        }
 418        $swatches_js.= "}";
 419
 420        if($swatches_js == "{}")
 421            $swatches_js = "null";
 422
 423        $layout->add_script('
 424            $("input[name=\"'.$name.'\"]").colorpicker({
 425                altField: $("input[name=\"'.$name.'\"]").next().find("> div"),
 426                altOnChange: true,
 427                regional: "'.$user->language.'",
 428                colorFormat: ["#HEX"],
 429                draggable: true,
 430                parts: ["header", "map", "bar", "hex", "hsv", "rgb", "preview", "swatches", "footer"],
 431                //layout: { "memory": [6,0,1,5] },
 432                alpha: false,
 433                showNoneButton: false,
 434                position: {
 435                    my: "left top",
 436                    at: "right+8 top",
 437                    of: $("input[name=\"'.$name.'\"]").next()
 438                },
 439                okOnEnter: true,
 440                revert: true,
 441                swatches: '.$swatches_js.',
 442                swatchesWidth: 80,
 443                open: function(event, color)
 444                {                
 445                    if($(".ui-colorpicker-dialog").position().top + $(".ui-colorpicker-dialog").height() > $(window).height())                    
 446                        $(".ui-colorpicker-dialog").css("top", $(window).height() - $(".ui-colorpicker-dialog").height() - 8);
 447                                        
 448                    $("input[name=\"'.$name.'\"]").data("previous", $("input[name=\"'.$name.'\"]").val());
 449                    $("input[name=\"'.$name.'\"]").next().children().css("backgroundColor", $("input[name=\"'.$name.'\"]").data("previous"));
 450                    $("input[name=\"'.$name.'\"]").colorpicker("setColor", $("input[name=\"'.$name.'\"]").data("previous"));
 451                },
 452                select: function(event, color)
 453                {
 454                    '.(!empty($onchange)? $onchange.'($("input[name=\"'.$name.'\"]"))' : '').'                    
 455                },
 456                cancel: function(event, color)
 457                {
 458                    $("input[name=\"'.$name.'\"]").val($("input[name=\"'.$name.'\"]").data("previous"));
 459                    $("input[name=\"'.$name.'\"]").next().children().css("backgroundColor", $("input[name=\"'.$name.'\"]").data("previous"));
 460                }
 461            });
 462        ');
 463
 464        return $out;
 465    }
 466
 467	public function scriptarea($name, $value, $syntax="js", $style= " width: 75%; height: 250px; ")
 468	{
 469		global $layout;
 470		
 471		$out = '<textarea name="'.$name.'" id="'.$name.'" style=" '.$style.' " rows="10">'.$value.'</textarea>';
 472
 473		$layout->add_script('
 474			$(window).on("load", function()
 475			{
 476				var cm = CodeMirror.fromTextArea(
 477				    document.getElementById("'.$name.'"), 
 478                    {
 479                        mode: "text/html", 
 480                        tabMode: "indent",
 481                        lineNumbers: true,
 482                        styleActiveLine: true,
 483                        matchBrackets: true,
 484                        autoCloseTags: true,
 485                        extraKeys: {"Ctrl-Space": "autocomplete"}
 486                    }
 487                );
 488
 489		        CodeMirror.commands.autocomplete = function(cm) {
 490                    CodeMirror.showHint(cm, CodeMirror.htmlHint);
 491                }
 492
 493				navigate_codemirror_instances.push(cm);
 494	
 495				$("#'.$name.'").next().attr("style", "'.$style.'");
 496				$(".CodeMirror-scroll").css({ width: "100%", height: "100%"});
 497				
 498				cm.refresh();
 499			});
 500		');
 501
 502		return $out;
 503	}
 504	
 505	public function editorfield($name, $value, $width="80%", $lang="es", $website_id=NULL)
 506	{
 507		global $layout;
 508		global $website;
 509		global $user;
 510
 511		$height = 400;
 512        
 513        $ws = $website;
 514        if(!empty($website_id) && $website_id!=$website->id)
 515        {
 516            $ws = new website();
 517            $ws->load($website_id);
 518        }
 519
 520		$text = htmlentities($value, ENT_HTML5 | ENT_NOQUOTES, 'UTF-8', true);
 521
 522		// remove unneeded new lines (to fix a problem of extra spaces in pre/code tags)
 523		$text = str_replace('&NewLine;', '', $text);
 524
 525		$out = '<textarea name="'.$name.'" id="'.$name.'" style=" width: '.$width.'; height: '.$height.'px; ">'.$text.'</textarea>';
 526
 527        $content_css = $ws->content_stylesheets('tinymce', 'content');
 528
 529        // note, a file in the "content_selectable" property must also be in the "content" property for TinyMCE to work as expected
 530        $content_css_selectable = $ws->content_stylesheets('tinymce', 'content_selectable');
 531
 532		$tinymce_language = $user->language;
 533
 534        $layout->add_script('    
 535            tinyMCE.baseURL = "'.NAVIGATE_URL.'/lib/external/tinymce4";
 536            $("#'.$name.'").tinymce(
 537            {
 538                language: "'.$tinymce_language.'",
 539                
 540                width: ($("#'.$name.'").width()) + "px",
 541                height: $("#'.$name.'").height() + "px",
 542                resize: "both",
 543                
 544                menubar: false,
 545                theme: "modern",
 546                skin: "navigatecms-cupertino",
 547                			    
 548			    plugins: [
 549				    "compat3x noneditable",
 550				    "advlist autolink nv_link image lists charmap print preview hr anchor pagebreak",
 551				    "searchreplace wordcount visualblocks visualchars fullscreen media nonbreaking",
 552				    "table directionality template textcolor paste textcolor colorpicker textpattern",
 553				    "codesample codemirror importcss imagetools paste magicline fontawesome nv_rollups" // add fullpage to edit full HTML code with head and body tags
 554				],
 555				
 556				external_plugins: {
 557				    "loremipsum": "'.NAVIGATE_URL.'/lib/external/tinymce4/plugins/loremipsum/editor_plugin.js",
 558				    "imgmap": "'.NAVIGATE_URL.'/lib/external/tinymce4/plugins/imgmap/editor_plugin.js",
 559				    "style": "'.NAVIGATE_URL.'/lib/external/tinymce4/plugins/style/editor_plugin.js",
 560				    "xhtmlxtras": "'.NAVIGATE_URL.'/lib/external/tinymce4/plugins/xhtmlxtras/editor_plugin.js"
 561				},
 562				
 563				toolbar: [
 564					"formatselect fontselect fontsizeselect | forecolor | backcolor | removeformat | searchreplace code",
 565                    "bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | outdent indent blockquote | bullist numlist | nv_rollup_special_char",
 566                    "styleselect | styleprops attribs | table | nv_rollup_links | image imgmap media codesample | magicline | undo redo"
 567                ],
 568
 569				toolbar_items_size: "small",
 570				
 571				// forced fix to avoid tinymce adding <p> element on non block elements (span, i, etc)
 572				// needed mainly for Codemirror plugin, but force_p_newlines is deprecated by the TinyMCE team
 573				forced_root_block: "",
 574				force_br_newlines : true,
 575                force_p_newlines : true,
 576				
 577			    browser_spellcheck: true,
 578                spellchecker_language: "'.$lang.'",
 579                
 580                noneditable_noneditable_class: "fa",    // without this, TinyMCE removes the Font Awesome icons when editing the content
 581                
 582                media_live_embeds: false, // disable iframe loading (like videos) to allow resizing
 583                
 584                magicline_color: "#0070a3",
 585                magicline_targetedItems: ["DIV", "IMG", "IFRAME", "PRE", "TABLE", "ARTICLE", "UL", "OL", "BLOCKQUOTE", "TR"],
 586                magicline_triggerMargin: 24,
 587			    
 588			    codemirror: {
 589					path:  "'.NAVIGATE_URL.'/lib/external/codemirror",
 590				    indentOnInit: true,
 591                    config: {
 592                        mode: "htmlmixed",
 593                        lineNumbers: true
 594                    },
 595                    jsFiles: [
 596                        "mode/htmlmixed/htmlmixed.js"
 597                    ]
 598				},
 599				
 600				image_advtab: true,
 601				
 602				automatic_uploads: true,
 603			    paste_data_images: true,
 604				images_upload_url: "navigate_upload.php?engine=tinymce&session_id='.session_id().'&debug",
 605				
 606				fontsize_formats: "8px 9px 10px 11px 12px 13px 14px 15px 16px 17px 18px 20px 24px 26px 28px 30px 32px 36px 42px 48px 56px 64px", 
 607                
 608                content_css: "'.$content_css.'",
 609                
 610				style_formats_merge: true,
 611                importcss_append: false,
 612                importcss_file_filter: function(value) 
 613                {
 614                    var files = "'.$content_css_selectable.'";    
 615                    
 616                    if(files.indexOf(",") > -1)
 617                    {
 618                        files = files.split(",");                                                                                                                                 
 619	                    for(var i=0; i < files.length; i++)
 620	                    {
 621	                        if(value.indexOf(files[i]) !== -1)
 622	                        {
 623	                            return true;
 624	                        }
 625	                    }
 626	                    return false;
 627                    }
 628                    else
 629                    {
 630                        return (value==files);
 631                    }
 632                },               
 633                                
 634                //  https://www.tinymce.com/docs/configure/url-handling
 635                convert_urls: false,
 636                relative_urls: true,
 637                remove_script_host: false,
 638                
 639                // https://www.tinymce.com/docs/configure/content-filtering/
 640                valid_elements: "*[*],+a[*],+p[*],#i",
 641                custom_elements: "nv,code,pre,nvlist,nvlist_conditional,figure,article,header,footer,post,nav",
 642                extended_valid_elements: "+nv[*],+pre[*],+code[*],+nvlist[*],+nvlist_conditional[*],+figure[*],+article[*],+nav[*],+i[*],+span[*],+em[*],+b[*],*[*]",
 643                valid_children: "+a[div|p|li],+body[style|script|nv|nvlist|nvlist_conditional],+code[nv|nvlist|nvlist_conditional]",
 644                
 645                paste_as_text: true,
 646                
 647                // https://www.tinymce.com/docs/configure/content-filtering/#allow_html_in_named_anchor
 648                allow_html_in_named_anchor: true,          
 649                
 650                // events
 651                handle_event_callback : "navigate_tinymce_event",
 652                
 653                // before rendering this tinymce
 654                setup: function(editor)
 655                {
 656	                editor.on("init", function() 
 657	                { 	                
 658				        $(editor.getWin()).on("scroll blur focus", function(e)
 659				        {
 660                            navigate_tinymce_event(e, "'.$name.'");
 661				        });
 662				        
 663				        // restore last known iframe scroll position
 664				        navigate_tinymce_event({type: "focus"}, "'.$name.'", true);
 665	                    setTimeout(function()
 666	                    {
 667	                        navigate_tinymce_event({type: "focus"}, "'.$name.'", true);
 668	                    }, 25);
 669				    });					    		    				   
 670                },
 671                
 672                // just after rendering this tinymce 
 673                init_instance_callback: function(editor)
 674                {                      
 675					// find missing images
 676					$("#'.$name.'").parent().find("iframe").contents().find("img").each(function()
 677					{
 678						if( (typeof this.naturalWidth != "undefined" && this.naturalWidth == 0 ) 
 679					        || this.readyState == "uninitialized" )					         
 680				        {
 681					        $(this).addClass("nomagicline");
 682					    }
 683					});
 684                
 685                    $("#'.$name.'").parent().find("iframe").droppable(
 686                    {
 687                        drop: function(event, ui)
 688                        {
 689                            if(!$(ui.draggable).attr("id")) // not a file!
 690                            {
 691                                $("#'.$name.'_tbl").css("opacity", 1);
 692                                return;
 693                            }
 694
 695                            var file_id = $(ui.draggable).attr("id").substring(5);
 696                            if(!file_id || file_id=="" || file_id==0) return;
 697                            var media = $(ui.draggable).attr("mediatype");
 698                            var mime = $(ui.draggable).attr("mimetype");
 699                            var web_id = "'.$ws->id.'";
 700                            navigate_tinymce_add_content($("#'.$name.':tinymce").attr("id"), file_id, media, mime, web_id, ui.draggable);
 701                            $("#'.$name.'").parent().find("> .mce-tinymce").css("opacity", 1);
 702                        },
 703                        over: function(event, ui)
 704                        {
 705                            if(!$(ui.draggable).attr("id")) // not a file!
 706                                return;
 707
 708                            $("#'.$name.'").parent().find("> .mce-tinymce").css("opacity", 0.75);
 709                        },
 710                        out: function(event, ui)
 711                        {
 712                            $("#'.$name.'").parent().find("> .mce-tinymce").css("opacity", 1);
 713                        }
 714                    });
 715                    
 716                    // deprecated, but the only way we found to activate the button on init
 717	                tinyMCE.get("'.$name.'").controlManager.setActive("magicline", true);
 718	                	                
 719                    // user warning to avoid losing unsaved changes
 720                    editor.on("change", function()
 721                    {
 722                        navigate_beforeunload_register();
 723                    });
 724                    
 725                    // workaround for TinyMCE 4.6.2 fontsize selector bug
 726                    var fontsize_button = $("#'.$name.'").prev().find(".mce-toolbar-grp button").eq(2);
 727                    if( $(fontsize_button).find(".mce-txt").length == 0)
 728                        $(fontsize_button).append("<span class=\"mce-txt\">12px</span>");
 729                }
 730            });
 731        ');
 732
 733        $layout->navigate_editorfield_link_dialog();
 734
 735		return $out;
 736	}
 737	
 738	public function dropbox($name, $value=0, $media="", $disabled=false, $default_value=null, $options=array(), $website_id=null)
 739	{
 740		global $layout;
 741		global $website;
 742        global $theme;
 743
 744        if(empty($website_id))
 745            $website_id = $website->id;
 746
 747		$out = array();
 748        $out[] = '<div id="'.$name.'-droppable-wrapper" class="navigate-droppable-wrapper">';
 749
 750		$out[] = '<input type="hidden" id="'.$name.'" name="'.$name.'" value="'.$value.'" />';		
 751
 752		$out[] = '<div id="'.$name.'-droppable" class="navigate-droppable ui-corner-all" data-media="'.$media.'">';
 753
 754		if(!empty($value))
 755		{
 756			if($media=='image')
 757            {
 758                $f = new file();
 759                $f->load($value);
 760                $out[] = '<img title="'.$f->name.'" src="'.NAVIGATE_DOWNLOAD.'?wid='.$website_id.'&id='.$f->id.'&amp;disposition=inline&amp;width=75&amp;height=75"
 761                                                    data-src-original="'.NAVIGATE_DOWNLOAD.'?wid='.$website_id.'&id='.$f->id.'&amp;disposition=inline" />';
 762            }
 763            else if($media=='video')
 764            {
 765                $layout->add_script('
 766                    $(window).load(function() { navigate_dropbox_load_video("'.$name.'", "'.$value.'"); });
 767                ');
 768
 769                $out[] = '<figure class="navigatecms_loader"></figure>';
 770            }
 771			else
 772            {
 773                $f = new file();
 774                $f->load($value);
 775				$out[] = '<img title="'.$f->name.'" src="'.(navibrowse::mimeIcon($f->mime, $f->type)).'" width="50" height="50" /><br />'.$f->name;
 776            }
 777		}
 778		else
 779        {
 780			$out[] = '	<img src="img/icons/misc/dropbox.png" vspace="18" />';
 781        }
 782		$out[] = '</div>';
 783
 784        // set parent row as overflow:visible to let the whole contextmenu appear
 785        $layout->add_script('
 786            $(".navigate-droppable-wrapper").parent().css("overflow", "visible");
 787        ');
 788
 789        $contextmenu = false;
 790
 791		if(!$disabled)
 792		{
 793			$out[] = '<div class="navigate-droppable-cancel"><img src="img/icons/silk/cancel.png" /></div>';
 794            if($media=='image')
 795            {
 796                if($options == 'a:0:{}')
 797                    $options = array();
 798
 799                if(empty($options) && !empty($default_value))
 800                    $options = array($default_value => t(199, "Default value"));
 801                else
 802                    $options = (array)$options;
 803
 804                if(!empty($options))
 805                {
 806                    $out[] = '
 807                        <div class="navigate-droppable-create">
 808                            <img src="img/icons/silk/add.png" />
 809                        </div>
 810                    ';
 811
 812                    // "create" context menu actions (image picker)
 813                    $ws = new website();
 814                    if($website_id==$website->id)
 815                        $ws = $website;
 816                    else
 817                        $ws->load($website_id);
 818
 819                    $ws_theme = new theme();
 820                    if($website_id==$website->id)
 821                        $ws_theme = $theme;
 822                    else
 823                        $ws_theme->load($ws->theme);
 824
 825                    $layout->add_content('
 826                        <ul id="'.$name.'-image_picker" class="navigate-image-picker navi-ui-widget-shadow">
 827                            '.implode("\n", array_map(
 828                                function($k, $v) use ($website_id, $ws_theme)
 829                                {
 830                                    if(!empty($ws_theme))
 831                                        $v = $ws_theme->t($v);
 832
 833                                    return '
 834                                        <li data-value="'.$k.'" data-src="'.NAVIGATE_DOWNLOAD.'?wid='.$website_id.'&id='.$k.'&amp;disposition=inline&amp;width=75&amp;height=75">
 835                                            <a href="#">
 836                                                <img title="'.$v.'" src="'.NAVIGATE_DOWNLOAD.'?wid='.$website_id.'&id='.$k.'&amp;disposition=inline&amp;width=48&amp;height=48" />
 837                                                <span>'.$v.'</span>
 838                                            </a>
 839                                        </li>
 840                                    ';
 841                                },
 842                                array_keys($options),
 843                                array_values($options)
 844                            )).'
 845                        </ul>
 846                    ');
 847
 848                    $layout->add_script('
 849                        $("#'.$name.'-droppable").parent().find(".navigate-droppable-create").on(
 850                        "click",
 851                        function(ev)
 852				        {
 853                            navigate_hide_context_menus();
 854                            setTimeout(function()
 855                            {
 856                                $("#'.$name.'-image_picker").menu();
 857                                $("#'.$name.'-image_picker").css({left: ev.pageX, top: ev.pageY});
 858                                $("#'.$name.'-image_picker").show();
 859                                
 860                                if($("#'.$name.'-image_picker").position().top + $("#'.$name.'-image_picker").height() > $(window).height())                    
 861                                    $("#'.$name.'-image_picker").css("top", $(window).height() - $("#'.$name.'-image_picker").height() - 8);
 862
 863                                $("#'.$name.'-image_picker li").off().on("click", function()
 864                                {
 865                                    $("#'.$name.'").val($(this).data("value"));
 866                                    $("#'.$name.'-droppable").html("<img src=\"" + $(this).data("src") + "\" />");
 867                                    $("#'.$name.'-droppable").parent().find(".navigate-droppable-cancel").show();
 868                                    $("#'.$name.'-droppable").parent().find(".navigate-droppable-create").hide();
 869                                });
 870                            }, 100);
 871                        });
 872                    ');
 873
 874                    $contextmenu = true;
 875                }
 876
 877                // images: add context menu over the image itself to define focal point, description and title...
 878                $out[] = '
 879                    <ul class="navigate-droppable-edit-contextmenu" style="display: none;">
 880                        <li action="permissions"><a href="#"><span class="ui-icon ui-icon-key"></span>'.t(17, "Permissions").'</a></li>
 881                        <li action="focalpoint"><a href="#"><span class="ui-icon ui-icon-image"></span>'.t(540, "Focal point").'</a></li>
 882                        <li action="description"><a href="#"><span class="ui-icon ui-icon-comment"></span>'.t(334, 'Description').'</a></li>
 883                        <li action="preview"><a href="#"><span class="ui-icon ui-icon-zoomin"></span>'.t(274, 'Preview').'</a></li>
 884                    </ul>
 885                ';
 886                
 887                $layout->add_script('
 888                    $("#'.$name.'-droppable").on("contextmenu", function(ev)
 889                    {
 890                        ev.preventDefault();
 891                        navigate_hide_context_menus();
 892                        var file_id = $("#'.$name.'").val();
 893                        if(!file_id || file_id=="" || file_id==0) return;
 894
 895                        setTimeout(function()
 896                        {
 897                            var menu_el = $("#'.$name.'-droppable").parent().find(".navigate-droppable-edit-contextmenu");
 898
 899							var menu_el_clone = menu_el.clone();
 900							menu_el_clone.appendTo("body");
 901
 902                            menu_el_clone.menu();
 903
 904                            menu_el_clone.css({
 905                                "z-index": 100000,
 906                                "position": "absolute",
 907                                "left": ev.clientX,
 908                                "top": ev.clientY
 909                            }).addClass("navi-ui-widget-shadow").show();
 910
 911	                        menu_el_clone.find("a").on("click", function(ev)
 912		                    {
 913		                        ev.preventDefault();
 914		                        var action = $(this).parent().attr("action");
 915		                        var file_id = $("#'.$name.'").val();
 916
 917		                        switch(action)
 918		                        {
 919		                            case "permissions":
 920		                            navigate_contextmenu_permissions_dialog(file_id);
 921		                            break;
 922
 923		                            case "focalpoint":
 924		                            navigate_media_browser_focalpoint(file_id);
 925		                            break;
 926
 927		                            case "description":
 928		                            $.get(
 929		                                NAVIGATE_APP + "?fid=files&act=json&op=description&id=" + file_id,
 930		                                function(data)
 931		                                {
 932		                                    data = $.parseJSON(data);
 933		                                    navigate_contextmenu_description_dialog(file_id, $("#'.$name.'-droppable"), data.title, data.description);
 934		                                }
 935		                            );
 936		                            break;
 937		                            
 938		                            case "preview":
 939		                                $("#'.$name.'-droppable img[data-src-original]").trigger("dblclick");
 940		                            break;
 941		                        }
 942		                    });
 943                        }, 100);
 944                    });
 945                ');
 946            }
 947            else if($media=='video')
 948            {
 949                $out[] = '
 950                    <div class="navigate-droppable-create">
 951                        <img src="img/icons/silk/add.png" />
 952                        <ul class="navigate-droppable-create-contextmenu" data-field-id="'.$name.'">
 953                            <li action="default" value="'.$default_value.'"><a href="#"><span class="fa fa-lg fa-eraser"></span> '.t(199, "Default value").'</a></li>
 954                            <li action="youtube_url"><a href="#"><span class="fa fa-lg fa-youtube-square fa-align-center"></span> Youtube URL</a></li>
 955                            <li action="vimeo_url"><a href="#"><span class="fa fa-lg fa-vimeo-square fa-align-center"></span> Vimeo URL</a></li>
 956                        </ul>
 957                    </div>
 958                ';
 959
 960                // context menu actions
 961                $layout->add_script('
 962                    if('.(empty($default_value)? 'true' : 'false').')
 963                        $("#'.$name.'-droppable").parent().find(".navigate-droppable-create-contextmenu li[action=default]").remove();
 964
 965                    $("#'.$name.'-droppable").parent()
 966                        .find(".navigate-droppable-create")
 967                        .find(".navigate-droppable-create-contextmenu li")
 968                        .on("click", function()
 969                        {
 970                            setTimeout(function() { navigate_hide_context_menus(); }, 100);
 971
 972                            switch($(this).attr("action"))
 973                            {
 974                                case "default":
 975                                    $("#'.$name.'-droppable").html(\'<figure class="navigatecms_loader"></figure>\');
 976                                    navigate_dropbox_load_video("'.$name.'", "'.$default_value.'");
 977                                    break;
 978
 979                                case "youtube_url":
 980                                    $("<div><form action=\"#\" onsubmit=\"return false;\"><input type=\"text\" name=\"url\" value=\"\" style=\"width: 100%;\" /></form></div>").dialog({
 981                                        "title": "Youtube URL",
 982                                        "modal": true,
 983                                        "width": 500,
 984                                        "height": 120,
 985                                        "buttons": {
 986                                            "'.t(190, "Ok").'": function(e, ui)
 987                                            {
 988                                                var reference = navigate_youtube_reference_from_url($(this).find("input").val());
 989                                                if(reference && reference!="")
 990                                                {
 991                                                    $("#'.$name.'-droppable").html(\'<figure class="navigatecms_loader"></figure>\');
 992                                                    navigate_dropbox_load_video("'.$name.'", "youtube#" + reference);
 993                                                }
 994                                                $(this).dialog("close");
 995                                            },
 996                                            "'.t(58, "Cancel").'": function() { $(this).dialog("close"); }
 997                                        }
 998                                    });
 999                                    break;
1000
1001                                case "vimeo_url":
1002                                    $("<div><form action=\"#\" onsubmit=\"return false;\"><input type=\"text\" name=\"url\" value=\"\" style=\"width: 100%;\" /></form></div>").dialog({
1003                                        "title": "Vimeo URL",
1004                                        "modal": true,
1005                                        "width": 500,
1006                                        "height": 120,
1007                                        "buttons": {
1008                                            "'.t(190, "Ok").'": function(e, ui)
1009                                            {
1010                                                var reference = navigate_vimeo_reference_from_url($(this).find("input").val());
1011                                                if(reference && reference!="")
1012                                                {
1013                                                    $("#'.$name.'-droppable").html(\'<figure class="navigatecms_loader"></figure>\');
1014                                                    navigate_dropbox_load_video("'.$name.'", "vimeo#" + reference);
1015                                                }
1016                                                $(this).dialog("close");
1017                                            },
1018                                            "'.t(58, "Cancel").'": function() { $(this).dialog("close"); }
1019                                        }
1020                                    });
1021                                    break;
1022                            }
1023                        }
1024                    );
1025                ');
1026
1027                $contextmenu = true;
1028            }
1029
1030			$layout->add_script('
1031				$("#'.$name.'-droppable").parent().find(".navigate-droppable-cancel").on("click", function()
1032				{
1033					$("#'.$name.'").val("0");
1034					$("#'.$name.'-droppable").html(\'<img src="img/icons/misc/dropbox.png" vspace="18" />\');
1035					$("#'.$name.'-droppable").parent().find(".navigate-droppable-cancel").hide();
1036					$("#'.$name.'-droppable").parent().find(".navigate-droppable-create").show();
1037					$("#'.$name.'-droppable-info").children().html("");
1038					navigate_media_browser_refresh_files_used();
1039				});
1040
1041				$("#'.$name.'-droppable").parent().find(".navigate-droppable-create").on("click", function(ev)
1042				{
1043                    navigate_hide_context_menus();
1044                    $("ul[data-context-menu-temporary-clone=true]").remove();
1045
1046                    setTimeout(function()
1047                    {
1048                        var menu_el = $("#'.$name.'-droppable").parent().find(".navigate-droppable-create-contextmenu");
1049
1050                        menu_el.menu();
1051
1052                        menu_el.css({
1053                            "z-index": 100000,
1054                            "position": "absolute"
1055                        }).addClass("navi-ui-widget-shadow").show();
1056                    }, 100);
1057				});
1058			');
1059			
1060			if(!empty($media))
1061				$accept = 'accept: ".draggable-'.$media.'",';
1062							
1063			$layout->add_script('
1064				$("#'.$name.'-droppable").droppable(
1065				{
1066					'.$accept.'
1067					hoverClass: "navigate-droppable-hover",
1068					drop: function(event, ui) 
1069					{
1070						var file_id = $(ui.draggable).attr("id").substring(5);
1071						$("#'.$name.'").val(file_id);
1072						var draggable_content = $(ui.draggable);
1073
1074						if($(draggable_content).find(".file-image-wrapper").length > 0)
1075						{
1076						    draggable_content = $(draggable_content).find(".file-image-wrapper").html();
1077                        }
1078                        else
1079                        {
1080                            draggable_content = $(ui.draggable).html();
1081                        }
1082
1083						$(this).html(draggable_content);
1084						$(this).find("div.file-access-icons").remove();
1085
1086						$("#'.$name.'-droppable").parent().find(".navigate-droppable-cancel").show();
1087					    $("#'.$name.'-droppable").parent().find(".navigate-droppable-create").hide();
1088                        $("#'.$name.'-droppable-info").find(".navigate-droppable-info-title").html("");
1089                        $("#'.$name.'-droppable-info").find(".navigate-droppable-info-provider").html("");
1090                        $("#'.$name.'-droppable-info").find(".navigate-droppable-info-extra").html("");
1091					}
1092				});
1093			');
1094
1095            if(empty($value) && $contextmenu)
1096            {
1097                $layout->add_script('
1098                    $("#'.$name.'-droppable").parent().find(".navigate-droppable-create").show();
1099                    $("#'.$name.'-droppable").parent().find(".navigate-droppable-cancel").hide();
1100                ');
1101            }
1102            else if(!empty($value))
1103            {
1104                $layout->add_script('
1105                    $("#'.$name.'-droppable").parent().find(".navigate-droppable-cancel").show();
1106                    $("#'.$name.'-droppable").parent().find(".navigate-droppable-create").hide();
1107                ');
1108            }
1109
1110		}
1111
1112        $out[] = '<div id="'.$name.'-droppable-info" class="navigate-droppable-info">';
1113        $out[] = '  <div class="navigate-droppable-info-title"></div>';
1114        $out[] = '  <div class="navigate-droppable-info-extra"></div>';
1115        $out[] = '  <div class="navigate-droppable-info-provider"></div>';
1116        $out[] = '</div>';
1117
1118        $out[] = '</div>'; // close droppable wrapper
1119				
1120		return implode("\n", $out);
1121	}
1122
1123    public function dropdown_tree($id, $tree, $selected_value="", $on_change="")
1124    {
1125        global $layout;
1126
1127        $out = array();
1128
1129        // TODO: check available dropdown_tree extensions or just use the default
1130        $out[] = '<input type="hidden" id="'.$id.'" name="'.$id.'" value="'.$selected_value.'" />';
1131
1132        $path = "";
1133
1134        $out[] = '<input type="text" id="tree_path_'.$id.'" value="'.$path.'" readonly="true" />';
1135        $out[] = '<img src="img/icons/silk/erase.png" width="16" height="16" align="absmiddle"'.
1136					 'style=" cursor: pointer; " onclick=" tree_wrapper_'.md5($id).'_reset(); " />';
1137
1138        if(!empty($on_change))
1139            $on_change .= '(value);';
1140
1141        if(empty($tree))
1142            $tree = '<ul><li value="0">&nbsp;</li></ul>';
1143
1144        $out[] = '<div style="float: left;" id="tree_wrapper_'.$id.'">'.$tree.'</div>';
1145
1146        $layout->add_script('
1147            $("#tree_wrapper_'.$id.' span").wrap("<a>").css("cursor", "pointer");
1148            $("#tree_wrapper_'.$id.' ul:first").menu({
1149                select: function(event, ui)
1150                {
1151                    var value = $(ui.item).attr("value");
1152
1153                    if($(ui.item).find("div:first").hasClass("ui-state-disabled"))
1154                        value = $("#'.$id.'").val();
1155
1156                    $("#'.$id.'").val(value);
1157                    tree_wrapper_'.md5($id).'_path(val…

Large files files are truncated, but you can click here to view the full file