【Javascript】FC2のプラグインを改造してみよー(続編)
2014.08.06 12:56 | コメント(0) | 技術
この記事は最終更新日から1年以上が経過しています。
さつきさんですぜーい。
前回書かせていただいたFC2のプラグインを改造してみよーですが、
あのあとさらに改良してみました。
主な改良点は、
- XSS対策(根本的な対策ではないです)
- 各要素のデザインをJSONで内包
- 何度も使用する関数をカプセル化
くらいですかねー。
最終的に表示される内容は前回と変わってません。
もくじ
XSS対策
※間違った解釈でしたらご指摘ください。
まず、XSSってなんだ。
- XSS(Cross Site Scripting):くろすさいとすくりぷてぃんぐ
- 入力フォームなんかに特定のスクリプトを介在させ、POST時にスクリプトを実行させること。
ユーザが動的にフォームに入力することでスクリプトが実行されるため、例えば、会員制サイトなどで、IDや平文のパスワードを抜き出したり、cookie情報をごっそり持ち出したりすることができる。
これは、入力フォームに入力されたスクリプトコードをエスケープできていないために生じる。現状のほとんどのサイトはXSSへの対策を行っている・・・はず。
なお、「XSS」と省略するのは、「CSS」と略してしまうと、CSS(Cascading Style Sheets)と区別がつかなくなるため。
XSSの例)
XSS対策がされていない入力フォームに、"<script type="text/javascript">alert('Hello, World');</scrript>
"と入力すると、POST後のページで「Hello, World」というポップアップが表示される。
はい、怖いですねー。
あなたのサイトは大丈夫ですか?
まあ、こういったことは基本的にサーバサイド(アプリケーション内)で設定するものですが、
なんかブラウザでも個別に対策してるのがあるっぽい。
前回の記事で私が嘆いていたXSSエラーはどうやらブラウザの問題だったみたいでした。
エラー内容は、特定のJavascriptの記述(関数を定義するなど)を行った場合、
The XSS Auditor refused to execute a scriptと、コンソールにエラーが出ておりました。
内容は見ての通り、「XSSぽいんでスクリプトの実行を拒否しまっせ」ですね。
どうやら、これはプレビュー表示をしたときのみ起こる模様。
(設定反映後の通常ページでは問題なく表示される。)
ふむ、本番ページとプレビューページの違いってなんだろう。
そうですね、リクエスト方法です。
本番ページはGETであるのに対し、プレビューページはPOST形式です。
プレビューページでは、今触ってるプラグインのデータをPOSTし、擬似的にページに当てはめた結果を見せてるので、いろんなところで不具合が出てます。
おそらく、今回怒られた内容は、HTMLコードの中にJavascriptの記述を紛れ込ませたからかなと。
私がプレビュー機能を利用してなんか悪いことをしようと企んでいるぞ!とブラウザは判別したようです。
さて、じゃあどうしよう。
とりあえず、各ブラウザで動くかどうか判別。
ブラウザ | バージョン | 結果 |
---|---|---|
Internet Explorer | 11 | ○ |
Fire Fox | 31.0 | ○ |
Google Chrome | 36.0.1985.125 m | × |
お・・・w
Chromeさんダメじゃん。
IEの旧バージョンまでは見てません。仕事以外でそこまで面倒見れませんぬ。
んーFFなんかで見ると問題ないとこを見るとやっぱりブラウザの独自実装みたいですねー。
さて、じゃあどうしよう。
Chromeで見たらプレビューでは何も表示されません!じゃさすがに悲しい。
とりあえずこんな風に見えるよってことだけ伝えられたらいいかなーということで、
サンプルコードを埋め込んでみました。
Chromeでプレビューするとこんな感じに見えます。
ソースコードは後程ー。
あとはざっくり行きます。
各要素のデザインをJSONで内包
もうCSS使わずにプラグインの記述内でデザインできるようにしました。
使いやすいかは微妙だけど、CSSを別に作成する手間は省けますね。
何度も使用する関数をカプセル化
Javascriptのエラーを吐く原因がブラウザ側のXSS対策ということが分かったので、バカっぽい冗長なコードはやめました。
それでは、変更後のコードを載せますね。
コードを表示コードを隠す
/* サンプルのHTMLコードを埋め込んでおきます。
* Javascriptが動かない場合は、下記の内容が表示されます。 */
<p id="archive_sample" style="font-weight: bold; color:#f00; font-size: 90%;">
※お使いのブラウザでは、プレビュー画面に正しい内容を表示できません。<br />
現在、サンプルを表示しています。<br />
正しい内容は設定後に反映されます。
</p>
<table id="archive" style="padding: auto 2px; width: 90%; margin: auto; text-align: center;">
<tbody>
<tr>
<th colspan="6" style="font-size: 110%; padding-top: 10px;"><strong>YYYY年(n)</strong></th>
</tr>
<tr>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">12</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">11</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">10</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">09</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">08</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">07</span></td>
</tr>
<tr>
<td><span style="color: #828282;" title="YYYY年MM月(0)">06</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">05</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">04</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">03</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">02</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">01</a></td>
</tr>
<tbody>
<tr>
<th colspan="6" style="font-size: 110%; padding-top: 10px;"><strong>YYYY年(n)</strong></th>
</tr>
<tr>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">12</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">11</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">10</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">09</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">08</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">07</span></td>
</tr>
<tr>
<td><span style="color: #828282;" title="YYYY年MM月(0)">06</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">05</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">04</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">03</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">02</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">01</a></td>
</tr>
</tbody>
</table>
<script type="text/javascript">
/* CSS Values.
* Change your own layouts.
* Set scripts will start at line:163~ */
/* テーブル全体のレイアウトを設定。*/
var table_style_hash = {
'padding' : 'auto 2px',
'width' : '90%',
'margin' : 'auto',
'text-align': 'center',
}
/* 年を表示しているセルのレイアウトを設定。 */
var year_cell_hash = {
'padding-top': '10px',
'font-size' : '110%'
}
/* 月を表示しているセルのレイアウトを設定。 */
var month_link_hash = {
'font-weight': 'bold',
'color' : '#9f7349'
}
/* 記事が1件もない月のレイアウトを設定。 */
var inactive_hash = { 'color': '#828282' };
/* 設定したレイアウトの配列。
* 多重配列になってます。後ろの方でこの配列を使います。 */
var styles = [ table_style_hash, year_cell_hash,
month_link_hash, inactive_hash ];
/* 月を表示するセルを作成する関数。
* 外部からは「年」が渡ってきます。 */
function setYear(year) {
var year_id = 'archive_' + year;
var year_cell = $('#' + year_id);
/* 年を表示するセルが既にあるかどうかを判別。
* まだない場合だけ作成します。 */
if ( year_cell.length == 0 ) {
parent.append(
$('<tr>')
.attr('id', year_id)
.append(
$('<th>')
.attr('colspan', 6)
.append(year + '年')
)
);
}
}
/* 月を表示するセルを作成する関数。
* 外部からは「年」、「月」、「記事があるかどうか」が渡ってきます。 */
function setMonths(year, month, active) {
/* 月を表示するセルの親を判別します。
* 下半期なら上段、上半期なら下段となるように設定します。 */
if ( month > 6 ) {
var target_id = year + '_u';
} else {
var target_id = year + '_l';
}
/* 月を表示するセルの親があるかどうかを判別します。
* まだない場合は作成します。 */
var target = $('#' + target_id);
if ( target.length == 0 ) {
parent.append(
$('<tr>')
.attr('id', target_id)
);
var target = $('#' + target_id);
}
/* 対象の月に記事があるかないかを判別します。
* 記事がある場合はその月の記事を表示するページへのリンクを設置します。 */
if ( active ) {
target.append(
$('<td>').append(
$('<a>')
.attr('href', url)
/* タイトルを設定します。マウスオーバーした際に表示される内容です。 */
.attr('title', year + '年' + month + '月(' + count + ')')
.append( ('0' + month).slice(-2) ) /* 月を一律で2ケタ表示にします。 */
)
);
} else {
target.append(
$('<td>').addClass('inactive').append(
$('<span>')
/* タイトルを設定します。マウスオーバーした際に表示される内容です。 */
.attr('title', year + '年' + month + '月(0)')
.append( ('0' + month).slice(-2) ) /* 月を一律で2ケタ表示にします。 */
)
);
}
}
var parent = $('table#archive'); /* 月別アーカイブの親を指定します。 */
var prev_month = 12; /* 比較月の初期値を設定します。後で使います。 */
/* サンプルのHTMLコードを削除します。XSS対策により、エラーが起こるブラウザでは、
* スクリプトが実行されないため、サンプルコードが削除されることなく残ります。 */
parent.empty();
$('#archive_sample').remove();
<!--archive--> /* 月別アーカイブループ開始 */
/* 「年」、「月」、「月の記事数」、
* 「月の記事を表示するページのURL」を変数にセットします。 */
var year = <%archive_year>;
var month = parseInt('<%archive_month>');
var count = <%archive_count>;
var url = '<%archive_link>';
/* 年を表示するセルを作成します。 */
setYear(year);
/* 月を表示するセルをどのように作成するか、条件分岐させます。
* (Swtich文の方が良かったな・・・。もうめんどくさいので直しません。)
* 対象月より、比較月の値の方が大きいとき: 対象月まで月を表示するセルを作成します。
* 対象月より、比較月の値の方が小さいとき: 前の年(ループは降順なので、厳密には次の年)
* の1月まで月を表示するセルを作成します。 */
if ( month < prev_month ) {
while ( month < prev_month ) {
setMonths(year, prev_month, false);
prev_month -= 1;
}
} else {
if ( month != prev_month ) {
/* これは前の年(次の年)の処理 */
while ( 1 <= prev_month ) {
setMonths(year + 1, prev_month, false);
prev_month -= 1;
}
prev_month = 12;
/* ここから今年の処理 */
if ( month < prev_month ) {
while ( month < prev_month ) {
setMonths(year, prev_month, false);
prev_month -= 1;
}
}
}
}
/* 対象月を表示するセルを作成します。 */
setMonths(year, month, true);
prev_month -= 1;
<!--/archive--> /* アーカイブループ終了。 */
/* この時点では一番古い年が12ヶ月表示されていない可能性があるので、
* まだ表示されていない月のセルを作成します。 */
/* 月別アーカイブの中の最後の要素を取得します。 */
var last_child = $('table#archive tr:last-child');
/* 上記要素のIDから年を取得します。 */
var target_year = last_child.attr('id').replace(/(_u|_d)/, '');
/* すでに作成済みの月セルがいくつあるか確認します。 */
var fill_length = $('#' + target_year + '_u td').length + $('#' + target_year
+ '_d td').length;
/* 上記で取得した数を12から引いて不足分を確認します。 */
var remain_month = 12 - fill_length;
/* 不足している月を表示するセルを作成します。 */
while ( 0 < remain_month ) {
setMonths(target_year, remain_month, false);
remain_month -= 1;
}
/* 年を表示するセルに、年間の記事総数を表示します。 */
var archives = $('table#archive tr[id*="archive_"]');
$.each(archives, function(idx, elem) {
/* 「elem」の返り値はObjectクラスではないので、
* jQueryの「.attr(key, value)」プロパティは使用できません。 */
var year = elem.getAttribute('id').replace('archive_', '');
var count = 0; /* 年間記事総数の初期値を設定します。 */
$.each(['_u', '_l'], function(i, str) {
var parent = $('#' + year + str);
var children = parent.find('td');
$.each(children, function(j, child) {
/* 「child」の返り値は月を表示するセル単体です。
* 「child」の最初の子要素に設定されているtitle属性の後ろから2番目の1文字を
* 抜き出して、整数クラスに置き換えています。少し頭が悪い書き方です。 */
var m_count = parseInt(child.children[0].getAttribute('title').slice(-2, -1));
count = count + m_count;
})
})
elem.children[0].innerHTML += '(' + count + ')';
})
/* 最後に各要素にスタイルを当てていきます。
* 各要素のプロパティリストと、実行スクリプトを離した理由は、
* プロパティの設定だけ簡単に触ってもらえたらなあという理由です。
* こっちはあんまり触らないかな? */
var year_cells = $('table#archive th');
var month_links = $('table#archive td a');
var inactives = $('table#archive .inactive');
var elements = {
0: parent,
1: year_cells,
2: month_links,
3: inactives
}
$.each(styles, function(i, hash) {
var targets = elements[i];
$.each(hash, function(key, value) {
targets.css(key, value);
})
});
</script>
コードを隠す
コメントアウトなし版はこちら閉じる
<p id="archive_sample" style="font-weight: bold; color:#f00; font-size: 90%;">
※お使いのブラウザでは、プレビュー画面に正しい内容を表示できません。<br />
現在、サンプルを表示しています。<br />
正しい内容は設定後に反映されます。
</p>
<table id="archive" style="padding: auto 2px; width: 90%; margin: auto; text-align: center;">
<tbody>
<tr>
<th colspan="6" style="font-size: 110%; padding-top: 10px;"><strong>YYYY年(n)</strong></th>
</tr>
<tr>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">12</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">11</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">10</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">09</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">08</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">07</span></td>
</tr>
<tr>
<td><span style="color: #828282;" title="YYYY年MM月(0)">06</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">05</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">04</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">03</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">02</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">01</a></td>
</tr>
<tbody>
<tr>
<th colspan="6" style="font-size: 110%; padding-top: 10px;"><strong>YYYY年(n)</strong></th>
</tr>
<tr>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">12</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">11</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">10</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">09</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">08</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">07</span></td>
</tr>
<tr>
<td><span style="color: #828282;" title="YYYY年MM月(0)">06</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">05</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">04</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">03</a></td>
<td><span style="color: #828282;" title="YYYY年MM月(0)">02</span></td>
<td><a href="javascript:;" style="font-weight: bold; color: #9f7349;" title="YYYY年MM月(n)">01</a></td>
</tr>
</tbody>
</table>
<script type="text/javascript">
/* CSS Values.
* Change your own layouts.
* Set scripts will start at line:163~ */
var table_style_hash = {
'padding' : 'auto 2px',
'width' : '90%',
'margin' : 'auto',
'text-align': 'center',
}
var year_cell_hash = {
'padding-top': '10px',
'font-size' : '110%'
}
var month_link_hash = {
'font-weight': 'bold',
'color' : '#9f7349'
}
var inactive_hash = { 'color': '#828282' };
var styles = [ table_style_hash, year_cell_hash,
month_link_hash, inactive_hash ];
function setYear(year) {
var year_id = 'archive_' + year;
var year_cell = $('#' + year_id);
if ( year_cell.length == 0 ) {
parent.append(
$('<tr>')
.attr('id', year_id)
.append(
$('<th>')
.attr('colspan', 6)
.append(year + '年')
)
);
}
}
function setMonths(year, month, active) {
if ( month > 6 ) {
var target_id = year + '_u';
} else {
var target_id = year + '_l';
}
var target = $('#' + target_id);
if ( target.length == 0 ) {
parent.append(
$('<tr>')
.attr('id', target_id)
);
var target = $('#' + target_id);
}
if ( active ) {
target.append(
$('<td>').append(
$('<a>')
.attr('href', url)
.attr('title', year + '年' + month + '月(' + count + ')')
.append( ('0' + month).slice(-2) )
)
);
} else {
target.append(
$('<td>').addClass('inactive').append(
$('<span>')
.attr('title', year + '年' + month + '月(0)')
.append( ('0' + month).slice(-2) )
)
);
}
}
var parent = $('table#archive');
var prev_month = 12;
parent.empty();
$('#archive_sample').remove();
<!--archive-->
var year = <%archive_year>;
var month = parseInt('<%archive_month>');
var count = <%archive_count>;
var url = '<%archive_link>';
setYear(year);
if ( month < prev_month ) {
while ( month < prev_month ) {
setMonths(year, prev_month, false);
prev_month -= 1;
}
} else {
if ( month != prev_month ) {
while ( 1 <= prev_month ) {
setMonths(year + 1, prev_month, false);
prev_month -= 1;
}
prev_month = 12;
if ( month < prev_month ) {
while ( month < prev_month ) {
setMonths(year, prev_month, false);
prev_month -= 1;
}
}
}
}
setMonths(year, month, true);
prev_month -= 1;
<!--/archive-->
var last_child = $('table#archive tr:last-child');
var target_year = last_child.attr('id').replace(/(_u|_d)/, '');
var fill_length = $('#' + target_year + '_u td').length + $('#' + target_year
+ '_d td').length;
var remain_month = 12 - fill_length;
while ( 0 < remain_month ) {
setMonths(target_year, remain_month, false);
remain_month -= 1;
}
var archives = $('table#archive tr[id*="archive_"]');
$.each(archives, function(idx, elem) {
var year = elem.getAttribute('id').replace('archive_', '');
var count = 0;
$.each(['_u', '_l'], function(i, str) {
var parent = $('#' + year + str);
var children = parent.find('td');
$.each(children, function(j, child) {
var m_count = parseInt(child.children[0].getAttribute('title').slice(-2, -1));
count = count + m_count;
})
})
elem.children[0].innerHTML += '(' + count + ')';
})
var year_cells = $('table#archive th');
var month_links = $('table#archive td a');
var inactives = $('table#archive .inactive');
var elements = {
0: parent,
1: year_cells,
2: month_links,
3: inactives
}
$.each(styles, function(i, hash) {
var targets = elements[i];
$.each(hash, function(key, value) {
targets.css(key, value);
})
});
</script>
閉じる
というわけで、少し直してみました。
個人的なもやもやはだいぶ解消しましたw
やっぱり気になったことはそのまんま放置はいけないね!
0いいね!
(´・ω・`)何度も押してくれて嬉しいけど、一回きりなんだ。
(´・ω・`)ごめんね?
- 技術の記事
-
- 【Javascript】プラグインまたちょっくら触ってみた
- 【Apache・DB】夏だから不思議なエラーのお話
- 【Javascript】FC2のプラグインを改造してみよー(続編)
- 【Javascript】FC2のプラグインを改造してみよー
- 【Rails】helperさんのおかしな挙動
まだコメントがありません...(´・ω・`)