Есть форма отправки файлов, через iframe. по окончании загрузки этого iframe берется его содержимое. Его нужно преобразовать в JSON. Простой eval('('+ данные из iftame + ')') подходит, но только для очень примитивных объектов. Когда в одном из них хранится html, преобразовать в JSON не получается. Я решил делать немного по-другому. Выводить в iframe не просто JSON а его base64 строку. Потом ее декодировать и сделать eval. Из недостатков - отсутствие нормальной поддержки кириллицы. Тогда перед тем как делать на сервере base64 encode я конвертирую строку из win1251 в utf8. На клиентской стороне это дело дешифруется и декодируется. Внимание вопрос: можно ли это сделать по-другому, без плясок с бубном как сейчас?
Делал я как-то аякс-интерфейс добавления объявления о продаже автомобиля, вот кусок: [js] new Ajax_upload('#car-foto-upload1', { action: '/system/php/document8110.php?uploadcarfoto=1&edit=' + document.getElementById('OnEdit').value, name: 'foto', onSubmit: function(file, extension) { }, onComplete: function(file, response) { //$('#debug').html(response); var resp = eval('(' + response + ')'); var fotoId = resp['id']; $("#car-small-foto1").attr('src', '/files/8088/small_sale_' + fotoId + '_foto.jpg?' + Math.round(Math.random()*100000)); $("#showFoto1").attr('href', '/files/8088/sale_' + fotoId + '_foto.jpg?' + Math.round(Math.random()*100000)); $("#car-foto-upload1").attr('value','Изменить...'); $("#showFoto1").attr('alt', "Просмотреть"); $("#saleId").attr('value', fotoId); $("#car-foto-upload2").attr('disabled',0); } } );[/js] В response, как видно, забирается ответ сервера на загрузку. Про функцию json_encode я незнал (а может её еще небыло тогда), поэтому написал класс для формирования JSON, все работало нормально, без разницы - с html в ответе или без.
у меня на сервере почти json_encode. Почти потому что стандартный неплохо так обрезал часть информации, поэтому взял json_encode из комментов. ну а у меня сейчас: [js] // ... if(o.files) { o.onBeforeSend.call(); // это мы создаем фрейм, если его нет if($('#jqfu').html()==null) $('<iframe id="jqfu" name="jqfu" src="#"></iframe>').appendTo('body'); $('#jqfu').hide(); // меняем target у формы на фрейм form.attr('target', 'jqfu') // назначаем что делать после того как фрейм загрузится $('#jqfu').load(function() { var json = $('#jqfu').contents().find('body').html(); // если убрать Base64.decode то все посыпется json = '(' + Base64.decode(json) + ')'; //console.log(json); json = eval(json); //console.log(json); o.onSuccess.call(null, json); }); // возвращаем true для того что бы произошла отправка формы return true; } // ... [/js]
...забыл ajax_upload приложить, его в самом jQuery нету: [js]/** * Ajax upload plugin for jQuery * Project page - http://valums.com/ajax-upload/ * Copyright (c) 2008 Andris Valums, http://valums.com * Licensed under the MIT license (http://valums.com/mit-license/) */ (function($){ // we need jQuery to run if ( ! $) return; /** * Function generates unique id */ var get_uid = function(){ var uid = 0; return function(){ return uid++; } }(); /** * button - jQuery element * option - hash of options : * action - URL which iframe will use to post data * name - file input name * data - extra data hash to post within the file * onSubmit - callback to fire on file submit * onComplete - callback to fire when iframe has finished loading */ window.Ajax_upload = function(button, options){ // make sure it is jquery object button = $(button); if (button.size() != 1 ){ console.error('You passed ', button.size(),' elements to ajax_upload at once'); return false; } this.button = button; this.wrapper = null; this.form = null; this.input = null; this.iframe = null; this.disabled = false; this.submitting = false; this.settings = { // Location of the server-side upload script action: 'upload.php', // File upload name name: 'userfile', // Additional data to send data: {}, // Callback to fire when user selects file // You can return false to cancel upload onSubmit: function(file, extension) {}, // Fired when file upload is completed onComplete: function(file, response) {} }; // Merge the users options with our defaults $.extend(this.settings, options); this.create_wrapper(); this.create_input(); if (jQuery.browser.msie){ // fix ie transparent background bug this.make_parent_opaque(); } this.create_iframe(); } // assigning methods to our class Ajax_upload.prototype = { set_data : function(data){ this.settings.data = data; }, disable : function(){ this.disabled = true; if ( ! this.submitting){ this.input.attr('disabled', true); this.button.removeClass('hover'); } }, enable : function(){ this.disabled = false; this.input.attr('disabled', false); }, /** * Creates wrapper for button and invisible file input */ create_wrapper : function(){ // Shorten names var button = this.button, wrapper; wrapper = this.wrapper = $('<div></div>') .insertAfter(button) .append(button); // wait a bit because of FF bug // it can't properly calculate the outerHeight setTimeout(function(){ wrapper.css({ position: 'relative' ,display: 'block' ,overflow: 'hidden' // we need dimensions because of ie bug that allows to move // input outside even if overflow set to hidden /*,height: button.outerHeight(true) ,width: button.outerWidth(true)*/ }); }, 1); var self = this; wrapper.mousemove(function(e){ // Move the input with the mouse, so the user can't misclick it if (!self.input) { return; } self.input.css({ top: e.pageY - wrapper.offset().top - 5 + 'px' ,left: e.pageX - wrapper.offset().left - 150 + 'px' }); }); }, /** * Creates invisible file input above the button */ create_input : function(){ var self = this; this.input = $('<input type="file" />') .attr('name', this.settings.name) .css({ 'position' : 'absolute' ,'margin': 0 ,'padding': 0 ,'width': '200px' ,'height': '10px' ,'opacity': 0 }) .change(function(){ if ($(this).val() == ''){ // there is no file return; } // we need to lock "disable" method self.submitting = true; // Submit form when value is changed self.submit(); if (self.disabled){ self.disable(); } // unlock "disable" method self.submitting = false; }) .appendTo(this.wrapper) // Emulate button hover effect .hover( function(){self.button.addClass('hover');} ,function(){self.button.removeClass('hover');} ); if (this.disabled){ this.input.attr('disabled', true); } }, /** * Creates iframe with unique name */ create_iframe : function(){ // unique name // We cannot use getTime, because it sometimes return // same value in safari var id = 'valums97hhu' + get_uid(); // create iframe, so we dont need to refresh page this.iframe = $('<iframe id="' + id + '" name="' + id + '"></iframe>') .css('display', 'none') .appendTo('body'); }, /** * Upload file without refreshing the page */ submit : function(){ var self = this, settings = this.settings; // get filename from input var file = this.file_from_path(this.input.val()); // execute user event if (settings.onSubmit.call(this, file, this.get_ext(file)) === false){ // Do not continue if user function returns false return; } this.create_form(); this.input.appendTo(this.form); this.form.submit(); this.input.remove(); this.input = null; this.form.remove(); this.form = null; this.submitting = false; // create new input this.create_input(); var iframe = this.iframe; iframe.load(function(){ var response = iframe.contents().find('body').html(); settings.onComplete.call(self, file, response); /// Workaround for FF2 bug, which causes cursor to be in busy state after post. setTimeout(function(){ iframe.remove(); }, 1); }); // Create new iframe, so we can have multiple uploads at once this.create_iframe(); }, /** * Creates form, that will be submitted to iframe */ create_form : function(){ // method, enctype must be specified here // because changing this attr on the fly is not allowed in IE 6/7 this.form = $('<form method="post" enctype="multipart/form-data"></form>') .attr({ "action" : this.settings.action ,"target" : this.iframe.attr('name') }) .appendTo('body'); // Create hidden input element for each data key for (var i in this.settings.data){ $('<input type="hidden" />') .appendTo(this.form) .attr({ 'name': i ,'value': this.settings.data }); } }, file_from_path : function(file){ return file.replace(/.*(\/|\\)/, ""); }, get_ext : function(file){ return (/[.]/.exec(file)) ? /[^.]+$/.exec(file.toLowerCase()) : ''; }, make_parent_opaque : function(){ // ie transparent background bug this.button.add(this.button.parents()).each(function(){ var color = $(this).css('backgroundColor'); var image = $(this).css('backgroundImage'); if ( color != 'transparent' || image != 'none'){ $(this).css('opacity', 1); return false; } }); } }; })(jQuery);[/js] Этот плагин всё делает за тебя
та я про этот ajax-upload в курсе, по тексту, увидел из плагина: var response = iframe.contents().find('body').html(); то, что я писал: var json = $('#jqfu').contents().find('body').html(); бу-го-го, дык действия что у меня что у чувака-создателя плагина идентичные.
Koc У меня подозрение, что JSON-строка с объектами, включающими HTML, просто некорректно генерится, потому и в eval не выполняется.
если отсылать через jquery.ajax и принимать тип json то все ок. А вот когда берем содержимое iframe между тегами body - получаются проблемы. вот ф-ция json которой я кодирую. PHP: <?php function json($a = false) { if(is_null($a)) return 'null'; if($a === false) return 'false'; if($a === true) return 'true'; if(is_scalar($a)) { if(is_float($a)) { // Always use "." for floats. return floatval(str_replace(",", ".", strval($a))); } if(is_string($a)) { static $jsonReplaces = array(array("\\", "/", "\n", "\t", "\r", "\b", "\f", '"'), array('\\\\', '\\/', '\\n', '\\t', '\\r', '\\b', '\\f', '\"')); return '"' . str_replace($jsonReplaces[0], $jsonReplaces[1], $a) . '"'; } else return $a; } $isList = true; for($i = 0, reset($a); $i < count($a); $i++, next($a)) { if(key($a) !== $i) { $isList = false; break; } } $result = array(); if($isList) { foreach($a as $v) $result[] = json($v); return '[' . join(',', $result) . ']'; } else { foreach($a as $k => $v) $result[] = json($k) . ':' . json($v); return '{' . join(',', $result) . '}'; } } ?>